|
| 1 | +# Vortex Simulation Backend — Implementation Plan (Step-by-step, aligned to current UI) |
| 2 | + |
| 3 | +This plan turns `docs/vortex-simulation-processes.md` + `docs/vortex-simulation-tech-architecture.md` into an executable roadmap. |
| 4 | + |
| 5 | +## Guiding principles |
| 6 | +- Ship a **thin vertical slice** first: auth → gate → read models → one write action → feed events. |
| 7 | +- Keep domain logic **pure and shared** (state machine + events). The API is a thin adapter. |
| 8 | +- Prefer **deterministic**, testable transitions; avoid “magic UI-only numbers”. |
| 9 | +- Enforce gating on **every write**: “browse open, write gated”. |
| 10 | +- Minimize UI churn: start by making API responses **match the shapes** currently provided by `src/data/mock/*`, then gradually improve. |
| 11 | + |
| 12 | +## Phase 0 — Lock v1 decisions (1–2 days) |
| 13 | +1) Decide database now: **Postgres (recommended)** or **D1**. |
| 14 | +2) Decide gating source priority: **RPC first** with **Subscan fallback**. |
| 15 | +3) Decide what “active Human Node” means for v1: |
| 16 | + - “In current validator set”, or |
| 17 | + - “Bioauth epochs 42/42 last week”, or |
| 18 | + - a composite rule. |
| 19 | +4) Decide era length for simulation rollups (e.g. 1 week) and where it’s configured. |
| 20 | + |
| 21 | +Deliverable: a short “v1 constants” section committed to docs or config. |
| 22 | + |
| 23 | +## Phase 1 — Define contracts that mirror the UI (1–2 days) |
| 24 | +The UI is currently driven by `src/data/mock/*` (e.g. proposals list cards, proposal pages, chamber detail). Start by freezing the “API contract” so backend and frontend can meet in the middle. |
| 25 | + |
| 26 | +1) Define response DTOs that match the current UI needs: |
| 27 | + - Chambers directory card: id/name/multiplier + stats + pipeline. |
| 28 | + - Chamber detail: stage-filtered proposals + governors + threads/chat. |
| 29 | + - Proposals list: the exact data currently rendered in collapsed/expanded cards. |
| 30 | + - Proposal pages: PP / Chamber vote / Formation page models. |
| 31 | + - Courts list + courtroom page model. |
| 32 | + - Human nodes list + profile model. |
| 33 | + - Feed item model (the card layout currently used). |
| 34 | +2) Decide how IDs work across the system (proposalId, chamberId, humanId) and make them consistent. |
| 35 | + |
| 36 | +Deliverable: a short “API contract v1” section (types + endpoint list) that the backend must satisfy. |
| 37 | + |
| 38 | +## Phase 2 — API + DB skeleton (1–3 days) |
| 39 | +1) Create API worker entry (Cloudflare Worker / Pages Function). |
| 40 | +2) Add DB migrations + Drizzle schema (minimal tables first). |
| 41 | +3) Add `GET /api/health`. |
| 42 | +4) Add a `clock_state` row and an admin-only `POST /api/clock/advance-era` stub (no rollup logic yet). |
| 43 | +5) Add a small “seed” script/job that imports today’s `src/data/mock/*` into DB tables (so the first backend responses can match the UI immediately). |
| 44 | + |
| 45 | +Deliverable: deployed API that responds and can connect to the DB. |
| 46 | + |
| 47 | +## Phase 3 — Auth + eligibility gate (3–7 days) |
| 48 | +1) `POST /api/auth/nonce`: |
| 49 | + - store nonce with expiry |
| 50 | + - rate limit per IP/address |
| 51 | +2) `POST /api/auth/verify`: |
| 52 | + - verify signature |
| 53 | + - create/find `users` row |
| 54 | + - create session cookie/JWT |
| 55 | +3) `GET /api/gate/status`: |
| 56 | + - read session address |
| 57 | + - query eligibility via RPC/Subscan |
| 58 | + - cache result with TTL (`eligibility_cache`) |
| 59 | +4) Frontend wiring: |
| 60 | + - add “Connect wallet / Verify” UI (even a simple modal is enough) |
| 61 | + - disable all write buttons unless eligible (and show a short reason on hover) |
| 62 | + - allow non-eligible users to browse everything |
| 63 | + |
| 64 | +Deliverable: users can log in; the UI knows if they’re eligible; buttons are blocked for non-eligible users. |
| 65 | + |
| 66 | +## Phase 4 — Read models first (3–8 days) |
| 67 | +Goal: replace `src/data/mock/*` progressively with API reads, with minimal UI refactor. |
| 68 | + |
| 69 | +1) `GET /api/chambers`: |
| 70 | + - return chamber list + multipliers + computed stats |
| 71 | + - match `src/data/mock/chambers.ts` shape first |
| 72 | +2) `GET /api/proposals?stage=...`: |
| 73 | + - return proposal list cards for the Proposals page |
| 74 | + - match `src/data/mock/proposals.ts` shape first |
| 75 | +3) `GET /api/proposals/:id`: |
| 76 | + - return proposal page model for ProposalPP/Chamber/Formation |
| 77 | + - match `src/data/mock/proposalPages.ts` getters first |
| 78 | +4) `GET /api/feed?cursor=...`: |
| 79 | + - return feed items derived from `events` |
| 80 | + - match `src/data/mock/feed.tsx` shape first |
| 81 | + |
| 82 | +Frontend: |
| 83 | +- Create a tiny `src/lib/api.ts` fetch wrapper (base URL, typed helpers, error handling). |
| 84 | +- Add `src/lib/useApiQuery.ts` (simple cache) or adopt TanStack Query later. |
| 85 | +- Swap one page at a time from mock imports to API reads; keep a dev fallback flag if needed. |
| 86 | + |
| 87 | +Deliverable: app renders from backend reads (at least Chambers + Proposals + Feed). |
| 88 | + |
| 89 | +## Phase 5 — Event log (feed) as the backbone (2–6 days) |
| 90 | +1) Create `events` table (append-only). |
| 91 | +2) Define event types (union) and payload schemas (zod). |
| 92 | +3) Implement a simple “projector”: |
| 93 | + - basic derived feed cards from events |
| 94 | + - cursors for pagination |
| 95 | +4) Backfill initial events from seeded mock data (so the feed isn’t empty on day 1). |
| 96 | + |
| 97 | +Deliverable: feed is powered by real events; pages can also show histories from the event stream. |
| 98 | + |
| 99 | +## Phase 6 — First write slice: Proposal pool voting (4–10 days) |
| 100 | +1) Implement `POST /api/command` with: |
| 101 | + - auth required |
| 102 | + - gating required (`isActiveHumanNode`) |
| 103 | + - idempotency key support |
| 104 | +2) Implement `pool.vote` command: |
| 105 | + - write pool vote with unique constraint (proposalId + userId) |
| 106 | + - emit `pool.vote_cast` |
| 107 | + - update/compute pool metrics |
| 108 | + - if gates met, emit `proposal.moved_to_vote` + transition record |
| 109 | +3) Frontend: |
| 110 | + - ProposalPP page upvote/downvote calls API |
| 111 | + - optimistic UI optional (but must reconcile) |
| 112 | + |
| 113 | +Deliverable: users can perform one real action (pool vote) and see it in metrics + feed. |
| 114 | + |
| 115 | +## Phase 7 — Chamber vote (decision) + CM awarding (5–14 days) |
| 116 | +1) Add `chamber.vote` command: |
| 117 | + - yes/no/abstain |
| 118 | + - quorum + passing rule evaluation |
| 119 | + - emit events |
| 120 | +2) On pass: |
| 121 | + - transition to Formation if eligible |
| 122 | + - award CM (LCM per chamber) and recompute derived ACM |
| 123 | +3) Frontend: |
| 124 | + - ProposalChamber becomes real |
| 125 | + |
| 126 | +Deliverable: end-to-end proposal lifecycle from pool → vote (pass/fail) is operational. |
| 127 | + |
| 128 | +## Phase 8 — Formation v1 (execution) (5–14 days) |
| 129 | +1) Formation project row is created when proposal enters Formation. |
| 130 | +2) `formation.join` fills team slots. |
| 131 | +3) `formation.milestone.submit` records deliverables. |
| 132 | +4) `formation.milestone.requestUnlock` emits an event; acceptance can be mocked initially. |
| 133 | +5) Formation metrics and pages read from DB/events. |
| 134 | + |
| 135 | +Deliverable: Formation pages become real and emit feed events. |
| 136 | + |
| 137 | +## Phase 9 — Courts v1 (disputes) (5–14 days) |
| 138 | +1) `court.case.report` creates or increments cases. |
| 139 | +2) Case state machine: Jury → Session live → Ended (driven by time or thresholds). |
| 140 | +3) `court.case.verdict` records guilty/not-guilty. |
| 141 | +4) Outcome hooks (v1): |
| 142 | + - hold/release a milestone unlock request |
| 143 | + - flag identity as “restricted” (simulation only) |
| 144 | + |
| 145 | +Deliverable: courts flow works and affects off-chain simulation outcomes. |
| 146 | + |
| 147 | +## Phase 10 — Era rollups + tier statuses (ongoing) |
| 148 | +1) Implement cron rollup: |
| 149 | + - freeze era action counts |
| 150 | + - compute `isActiveGovernorNextEra` |
| 151 | + - compute tier decay + statuses (Ahead/Stable/Falling behind/At risk/Losing status) |
| 152 | + - update quorum baselines |
| 153 | +2) Store `era_snapshots` and emit `era.rolled` events. |
| 154 | + |
| 155 | +Deliverable: system “moves” with time and feels like governance. |
| 156 | + |
| 157 | +## Phase 11 — Hardening + moderation (ongoing) |
| 158 | +- Rate limiting (per IP/address) and anti-spam (per-era quotas). |
| 159 | +- Auditability: make all state transitions and changes event-backed. |
| 160 | +- Admin tools: manual “advance era”, seed data, freeze/unfreeze. |
| 161 | +- Observability: logs + basic metrics for rollups and gating failures. |
| 162 | +- Moderation controls (off-chain): |
| 163 | + - temporary action lock for a user |
| 164 | + - court-driven restrictions flags (simulation) |
| 165 | + |
| 166 | +## Suggested implementation order (lowest risk / highest value) |
| 167 | +1) Auth + gate |
| 168 | +2) Read models for Chambers + Proposals + Feed |
| 169 | +3) Event log |
| 170 | +4) Pool voting |
| 171 | +5) Chamber voting + CM awarding |
| 172 | +6) Formation |
| 173 | +7) Courts |
| 174 | +8) Era rollups + tier statuses |
| 175 | + |
| 176 | +## Milestone definition for “proto-vortex launch” |
| 177 | +Minimum viable proto-vortex for community: |
| 178 | +- Login with wallet signature |
| 179 | +- Eligibility gate from mainnet |
| 180 | +- Read-only browsing for all users |
| 181 | +- Eligible users can: |
| 182 | + - upvote/downvote in pool |
| 183 | + - vote yes/no/abstain in chamber vote |
| 184 | +- Feed shows real events |
| 185 | +- Era rollup runs at least manually (admin endpoint) |
| 186 | + |
| 187 | +## Notes specific to the current UI |
| 188 | +- The UI already has the key surfaces for v1: |
| 189 | + - `ProposalCreation` wizard (draft), ProposalPP (pool), ProposalChamber (vote), ProposalFormation (formation), Courts/Courtroom (courts). |
| 190 | +- Start by returning API payloads that match the existing mock getters (`src/data/mock/proposalPages.ts`, etc.) to avoid rewriting UI components. |
| 191 | +- Migrate page-by-page: keep visuals stable while the data source shifts from `src/data/mock/*` to `/api/*`. |
0 commit comments