ape-claw 0.1.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 (114) hide show
  1. package/.cursor/skills/ape-claw/SKILL.md +322 -0
  2. package/LICENSE +21 -0
  3. package/README.md +826 -0
  4. package/allowlists/opensea-slug-overrides.json +13 -0
  5. package/allowlists/recommended.apechain.json +322 -0
  6. package/config/clawbots.example.json +3 -0
  7. package/config/policy.example.json +27 -0
  8. package/data/starter-pack-bundle.json +1 -0
  9. package/data/starter-pack.json +495 -0
  10. package/docs/ACP_BOUNTIES.md +108 -0
  11. package/docs/APECLAW_V2_ALPHA.md +206 -0
  12. package/docs/AUTONOMY_AND_SUBSTRATE.md +69 -0
  13. package/docs/CLAWBOTS_AND_INVITES.md +102 -0
  14. package/docs/CLI_GUIDE.md +124 -0
  15. package/docs/CONTRIBUTING.md +130 -0
  16. package/docs/DASHBOARD_GUIDE.md +108 -0
  17. package/docs/GLOBAL_BACKEND.md +145 -0
  18. package/docs/ONCHAIN_V2_GUIDE.md +140 -0
  19. package/docs/PRODUCT_OVERVIEW.md +127 -0
  20. package/docs/README.md +40 -0
  21. package/docs/SKILLCARDS_AND_IMPORTER.md +147 -0
  22. package/docs/STARTER_PACK.md +297 -0
  23. package/docs/SUPPORTED_NETWORKS.md +58 -0
  24. package/docs/TELEMETRY_AND_EVENTS.md +103 -0
  25. package/docs/THE_POD_RUNNER.md +198 -0
  26. package/docs/V1_WORKFLOWS.md +108 -0
  27. package/docs/V2_ONCHAIN_SKILLS.md +157 -0
  28. package/docs/WEB4_PLAN_STATUS.md +95 -0
  29. package/docs/WEB4_SWARM_MODEL.md +104 -0
  30. package/docs/archive/AUTONOMY_AND_SUBSTRATE.md +66 -0
  31. package/docs/archive/WEB4_PLAN_STATUS.md +93 -0
  32. package/docs/archive/WEB4_SWARM_MODEL.md +98 -0
  33. package/docs/developer/01-architecture.md +345 -0
  34. package/docs/developer/02-contracts.md +1034 -0
  35. package/docs/developer/03-writing-modules.md +513 -0
  36. package/docs/developer/04-skillcard-spec.md +336 -0
  37. package/docs/developer/05-backend-api.md +1079 -0
  38. package/docs/developer/06-telemetry.md +798 -0
  39. package/docs/developer/07-testing.md +546 -0
  40. package/docs/developer/08-contributing.md +211 -0
  41. package/docs/operator/01-quickstart.md +49 -0
  42. package/docs/operator/02-dashboard.md +174 -0
  43. package/docs/operator/03-cli-reference.md +818 -0
  44. package/docs/operator/04-skills-library.md +169 -0
  45. package/docs/operator/05-pod-operations.md +314 -0
  46. package/docs/operator/06-deployment.md +299 -0
  47. package/docs/operator/07-safety-and-policy.md +311 -0
  48. package/docs/operator/08-troubleshooting.md +457 -0
  49. package/docs/operator/09-env-reference.md +238 -0
  50. package/docs/social/STARTER_PACK_THREAD.md +209 -0
  51. package/package.json +77 -0
  52. package/skillcards/import-sources.json +93 -0
  53. package/skillcards/seed/acp-bounty-poll.v1.json +38 -0
  54. package/skillcards/seed/acp-bounty-post.v1.json +55 -0
  55. package/skillcards/seed/acp-browse.v1.json +41 -0
  56. package/skillcards/seed/acp-fulfill-and-route.v1.json +56 -0
  57. package/skillcards/seed/apeclaw-bridge-relay.v1.json +46 -0
  58. package/skillcards/seed/apeclaw-nft-autobuy.v1.json +60 -0
  59. package/skillcards/seed/apeclaw-receipt-recorder.v1.json +64 -0
  60. package/skillcards/seed/humanizer.v1.json +74 -0
  61. package/skillcards/seed/otherside-navigator.v1.json +116 -0
  62. package/skillcards/seed/stonkbrokers-launcher.v1.json +280 -0
  63. package/skillcards/seed/walkie-p2p.v1.json +66 -0
  64. package/src/cli/index.mjs +8 -0
  65. package/src/cli.mjs +1929 -0
  66. package/src/lib/bridge-relay.mjs +294 -0
  67. package/src/lib/clawbots.mjs +94 -0
  68. package/src/lib/io.mjs +36 -0
  69. package/src/lib/market.mjs +233 -0
  70. package/src/lib/nft-opensea.mjs +159 -0
  71. package/src/lib/paths.mjs +17 -0
  72. package/src/lib/pod-init.mjs +40 -0
  73. package/src/lib/policy.mjs +112 -0
  74. package/src/lib/rpc.mjs +49 -0
  75. package/src/lib/telemetry.mjs +92 -0
  76. package/src/lib/v2-onchain-abi.mjs +294 -0
  77. package/src/lib/v2-skillcard.mjs +27 -0
  78. package/src/server/index.mjs +169 -0
  79. package/src/server/logger.mjs +21 -0
  80. package/src/server/middleware/auth.mjs +90 -0
  81. package/src/server/middleware/body-limit.mjs +35 -0
  82. package/src/server/middleware/cors.mjs +33 -0
  83. package/src/server/middleware/rate-limit.mjs +44 -0
  84. package/src/server/routes/chat.mjs +178 -0
  85. package/src/server/routes/clawbots.mjs +182 -0
  86. package/src/server/routes/events.mjs +95 -0
  87. package/src/server/routes/health.mjs +72 -0
  88. package/src/server/routes/pod.mjs +64 -0
  89. package/src/server/routes/quotes.mjs +161 -0
  90. package/src/server/routes/skills.mjs +239 -0
  91. package/src/server/routes/static.mjs +161 -0
  92. package/src/server/routes/v2.mjs +48 -0
  93. package/src/server/sse.mjs +73 -0
  94. package/src/server/storage/file-backend.mjs +295 -0
  95. package/src/server/storage/index.mjs +37 -0
  96. package/src/server/storage/sqlite-backend.mjs +380 -0
  97. package/src/telemetry-server.mjs +1604 -0
  98. package/ui/css/dashboard.css +792 -0
  99. package/ui/css/skills.css +689 -0
  100. package/ui/docs.html +840 -0
  101. package/ui/favicon-180.png +0 -0
  102. package/ui/favicon-192.png +0 -0
  103. package/ui/favicon-32.png +0 -0
  104. package/ui/favicon-lobster.png +0 -0
  105. package/ui/favicon.svg +10 -0
  106. package/ui/index.html +2957 -0
  107. package/ui/js/dashboard.js +1766 -0
  108. package/ui/js/skills.js +1621 -0
  109. package/ui/pod.html +909 -0
  110. package/ui/shared/motion.css +286 -0
  111. package/ui/shared/motion.js +170 -0
  112. package/ui/shared/sidebar-nav.css +379 -0
  113. package/ui/shared/sidebar-nav.js +137 -0
  114. package/ui/skills.html +2879 -0
@@ -0,0 +1,546 @@
1
+ # Testing
2
+
3
+ ## Overview
4
+
5
+ ApeClaw uses a multi-layered testing approach:
6
+ - **Smart Contract Tests**: Hardhat + viem for onchain contract testing
7
+ - **CLI Tests**: Node.js built-in test runner for command-line interface testing
8
+ - **Policy Tests**: Unit tests for policy enforcement logic
9
+ - **Integration Tests**: End-to-end workflows
10
+
11
+ ## Smart Contract Tests
12
+
13
+ ### Running Tests
14
+
15
+ Run all contract tests:
16
+
17
+ ```bash
18
+ npm run contracts:test
19
+ ```
20
+
21
+ This runs Hardhat tests using the Node.js test runner. For Solidity-specific tests:
22
+
23
+ ```bash
24
+ npm run contracts:test:solidity
25
+ ```
26
+
27
+ Compile contracts first:
28
+
29
+ ```bash
30
+ npm run contracts:compile
31
+ ```
32
+
33
+ ### Test Structure
34
+
35
+ Contract tests live in `contracts-test/` directory:
36
+
37
+ - **`v2-registry.test.js`**: SkillNFT minting/ownership, SkillRegistry version publishing, PodVault royalty routing, IntentRegistry create/cancel flows, ReceiptRegistry immutable receipt recording, AgentAccount + PolicyEngine integration, module execution with policy gating
38
+ - **`policy-engine.test.js`**: PolicyEngine preCheck passes/reverts, value cap enforcement, owner-only access control
39
+
40
+ ### Writing Tests
41
+
42
+ Tests use Hardhat's viem integration with Node.js test runner:
43
+
44
+ ```javascript
45
+ import assert from "node:assert/strict";
46
+ import { describe, it } from "node:test";
47
+ import hre from "hardhat";
48
+ import { keccak256, toHex } from "viem";
49
+
50
+ const { viem } = await hre.network.connect();
51
+
52
+ describe("MyContract", () => {
53
+ it("does something", async () => {
54
+ const publicClient = await viem.getPublicClient();
55
+ const contract = await viem.deployContract("MyContract");
56
+
57
+ // Test logic here
58
+ const result = await contract.read.someFunction();
59
+ assert.equal(result, expectedValue);
60
+ });
61
+ });
62
+ ```
63
+
64
+ ### Test Patterns
65
+
66
+ #### Testing SkillNFT
67
+
68
+ ```javascript
69
+ it("mints SkillNFT and publishes immutable version", async () => {
70
+ const publicClient = await viem.getPublicClient();
71
+
72
+ const skillNft = await viem.deployContract("SkillNFT");
73
+ const registry = await viem.deployContract("SkillRegistry", [skillNft.address]);
74
+
75
+ const mintTx = await skillNft.write.mintSkill([0n]);
76
+ await publicClient.waitForTransactionReceipt({ hash: mintTx });
77
+
78
+ const owner = await skillNft.read.ownerOf([1n]);
79
+ assert.ok(owner, "owner should exist");
80
+
81
+ const versionHash = keccak256(toHex("v1.0.0"));
82
+ const contentHash = keccak256(toHex('{"name":"demo-skill","version":"1.0.0"}'));
83
+
84
+ const pubTx = await registry.write.publishVersion([
85
+ 1n,
86
+ versionHash,
87
+ contentHash,
88
+ "ipfs://example",
89
+ 1,
90
+ ]);
91
+ await publicClient.waitForTransactionReceipt({ hash: pubTx });
92
+
93
+ const count = await registry.read.versionCount([1n]);
94
+ assert.equal(count, 1n);
95
+ });
96
+ ```
97
+
98
+ #### Testing AgentAccount + PolicyEngine Integration
99
+
100
+ ```javascript
101
+ it("executes a policy-gated module call and records a receipt", async () => {
102
+ const publicClient = await viem.getPublicClient();
103
+ const [walletClient] = await viem.getWalletClients();
104
+
105
+ const receipts = await viem.deployContract("ReceiptRegistry");
106
+ const policy = await viem.deployContract("PolicyEngine", [walletClient.account.address]);
107
+ const agent = await viem.deployContract("AgentAccount", [
108
+ walletClient.account.address,
109
+ policy.address,
110
+ receipts.address
111
+ ]);
112
+
113
+ const module = await viem.deployContract("SwapModule");
114
+ const target = await viem.deployContract("MockTarget");
115
+
116
+ // Configure policy: allow module, target, and selector
117
+ await policy.write.setModuleAllowed([module.address, true]);
118
+ await policy.write.setTargetAllowed([target.address, true]);
119
+ await policy.write.setSelectorAllowed([target.address, selector, true]);
120
+
121
+ // Execute through AgentAccount
122
+ const tx = await agent.write.executeSkill([
123
+ module.address,
124
+ input,
125
+ 0n,
126
+ traceIdHash,
127
+ subjectHash,
128
+ uri
129
+ ]);
130
+ await publicClient.waitForTransactionReceipt({ hash: tx });
131
+
132
+ // Verify receipt was recorded
133
+ const ok = await receipts.read.isRecorded([traceIdHash]);
134
+ assert.equal(ok, true);
135
+
136
+ // Test fail-closed behavior: block selector and confirm it fails
137
+ await policy.write.setSelectorAllowed([target.address, selector, false]);
138
+ let threw = false;
139
+ try {
140
+ await agent.write.executeSkill([...]);
141
+ } catch (e) {
142
+ threw = true;
143
+ }
144
+ assert.equal(threw, true);
145
+ });
146
+ ```
147
+
148
+ ### Local Devnet Testing
149
+
150
+ #### Running Hardhat Node
151
+
152
+ Start a local Hardhat node:
153
+
154
+ ```bash
155
+ npx hardhat node
156
+ ```
157
+
158
+ This starts a local Ethereum node on `http://127.0.0.1:8545` with 20 pre-funded accounts.
159
+
160
+ #### Deploying with Seed Script
161
+
162
+ Deploy contracts and seed SkillCards:
163
+
164
+ ```bash
165
+ npm run contracts:seed
166
+ ```
167
+
168
+ Or explicitly target localhost:
169
+
170
+ ```bash
171
+ npx hardhat run contracts-scripts/deploy-and-seed-v2-alpha.js --network localhost
172
+ ```
173
+
174
+ The seed script:
175
+ 1. Deploys all v2 contracts (SkillNFT, SkillRegistry, PolicyEngine, AgentAccount, etc.)
176
+ 2. Configures PolicyEngine with module allowlists
177
+ 3. Reads all JSON SkillCards from `skillcards/seed/`
178
+ 4. Mints SkillNFTs and publishes versions to the registry
179
+ 5. Outputs contract addresses and deployment info to `state/v2-deployments/localhost.json`
180
+
181
+ #### Testing the Full Flow
182
+
183
+ 1. **Start local node**:
184
+ ```bash
185
+ npx hardhat node
186
+ ```
187
+
188
+ 2. **In another terminal, deploy contracts**:
189
+ ```bash
190
+ npm run contracts:seed
191
+ ```
192
+
193
+ 3. **Export contract addresses** (from seed output):
194
+ ```bash
195
+ export APE_CLAW_V2_SKILL_NFT=0x...
196
+ export APE_CLAW_V2_SKILL_REGISTRY=0x...
197
+ export APE_CLAW_V2_POLICY_ENGINE=0x...
198
+ export APE_CLAW_V2_AGENT_ACCOUNT=0x...
199
+ ```
200
+
201
+ 4. **Test CLI commands**:
202
+ ```bash
203
+ node ./src/cli.mjs doctor --json
204
+ node ./src/cli.mjs chain info --json
205
+ ```
206
+
207
+ 5. **Verify onchain state**:
208
+ ```bash
209
+ # Query SkillRegistry
210
+ node -e "
211
+ import { createPublicClient, http } from 'viem';
212
+ import { hardhat } from 'viem/chains';
213
+ const client = createPublicClient({ chain: hardhat, transport: http() });
214
+ // Query contract...
215
+ "
216
+ ```
217
+
218
+ ## CLI Tests
219
+
220
+ ### Running CLI Tests
221
+
222
+ Run all CLI tests:
223
+
224
+ ```bash
225
+ npm test
226
+ ```
227
+
228
+ This runs `hardhat compile && node --test`, which:
229
+ 1. Compiles Solidity contracts
230
+ 2. Runs all tests in `test/` directory using Node.js test runner
231
+
232
+ ### Test Files
233
+
234
+ - `test/policy.test.mjs`: Policy enforcement unit tests
235
+ - `test/cli.test.mjs`: CLI command integration tests
236
+ - `test/server.test.mjs`: Server integration tests (health, events, skills, clawbots, CORS, rate limiting, body limits, backlog, quotes auth)
237
+
238
+ ### Writing CLI Tests
239
+
240
+ CLI tests use Node.js built-in test runner:
241
+
242
+ ```javascript
243
+ import test from "node:test";
244
+ import assert from "node:assert/strict";
245
+ import { execSync } from "node:child_process";
246
+
247
+ function run(cmd) {
248
+ return execSync(cmd, { cwd: process.cwd(), encoding: "utf8" });
249
+ }
250
+
251
+ test("doctor command returns json with chainId", () => {
252
+ const out = run("node ./src/cli.mjs doctor --json");
253
+ const data = JSON.parse(out);
254
+ assert.equal(data.chainId, 33139);
255
+ assert.ok(Array.isArray(data.issues));
256
+ });
257
+ ```
258
+
259
+ ### Common Test Scenarios
260
+
261
+ #### Testing JSON Output
262
+
263
+ All commands support `--json` flag for structured output:
264
+
265
+ ```javascript
266
+ test("command returns valid JSON", () => {
267
+ const out = run("node ./src/cli.mjs doctor --json");
268
+ const data = JSON.parse(out);
269
+ assert.equal(data.ok, true);
270
+ assert.ok(typeof data.chainId === "number");
271
+ });
272
+ ```
273
+
274
+ #### Testing Error Handling
275
+
276
+ ```javascript
277
+ function runFail(cmd) {
278
+ try {
279
+ execSync(cmd, { cwd: process.cwd(), encoding: "utf8", stdio: "pipe" });
280
+ return "";
281
+ } catch (err) {
282
+ return `${err.stdout}\n${err.stderr}`.trim();
283
+ }
284
+ }
285
+
286
+ test("nft buy requires simulation first", () => {
287
+ const msg = runFail("node ./src/cli.mjs nft buy --quote q_test --execute --json");
288
+ assert.match(msg, /Simulation required before execute/);
289
+ });
290
+ ```
291
+
292
+ #### Testing Policy Enforcement
293
+
294
+ ```javascript
295
+ test("buy policy blocks disallowed currency", () => {
296
+ const res = enforceBuyPolicy({
297
+ policy,
298
+ collection: "DSNRS",
299
+ maxPrice: 10,
300
+ currency: "USDC", // Not in allowlist
301
+ allowlist: [{ name: "DSNRS" }],
302
+ });
303
+ assert.equal(res.ok, false);
304
+ });
305
+ ```
306
+
307
+ ## API Testing
308
+
309
+ ### Coverage
310
+
311
+ Run tests with `c8` code coverage:
312
+
313
+ ```bash
314
+ npm run test:coverage
315
+ ```
316
+
317
+ This generates a coverage report for CLI, policy, and server tests.
318
+
319
+ ### Telemetry Server Endpoints
320
+
321
+ The telemetry server (`src/server/index.mjs`) exposes several endpoints:
322
+
323
+ - `GET /`: Dashboard UI
324
+ - `GET /events`: Server-Sent Events stream
325
+ - `GET /events/backlog`: Historical events (JSONL)
326
+ - `GET /api/allowlist`: Collection metadata
327
+ - `POST /api/clawbots/register`: Register a clawbot
328
+ - `POST /api/clawbots/verify`: Verify clawbot token
329
+
330
+ ### Testing with curl
331
+
332
+ #### Check Server Health
333
+
334
+ ```bash
335
+ curl http://localhost:8787/
336
+ ```
337
+
338
+ #### Get Event Backlog
339
+
340
+ ```bash
341
+ curl http://localhost:8787/events/backlog | head -20
342
+ ```
343
+
344
+ #### Register Clawbot
345
+
346
+ ```bash
347
+ curl -X POST http://localhost:8787/api/clawbots/register \
348
+ -H "Content-Type: application/json" \
349
+ -d '{
350
+ "agentId": "test-bot",
351
+ "name": "Test Bot",
352
+ "api": "https://apeclaw.ai"
353
+ }'
354
+ ```
355
+
356
+ #### Verify Clawbot Token
357
+
358
+ ```bash
359
+ curl "http://localhost:8787/api/clawbots/verify?agentId=test-bot&token=claw_..."
360
+ ```
361
+
362
+ #### Stream Live Events (SSE)
363
+
364
+ ```bash
365
+ curl -N http://localhost:8787/events
366
+ ```
367
+
368
+ ### Common Test Scenarios
369
+
370
+ #### Test Event Emission
371
+
372
+ 1. Start telemetry server:
373
+ ```bash
374
+ npm run telemetry
375
+ ```
376
+
377
+ 2. Run CLI command that emits events:
378
+ ```bash
379
+ node ./src/cli.mjs doctor --json
380
+ ```
381
+
382
+ 3. Check events in backlog:
383
+ ```bash
384
+ curl http://localhost:8787/events/backlog | jq '.[-1]'
385
+ ```
386
+
387
+ #### Test SSE Stream
388
+
389
+ ```bash
390
+ # Terminal 1: Start server
391
+ npm run telemetry
392
+
393
+ # Terminal 2: Stream events
394
+ curl -N http://localhost:8787/events
395
+
396
+ # Terminal 3: Trigger event
397
+ node ./src/cli.mjs chain info --json
398
+ ```
399
+
400
+ ## Integration Testing
401
+
402
+ ### Full Flow: Deploy → Seed → Mint → Verify
403
+
404
+ 1. **Start local Hardhat node**:
405
+ ```bash
406
+ npx hardhat node
407
+ ```
408
+
409
+ 2. **Deploy contracts and seed skills**:
410
+ ```bash
411
+ npm run contracts:seed
412
+ ```
413
+
414
+ 3. **Export contract addresses** (from seed output):
415
+ ```bash
416
+ export APE_CLAW_V2_SKILL_NFT=0x...
417
+ export APE_CLAW_V2_SKILL_REGISTRY=0x...
418
+ ```
419
+
420
+ 4. **Mint a SkillNFT via CLI** (if CLI command exists):
421
+ ```bash
422
+ node ./src/cli.mjs skill mint --skillcard skillcards/seed/apeclaw-nft-autobuy.json --json
423
+ ```
424
+
425
+ 5. **Verify onchain**:
426
+ ```bash
427
+ # Query SkillRegistry for published versions
428
+ node -e "
429
+ import { createPublicClient, http } from 'viem';
430
+ import { hardhat } from 'viem/chains';
431
+ const client = createPublicClient({ chain: hardhat, transport: http() });
432
+ // Query registry...
433
+ "
434
+ ```
435
+
436
+ 6. **Verify in UI**:
437
+ - Start telemetry server: `npm run telemetry`
438
+ - Open `http://localhost:8787`
439
+ - Check that events appear in dashboard
440
+
441
+ ### Testing Quote → Simulate → Execute Flow
442
+
443
+ 1. **Create quote**:
444
+ ```bash
445
+ node ./src/cli.mjs nft quote-buy \
446
+ --collection "Mintotaurs" \
447
+ --tokenId 123 \
448
+ --maxPrice 40 \
449
+ --currency APE \
450
+ --json
451
+ ```
452
+
453
+ 2. **Simulate**:
454
+ ```bash
455
+ node ./src/cli.mjs nft simulate --quote <quoteId> --json
456
+ ```
457
+
458
+ 3. **Execute** (dry-run by default):
459
+ ```bash
460
+ node ./src/cli.mjs nft buy --quote <quoteId> --execute --json
461
+ ```
462
+
463
+ 4. **Verify telemetry events**:
464
+ ```bash
465
+ curl http://localhost:8787/events/backlog | jq '.[] | select(.eventType == "nft.buy.confirmed")'
466
+ ```
467
+
468
+ ### Negative Test Cases
469
+
470
+ Test fail-closed behavior:
471
+
472
+ - **Stale quote**: Quote expires before execution
473
+ - **Fee cap breach**: Bridge fee exceeds `maxBridgeFeeBps`
474
+ - **Disallowed collection**: Collection not in allowlist
475
+ - **Disallowed currency**: Currency not in `currencyAllowlist`
476
+ - **Price cap breach**: Price exceeds `maxPricePerTx`
477
+ - **Missing simulation**: Execute without simulate step
478
+ - **Missing confirm phrase**: Execute without confirmation
479
+
480
+ Example:
481
+
482
+ ```javascript
483
+ test("nft buy execute requires simulation first", () => {
484
+ const quote = { quoteId: "q_test", /* ... */ };
485
+ // Save quote to state/quotes.json
486
+ const msg = runFail("node ./src/cli.mjs nft buy --quote q_test --execute --json");
487
+ assert.match(msg, /Simulation required before execute/);
488
+ });
489
+ ```
490
+
491
+ ## Continuous Integration
492
+
493
+ Tests run automatically on:
494
+ - Pull requests (via GitHub Actions)
495
+ - Pre-publish hook (`prepublishOnly` script)
496
+
497
+ See `.github/workflows/ci.yml` for CI configuration.
498
+
499
+ ## Test Coverage Goals
500
+
501
+ - **Smart Contracts**: All public functions should have tests
502
+ - **Policy Engine**: All policy enforcement paths should be tested
503
+ - **CLI Commands**: All commands should have JSON output tests
504
+ - **Negative Cases**: Fail-closed behavior must be verified
505
+ - **Integration**: End-to-end flows should be tested locally
506
+
507
+ ## Debugging Tests
508
+
509
+ ### Hardhat Tests
510
+
511
+ Enable verbose logging:
512
+
513
+ ```bash
514
+ DEBUG=hardhat:* npm run contracts:test
515
+ ```
516
+
517
+ ### CLI Tests
518
+
519
+ Run a specific test file:
520
+
521
+ ```bash
522
+ node --test test/policy.test.mjs
523
+ ```
524
+
525
+ Run with verbose output:
526
+
527
+ ```bash
528
+ node --test --test-reporter=spec test/cli.test.mjs
529
+ ```
530
+
531
+ ### Local Devnet Debugging
532
+
533
+ 1. Start Hardhat node with verbose logging:
534
+ ```bash
535
+ npx hardhat node --verbose
536
+ ```
537
+
538
+ 2. In another terminal, run tests with network targeting:
539
+ ```bash
540
+ npx hardhat test --network localhost
541
+ ```
542
+
543
+ 3. Use Hardhat console to inspect state:
544
+ ```bash
545
+ npx hardhat console --network localhost
546
+ ```