thevoidforge 21.0.11 → 21.0.12

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 (107) hide show
  1. package/dist/.claude/commands/ai.md +69 -0
  2. package/dist/.claude/commands/architect.md +121 -0
  3. package/dist/.claude/commands/assemble.md +201 -0
  4. package/dist/.claude/commands/assess.md +75 -0
  5. package/dist/.claude/commands/blueprint.md +135 -0
  6. package/dist/.claude/commands/build.md +116 -0
  7. package/dist/.claude/commands/campaign.md +201 -0
  8. package/dist/.claude/commands/cultivation.md +166 -0
  9. package/dist/.claude/commands/current.md +128 -0
  10. package/dist/.claude/commands/dangerroom.md +74 -0
  11. package/dist/.claude/commands/debrief.md +178 -0
  12. package/dist/.claude/commands/deploy.md +99 -0
  13. package/dist/.claude/commands/devops.md +143 -0
  14. package/dist/.claude/commands/gauntlet.md +140 -0
  15. package/dist/.claude/commands/git.md +104 -0
  16. package/dist/.claude/commands/grow.md +146 -0
  17. package/dist/.claude/commands/imagine.md +126 -0
  18. package/dist/.claude/commands/portfolio.md +50 -0
  19. package/dist/.claude/commands/prd.md +113 -0
  20. package/dist/.claude/commands/qa.md +107 -0
  21. package/dist/.claude/commands/review.md +151 -0
  22. package/dist/.claude/commands/security.md +100 -0
  23. package/dist/.claude/commands/test.md +96 -0
  24. package/dist/.claude/commands/thumper.md +116 -0
  25. package/dist/.claude/commands/treasury.md +100 -0
  26. package/dist/.claude/commands/ux.md +118 -0
  27. package/dist/.claude/commands/vault.md +189 -0
  28. package/dist/.claude/commands/void.md +108 -0
  29. package/dist/CHANGELOG.md +1918 -0
  30. package/dist/CLAUDE.md +250 -0
  31. package/dist/HOLOCRON.md +856 -0
  32. package/dist/VERSION.md +123 -0
  33. package/dist/docs/NAMING_REGISTRY.md +478 -0
  34. package/dist/docs/methods/AI_INTELLIGENCE.md +276 -0
  35. package/dist/docs/methods/ASSEMBLER.md +142 -0
  36. package/dist/docs/methods/BACKEND_ENGINEER.md +165 -0
  37. package/dist/docs/methods/BUILD_JOURNAL.md +185 -0
  38. package/dist/docs/methods/BUILD_PROTOCOL.md +426 -0
  39. package/dist/docs/methods/CAMPAIGN.md +568 -0
  40. package/dist/docs/methods/CONTEXT_MANAGEMENT.md +189 -0
  41. package/dist/docs/methods/DEEP_CURRENT.md +184 -0
  42. package/dist/docs/methods/DEVOPS_ENGINEER.md +295 -0
  43. package/dist/docs/methods/FIELD_MEDIC.md +261 -0
  44. package/dist/docs/methods/FORGE_ARTIST.md +108 -0
  45. package/dist/docs/methods/FORGE_KEEPER.md +268 -0
  46. package/dist/docs/methods/GAUNTLET.md +344 -0
  47. package/dist/docs/methods/GROWTH_STRATEGIST.md +466 -0
  48. package/dist/docs/methods/HEARTBEAT.md +168 -0
  49. package/dist/docs/methods/MCP_INTEGRATION.md +139 -0
  50. package/dist/docs/methods/MUSTER.md +148 -0
  51. package/dist/docs/methods/PRD_GENERATOR.md +186 -0
  52. package/dist/docs/methods/PRODUCT_DESIGN_FRONTEND.md +250 -0
  53. package/dist/docs/methods/QA_ENGINEER.md +337 -0
  54. package/dist/docs/methods/RELEASE_MANAGER.md +145 -0
  55. package/dist/docs/methods/SECURITY_AUDITOR.md +320 -0
  56. package/dist/docs/methods/SUB_AGENTS.md +335 -0
  57. package/dist/docs/methods/SYSTEMS_ARCHITECT.md +171 -0
  58. package/dist/docs/methods/TESTING.md +359 -0
  59. package/dist/docs/methods/THUMPER.md +175 -0
  60. package/dist/docs/methods/TIME_VAULT.md +120 -0
  61. package/dist/docs/methods/TREASURY.md +184 -0
  62. package/dist/docs/methods/TROUBLESHOOTING.md +265 -0
  63. package/dist/docs/patterns/README.md +52 -0
  64. package/dist/docs/patterns/ad-billing-adapter.ts +537 -0
  65. package/dist/docs/patterns/ad-platform-adapter.ts +421 -0
  66. package/dist/docs/patterns/ai-classifier.ts +195 -0
  67. package/dist/docs/patterns/ai-eval.ts +272 -0
  68. package/dist/docs/patterns/ai-orchestrator.ts +341 -0
  69. package/dist/docs/patterns/ai-router.ts +194 -0
  70. package/dist/docs/patterns/ai-tool-schema.ts +237 -0
  71. package/dist/docs/patterns/api-route.ts +241 -0
  72. package/dist/docs/patterns/backtest-engine.ts +499 -0
  73. package/dist/docs/patterns/browser-review.ts +292 -0
  74. package/dist/docs/patterns/combobox.tsx +300 -0
  75. package/dist/docs/patterns/component.tsx +262 -0
  76. package/dist/docs/patterns/daemon-process.ts +338 -0
  77. package/dist/docs/patterns/data-pipeline.ts +297 -0
  78. package/dist/docs/patterns/database-migration.ts +466 -0
  79. package/dist/docs/patterns/e2e-test.ts +629 -0
  80. package/dist/docs/patterns/error-handling.ts +312 -0
  81. package/dist/docs/patterns/execution-safety.ts +601 -0
  82. package/dist/docs/patterns/financial-transaction.ts +342 -0
  83. package/dist/docs/patterns/funding-plan.ts +462 -0
  84. package/dist/docs/patterns/game-entity.ts +137 -0
  85. package/dist/docs/patterns/game-loop.ts +113 -0
  86. package/dist/docs/patterns/game-state.ts +143 -0
  87. package/dist/docs/patterns/job-queue.ts +225 -0
  88. package/dist/docs/patterns/kongo-integration.ts +164 -0
  89. package/dist/docs/patterns/middleware.ts +363 -0
  90. package/dist/docs/patterns/mobile-screen.tsx +139 -0
  91. package/dist/docs/patterns/mobile-service.ts +167 -0
  92. package/dist/docs/patterns/multi-tenant.ts +382 -0
  93. package/dist/docs/patterns/oauth-token-lifecycle.ts +223 -0
  94. package/dist/docs/patterns/outbound-rate-limiter.ts +260 -0
  95. package/dist/docs/patterns/prompt-template.ts +195 -0
  96. package/dist/docs/patterns/revenue-source-adapter.ts +311 -0
  97. package/dist/docs/patterns/service.ts +224 -0
  98. package/dist/docs/patterns/sse-endpoint.ts +118 -0
  99. package/dist/docs/patterns/stablecoin-adapter.ts +511 -0
  100. package/dist/docs/patterns/third-party-script.ts +68 -0
  101. package/dist/scripts/thumper/gom-jabbar.sh +241 -0
  102. package/dist/scripts/thumper/relay.sh +610 -0
  103. package/dist/scripts/thumper/scan.sh +359 -0
  104. package/dist/scripts/thumper/thumper.sh +190 -0
  105. package/dist/scripts/thumper/water-rings.sh +76 -0
  106. package/package.json +1 -1
  107. package/dist/tsconfig.tsbuildinfo +0 -1
@@ -0,0 +1,359 @@
1
+ # TESTING PROTOCOL
2
+ ## Lead Agent: **Batman** · Sub-agent: **Nightwing** (Regression Guardian)
3
+
4
+ > *"The test suite is your silent guardian."*
5
+
6
+ ## Purpose
7
+
8
+ Automated tests catch regressions that manual checklists miss. Manual verification catches UX and integration issues that automated tests miss. Use both. Neither replaces the other.
9
+
10
+ ## Testing Pyramid
11
+
12
+ ```
13
+ / E2E \ ← Few: critical user journeys only
14
+ / Integration \ ← Some: API routes, service interactions
15
+ / Unit Tests \ ← Many: business logic, utils, transforms
16
+ ```
17
+
18
+ ## Framework-to-Test-Runner Mapping
19
+
20
+ | Framework | Unit Tests | Integration Tests | E2E | Assertion |
21
+ |-----------|-----------|------------------|-----|-----------|
22
+ | Next.js / Node | vitest or jest | vitest + supertest | Playwright | expect (vitest/jest) |
23
+ | Express | vitest or jest | vitest + supertest | Playwright | expect (vitest/jest) |
24
+ | Django | pytest | pytest + Django test client | Playwright | assert (pytest) |
25
+ | Rails | RSpec or Minitest | RSpec + request specs | Playwright | expect (RSpec) |
26
+ | Go | testing (stdlib) | testing + httptest | Playwright | testify |
27
+ | Spring Boot | JUnit 5 | JUnit 5 + MockMvc | Playwright | AssertJ |
28
+
29
+ Adapt the patterns below to your stack's test runner. The principles (test behavior not implementation, deterministic, co-located) are universal.
30
+
31
+ ## When to Write Which
32
+
33
+ | Code Type | Test Type | Tool | Coverage Target |
34
+ |-----------|-----------|------|----------------|
35
+ | Pure functions, utils, transforms | Unit | vitest / jest | High — these are cheap to test |
36
+ | Service layer (business logic) | Unit | vitest / jest | High — core value lives here |
37
+ | API routes | Integration | vitest / supertest | All routes — validate input/output contracts |
38
+ | Database queries | Integration | vitest + test DB | Complex queries, edge cases |
39
+ | Auth flows | Integration | vitest / supertest | All auth paths including failures |
40
+ | Full user journeys | E2E / Manual | Playwright or manual | Top 3-5 critical paths only |
41
+ | UI components | Manual | Browser | Visual, interaction, responsive, a11y |
42
+ | Edge cases, broken states | Manual | Browser | Red Hood's domain — break it on purpose |
43
+
44
+ ## Unit Test Rules
45
+
46
+ 1. Test behavior, not implementation. Assert outcomes, not internals.
47
+ 2. One assertion concept per test. Name describes what's being verified.
48
+ 3. No mocking unless the dependency is external (API, DB, filesystem).
49
+ 4. Use factories for test data, not raw objects scattered across files.
50
+ 5. Tests must be deterministic — no time-dependent, order-dependent, or network-dependent tests.
51
+
52
+ ### Pattern
53
+
54
+ ```typescript
55
+ // services/__tests__/billing.test.ts
56
+ import { describe, it, expect } from 'vitest'
57
+ import { calculateProration } from '../billing'
58
+
59
+ describe('calculateProration', () => {
60
+ it('returns zero when upgrade happens on billing date', () => {
61
+ const result = calculateProration({
62
+ currentPlan: 'basic',
63
+ newPlan: 'pro',
64
+ billingDate: new Date('2026-03-01'),
65
+ upgradeDate: new Date('2026-03-01'),
66
+ })
67
+ expect(result.amount).toBe(0)
68
+ })
69
+
70
+ it('prorates proportionally for mid-cycle upgrade', () => {
71
+ const result = calculateProration({
72
+ currentPlan: 'basic',
73
+ newPlan: 'pro',
74
+ billingDate: new Date('2026-03-01'),
75
+ upgradeDate: new Date('2026-03-15'),
76
+ })
77
+ expect(result.amount).toBeGreaterThan(0)
78
+ expect(result.daysRemaining).toBe(16)
79
+ })
80
+ })
81
+ ```
82
+
83
+ ## Integration Test Rules
84
+
85
+ 1. Use a real test database (SQLite in-memory or Docker Postgres).
86
+ 2. Reset state between tests (transactions or truncation).
87
+ 3. Test the full request/response cycle for API routes.
88
+ 4. Validate response shape, status codes, and error formats.
89
+ 5. Test auth: authenticated, unauthenticated, wrong role, wrong owner.
90
+ 6. **Validation constraint smoke test:** For each model with validation constraints (Pydantic `Field`, Zod schema, Joi, class-validator decorators), write at least one test that sends invalid input and verifies rejection. Frameworks may silently ignore constraints on incompatible types (e.g., Pydantic v2 ignores `max_length` on dict). The test catches this. (Field report #99: `max_length=50` on a dict field was silently ignored — no size validation occurred.)
91
+ 7. **Route integration test mandate:** For each API route, at least one test must exercise the full HTTP → handler → service → response path. Unit tests on service functions are not sufficient — they miss middleware, auth guards, request parsing, and response formatting. A route with no integration test is an untested route. (Field report #119: zero integration tests across 278 unit tests — route-level regressions were invisible.)
92
+
93
+ ### Pattern
94
+
95
+ ```typescript
96
+ // api/__tests__/projects.test.ts
97
+ import { describe, it, expect, beforeEach } from 'vitest'
98
+ import { createTestApp, createTestUser } from '../test-utils'
99
+
100
+ describe('POST /api/projects', () => {
101
+ let app: TestApp
102
+ let user: TestUser
103
+
104
+ beforeEach(async () => {
105
+ app = await createTestApp()
106
+ user = await createTestUser(app)
107
+ })
108
+
109
+ it('creates project for authenticated user', async () => {
110
+ const res = await app.post('/api/projects')
111
+ .auth(user.token)
112
+ .send({ name: 'Test Project' })
113
+
114
+ expect(res.status).toBe(201)
115
+ expect(res.body.project.name).toBe('Test Project')
116
+ expect(res.body.project.ownerId).toBe(user.id)
117
+ })
118
+
119
+ it('rejects unauthenticated request', async () => {
120
+ const res = await app.post('/api/projects')
121
+ .send({ name: 'Test Project' })
122
+
123
+ expect(res.status).toBe(401)
124
+ })
125
+
126
+ it('validates input', async () => {
127
+ const res = await app.post('/api/projects')
128
+ .auth(user.token)
129
+ .send({ name: '' })
130
+
131
+ expect(res.status).toBe(400)
132
+ expect(res.body.error.code).toBe('VALIDATION_ERROR')
133
+ })
134
+ })
135
+ ```
136
+
137
+ ## E2E Testing
138
+
139
+ E2E tests sit at the top of the testing pyramid. They're slow, expensive, and brittle compared to unit tests — but they catch integration failures that nothing else can. A unit test verifies that `calculateTotal()` returns the right number; an E2E test verifies that the user can actually complete a purchase.
140
+
141
+ ### Where E2E Fits
142
+
143
+ ```
144
+ / E2E \ ← 10-15 tests. Critical user journeys ONLY.
145
+ / Integration \ ← API routes, service interactions, auth flows
146
+ / Unit Tests \ ← Business logic, utils, transforms, edge cases
147
+ ```
148
+
149
+ **Write E2E when:**
150
+ - Testing critical user journeys (signup, purchase, core workflow)
151
+ - Verifying a11y across real page compositions (axe-core in browser)
152
+ - Testing cross-component flows where integration tests can't reach (navigation, redirects, auth state persistence)
153
+ - Validating real browser behavior (CSP headers, cookie handling, responsive layout)
154
+
155
+ **Write unit/integration instead when:**
156
+ - Testing business logic, calculations, or data transforms
157
+ - Testing API input validation and error handling
158
+ - Testing edge cases and boundary conditions
159
+ - Testing isolated component behavior
160
+
161
+ ### Performance Budget
162
+
163
+ | Metric | Limit | Action if Exceeded |
164
+ |--------|-------|--------------------|
165
+ | Total CI time | 2 minutes | Shard tests across workers |
166
+ | Test count | 50 | Shard or prune — you're testing too many paths |
167
+ | Single test duration | 30 seconds | Split into smaller focused tests |
168
+ | Retry rate | < 5% per week | Fix or quarantine flaky tests |
169
+
170
+ Sharding in CI (when you hit 50+ tests):
171
+ ```yaml
172
+ # GitHub Actions example
173
+ strategy:
174
+ matrix:
175
+ shard: [1/3, 2/3, 3/3]
176
+ steps:
177
+ - run: npx playwright test --shard=${{ matrix.shard }}
178
+ ```
179
+
180
+ ### Flaky Test Protocol
181
+
182
+ Flaky tests erode trust in the test suite. Huntress (stability monitor) tracks flake rates.
183
+
184
+ 1. **Detection:** Test fails intermittently — passes on retry but fails on fresh runs
185
+ 2. **Annotation:** After 3 flakes in a week, add `@flaky` tag with a tracking issue
186
+ 3. **Quarantine:** Flaky tests run in a separate CI job (`grep: /@flaky/`) with 3 retries
187
+ 4. **Investigation:** Root-cause the flakiness — usually timing, external state, or test ordering
188
+ 5. **Resolution:** Fix within 2 weeks or rewrite. If unfixable, demote to manual verification
189
+ 6. **Return:** Remove `@flaky` tag — test returns to the stable suite
190
+
191
+ ```typescript
192
+ // Annotating a flaky test
193
+ test('payment webhook processes correctly', {
194
+ tag: ['@flaky'],
195
+ annotation: { type: 'flaky', description: 'Webhook timing race — tracking in #234' },
196
+ }, async ({ page }) => {
197
+ // test body
198
+ });
199
+ ```
200
+
201
+ **Rules:**
202
+ - NEVER use `@flaky` to suppress real bugs
203
+ - NEVER use `page.waitForTimeout()` — it's the #1 cause of flakiness
204
+ - NEVER use `waitForLoadState('networkidle')` — it's non-deterministic
205
+ - ALWAYS use explicit waits: `await expect(locator).toBeVisible()`
206
+
207
+ ### Framework-to-Test-Runner Mapping (E2E Column)
208
+
209
+ | Framework | Unit | Integration | E2E Runner | E2E Start Command |
210
+ |-----------|------|-------------|------------|-------------------|
211
+ | Next.js / Node | vitest or jest | vitest + supertest | Playwright | `next dev -p 3199` |
212
+ | Express | vitest or jest | vitest + supertest | Playwright | `PORT=3199 npx tsx src/server.ts` |
213
+ | Django | pytest | pytest + Django client | Playwright | `python manage.py runserver 3199` |
214
+ | Rails | RSpec / Minitest | RSpec + request specs | Playwright | `RAILS_ENV=test bin/rails server -p 3199` |
215
+ | Go | testing (stdlib) | testing + httptest | Playwright | `PORT=3199 go run ./cmd/server` |
216
+ | Spring Boot | JUnit 5 | JUnit 5 + MockMvc | Playwright | `./gradlew bootRun --args='--server.port=3199'` |
217
+
218
+ ### PRD Frontmatter
219
+
220
+ Add `e2e: yes | no` to PRD frontmatter. Defaults:
221
+ - `yes` — full-stack, static-site (has pages to test)
222
+ - `no` — api-only, prototype (no browser UI, or too early)
223
+
224
+ The Build Protocol reads this flag to decide whether Phase 10 (E2E tests) runs.
225
+
226
+ ### E2E Test File Conventions
227
+
228
+ ```
229
+ e2e/
230
+ fixtures.ts ← axe-core fixture, auth helper, network mocks
231
+ auth.setup.ts ← Login via API, save session state
232
+ smoke.test.ts ← Page loads, no crashes, a11y clean
233
+ auth.test.ts ← Login/logout/session persistence
234
+ [feature].test.ts ← One file per critical journey
235
+ page-objects/
236
+ login.page.ts ← Page Object Model classes
237
+ dashboard.page.ts
238
+ .auth/
239
+ session.json ← Saved auth state (gitignored)
240
+ ```
241
+
242
+ ### Reference Pattern
243
+
244
+ See `/docs/patterns/e2e-test.ts` for the complete reference implementation:
245
+ - Page Object Model example
246
+ - axe-core a11y fixture
247
+ - Auth helper (login via API, reuse session)
248
+ - Network mock (intercept external API calls)
249
+ - WebSocket mock
250
+ - CWV measurement helper
251
+ - Flaky test annotation pattern
252
+ - Framework-specific Playwright config
253
+
254
+ ## Testing Anti-Patterns
255
+
256
+ **No hardcoded dates:** Never use hardcoded dates in tests. Use relative datetime (e.g., `new Date(Date.now() - 86400000)` for 'yesterday'). A test with `expect(date).toBe('2026-03-15')` becomes a time bomb that fails when the date passes.
257
+
258
+ **Mock signature verification:** When mocking external dependencies, verify the mocked methods exist on the real class. A mock that defines `sendMessage()` when the real SDK uses `send_message()` creates false confidence — tests pass but the integration fails. Pattern: `expect(Object.keys(mock)).toEqual(expect.arrayContaining(Object.keys(realInstance)))`.
259
+
260
+ **No source-code string assertions:** Never assert on status code strings or error class names found in source code (`'403' in source`, `'HTTPException' in source`). These break on any refactor that changes error handling mechanics (e.g., `HTTPException(403)` → `Errors.forbidden()`). Test the actual HTTP response status and body instead. (Field report #227)
261
+
262
+ **Error format migration checklist:** Before committing any change to error response shape (e.g., `{"detail": ...}` → `{"error": {"code", "message"}}`), grep test files for the old shape. Tests asserting `response["detail"]` will silently pass if the test never reaches the assertion (wrong status code) or will fail confusingly. Fix all test assertions to match the new shape in the same commit. (Field report #227)
263
+
264
+ **Standalone test app handler registration (FastAPI/Express):** When tests create their own application instance (`FastAPI()`, `express()`) for isolated testing, register all custom error handlers from the main app (`app.add_exception_handler(ApiError, api_error_handler)` or equivalent). Without this, custom error classes propagate as unhandled exceptions instead of structured JSON — tests pass for the wrong reason. (Field report #227)
265
+
266
+ **Version-agnostic assertions:** When asserting on prefixed or versioned values (encryption prefixes, API version headers, token formats), use the stable prefix, not the exact version. `startswith("enc:")` survives key rotation; `startswith("enc::")` breaks when the format becomes `enc:v1::`. Assert on the behavior ("value is encrypted") not the version ("value uses encryption v1"). (Field report #227)
267
+
268
+ ## What NOT to Test Automatically
269
+
270
+ - Visual appearance (manual — Arwen's domain)
271
+ - "Does this feel right?" UX flows (manual — Elrond's domain)
272
+ - Performance under real conditions (manual — Fury/Gimli's domain)
273
+ - Third-party service behavior (mock it, don't call it)
274
+ - Implementation details that could change without affecting behavior
275
+
276
+ ## Test File Conventions
277
+
278
+ ```
279
+ src/
280
+ services/
281
+ billing.ts
282
+ __tests__/
283
+ billing.test.ts ← Unit tests co-located
284
+ api/
285
+ projects/
286
+ route.ts
287
+ __tests__/
288
+ projects.test.ts ← Integration tests co-located
289
+ test-utils/
290
+ index.ts ← Shared factories, helpers, test app setup
291
+ ```
292
+
293
+ ## Running Tests
294
+
295
+ ```bash
296
+ # All tests
297
+ npm test
298
+
299
+ # Unit tests only
300
+ npm run test:unit
301
+
302
+ # Integration tests only
303
+ npm run test:integration
304
+
305
+ # Watch mode during development
306
+ npm run test:watch
307
+
308
+ # Coverage report
309
+ npm run test:coverage
310
+ ```
311
+
312
+ ## Regression Strategy
313
+
314
+ Automated tests are the **first line** of regression defense. The manual regression checklist in `/docs/qa-prompt.md` is the **second line** — it catches integration and UX regressions that tests can't.
315
+
316
+ When Nightwing adds a regression checklist item, also ask: "Can this be an automated test?" If yes, write the test AND add the checklist item. The test runs on every change; the checklist runs on QA passes.
317
+
318
+ ## Relationship to QA_ENGINEER.md
319
+
320
+ Batman's QA protocol now includes automated testing as Step 3.5 (between "Find Bugs" and "Bug Tracker"). The sequence:
321
+
322
+ 1. Oracle scans statically
323
+ 2. Red Hood breaks dynamically
324
+ 3. Alfred reviews dependencies
325
+ 4. Lucius reviews config
326
+ 5. **Nightwing runs the test suite and reports failures**
327
+ 6. All findings go into the bug tracker
328
+ 7. Fixes include new tests to prevent regression
329
+
330
+ ### Test Schema vs. Production Schema
331
+ Verify test fixtures (conftest, factories, seed files) create ALL tables from the migration runner. Compare table lists between the test DB setup and the production schema — any table present in migrations but missing from test fixtures is a gap that causes silent test failures. This is especially critical when using a hardcoded migration list instead of the actual migration runner.
332
+ (Field report #21: conftest missed tables from later migrations — ALTER TABLE on non-existent table was silently caught.)
333
+
334
+ ### Database Fixtures — Use the Production Schema
335
+
336
+ Always use the test framework's shared database fixture (e.g., conftest `db` fixture) for store and service tests. It applies the full production schema + all migrations.
337
+
338
+ Do NOT create custom DDL in test files — it drifts from the real schema (missing NOT NULL constraints, columns added by later migrations, different defaults). If you need tables the shared fixture doesn't have, add them AFTER the fixture yields — don't replace it.
339
+
340
+ Custom DDL causes test DB schema mismatches that require 2-3 fix-and-retry cycles per occurrence. (Field report #31)
341
+
342
+ ## Setup Checklist
343
+
344
+ When setting up testing for a new project:
345
+
346
+ - [ ] Install test runner (`npm i -D vitest` or `jest`)
347
+ - [ ] Add test scripts to `package.json`
348
+ - [ ] Create `test-utils/` with app setup and factories
349
+ - [ ] Configure test database (if applicable)
350
+ - [ ] Write first test for the most critical service function
351
+ - [ ] Write first integration test for the most important API route
352
+ - [ ] Verify `npm test` passes in CI-equivalent conditions
353
+ - [ ] **E2E (if PRD `e2e: yes`):** Install Playwright (`npm i -D @playwright/test @axe-core/playwright`)
354
+ - [ ] **E2E:** Install browser (`npx playwright install chromium`)
355
+ - [ ] **E2E:** Create `playwright.config.ts` with framework-appropriate `webServer.command`
356
+ - [ ] **E2E:** Create `e2e/fixtures.ts` with axe-core fixture and auth helper
357
+ - [ ] **E2E:** Write first smoke test — page loads, no a11y violations
358
+ - [ ] **E2E:** Add `e2e/.auth/`, `test-results/`, `playwright-report/` to `.gitignore`
359
+ - [ ] **E2E:** Verify `npx playwright test` passes in CI-equivalent conditions (< 2 min)
@@ -0,0 +1,175 @@
1
+ # THE THUMPER — Chani's Worm Rider
2
+ ## Lead Agent: **Chani** (Chani Kynes) · Sub-agents: Dune Universe
3
+
4
+ > *"Tell me of your homeworld, Usul."*
5
+
6
+ ## What Is This
7
+
8
+ Send prompts to Claude Code from Telegram and get responses back. Plant a thumper, ride the sandworm, command from anywhere.
9
+
10
+ ## Identity
11
+
12
+ **Chani** is a Worm Rider. She doesn't write code — she ensures The Voice reaches its destination across any environment. Her domain is cross-environment session bridging: connecting your Telegram to a live Claude Code session.
13
+
14
+ **Behavioral directives:** Every channel must pass the Gom Jabbar. Default to the most reliable worm path. Never store credentials outside the sietch vault. When a signal fails, notify the sender — silence is betrayal in the desert.
15
+
16
+ **See `/docs/NAMING_REGISTRY.md` for the full Dune character pool.**
17
+
18
+ ## Sub-Agent Roster
19
+
20
+ | Agent | Name | Role | Lens |
21
+ |-------|------|------|------|
22
+ | Channel Security | **Stilgar** | Naib — protects the tribe's secrets | No outsider enters the sietch. |
23
+ | Protocol Parsing | **Thufir** | Mentat — message processing | A million computations per second. |
24
+ | Relay Operations | **Idaho** | Swordmaster — the eternal connection | Dies a thousand times, always returns. |
25
+ | Authentication | **Mohiam** | Reverend Mother — the Gom Jabbar | "Put your hand in the box." |
26
+
27
+ ## Goal
28
+
29
+ Authenticated, bidirectional Telegram bridge to Claude Code. One command to start, passphrase-gated, works across macOS local, macOS+tmux, headless Linux SSH, and Linux+tmux.
30
+
31
+ ## When to Call Other Agents
32
+
33
+ | Situation | Hand off to |
34
+ |-----------|-------------|
35
+ | Security review of credential handling | **Kenobi** (Security) |
36
+ | Infrastructure for daemon supervision | **Kusanagi** (DevOps) |
37
+ | Architecture of transport layer | **Picard** (Architecture) |
38
+ | Bug in injection mechanics | **Batman** (QA) |
39
+
40
+ ## Operating Rules
41
+
42
+ 1. Every session must pass the Gom Jabbar — no exceptions.
43
+ 2. Credentials live in `sietch.env` (chmod 600, umask 077). Never committed.
44
+ 3. The Gom Jabbar hash is session-scoped — destroyed on `/thumper off`.
45
+ 4. Passphrase messages are deleted from Telegram. If deletion fails, the session is invalidated.
46
+ 5. After 60 minutes idle, re-authentication is required.
47
+ 6. 3 failed auth attempts trigger a 5-minute lockout.
48
+ 7. Messages during auth challenge are discarded, not queued.
49
+ 8. The water rings hook always exits 0 — never blocks Claude Code.
50
+ 9. Log operations, not message content.
51
+ 10. Control characters (0x00-0x1F, 0x7F) are stripped before injection. Newlines collapsed to spaces.
52
+ 11. The thumper must never run as root.
53
+
54
+ ## Worm Paths (Transport Vectors)
55
+
56
+ | Worm Path | Environment | Mechanism | Platform |
57
+ |-----------|-------------|-----------|----------|
58
+ | **TMUX_SENDKEYS** | Any with tmux | `tmux send-keys -l -t [session]` | Cross-platform |
59
+ | **PTY_INJECT** | Headless Linux SSH | Write to `/proc/[pid]/fd/0` | Linux only |
60
+ | **OSASCRIPT** | macOS Terminal.app / iTerm2 | File-based AppleScript injection | macOS only |
61
+
62
+ **Detection priority:** TMUX (most reliable) > Headless SSH > macOS local > Linux PTY > manual override. OSASCRIPT is only auto-selected for Terminal.app and iTerm2 — VS Code, Warp, Alacritty, and Kitty users get explicit guidance to use tmux. Windows Git Bash/MSYS2 gets a "use WSL" message.
63
+
64
+ ## The Gom Jabbar Protocol
65
+
66
+ **How it works:**
67
+ 1. On `/thumper on`, the relay sends a challenge to Telegram: "Choose your word of passage."
68
+ 2. You type a passphrase (any word or phrase) in the Telegram chat.
69
+ 3. The passphrase is hashed with PBKDF2 (100k iterations via python3, HMAC-SHA256 fallback) and stored in `.gom-jabbar`.
70
+ 4. The passphrase message is deleted from Telegram via the `deleteMessage` API (3 retries).
71
+ 5. If deletion fails, the session is invalidated and you must choose a new passphrase.
72
+ 6. Messages flow normally while authenticated.
73
+ 7. After 60 minutes idle: "The sands have shifted. Speak your word of passage."
74
+ 8. 3 wrong attempts: locked for 5 minutes.
75
+
76
+ **Security properties:**
77
+ - PBKDF2 with 100k iterations prevents brute force
78
+ - Message deletion removes passphrase from chat history
79
+ - Session-scoped hash — destroyed on `/thumper off`
80
+ - No message queuing during auth — prevents unauthenticated payload laundering
81
+ - Empty hash bypass prevention — refuses auth when hashing tools are unavailable
82
+
83
+ ## Setup Flow
84
+
85
+ `/thumper setup` walks through everything interactively:
86
+ 1. **Bot creation:** Guides you through BotFather or accepts an existing token. Auto-detects your chat ID.
87
+ 2. **Environment scan:** Auto-detects your terminal and selects the best worm path.
88
+ 3. **Config write:** Creates `.voidforge/thumper/sietch.env` (chmod 600, umask 077).
89
+ 4. **Activate:** Offers to start the channel immediately.
90
+
91
+ No manual config editing required — `scan.sh` handles everything.
92
+
93
+ **Non-interactive mode:** Claude Code's Bash tool doesn't support interactive stdin (`read -r -p`). For setup from within Claude Code, use: `bash scripts/thumper/scan.sh --token YOUR_BOT_TOKEN --chat-id YOUR_CHAT_ID`. This skips all prompts and writes the sietch vault directly. (Field report #35)
94
+
95
+ ## Usage
96
+
97
+ ```
98
+ /thumper setup — First-time scan or re-configure
99
+ /thumper on — Start sandworm daemon + Gom Jabbar challenge
100
+ /thumper off — Stop daemon, destroy auth state
101
+ /thumper status — Channel state, worm path, auth state, log size
102
+ ```
103
+
104
+ ## Scripts
105
+
106
+ | Script | Purpose |
107
+ |--------|---------|
108
+ | `thumper.sh` | Router — dispatches on/off/setup/status |
109
+ | `scan.sh` | Setup wizard — bot creation, env detection, config write |
110
+ | `relay.sh` | Sandworm daemon — polls Telegram, injects into Claude Code |
111
+ | `gom-jabbar.sh` | Auth protocol — sourced by relay.sh, not standalone |
112
+ | `water-rings.sh` | Stop hook — sends task completion notification to Telegram |
113
+
114
+ ## Water Rings (Stop Hook)
115
+
116
+ `water-rings.sh` is a Claude Code Stop hook registered in `.claude/settings.json`. It fires every time Claude Code finishes responding.
117
+
118
+ 1. Checks if config exists and channel is active — exits silently if not.
119
+ 2. Reads session JSON from stdin, extracts last assistant message.
120
+ 3. Truncates to 3600 chars, sends to Telegram in a background process.
121
+ 4. Always exits 0 — never blocks Claude Code.
122
+
123
+ The shutdown logic (killing daemon, removing channel flag, destroying auth) lives in `thumper.sh cmd_off`, not in the stop hook.
124
+
125
+ ## Security Considerations
126
+
127
+ ### Mitigations (implemented)
128
+
129
+ - Gom Jabbar with PBKDF2 hashing and message deletion
130
+ - Root guard (`id -u` check, unspoofable on macOS bash 3.2)
131
+ - Control character sanitization (Ctrl+C, ESC, ANSI injection prevented)
132
+ - Message length cap (8192 chars)
133
+ - Config injection prevention (`printf '%q'`, umask 077)
134
+ - AppleScript injection prevention (file-based, user text never in source)
135
+ - Atomic state files (write-to-tmp-then-rename)
136
+ - Empty hash bypass prevention
137
+
138
+ ### Known risks (inherent)
139
+
140
+ - **Prompt injection:** Telegram messages are Claude Code prompts. Mitigated by settings.json deny list + Gom Jabbar.
141
+ - **Data exfiltration via water rings:** Up to 3600 chars of output sent to Telegram.
142
+ - **Bot token in process listing:** Telegram API constraint. Low risk on single-user machines.
143
+ - **CHAT_ID is not a secret:** Bot token is the primary credential.
144
+ - **PTY race condition:** Mitigated by control character sanitization.
145
+
146
+ ## Troubleshooting
147
+
148
+ **Problem: "The sands have shifted" keeps appearing**
149
+ Your 60-minute idle timeout expired. Re-enter your passphrase. To change the timeout, edit `GOM_JABBAR_IDLE_TIMEOUT` in `gom-jabbar.sh`.
150
+
151
+ **Problem: "Could not erase your word from the sands"**
152
+ Telegram failed to delete the passphrase. Session invalidated for safety. Manually delete the message from chat. Run `/thumper off` then `/thumper on`.
153
+
154
+ **Problem: Sandworm starts but messages don't inject**
155
+ Check `/thumper status` for worm path and auth state. For PTY_INJECT: is Claude running? For TMUX: does session name match? For OSASCRIPT: is Terminal/iTerm2 focused with Accessibility permissions?
156
+
157
+ **Problem: Bot doesn't respond at all**
158
+ Check worm log: `tail -f .voidforge/thumper/worm.log`. Verify token: `curl https://api.telegram.org/bot[TOKEN]/getMe`.
159
+
160
+ **Problem: Locked out (3 failed attempts)**
161
+ Wait 5 minutes. If you forgot your passphrase: `/thumper off` then `/thumper on` to choose a new one.
162
+
163
+ ## Deliverables
164
+
165
+ 1. `scripts/thumper/` — thumper.sh, scan.sh, relay.sh, gom-jabbar.sh, water-rings.sh
166
+ 2. `.claude/commands/thumper.md` — Slash command
167
+ 3. `.claude/settings.json` — Stop hook registration
168
+ 4. This document
169
+
170
+ ## Handoffs
171
+
172
+ - Security review → **Kenobi**, log to `/logs/handoffs.md`
173
+ - Infrastructure → **Kusanagi**, log to `/logs/handoffs.md`
174
+ - Architecture → **Picard**, log to `/logs/handoffs.md`
175
+ - Testing → **Batman**, log to `/logs/handoffs.md`
@@ -0,0 +1,120 @@
1
+ # TIME VAULT — Seldon's Session Intelligence Preservation
2
+ ## Lead Agent: **Hari Seldon** (Foundation) · Sub-agent: **Gaal Dornick** (Foundation)
3
+
4
+ > *"The future is already written. I merely ensure the right people can read it."*
5
+
6
+ ## Identity
7
+
8
+ **Hari Seldon** invented psychohistory — the science of predicting the behavior of large populations. In VoidForge, his Time Vault distills the expensive analysis from one session into a portable briefing that lets the next session skip the re-derivation. Seldon compresses, Jake Sisko writes, Gaal Dornick gathers the raw materials.
9
+
10
+ ## Goal
11
+
12
+ Preserve expensive analysis across session boundaries. A new session with a vault briefing should reach productive work in under 60 seconds. Without one, recovering context costs 5-15 minutes of re-reading and re-tracing.
13
+
14
+ ## Operating Rules
15
+
16
+ ### 1. Include What's Expensive to Re-Derive
17
+
18
+ These items require synthesis, tracing, or multi-file analysis to produce. They cannot be recovered by reading a single file:
19
+
20
+ - **Decisions and rationale** — "We chose X over Y because Z" requires the full context that led to the decision
21
+ - **Failed approaches** — Knowing what didn't work (and why) prevents repeated dead ends
22
+ - **Cross-module relationships** — Which files connect to which, non-obvious wiring, integration points
23
+ - **Agent findings** — Consolidated results from review passes (not the full tables — the actionable summary)
24
+ - **Execution plan** — Ordered next steps with dependencies, derived from current state analysis
25
+ - **Test state** — What passes, what fails, what's missing, what was skipped
26
+
27
+ ### 2. Exclude What's Cheap to Re-Read
28
+
29
+ These items are stored in well-known locations and can be loaded on demand:
30
+
31
+ - Full file contents (git)
32
+ - PRD text (`/docs/PRD.md`)
33
+ - Method doc contents (`/docs/methods/`)
34
+ - Pattern references (`/docs/patterns/`)
35
+ - Agent naming registry (`/docs/NAMING_REGISTRY.md`)
36
+ - Build journal entries (`/logs/` — read directly when needed)
37
+
38
+ ### 3. Psychohistorical Compression
39
+
40
+ Seldon doesn't transcribe — he compresses. A 3-hour session that produced 47 findings across 6 agents becomes:
41
+
42
+ > "Security pass found 3 Critical (all fixed): CSRF on /api/settings, missing rate limit on /auth/login, leaked stack trace in error handler. UX pass found 12 issues (10 fixed, 2 deferred): focus management in modal, contrast on muted text. QA found the modal fix broke keyboard nav — re-fixed in Pass 2. Next: deploy checklist, then Phase 13 launch verification."
43
+
44
+ Not 47 rows. The signal, compressed.
45
+
46
+ ### 4. Per-Task File Lists
47
+
48
+ For each unit of work completed in the session, list the files created or modified. This lets the next session quickly scope what changed without running `git log --stat` across dozens of commits:
49
+
50
+ ```
51
+ ### Auth middleware refactor
52
+ - src/middleware/auth.ts (modified — added rate limiting)
53
+ - src/middleware/rate-limit.ts (created)
54
+ - tests/middleware/auth.test.ts (modified — 4 new tests)
55
+ ```
56
+
57
+ ### 5. Vault File Format
58
+
59
+ Files live at `/logs/vault-YYYY-MM-DD.md` with YAML frontmatter:
60
+
61
+ ```yaml
62
+ ---
63
+ sealed: 2026-03-26T14:30:00Z
64
+ project: my-app
65
+ branch: main
66
+ commit: abc1234
67
+ session_focus: Security pass + UX fixes for Phase 10-11
68
+ ---
69
+ ```
70
+
71
+ The frontmatter enables programmatic reading by other commands (`/campaign`, `/current`, `/thumper`).
72
+
73
+ ### 6. Pickup Prompt
74
+
75
+ Every vault produces a pickup prompt — a copyable block that the next session pastes to recover context immediately:
76
+
77
+ ```
78
+ Read /logs/vault-2026-03-26.md for session context.
79
+ Then read /logs/build-state.md and /logs/campaign-state.md for operational state.
80
+ Resume from: Phase 12 deploy checklist — all review passes complete.
81
+ ```
82
+
83
+ The pickup prompt is the vault's delivery mechanism. It's printed to console, not buried in a file.
84
+
85
+ ## Integration Points
86
+
87
+ | Command | How It Uses the Vault |
88
+ |---------|----------------------|
89
+ | `/campaign` | Reads most recent vault at Step 0 (state recovery). Seals a vault at campaign pause/end. |
90
+ | `/debrief` | Vault provides session context for Bashir's post-mortem analysis. |
91
+ | `/thumper` | `--for trigger` format enables automated session pickup via Telegram bridge. |
92
+ | `/current` | Deep Current reads vault history to track session-over-session progress. |
93
+ | Session start | User pastes pickup prompt. Agent reads vault. Context recovered. |
94
+
95
+ ## When to Seal a Vault
96
+
97
+ - **End of session** — Always. Even short sessions produce decisions worth preserving.
98
+ - **Before context checkpoint** — If approaching 70%+ context usage, seal first.
99
+ - **Campaign pause** — When `/campaign` pauses between missions across sessions.
100
+ - **Before destructive operations** — Before `git reset`, branch switches, or major refactors.
101
+
102
+ ### 7. Operational Learnings Sync
103
+
104
+ At session end, before sealing the vault, check for approved operational learnings from this session:
105
+
106
+ 1. If `/debrief` ran and produced approved learnings → they're already in `docs/LEARNINGS.md`
107
+ 2. If no debrief ran but the session discovered operational facts (API quirks, decision rationale, root causes that took multiple attempts) → Seldon flags candidates using the same criteria as FIELD_MEDIC.md Step 2.5
108
+ 3. Present candidates to user for approval. Append approved entries to `docs/LEARNINGS.md`
109
+ 4. Include in the vault's "Open Items" section: *"[N] operational learnings added to LEARNINGS.md this session"*
110
+
111
+ This ensures learnings are captured even in sessions that don't run a formal debrief. The vault is the last checkpoint — if a learning was discovered but not captured by debrief, the vault catches it.
112
+
113
+ **Do NOT duplicate LEARNINGS.md content in the vault narrative.** The vault references the file; the file holds the structured entries. The vault says "3 learnings captured" — it doesn't repeat them. See ADR-035 for the full design rationale.
114
+
115
+ ## Anti-Patterns
116
+
117
+ - **Vault as transcript** — Don't dump the session log. Compress to signal.
118
+ - **Vault as documentation** — Don't write docs in the vault. Write docs in `/docs/`, reference them from the vault.
119
+ - **Vault without pickup prompt** — A vault file nobody reads is wasted. The pickup prompt is mandatory.
120
+ - **Stale vaults** — Vaults older than 7 days are historical, not operational. The next session should read the most recent vault, not the oldest.