# Official lists

Published preparedness lists anyone can read, and the editor's draft, two-person review and publish workflow (US-37).

## What it does [#what-it-does]

An official list is a versioned set of rules (ADR-0016): every quantity cites its source, date and version, and a **published version never changes**. The app ships `DE-2025.10` and `EU-72H` from `@varu/preparedness`; the same two are seeded as published rows, and newer versions come from the editor.

An **editorial Organization** curates a country's list (`list_curator`, set by the seed or an admin). Its Members with the Org role `editor` and two-factor sign-in work on a **draft**: it starts from the country's current version (or the EU fallback), they add, change or remove rules, and the screen shows "changes only" against that base. **Request review** locks the draft and records the author's sign-off; a **second, different editor approves**; then either publishes. Publishing inserts the version and its rules and cites both reviewers (RNF12). A correction is a new version id.

## Procedures [#procedures]

| Procedure                                                         | Who                                            | Failures                                                            |
| ----------------------------------------------------------------- | ---------------------------------------------- | ------------------------------------------------------------------- |
| `lists.current` · `GET /api/lists/{country}`                      | anyone, no account                             | `NOT_FOUND` (no list, not even the EU fallback)                     |
| `lists.version` · `GET /api/lists/{country}/versions/{versionId}` | anyone                                         | `NOT_FOUND`                                                         |
| `lists.changesSince` · `GET /api/lists/{country}/changes?since=`  | anyone                                         | `NOT_FOUND` (unknown `since`)                                       |
| `lists.editor.overview`, `lists.editor.draft`                     | `editor` with 2FA in the Active Organization   | `FORBIDDEN`, `NOT_FOUND`                                            |
| `lists.editor.createDraft`                                        | `editor` with 2FA in the curating Organization | `FORBIDDEN` (not curated), `CONFLICT` (version id taken)            |
| `lists.editor.saveRule`, `lists.editor.removeRule`                | same                                           | `CONFLICT` (draft in review or published)                           |
| `lists.editor.requestReview`                                      | same                                           | `CONFLICT` (not editing)                                            |
| `lists.editor.approve`                                            | a different `editor`                           | `CONFLICT` (not in review, already signed)                          |
| `lists.editor.publish`                                            | same                                           | `CONFLICT` with `data: { have, need }` under two distinct sign-offs |

Every editor write is audited (`list.draft_created`, `list.rule_changed`, `list.review_requested`, `list.signed_off`, `list.published`).

## Data and isolation [#data-and-isolation]

| Table                               | Tenant access                                                                                                                                                                                    |
| ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `list_curator`                      | select own Organization's rows; written by the system only                                                                                                                                       |
| `list_draft`, `list_draft_rule`     | the curating Organization; a published draft admits no update                                                                                                                                    |
| `list_sign_off`                     | insert-only, an editor signs only for themself                                                                                                                                                   |
| `list_version`, `list_version_rule` | select for everyone; insert only by the curating Organization with two distinct sign-offs (policy); **no UPDATE/DELETE grant, and a trigger refuses UPDATE, DELETE and TRUNCATE for every role** |

Migrations: `20261106193939_lists` (tables, policies) and `20261106193946_lists_grants_immutable_seed` (grants, immutability triggers, the two shipped versions). `lists.procedures.test.ts` checks that the seeded rows equal the package.

## Key files [#key-files]

- [`packages/api/src/features/lists/lists.procedures.ts`](https://github.com/CarlosZiegler/varu/blob/main/packages/api/src/features/lists/lists.procedures.ts)
- [`packages/api/src/features/lists/lists.service.ts`](https://github.com/CarlosZiegler/varu/blob/main/packages/api/src/features/lists/lists.service.ts)
- [`packages/db/src/schema/lists.ts`](https://github.com/CarlosZiegler/varu/blob/main/packages/db/src/schema/lists.ts)
- [`packages/preparedness/src/diff.ts`](https://github.com/CarlosZiegler/varu/blob/main/packages/preparedness/src/diff.ts) ("changes only")
- [`apps/web/src/features/lists/lists.page.tsx`](https://github.com/CarlosZiegler/varu/blob/main/apps/web/src/features/lists/lists.page.tsx) (D-Editor, `/official-list`)
- [`apps/web/src/routes/api/lists/$.ts`](https://github.com/CarlosZiegler/varu/blob/main/apps/web/src/routes/api/lists/$.ts) (plain HTTP GETs)

