This PR delivers the first three items in the documented backend sequence: CR-72 (roadmap aligned with the real stack), CR-73 (CreateFlowState formalized + Zod validation and size limits on draft/publish APIs), and CR-74 (magic-link sign-in end-to-end: APIs, Prisma, mail, /login, session-aware header, placeholder /profile).
It also includes the supporting repo work that shows up in the same branch: Prisma schema/migrations, app/api/* route handlers (auth, drafts, rules, templates, health), lib/server/*, Docker/Mailhog, CONTRIBUTING / .env.example, create-flow backend sync wiring, Playwright (standalone e2e server, visual snapshot refresh, visual:update fix), Vitest fixes, navigation session integration, and Storybook/tests for Login.
Out of scope here (next tickets):CR-75 create-flow session UI in CreateFlowTopNav, and later items in the chain (CR-76+).
Changes
CR-72 — Roadmap & contributor truth
Update docs/backend-roadmap.md: current backend (Postgres/Prisma, APIs, draft sync, build order, operator vs shipped vs remaining).
Login /login reliability:usePortal={false} for full-page login + ESLint-safe portal readiness (requestAnimationFrame) for modal path.
Screenshots
How to Test
Env & DB:.env from .env.example, Postgres + npx prisma migrate dev (or deploy) per CONTRIBUTING.
CR-73: Exercise PUT /api/drafts/me and POST /api/rules with invalid/oversized JSON; expect 400 with structured error, not 500.
CR-74:/login → request link → complete verify (logs or Mailhog); /api/auth/session with cookie; header Profile → /profile; logout clears session.
Automation:npm run lint, npm run test, CI= npm run e2e (or CI pipeline with build + standalone server on 127.0.0.1:3010).
Storybook: Login stories if you want visual review without full app.
Notes
Linear: Close or update CR-72, CR-73, and CR-74 to match this PR; CR-75 remains the natural follow-up (create-flow session + sign-out in flow).
CR-74 residual: On real staging/production, confirm magic-link URLs match the public site (Host / reverse proxy vs request.nextUrl.origin); can stay a small follow-up issue.
Review tip: Many files are PNG snapshot updates—filter *.png in the diff if helpful.
Local e2e: If CI=1 is set locally, Playwright may not start webServer—use CI= or run npm run build && npm run start:e2e yourself.
# Backend foundation: CR-72–CR-74 (roadmap, CreateFlow validation, magic-link auth)
## Overview
This PR delivers the first three items in the documented backend sequence: **[CR-72](https://linear.app/community-rule/issue/CR-72/backend-align-docsbackend-roadmapmd-with-current-codebase)** (roadmap aligned with the real stack), **[CR-73](https://linear.app/community-rule/issue/CR-73/backend-formalize-createflowstate-validate-draftpublish-api-payloads)** (`CreateFlowState` formalized + Zod validation and size limits on draft/publish APIs), and **[CR-74](https://linear.app/community-rule/issue/CR-74/backend-email-otp-sign-in-ui-existing-apis)** (magic-link sign-in end-to-end: APIs, Prisma, mail, `/login`, session-aware header, placeholder `/profile`).
It also includes the **supporting repo work** that shows up in the same branch: **Prisma schema/migrations**, **`app/api/*` route handlers** (auth, drafts, rules, templates, health), **`lib/server/*`**, **Docker/Mailhog**, **CONTRIBUTING / `.env.example`**, **create-flow backend sync** wiring, **Playwright** (standalone e2e server, visual snapshot refresh, `visual:update` fix), **Vitest** fixes, **navigation** session integration, and **Storybook/tests** for Login.
**Out of scope here (next tickets):** **CR-75** create-flow session UI in **CreateFlowTopNav**, and later items in the chain (CR-76+).
## Changes
### CR-72 — Roadmap & contributor truth
- Update **`docs/backend-roadmap.md`**: current backend (Postgres/Prisma, APIs, draft sync, build order, operator vs shipped vs remaining).
- Expand **`docs/backend-linear-tickets.md`**: ticket text, Linear mapping table, CR-74 residuals, dependency notes (e.g. CR-84/CR-85/CR-86).
- **`CONTRIBUTING.md`**: API overview, magic-link local testing, Mailhog, env hints.
- **`.env.example`**, **`README.md`** touch-ups as needed for local/backend setup.
### CR-73 — Create flow types & API validation
- **`app/create/types.ts`**: structured **`CreateFlowState`** (replacing loose `unknown` where agreed).
- **`lib/server/validation/`**: Zod (and helpers) for draft payload and publish document; plain JSON / size checks; consistent **400** responses on bad input.
- **`app/api/drafts/me/route.ts`**, **`app/api/rules/route.ts`**: validate bodies with those schemas.
- **`tests/unit/createFlowValidation.test.ts`**: schema/route-shape coverage.
- **`package.json`**: **`zod`** (and lockfile) as required.
### CR-74 — Magic-link auth & session UX
- **APIs:** `POST /api/auth/magic-link/request`, `GET /api/auth/magic-link/verify`, `GET /api/auth/session`, `POST /api/auth/logout`.
- **Data:** **`prisma/schema.prisma`**, migrations (**`MagicLinkToken`**, etc.), **`lib/server/`** (session, mail, hash, rate limit, **`navAuth`** for SSR header).
- **UI:** **`app/login/`**, **`Login`** modal stack (**`LoginForm`**, container/view/types), **`app/profile/`** placeholder.
- **Nav:** **`ConditionalNavigation`** / **`TopNav`**: **Log in** vs **Profile** from real session; client/server split where needed.
- **Client:** **`lib/create/api.ts`** (`requestMagicLink`, session helpers, **`safeInternalPath`**), **`ContentLockup` / `ModalHeader`** adjustments for login/a11y.
- **`stories/modals/Login.stories.tsx`**, **`tests/components/Login*.tsx`**.
### Shared / cross-cutting (supports 72–74)
- **`CreateFlowBackendSync`**, **`CreateFlowContext`** / layout hooks for optional server draft sync.
- **`app/api/`** for **health**, **templates**, and other handlers present in the branch diff.
- **`Dockerfile`**, **`docker-compose.yml`**, **`.dockerignore`**, **`.gitea/workflows/ci.yaml`** updates.
- **`next.config.mjs`**, **`playwright.config.ts`**, **`package.json`** scripts (**`start:e2e`**, **`visual:update`** with **`--update-snapshots=all`**), **`scripts/seed-snapshots.sh`**.
- **Playwright visual PNG baselines**; **`tests/pages/home.test.jsx`** dynamic section wait.
- **Login `/login` reliability:** **`usePortal={false}`** for full-page login + ESLint-safe portal readiness (**`requestAnimationFrame`**) for modal path.
## Screenshots
<img width="739" alt="Screenshot 2026-04-06 at 4.58.47 PM.png" src="attachments/69452e3d-d4f2-43bf-be5f-c9caf5cb2a1a">
<img width="830" alt="Screenshot 2026-04-06 at 5.00.50 PM.png" src="attachments/9338eb56-8583-4f17-9bca-c6c2fbcbfe38">
<img width="1327" alt="Screenshot 2026-04-06 at 5.01.17 PM.png" src="attachments/d3c6a139-091b-4477-8041-f838956f353e">
<img width="835" alt="Screenshot 2026-04-06 at 5.03.19 PM.png" src="attachments/dd13a48b-f163-43b7-aec6-b59b5e8bc0b6">
<img width="835" alt="Screenshot 2026-04-06 at 5.03.36 PM.png" src="attachments/bb62ed0c-a523-420e-86de-a71c328fe8e3">
## How to Test
1. **Env & DB:** `.env` from **`.env.example`**, Postgres + **`npx prisma migrate dev`** (or deploy) per **CONTRIBUTING**.
2. **CR-73:** Exercise **`PUT /api/drafts/me`** and **`POST /api/rules`** with invalid/oversized JSON; expect **400** with structured error, not **500**.
3. **CR-74:** **`/login`** → request link → complete verify (logs or Mailhog); **`/api/auth/session`** with cookie; header **Profile** → **`/profile`**; logout clears session.
4. **Automation:** **`npm run lint`**, **`npm run test`**, **`CI= npm run e2e`** (or CI pipeline with build + standalone server on **127.0.0.1:3010**).
5. **Storybook:** Login stories if you want visual review without full app.
## Notes
- **Linear:** Close or update **CR-72**, **CR-73**, and **CR-74** to match this PR; **CR-75** remains the natural follow-up (create-flow session + sign-out in flow).
- **CR-74 residual:** On real **staging/production**, confirm magic-link URLs match the public site (**`Host`** / reverse proxy vs **`request.nextUrl.origin`**); can stay a small follow-up issue.
- **Review tip:** Many files are **PNG** snapshot updates—filter `*.png` in the diff if helpful.
- **Local e2e:** If **`CI=1`** is set locally, Playwright may not start **`webServer`**—use **`CI=`** or run **`npm run build && npm run start:e2e`** yourself.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Backend foundation: CR-72–CR-74 (roadmap, CreateFlow validation, magic-link auth)
Overview
This PR delivers the first three items in the documented backend sequence: CR-72 (roadmap aligned with the real stack), CR-73 (
CreateFlowStateformalized + Zod validation and size limits on draft/publish APIs), and CR-74 (magic-link sign-in end-to-end: APIs, Prisma, mail,/login, session-aware header, placeholder/profile).It also includes the supporting repo work that shows up in the same branch: Prisma schema/migrations,
app/api/*route handlers (auth, drafts, rules, templates, health),lib/server/*, Docker/Mailhog, CONTRIBUTING /.env.example, create-flow backend sync wiring, Playwright (standalone e2e server, visual snapshot refresh,visual:updatefix), Vitest fixes, navigation session integration, and Storybook/tests for Login.Out of scope here (next tickets): CR-75 create-flow session UI in CreateFlowTopNav, and later items in the chain (CR-76+).
Changes
CR-72 — Roadmap & contributor truth
docs/backend-roadmap.md: current backend (Postgres/Prisma, APIs, draft sync, build order, operator vs shipped vs remaining).docs/backend-linear-tickets.md: ticket text, Linear mapping table, CR-74 residuals, dependency notes (e.g. CR-84/CR-85/CR-86).CONTRIBUTING.md: API overview, magic-link local testing, Mailhog, env hints..env.example,README.mdtouch-ups as needed for local/backend setup.CR-73 — Create flow types & API validation
app/create/types.ts: structuredCreateFlowState(replacing looseunknownwhere agreed).lib/server/validation/: Zod (and helpers) for draft payload and publish document; plain JSON / size checks; consistent 400 responses on bad input.app/api/drafts/me/route.ts,app/api/rules/route.ts: validate bodies with those schemas.tests/unit/createFlowValidation.test.ts: schema/route-shape coverage.package.json:zod(and lockfile) as required.CR-74 — Magic-link auth & session UX
POST /api/auth/magic-link/request,GET /api/auth/magic-link/verify,GET /api/auth/session,POST /api/auth/logout.prisma/schema.prisma, migrations (MagicLinkToken, etc.),lib/server/(session, mail, hash, rate limit,navAuthfor SSR header).app/login/,Loginmodal stack (LoginForm, container/view/types),app/profile/placeholder.ConditionalNavigation/TopNav: Log in vs Profile from real session; client/server split where needed.lib/create/api.ts(requestMagicLink, session helpers,safeInternalPath),ContentLockup/ModalHeaderadjustments for login/a11y.stories/modals/Login.stories.tsx,tests/components/Login*.tsx.Shared / cross-cutting (supports 72–74)
CreateFlowBackendSync,CreateFlowContext/ layout hooks for optional server draft sync.app/api/for health, templates, and other handlers present in the branch diff.Dockerfile,docker-compose.yml,.dockerignore,.gitea/workflows/ci.yamlupdates.next.config.mjs,playwright.config.ts,package.jsonscripts (start:e2e,visual:updatewith--update-snapshots=all),scripts/seed-snapshots.sh.tests/pages/home.test.jsxdynamic section wait./loginreliability:usePortal={false}for full-page login + ESLint-safe portal readiness (requestAnimationFrame) for modal path.Screenshots
How to Test
.envfrom.env.example, Postgres +npx prisma migrate dev(or deploy) per CONTRIBUTING.PUT /api/drafts/meandPOST /api/ruleswith invalid/oversized JSON; expect 400 with structured error, not 500./login→ request link → complete verify (logs or Mailhog);/api/auth/sessionwith cookie; header Profile →/profile; logout clears session.npm run lint,npm run test,CI= npm run e2e(or CI pipeline with build + standalone server on 127.0.0.1:3010).Notes
Host/ reverse proxy vsrequest.nextUrl.origin); can stay a small follow-up issue.*.pngin the diff if helpful.CI=1is set locally, Playwright may not startwebServer—useCI=or runnpm run build && npm run start:e2eyourself.