vivraDocumentationV1Comprendre le vote

M4 · Product (T40–T44)

Exit gate: a complete cycle on testnet: the team publishes 5 candidates, people vote by email, the vote closes, the winner's operation starts automatically, and every dapp screen shows it live.

Design source: the Design canvas « Vivra · Landing & Dapp » (artboards Main, Vote, Candidate, Operating, Family). Ask the owner to share it with you. Copy layouts, colours, type and copy; do not redesign. Example data in the mockups must be replaced by API data.


T40 · Email auth#

Goal Passwordless email login with 6-digit code or magic link, no wallet.

Depends on T03

Read first cycles-candidats-et-vote.md (Vote par email), annexe-api.md (Connexion), annexe-base-de-donnees.md (users, auth_codes, sessions), securite-et-observabilite.md

Build apps/api/src/routes/auth.ts, packages/core/src/email.ts (normalize, disposable check), apps/api/src/lib/{mailer.ts,crypto.ts,turnstile.ts}, packages/config/disposable-domains.txt

Steps

  1. Normalize email (trim, lowercase); reject disposable domains (list + setting for extras).
  2. Store email_hash = HMAC-SHA256(EMAIL_HMAC_KEY) and email_enc = AES-256-GCM(EMAIL_ENC_KEY). Never log the email.
  3. POST /auth/email (Turnstile required) → 6-digit code + magic token, 15 min TTL, both hashed; send via Resend/Postmark; always 204.
  4. POST /auth/verify → 5 attempts max per code; creates session 30 days; cookie; returns can_vote_at = created_at + min_account_age_hours.
  5. GET /me, POST /auth/logout.

Done when

  • Tests: disposable domain → 422; 6th wrong code → code invalidated; expired code → 422
  • DB inspection test: no plaintext email anywhere in tables
  • Rate limits from annexe B enforced (tests)

T41 · Cycles, candidates and vote#

Goal The team composes and publishes a cycle; votes and supports work; the clock closes the vote and creates the operation.

Depends on T16, T22, T40

Read first cycles-candidats-et-vote.md (entire page), annexe-api.md (cycles, votes, supports, admin cycles), parcours-1.md, parcours-2.md

Build apps/api/src/routes/{cycles,candidates,votes,supports}.ts, apps/api/src/routes/admin/cycles.ts, apps/workers/src/jobs/cycles.clock.ts, apps/admin/app/cycles/*

Steps

  1. Admin: create cycle (number, times, min account age; cycle 1 defaults to 0h), pick 5 validated tokens, edit dossier texts (tagline, thesis, reasons ×3, plan, risks 1–3), trigger Scanner pre-reports, publish (freezes recovery scores, sets published_at, tokens → candidate).
  2. Public: GET /cycles/current, GET /candidates/:id (full dossier fields from annexe B), GET /cycles/:id.
  3. POST /votes with all rules of annexe A "Règles à faire respecter"; change of vote allowed until close; WS vote.tally ≤ 1/s.
  4. POST/DELETE /supports.
  5. cycles.clock: opens votes at vote_opens_at; at vote_closes_at computes winner (tie → most supports → earliest position), sets J0, creates the operation (T22), losing candidates → validated.

Done when

  • Vote rule tests: closed cycle, team account, too-new account, blocked account → refused with the right code
  • Tie-break test
  • E2E (API level): publish → 10 votes → close → operation exists with J0 = close time
  • Frozen score on the dossier does not change when the live score changes (test)

T42 · Dapp screens#

Goal Vote, Dossiers, War room, Revived & ledger, wired to the API and WebSocket, matching the mockups.

Depends on T22, T33, T41

Read first dapp-et-landing.md, the Design canvas artboards Vote / Candidate / Operating / Family, annexe-score-de-reprise.md (Sur la dapp), annexe-api.md (WebSocket)

Build apps/web/app/(dapp)/{vote,dossiers/[id],war-room/[id],ledger}/page.tsx, packages/ui/* components (Header, PfpAvatar, SocialButtons, ScoreRing, PillarBars, Countdown, LogFeed, SliceGrid, LedgerTable…)

Steps

  1. Build shared components first in packages/ui from the mockups (tokens: colours, radii, fonts).
  2. Vote: launchpad cards (banner, pfp, socials, score ring, key figures, share bar), comparison matrix, email vote flow (email → code → voted / switch).
  3. Dossier: tabs per candidate, banner + pfp + socials, market cap chart with peak and last-team-tweet markers, why we picked it, Scanner checks, how it died, takeover plan, risks, score breakdown with raw values, market, socials & docs, "if it wins".
  4. War room: header, progress, chart with slice markers, KPIs, streaming public log with agent filter, agents' spend, shipped outputs, slices grid + recent tx, Pons CTO steps.
  5. Revived & ledger: stats, weekly flywheel, family cards with verdict, filterable ledger, weekly burns, public wallets.
  6. Every number links to its source (tx, contract or ledger row). Disclaimer banner on each page. Responsive at 390px. Reduced motion respected.

Done when

  • Playwright: each screen renders with seeded data at 1440px and 390px, no console errors
  • Voting flow e2e with a test inbox (Mailpit in docker) works end to end
  • A new vote appears in shares in < 5s; a new log line appears in the war room without reload (e2e)
  • Visual review against the mockups attached to the PR (screenshots side by side)

T43 · Landing page#

Goal The marketing landing, light theme with orange gradient, as in the Design canvas artboard « Landing ».

Depends on T00 (UI tokens from T42 if available)

Read first dapp-et-landing.md, Design canvas artboard Main (Landing)

Build apps/web/app/(marketing)/page.tsx + section components

Steps

  1. Sections in order: hero (gradient, glass tray with the 4 step cards, rule numbers row), problem, agents (6 cards with output previews), flywheel (loop diagram, compounding points, the math card, coins paying in), 72-hour clock, recovery score, final CTA, footer.
  2. Live values (voters, countdown, coins paying in) come from /stats and /cycles/current; anything not yet live is hidden, not faked.
  3. SEO: title, description, OG image (generated with the brand template), sitemap, robots.

Done when

  • Lighthouse ≥ 90 on performance, accessibility, SEO (CI)
  • Playwright snapshot at 1440px and 390px
  • No example number from the mockups is hard-coded (grep test for "3,812", "$GRIDL", "+58%")

T44 · Vivra X bot#

Goal Automatic posts of the Vivra account for every event of the spec.

Depends on T27, T31, T41

Read first bot-x-et-integrations-sociales.md (Posts automatiques, Règles d'automatisation)

Build packages/templates/social/vivra/*.ts (one template per event), event hooks emitting social.publish jobs, visual templates (rendered PNG)

Steps

  1. Events: dossiers published (one per candidate), vote open, vote closed, operation milestones, each slice (after execution), verdict, weekly ledger, milestones.
  2. Each template is a pure function (data) → { text, media? }, ≤ 280 chars, tested.
  3. Respect rate rule and validation setting; slices are always posted after execution.

Done when

  • Snapshot tests for every template with fixture data, all ≤ 280 chars
  • Integration test: a slice execution emits exactly one post job, after the tx is confirmed
↑ ↓ pour parcourir · Entrée pour ouvrirVIVRA DOCS