# Schema-Drift: Testschema gegen Produktion

Die RLS- und Schema-Abnahme (`.github/workflows/rls.yml`) prüft Policies gegen ein
Testschema, das aus zwei Teilen entsteht: dem Bootstrap-Dump
`supabase/bootstrap/supabase-bootstrap-from-prod.sql` vom **2026-05-07** und der
Migrationskette in `scripts/rls-test/load-schema.sh`. Produktion ist seitdem
weitergezogen. Wie weit, stand nirgends — ein grüner Lauf konnte deshalb Aussagen
über Tabellen suggerieren, die es dort so nicht gibt.

Dieses Verzeichnis schließt die Lücke, soweit sie ohne Produktionszugang zu
schließen ist.

## Was hier liegt

| Datei | Zweck |
| --- | --- |
| `prod-tables-2026-08-04.txt` | Tabellenbestand des Live-Projekts, erhoben am 2026-08-04 über die Supabase-Management-API (`list_tables`). Nur Namen. |
| `baseline.json` | Eingefrorener Befund: jede bekannte Abweichung mit Klasse und Ursache. |
| `../../../scripts/schema-drift/compare.mjs` | Vergleich und Klassifikation. Fällt rot aus, sobald eine unbekannte Abweichung dazukommt oder eine bekannte verschwindet. |

## Aufruf

```bash
# gegen die dokumentierte Erhebung (Standard, kein Produktionszugang nötig)
node scripts/schema-drift/compare.mjs

# gegen einen echten Schema-Dump — der Zielzustand
node scripts/schema-drift/compare.mjs --dump /pfad/zu/prod-schema.sql

# Befund nach Prüfung als neue Baseline einfrieren
node scripts/schema-drift/compare.mjs --update-baseline
```

## Klassen

| Klasse | Bedeutung |
| --- | --- |
| `expected_migration_delta` | Der Unterschied entsteht durch eine Migration, die in der Kette bewusst enthalten, in Produktion aber noch nicht angewendet ist. Kein Drift, sondern der Zweck eines offenen Pull Requests. |
| `obsolete_bootstrap_object` | Steht im Testschema, in Produktion entfernt. Eine Migration im Repo belegt die Entfernung. |
| `missing_from_bootstrap` | Steht in Produktion, im Testschema nicht. Eine Migration im Repo legt die Tabelle an — sie ist nur nicht in der Ladekette. |
| `unresolved_without_prod_dump` | Keine Quelle im Repo. Ohne echten Dump nicht entscheidbar. |

## Befund vom 2026-08-04

30 Abweichungen, davon 28 vollständig aus dem Repository erklärbar:

- **12 × `missing_from_bootstrap`** — die Module Agenda (6, `agenda-schema` +
  `agenda-templates`) und LSC-Workshop V2 (6, `lsc-workshop-v2-schema`:
  `process_step_observations`, `process_step_optimizations`, `qaf_summary`,
  `qaf_process_mapping`, `lsc_tooltips`, `lsc_color_config`). Beide existieren als
  Migration im Repo, keine steht in der Ladekette — sie tragen keine
  Sicherheitsaussage, die dieser Stand behaupten würde.

  > Ursprünglich waren es 24. Das Sicherheitsreview zu #117 hat gezeigt, dass die
  > Klassifikation dreier Tabellen (`department_visibility`, `project_view_grants`,
  > `intake_submission_tokens`) in CI gar nicht geprüft werden konnte, weil sie im
  > Testschema fehlten. Die zugehörigen Migrationen (`intake-v1-schema`,
  > `intake-v1-public-submission-tokens`, `cross-dept-visibility-r40`) stehen
  > seitdem in der Kette — zusammen mit den beiden KAR-777-Sicherheitsfixes
  > `consultants-write-rls-admin-only` (#85) und `using-true-write-tranche-a` (#91),
  > die im Testschema ebenfalls gefehlt hatten.
- **14 × `obsolete_bootstrap_object`** — die 13 `cp_*`-Tabellen der am 2026-05-26
  entfernten Control-Plane (`control-plane-teardown`) und `shift_outputs`
  (`r6-shift-outputs-merge`).
- **2 × `expected_migration_delta`** — `rls_policy_snapshot` und `rls_access_class`
  entstehen erst mit `supabase-migration-internal-open-access-model.sql` (#117), die
  in Produktion bewusst noch nicht angewendet ist.
- **2 × `unresolved_without_prod_dump`** (unverändert):
  - `project_code_counters` existiert in Produktion, keine Migration im Repo legt sie an.
  - `project_id_sequence` existiert im Bootstrap-Dump, keine Migration im Repo entfernt
    sie, und in Produktion ist sie nicht mehr da.

## Offener Punkt: `production_schema_dump_validation_pending`

Die Erhebung liefert **Tabellennamen**. Nicht abgeglichen sind damit:

- Spalten, Typen, Nullability, Defaults, Constraints
- Policies, ihre Prädikate und Rollen
- Funktionen, Trigger, Views, Indizes
- Grants und Rollenzugehörigkeiten

Aussagen, die einen dieser Punkte brauchen, dürfen aus dieser Quelle **nicht**
abgeleitet werden. Was der RLS-Job belegt, belegt er über das im Repository
erzeugte Testschema — nicht über Produktion.

Aufgelöst wird der Punkt durch einen `pg_dump --schema-only` aus Produktion
(Operator-Zugang, siehe Stop-Regel „prod state is operator-owned" in `CLAUDE.md`).
Sobald er vorliegt:

```bash
node scripts/schema-drift/compare.mjs --dump <datei> --update-baseline
```

Danach ist die Namensliste hier nur noch Historie, und die 24 `missing_from_bootstrap`
sind entweder in die Ladekette aufgenommen oder als bewusst außerhalb dokumentiert.
