# RINX Roadmap — June 11, 2026 session

**Context:** PR-1–9 (Strike Segments stack) shipped. Aurora ingest live (~9M NC registration / ~36M history). Segment Engine + Studio shipped June 12.

**North star (unchanged):** Top 25 = WHO · Strike list = YOUR WHO · Battle cards = COST · Map = WHERE · Simulator = SCALE · Targeting = AMMO.

**Authoritative status:** `infra/LATEST-STATUS.json` (v1.24.0 — 1A Agency Console through PR-1A-2b shipped)

---

## 1A Agency Console — www product frame (2026-06-15)

**Spec:** `docs/superpowers/specs/2026-06-14-1a-agency-pinball-loop-design.md` · **Plan:** `docs/superpowers/plans/2026-06-14-1a-agency-pinball-loop.md` · **Doctrine:** `docs/superpowers/specs/2026-06-14-operator-doctrine-design.md`

| PR | Status | Notes |
|----|--------|-------|
| **PR-1A-1** | **Done** | Loop skeleton, plunger, FIND FX, attention timing, A/B |
| **PR-1A-2** | **Done** | FINISH portfolio (Sector Brief / Shooter Card / IE Receipt) |
| **PR-1A-4** | **Done** | Operator vs legacy dual gradient; sort modal; gates + copy |
| **PR-1A-3** | **Done** | EFFECT MAP + STACK; FIX\|EFFECT toggle; post-FINISH prompt |
| **PR-1A-2b** | **Done** | FINISH A/B metrics; role-weighted variants; canvas shooter share |
| **PR-1A-2c** | **Queued** | Agentic video FINISH stub — no provider until principal picks |
| **Task 12** | **Queued** | Paywall audit (spec §7) |
| **PR-17** | **Parallel** | Donor ingest — Summer 2026 theater elimination |
| **PR-16.5** | **Deferred** | Crowd merge — feeds richer STACK signal |

**Live:** `www.rinx.win` asset `20260614-1a-9` · preview `bb1254ea`

**Tests:** `node scripts/test-agency-loop.mjs` · `node scripts/smoke-www-proxy.mjs`

---

## Current state (2026-06-13)

| Area | Status |
|------|--------|
| **PR-20a** | **Done** — Filter DSL, preview/pull API, `staff_ops.rinx_segment_exports`, S3 CSV on `targeting API (unpublished)` |
| **PR-20b** | **Done** — Segment Studio v0.3.0 at `Segment Studio (unpublished)` (3-step wizard, geography + elections) |
| **PR-20c** | **Done** — Engagement bands (primary fighter, propensity, GOP loyalty) + filter adjudication |
| **Studio hardening** | **Done** — Async preview (CF 30s proxy bypass), per-step reset, segment/export delete (S3 + DB row only) |
| **PR-16** | **Done** — HITL commit gate + polish (bulk apply/commit, nuclear clear/restore) |
| **PR-21a** | **Done** — Donor ARM county/precinct scope, adaptive floor, geo panel, public meta, methodology |
| **PR-21b** | **Done** — COI Tier B engine + staff API + Studio panel (Tier A geo_presets; 18 NC suggestions) |
| **PR-22b** | **Done** — Cloudflare Access on ops; temp password gate retired |
| **PR-22c** | **In progress** — WAF + rate limits on `api.rinx.win` |
| **PR-16.5** | **Queued** — Reflexive crowd overlay merge (after 22c) |
| **PR-14.2** | **Deferred** — nocoDB staff UX redesign (principal use cases TBD) |

**Deploy surfaces:**

| Host | Stack | Branch / service |
|------|-------|------------------|
| `www.rinx.win` | Cloudflare Pages `rinx-win-main` | `main` |
| `Segment Studio (unpublished)` | Cloudflare Pages `rinx-ops-main` | **`production`** (custom domain) |
| `targeting API (unpublished)` | ECS `rinx-staging-targeting-api` | ECR `:latest` |
| `staff workflow (unpublished)` | ECS nocoDB | Infra only — UX paused |

---

## Historical — strike segments launch (June 10–11)

## Current state (done tonight)

| Area | Status |
|------|--------|
| PR-1–5 | Feature store, FastAPI cluster, Node bridge, Strike Segments UI, map overlay |
| PR-6 | Simulator footnote + capped efficiency boost |
| PR-7 | Unified ops export (CSV + print brief) |
| PR-8 | Admin commit overlay |
| PR-9 | Methodology transparency |
| Polish | Demo hit-rating calibration; Ops Brief RINX horizontal logo |
| Deploy | Full bundle workflow via `node scripts/build-deploy.mjs` |

---

## Next 10 PRs (PR-10 → PR-19)

Ordered by dependency. **PR-10–12 need your Aurora work (Section 3) unblocked or parallel.**

### PR-10 — Aurora voter schema & migration tooling
**Owner:** You (AWS) + thin code PR  
**Goal:** Replace ad-hoc JSON with a real NC voter analytics schema in Aurora Serverless v2.

- Tables (minimum): `voter_segments`, `segment_features`, `district_rollups`, `ingest_runs`, `feature_snapshots`
- History: `voter_history_events` (registration changes, primary participation flags, turnout bins by cycle)
- Alembic/Flyway or SQL migrations in repo: `services/targeting-api/migrations/`
- Seed script: validate row counts vs NC coverage expectations
- **Exit:** `SELECT count(*) FROM voter_segments WHERE state = 'NC'` returns production-scale rows

### PR-11 — targeting-api Aurora adapter
**Owner:** Code (after PR-10 schema exists)  
**Goal:** `cluster.py` reads features from Aurora instead of `voter-features-demo.json`.

- `RINX_DB_URL` (or Secrets Manager ref) replaces `RINX_VOTER_FEATURES` in prod
- `load_features()` → SQL query by `cd_id` list from strike districts
- Auto-detect: demo JSON locally, Aurora in ECS
- Production hit thresholds (0.85 / 0.70) apply when not on demo store
- **Exit:** ARM STRIKE on prod uses NC rows; smoke test passes against Aurora read replica

### PR-12 — NC voter history in clustering features
**Owner:** Code + your history ingest  
**Goal:** Segments reflect *history*, not just static propensity snapshots.

- Add feature cols: `primary_history_score`, `party_switch_propensity`, `turnout_delta_2cycle` (names TBD from your file layout)
- Update `voter-features.schema.json` + cluster weights / `turnout` preset
- Methodology section: what history fields mean
- **Exit:** Segment explain lines cite history (“Low primary history — persuasion + turnout focus” backed by real fields)

### PR-13 — ECS production stack (IaC)
**Owner:** You (AWS) + deploy doc PR  
**Goal:** Repeatable deploy off the laptop.

- Task definitions: `rinx-web` (Node + static), `rinx-targeting-api` (Python, internal only)
- ALB → `rinx-web:3001`; targeting API on service discovery / localhost sidecar pattern
- EFS mount: `data/` (geojson, jobs, overlay), Secrets Manager: `RINX_API_KEY`, `RINX_DB_URL`
- Health checks: `/api/health`, `/v1/health`
- **Exit:** `www.rinx.win` served from ECS; no manual SCP of partial files

### PR-14 — nocoDB staff ops plane
**Owner:** You (AWS ECS service) + integration PR  
**Goal:** PAC staff manage data without SSH.

- ECS service: nocoDB → Aurora (read/write bounded views for staff)
- Views: strike list exports log, donor verifications, targeting job queue, commit audit trail
- SSO or API token for staff; not public
- Node optional webhook: POST job-complete → nocoDB row
- **Exit:** Staff can see latest ARM runs and mark “reviewed” without admin.html alone
- **Status (2026-06-11):** Infra shipped (`staff_ops`, ECS, job sync). General staff UX **not** done — see PR-14.1.

### PR-14.1 — nocoDB workflow MVP (general staff)
**Owner:** Delegate UI setup to junior eng or junior political staff; principal verifies checklist only  
**Goal:** Comms / field staff can do three jobs in a browser without SQL, admin.html, or understanding nocoDB.

**Why this exists:** PR-14 built the pipes. PR-14.1 is the **control panel labels** — which screen shows “jobs waiting for review,” which column staff may edit, and which screens to hide so nobody browses 9M voter rows in a spreadsheet UI.

| Staff job | nocoDB screen (delegate sets up) | Staff never touches |
|-----------|-----------------------------------|---------------------|
| “Did last night’s voter file load?” | **Ingest latest** (2 rows) | Raw `voter_registration` |
| “What ARM runs need a human OK?” | **ARM jobs** table, filter `review_status = unreviewed` | Column `status` (API state machine) |
| “Who approved what?” | **Commit audit** (read-only) | Postgres, ECS, Terraform |

**Delegate tasks (setup once):**
1. External DB connection to `staff_ops` (readonly secret) — see [PR-14.1-nocodb-staff-setup.md](./PR-14.1-nocodb-staff-setup.md)
2. Create workflow bases only; **do not** expose full voter/history tables as grids
3. On `rinx_targeting_jobs`: `review_status` → Single Select (`unreviewed` / `reviewed` / `rejected`); hide `status`, `segments_json`, `summary_json`
4. Default view: Kanban or filtered grid — “Unreviewed jobs”
5. Set `RINX_STAFF_API_KEY` on targeting-api ECS (backup path: `review-arm-job.ps1`)

**Principal verification (no nocoDB knowledge required):**
- [ ] Junior logs in at `staff workflow host (unpublished)` and sees “Ingest latest” with recent dates
- [ ] After a donor ARM run, a new row appears in ARM jobs with `review_status = unreviewed`
- [ ] Junior changes that row to `reviewed`, fills `reviewed_by` — save succeeds (not “permission denied”)
- [ ] Junior cannot type arbitrary text into `review_status` (dropdown only)
- [ ] No base shows millions of voter rows

**Exit:** PR-14 original exit criterion met for non-technical staff.  
**Then:** PR-20a (segment engine for power users / political pros via PR-20b).

**Setup guide:** [PR-14.1-nocodb-staff-setup.md](./PR-14.1-nocodb-staff-setup.md)

**Status (2026-06-11):** **Paused** — infra + scripts shipped; staff UX not signed off. Design/use cases underspecified. **Do not iterate further until PR-14.2.** Backup path for ARM review: `review-arm-job.ps1` + `rinx/staff-api-key` (unchanged).

### PR-14.2 — nocoDB staff plane redesign *(target: ~June 14–16, 2026)*
**Owner:** Principal (design) → eng (implement)  
**Goal:** Revisit `staff workflow (unpublished)` with explicit use cases before more provisioning.

**Why paused:** PR-14.1 assumed nocoDB = “spreadsheet inbox” but did not lock who does what, what data is editable vs read-only, or what belongs in nocoDB vs Segment Studio vs API/scripts. External-Postgres + nocoDB auto-links made a simple grid fragile. **Not a tooling failure — spec gap.**

**Keep running (no teardown):**
- ECS `staff workflow (unpublished)`, `staff_ops` schema, job sync, `rinx/staff-api-key`, `repair-nocodb-staff.ps1`
- Staff review via API / `review-arm-job.ps1` until UI is redone

**Principal delivers before eng work (short doc or call notes):**

| Question | Example answers to pick from |
|----------|------------------------------|
| Who logs in? | Comms only · Field + comms · Political pros · Principal only |
| What do they **do** (verbs)? | Mark ARM reviewed · See ingest OK · Request segment pull · Read audit |
| What do they **never** see? | Voter rows · `segments_json` · AWS · Raw history |
| nocoDB vs Segment Studio? | nocoDB = HITL inbox only; pulls live in PR-20b `/studio` |
| Fault tolerance bar? | “Grids never 500” · “API backup always works” · both |

**PR-14.2 eng tasks (after design sign-off):**
1. One-page staff UX spec in repo (`docs/PR-14.2-...` when written)
2. Re-provision nocoDB from spec (or replace tool if spec says so)
3. Principal checklist §4 from PR-14.1, re-run on fresh base
4. Update PR-14.1 guide or supersede it

**Exit:** Principal signs off: “comms staffer X can do jobs A–C without help.”  
**Blocks:** PR-17/18 nocoDB dashboards only — PR-16/20a can proceed without this.

### PR-15 — NC ingest pipeline (S3 → Aurora)
**Owner:** You (AWS) + ETL script in repo  
**Goal:** Operationalize voter file refreshes.

- S3 bucket for raw NC files (voter roll, history, whatever vendor/format you have)
- Lambda or ECS scheduled task: validate → transform → upsert Aurora
- `ingest_runs` table: status, row counts, checksum, completed_at
- Alert on failure (SNS/email)
- **Exit:** Re-run ingest updates segments without redeploying site

### PR-20 — Segment Engine + Segment Studio (political professional gap)
**Owner:** Code + staff UI  
**Goal:** Checkbox criteria → small rich voter pulls for non-SQL staff; shared engine for power users.

- **PR-20a:** Filter DSL, `staff_ops.rinx_segment_exports`, preview/pull API, S3 CSV — **Done (2026-06-12)**
- **PR-20b:** Segment Studio at **`Segment Studio (unpublished)`** (NC geography + party + elections) — **Done (2026-06-12)**
- **PR-20c:** Propensity / loyalty bands in GUI (PR-12 features) — **Done (2026-06-12)**
- **Hardening:** Async preview, feature adjudication, reset/delete UX — **Done (2026-06-12)**
- **Spec:** [PR-20-segment-studio.md](./PR-20-segment-studio.md)
- **Exit:** Political pro pulls ≤5k walk-list rows without SQL; nocoDB stays workflow-only — **met**

### PR-16 — HITL commit workflow (staff approval gate)
**Owner:** Code + nocoDB  
**Goal:** No public map overlay until staff approves (agent-swarm precursor).

- Job states: `complete` → `pending_review` → `committed` | `rejected`
- Admin UI: review queue, diff preview, reject reason
- `/api/map-state` only serves `committed` overlays
- **Exit:** Donor ARM runs visible to donor; public map only after staff commit

### PR-17 — Donor & site action ingest
**Owner:** Code + Aurora/nocoDB  
**Goal:** Close the loop from site behavior to staff visibility.

- Events: strike list add/remove, ARM run, ops export download, battle card toggles (aggregated)
- Anedot webhook or manual donor verify → `donor_verifications` table
- No PII beyond what FEC/donor flow already allows
- **Exit:** nocoDB dashboard shows weekly donor engagement counts

### PR-18 — Email ops package (real backend)
**Owner:** Code + AWS (SES)  
**Goal:** Replace local-stub “Email my strike list” form.

- `POST /api/ops/email` — sends unified ops CSV/brief link or attachment via SES
- Consent checkbox enforced; unsubscribe token
- Staff sees send log in nocoDB
- **Exit:** Verified donor receives ops package in inbox

### PR-19 — Agent assist v0 (suggest, never auto-commit)
**Owner:** Code + AWS (later Bedrock/Lambda)  
**Goal:** First step toward “agent swarm” without bypassing HITL.

- After ARM run: optional staff-only summary (“top 3 HOT segments worth door knocks in SC-03”)
- Reads job JSON + NC context; writes suggestions to nocoDB, not public site
- **Exit:** Staff sees AI narrative beside job; zero auto-publish

### PR-21 — 1Password Business staff vault *(target: ~August 2026)*
**Owner:** Principal + first delegate hire  
**Goal:** Non-AWS staff and power users get passwords/keys in a browser app — not Secrets Manager, not Slack.

**Until then:** All staff-facing credentials live in one AWS secret: `rinx/staff-end-user-vault`. Re-run `scripts/sync-staff-end-user-vault.ps1` whenever a new staff-facing password, API key, or login is generated. Machine/infra secrets (`rinx/staff-api-key`, `rinx/nocodb-app`, Aurora admin, ECS env) stay in their own SM secrets; the vault **references** them for export.

| Vault (1Password) | Audience | Contents (from `staff-end-user-vault`) |
|-------------------|----------|----------------------------------------|
| **RINX Staff** | General comms / field staff | `staff workflow (unpublished)` login, SOP links |
| **RINX Staff (Delegate)** | Junior eng / setup delegate | `staff_ops` readonly DB (nocoDB re-wire only) |
| **RINX Ops** | Principal + political/technical power users, no AWS | Staff API key, `admin.html` overlay password |

**PR-21 tasks:**
1. Create 1Password Business org; invite first staff
2. Export `rinx/staff-end-user-vault` → import into vaults above (one-time migration script or manual)
3. Rotate nocoDB admin + staff API key at cutover; revoke shared admin after per-person logins
4. Document in PR-14.1 SOP: “get credentials from 1Password, not AWS”
5. Optional: Cloudflare Access in front of `staff workflow (unpublished)` (reduces shared-password surface)

**Exit:** Second human on PAC can log into staff tools without AWS CLI or principal handoff.  
**Depends on:** PR-14.1 done; ideally PR-16 (server-side admin gate) before rotating `admin.html` password.

---

## PR dependency graph

```
[You: Aurora schema] ──► PR-10 ──► PR-11 ──► PR-12
                              └──► PR-15 (ingest)
[You: ECS cluster]   ──► PR-13 ──► PR-14 (infra) ──► PR-14.1 (paused) ──► PR-14.2 (~Jun 14–16) staff redesign
                              └──► PR-20 (Segment Studio) ──► PR-16 ──► PR-19   [no nocoDB block]
PR-17 (donor ingest) ── after PR-14 infra; nocoDB dashboard waits PR-14.2
PR-18 (email)        ── after PR-7 (done) + SES; send log UI waits PR-14.2
PR-21 (1Password)    ── after first staff hire (~Aug 2026); vault staging: rinx/staff-end-user-vault (now)
```

**Suggested focus now:** **PR-22c** (WAF/rate limits) → then **PR-16.5** (crowd merge) or **PR-20d** (saved segments) — **not** more nocoDB tinkering until PR-14.2 design is written.

---

## 25 minor polishes (site UX / ops hygiene)

Low-risk, ship anytime between major PRs. Grouped by area.

### Deploy & docs (5)
1. Update `RELEASE-2026-06-10-strike-segments.md` — PR-6/7/9 done, demo tier note
2. Update `DEPLOY-rinx.win.md` §9 — remove stale “PR-7 not shipped”
3. Add `BUILD.json` release tag to admin status panel
4. `UPLOAD-THIS-FOLDER.txt` — mention logo assets in `images/`
5. Smoke test: assert ops CSV contains `[STRIKE TARGETS]` and `[STRIKE SEGMENTS]` blocks

### Strike Segments & export (6)
6. Ops CSV UTF-8 BOM for Excel on Windows
7. Ops export: include `jobId` and `preset` in CSV header comments
8. Print brief: print margins + `@page` footer with FEC disclaimer
9. Print brief: smaller logo on mobile print (`max-width: 160px`)
10. `#ops-export-summary` updates after ARM without page refresh edge cases
11. Disable DOWNLOAD OPS PACKAGE when strike list empty (grey button)

### Map & targeting (5)
12. Segment overlay legend — higher contrast HOT/WARM/COLD chips
13. Map: loading skeleton while `cd118.geojson` fetches
14. District tooltip — segment hit color matches table badge
15. “Tune focus” preset one-line descriptions (collapsed)
16. Restore last ARM run from hash deep link (`#strike=5,6,4`)

### Top 25 & strike list (5)
17. Strike list modal — “ARM STRIKE →” CTA when non-empty
18. Most Wanted card photo fallback — consistent aspect ratio
19. Filter modal — persist state to `localStorage`
20. Primary countdown badge — timezone-safe for NC/IN/SC primaries
21. Toast `aria-live="polite"` for screen readers

### Admin & trust (4)
22. Admin commit — confirm dialog shows district count + segment count
23. Admin — show `builtAt` from `BUILD.json` on login
24. Methodology — link to this roadmap from data sources section
25. Service worker — network-first for `targeting-engine.js` cache bust after deploy

---

## Your AWS checklist (NC voter data + staff backend)

Items **on you** to unblock PR-10–15. Check off as done.

### A. Aurora Serverless v2 (NC voter + history)

| # | Task | Notes |
|---|------|-------|
| A1 | Confirm Aurora cluster endpoint, engine version, capacity min/max | Serverless v2 ACU bounds for cost control |
| A2 | VPC + subnets + security groups | ECS tasks and Lambda must reach Aurora; no public DB |
| A3 | Secrets Manager: `rinx/aurora` URL with IAM auth or user/pass | Rotation policy |
| A4 | Document NC source files | Vendor, format (CSV/TXT/DB dump), refresh cadence, legal retention |
| A5 | Design schema with eng | Share column list for voter roll + history before PR-10 merges |
| A6 | Load initial NC dataset | One-state focus first; CD-level segment aggregates only for public API |
| A7 | History tables populated | Primary participation by cycle, reg changes — match PR-12 feature plan |
| A8 | Read replica (optional) | targeting-api read-only user for cluster queries |
| A9 | Backup / PITR enabled | Aurora backup window + test restore |
| A10 | Query audit | Log slow queries; no raw voter PII exported to rinx.win |

### B. S3 + ingest (operationalize refreshes)

| # | Task | Notes |
|---|------|-------|
| B1 | S3 bucket `rinx-nc-voter-raw` (private, encrypted) | Versioning on |
| B2 | IAM role for ingest job | Least privilege: S3 read, Aurora write |
| B3 | Schedule (EventBridge) or manual trigger | Document runbook |
| B4 | Share sample file + row counts | Needed for ETL PR-15 |

### C. ECS cluster (production site + workers)

| # | Task | Notes |
|---|------|-------|
| C1 | ECS cluster + Fargate capacity providers | Same region as Aurora |
| C2 | ECR repos: `rinx-web`, `rinx-targeting-api` | CI push images |
| C3 | Task def `rinx-web` | Node `serve.mjs`, port 3001, env from Secrets Manager |
| C4 | Task def `rinx-targeting-api` | Uvicorn :8000, **no public IP** |
| C5 | EFS volume for `data/` | geojson, targeting-jobs, cluster-overlay |
| C6 | ALB + ACM cert `www.rinx.win` | TLS termination |
| C7 | Target group health: `/api/health` | |
| C8 | `TARGETING_API_URL` = internal service connect DNS | e.g. `http://rinx-targeting-api:8000` |
| C9 | Deploy `pr7-rc` full bundle to EFS/image | Never partial file copy |
| C10 | CloudWatch logs per service | |

### D. nocoDB staff backend (ECS)

| # | Task | Notes |
|---|------|-------|
| D1 | ECS service nocoDB (official image) | Staff-only security group / VPN / IP allowlist |
| D2 | nocoDB → Aurora connection | Separate DB or schema `staff_ops`; not voter PII raw export |
| D3 | Base views: jobs, commits, donor events, ingest runs | Maps to PR-14 |
| D4 | Staff accounts / API tokens | No shared password in repo |
| D5 | Backup nocoDB metadata | |

### E. Secrets & compliance

| # | Task | Notes |
|---|------|-------|
| E1 | `RINX_API_KEY` in Secrets Manager (prod key ≠ dev) | Machine/infra — not in staff vault |
| E2 | FEC / PAC counsel sign-off on voter data use | Aggregated public output only |
| E3 | Data retention policy documented | Align with methodology privacy section |
| E4 | **`rinx/staff-end-user-vault`** — single bundle for staff+ end users | **Done (2026-06-11).** Sync via `scripts/sync-staff-end-user-vault.ps1` after any new staff-facing secret. Source of truth for PR-21 → 1Password export. |
| E5 | PR-21 1Password Business | Target ~Aug 2026 when second human joins PAC |

---

## When you return (~10 hours)

1. **Production:** Full folder upload `deploy/` → `www.rinx.win` if not already on `pr7-rc`
2. **Share with eng:** NC file schema sample (even 100 redacted rows) → kicks off PR-10
3. **Parallel:** ECS task defs (C1–C9) while schema is in flight
4. **Pick 3–5 polishes** from the list above for quick wins between PRs

---

## Explicitly later (not in PR-10–20)

- **PR-21** — 1Password Business (scheduled ~Aug 2026; vault staging active now)
- Full agent swarm / open-world donor-voter loop
- Multi-state beyond NC
- Cathartha / Irish fork (`:3000`) — separate repo
- Real-time FEC donor API (vs simulated $47 verify)

---

*Generated after PR-7 + Ops Brief logo ship. Head: `45602be`.*