Conventions (for Claude Code)
Rules every ticket follows. If a ticket and this page disagree, the ticket wins for that ticket only.
Folder layout#
vivra/
├── CLAUDE.md
├── apps/
│ ├── web/ Next.js 15: landing (/), dapp (/vote, /dossiers/[id], /war-room/[id], /ledger)
│ ├── admin/ Next.js 15: back-office (SIWE login), /projects, /cycles, /validations, /operations, /settings
│ ├── api/ Fastify 5: src/routes/<domain>.ts, src/plugins/*, src/ws.ts
│ └── workers/ BullMQ: src/queues/<queue>.ts, src/jobs/<job>.ts, src/scheduler.ts
├── packages/
│ ├── config/ env loading + validation (zod), shared constants
│ ├── db/ src/schema/*.ts (Drizzle), migrations/, src/seed.ts, src/client.ts
│ ├── core/ pure logic: status machine, dead/target/safety checks, recovery score, buyback planner, vote rules
│ ├── chain/ src/config.ts, src/abi/*.json, src/read/*.ts, src/write/*.ts, src/logs.ts
│ ├── agents/ src/llm.ts, src/runner.ts, src/tools/*.ts, src/specs/*.yaml, src/prompts/*.md, src/schemas/*.ts
│ ├── templates/ sites/, utilities/, brand/ (each template = folder + manifest.json)
│ └── ui/ React components + tokens (colors, type, radii)
├── contracts/ Foundry: src/BuybackBurner.sol, test/, script/
└── infra/ docker-compose.yml, Dockerfiles, deploy configTypeScript#
strict: true,noUncheckedIndexedAccess: true. Noany(useunknown+ zod).- ESM everywhere. Path aliases:
@vivra/db,@vivra/core,@vivra/chain,@vivra/agents,@vivra/config,@vivra/ui. - Amounts:
bigintfor wei and raw token units. Convert with helpers in@vivra/core/moneyonly:toWei,fromWei,formatToken,usd(value: string). - Dates:
Datein UTC, ISO strings over the wire. Helpers in@vivra/core/time:T(j0, hours),J(j0, days).
API#
- One Fastify plugin per domain:
auth,cycles,votes,candidates,operations,ledger,admin. - Every route declares zod
body/querystring/paramsandresponseschemas (fastify-type-provider-zod). - Errors: throw
ApiError(code, httpStatus, message); a global handler maps to{ error: { code, message } }. Codes: annexe B. - Rate limits with
@fastify/rate-limit, values from annexe B. - Sessions: cookie
vivra_session(HttpOnly, Secure, SameSite=Lax). Admin:vivra_adminvia SIWE.
Database#
- Drizzle schema mirrors annexe A exactly (names, types, enums). Any deviation = a note in the PR.
- Migrations are generated (
pnpm db:generate), never hand-edited after merge. - Append-only tables (
log_entries,ledger_entries,buyback_slices,admin_audit): no UPDATE/DELETE in code, exceptbuyback_slices.status/tx_hash/...transitions done by the executor. - Settings: read through
getSetting(key)with a typed registry in@vivra/core/settings(default values from the spec).
Workers#
- One queue per concern (annexe F list). Job names
<domain>.<action>. - Every job is idempotent: compute a deterministic
jobId, and check DB state before side effects. - Retries: 3 attempts, backoff 2 min / 10 min / 30 min, then mark failed + alert.
- Long-running chain reads chunk by block ranges (default 10,000 blocks, configurable).
Chain#
- All addresses come from
@vivra/chain/configperCHAIN_ENV(testnet | fork | mainnet). - ABIs are JSON copied from the explorer, never hand-written. Unknown ABI = stop and ask (⚠️ Pons).
- Writes go through
sendTx({ simulate, capCheck, send, waitFor:1, ledger })in@vivra/chain/write.
Agents#
- An agent = a YAML spec (
specs/<name>.yaml) + a prompt (prompts/<name>.md) + output zod schemas (schemas/<step>.ts) + a tool allowlist. - The runner enforces: max 25 tool turns, budget cap in USD, 10 min timeout, schema validation with one retry.
- Every LLM call and tool call writes a
log_entriesrow with a human summary generated by the runner.
Tests#
packages/core: unit tests for every function, including the spec's worked examples (e.g. the recovery score example = 68).packages/chain: tests against recorded fixtures (JSON logs) + an optional testnet suite behindTESTNET=1.apps/api: route tests with an in-memory Fastify instance + a test Postgres (docker).contracts/: Foundry unit + fuzz + fork tests (annexe D table).apps/web: Playwright smoke test per screen.
Pull requests#
- Branch
t<id>-<slug>. TitleT<id>: <title>. - Description: plan, files touched, decisions taken, the "Done when" checklist with the command used for each item, open questions.
- No PR mixes two tickets.