# Kundenansicht neu: Umsetzungsplan, Übersicht und Verträge > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. > Diese Übersicht ist der Vertrag zwischen den Paketen. Namen, Signaturen, Datenformen, Endpunkte und Dateizuständigkeiten stehen nur hier und gelten für alle Paket-Pläne. Weicht ein Paket-Plan davon ab, gilt diese Übersicht. **Goal:** Die Kundenansicht des Portals wird weiß und schlank wie das Ticketsystem, bekommt die Handy-Leiste „Fahrstuhl-Zeile“ und die Funktionen mit „Meine/Entdecken“. Die Funktionen stecken im Abo und werden eingeschaltet statt gekauft. Bestätigungen schrumpfen auf das rechtlich Nötige. Alles liegt hinter einem Pilot-Schalter, bis der Inhaber umschaltet. **Architecture:** - Die neue Oberfläche ist eine eigene Seite `public/neu/dashboard.html` mit eigenen ES-Modulen und CSS unter `public/neu/`. Der Server liefert sie unter derselben Adresse `/dashboard.html` aus, wenn der Pilot-Schalter für die Sitzung gilt. - Die Funktionsmodule (`public/termine.js`, `shop.js` …) bleiben gemeinsam. Ihr Aussehen kommt aus dem CSS der jeweils geladenen Seite, ihr Verhalten unterscheidet sich nur über das Feld `modell` der Server-Antwort. - Neue Server-Logik ist nur für Sitzungen mit neuer Ansicht aktiv (`neueAnsicht(req)`) oder rein additiv. Die alte Ansicht verhält sich bis zum Umschalten exakt wie heute. **Tech Stack:** - Portal: Node 20 (Prod-Container `node:20-alpine`, Host 24), Express 4, Appwrite-REST (`server/services/appwriteAdmin.js`), Stripe SDK 19, Vanilla JS ohne Build. - Tests Portal: `node --test` mit den Nachbauten aus `test/support/` (`appwriteMock`, `fakeStripe`, `testApp`, `fixtures`, `fakeDom`, `mailSink`) und Playwright-Skripte `test/ui/*.ui.mjs`. - WOMS: React 18, Vite, Tests `node --test` über `tests/unit/load.mjs`. **Spec:** `docs/superpowers/specs/2026-10-15-kundenansicht-neu-design.md` (Commit d3d2849, Abschnitte 1–8 mit E1–E21 und C1–C15). Jeder Implementierer liest die Spec, diese Übersicht und seinen Paket-Plan. **Code-Karten** (Stand 16.10., nur gelesen): `/root/reports/kundenansicht-neu-2026-10-15/karten/` - `huelle-tests.md`: dashboard, app.js, Router, Tests, Pins - `funktionen-server.md`: Funktionen, Zustände, Bestellen, Aktivieren, Abo-Abgleich - `abo-server.md`: Stundenkonto, Abo, Wechsel, Widerruf, Fassungen, Kundenart, Mails - `woms-vorschau.md`: WOMS-Stellen, Vorschau Dazu die Navigations-Demo `/root/reports/kundenansicht-neu-2026-10-15/handy-leiste/final-2-frei.html`. --- ## Ausführungsmodell ``` Phase A P0 Werkzeug, Pilot-Schalter, Vorschau-Gerüst (1 Agent, Hauptzweig) Phase B parallel in eigenen Worktrees: Strang UI: P1 Fundament+Hülle → P2 Website/Chat/Verträge Strang Server: P5 Abo-Modell Kern → P6 Abo-Modell Betrieb Strang Recht: P8 Rechtsrunde Fassung 6 (Entwürfe + Fassungs-Technik) Strang WOMS: P9 WOMS (gegen die Verträge V4/V9; Portal-Teile von P9 erst nach Merge von P5) Phase C Merge Server → Hauptzweig, dann Strang UI weiter: P3 Funktionen-Rahmen → P4 Funktionsmodule Phase D Merge Recht + UI → P7 Bestellseiten schlank (Hauptzweig) Phase E Integration, ganze Suite, Vorschau aktualisieren, Abschlussprüfung (mehrere Blickwinkel), Übergabe → der Inhaber pusht (Schalter off = für Kunden unsichtbar), Pilot fürs Team, Rechtsrunde mit dem Inhaber Phase F P10 Umschalten und Aufräumen (nur mit ausdrücklichem OK des Inhabers) ``` - **Ein Implementierer je Strang**, Modell `sonnet`. Die Tasks eines Strangs laufen der Reihe nach mit TDD. - **Prüfungen:** keine Reviewer je Task. Jeder Paket-Plan endet mit einer Selbstprüfung: eigene Tests grün, Verträge abgehakt, `git status` sauber. Eine gemeinsame Abschlussprüfung gibt es in Phase E. Kleine Nachprüfungen mit `haiku`. Kein `opus`, kein `fable` (Memory „feedback-billige-modelle“). - **Merge-Reihenfolge in den Hauptzweig:** P0 → Server (P5, P6) → Recht (P8) → UI (P1–P4) → P7. Konflikte entstehen nur in den Dateien, die die Zuständigkeitstabelle als „geteilt“ markiert. - **Tags:** nach jedem Paket `kn-p` im jeweiligen Zweig. Nach dem Merge auf den Hauptzweig `kn-p-haupt`. ### Pakete, Plan-Dateien, Worktrees | Paket | Plan-Datei (`docs/superpowers/plans/`) | Worktree | Branch | Start | |---|---|---|---|---| | P0 Werkzeug, Schalter, Vorschau | `2026-10-16-kundenansicht-01-werkzeug-vorschau.md` | `/home/webklar/apps/kundenbereich.webklar.com/dev-kundenansicht` | `feature/kundenansicht-neu` | sofort | | P1 Fundament und Hülle | `…-02-fundament-huelle.md` | `…/dev-kn-ui` | `kn/ui` | nach P0 | | P2 Website, Chat, Verträge | `…-03-website-chat-vertraege.md` | `…/dev-kn-ui` | `kn/ui` | nach P1 | | P3 Funktionen-Rahmen | `…-04-funktionen-rahmen.md` | `…/dev-kn-ui` | `kn/ui` (rebased auf Hauptzweig nach Server-Merge) | nach P2 und Server-Merge | | P4 Funktionsmodule | `…-05-funktionsmodule.md` | `…/dev-kn-ui` | `kn/ui` | nach P3 | | P5 Abo-Modell Kern | `…-06-abo-modell-kern.md` | `…/dev-kn-server` | `kn/server` | nach P0 | | P6 Abo-Modell Betrieb | `…-07-abo-modell-betrieb.md` | `…/dev-kn-server` | `kn/server` | nach P5 | | P7 Bestellseiten schlank | `…-08-bestellseiten.md` | `…/dev-kundenansicht` | `feature/kundenansicht-neu` | nach Merge von UI und Recht | | P8 Rechtsrunde Fassung 6 | `…-09-rechtsrunde.md` | `…/dev-kn-recht` | `kn/recht` | nach P0 | | P9 WOMS | `…-10-woms.md` | `/home/webklar/apps/ticket.webklar.com/dev-kundenart` | `feature/kundenart` (WOMS, von `origin/test`) | nach P0 | | P10 Umschalten und Aufräumen | `…-11-umschalten-aufraeumen.md` | `…/dev-kundenansicht` | `feature/kundenansicht-neu` | nach Phase E und OK des Inhabers | **Worktrees der Stränge** entstehen aus dem Stand nach P0, zum Beispiel: `git -C /home/webklar/apps/kundenbereich.webklar.com/dev-kundenansicht worktree add ../dev-kn-ui -b kn/ui kn-p0` - Danach `ln -s ../build/node_modules node_modules` im neuen Worktree. Der Symlink ist unversioniert, deshalb **nie `git add -A`**, Dateien immer einzeln hinzufügen. - Der WOMS-Worktree entsteht von `origin/test`: `git -C /home/webklar/apps/ticket.webklar.com/build worktree add ../dev-kundenart -b feature/kundenart origin/test`. Danach `npm ci` mit Speicherbremse, siehe Global Constraints. - Der WOMS-Zweig `fix/akquise-sichtbar` (`5f13553`, Worktree `dev-akquise-sichtbar`) ist nicht gepusht und ändert `CreateLeadForm` und `createLead`. P9 baut auf `origin/test`. Beim Merge in `test` gilt die Reihenfolge, die der Inhaber festlegt. Ist `fix/akquise-sichtbar` bis dahin in `test`, rebased P9 darauf. ### Dateizuständigkeit (Konfliktschutz) Jede Datei hat genau einen Besitzer. Wer etwas aus einer fremden Datei braucht, nutzt die Verträge V1–V12. | Datei / Bereich | Besitzer | |---|---| | `server/services/kundenansicht/schalter.js` (neu), Route `/dashboard.html` in `server/index.js`, Konfig `config.kundenansichtNeu`, Feld `ansicht` in `/api/auth/me`; ein **Platzhalter** `public/neu/dashboard.html` für den Routentest (ab P1 gehört die Datei P1) | P0 | | Werkzeug `/root/reports/kundenansicht-neu-2026-10-15/werkzeug/` (eigenes Git), `test/ui/kundenansicht-neu*.ui.mjs` | P0 legt an, P1–P4 und P7 ergänzen nur eigene Ansichten und Fixtures | | `public/neu/dashboard.html`, `public/neu/tokens.css`, `basis.css`, `huelle.css`, `public/neu/dom.js`, `api.js`, `router.js`, `routen.js`, `huelle.js`, `leiste.js`, `konto.js`, `fristen-streifen.js`, `zaehler.js`, `etage.js`, `reiter.js` | P1 | | `public/neu/ansicht-website.js`, `ansicht-chat.js`, `ansicht-vertraege.js`, `ansicht-stunden.js`, `ansicht-speicher.js`, `ansicht-gutschrift.js`, `ansicht-passwort.js`, `public/neu/ansichten.css`; `public/neu/ansicht-bestellen.js` und `ansicht-wechsel.js` als **1:1-Übernahme** der heutigen Bestell- und Wechselseiten im neuen Aussehen (alte Texte, Fassung 5, C14); öffentliche Seiten im neuen Stil unter `public/neu/oeffentlich/` (`login.html`, `kuendigen.html`, `widerrufen.html`, `offline.html`), die erst P10 ausliefert | P2 | | `public/neu/ansicht-funktionen.js`, `funktion-einschalten.js`, `public/neu/funktionen.css`, `public/neu/bilder/*` | P3 | | `public/neu/module.css` (neues Aussehen aller Funktionsmodule). In `public/termine*.js`, `shop*.js`, `buchungen*.js`, `zeiten*.js`, `funktionen.js` (nur News), `analyse*.js` und `funktion-bestellen.js` nur Verzweigungen nach `modell === 'abo'` und die Kurzlabels der Reiter (C2) | P4 | | Umbau von `public/neu/ansicht-bestellen.js` und `ansicht-wechsel.js` auf das schlanke Muster (Fassung 6). In `server/routes/abo.js` nur die Pfade für Fassung 6. `server/services/planSwitch.js:351` mit Umgebung. `server/services/vertragsNachweis.js` (neu) | P7 | | `server/services/kundenart.js` (neu), `server/services/aboFunktionen.js` (neu), `server/services/funktionEinschalten.js` (neu), `server/services/fristen.js` (neu), `server/routes/funktionen.js` (Katalog, Einschalten), `server/routes/fristen.js` (neu), `server/routes/auth.js` (nur `sanitizeCustomer` + Kundenart), `server/services/customerAdmin.js` (Kundenart), `server/services/featureRegistry.js` (Katalogfelder), `server/services/featureConsents.js` (Attribut `kundenart`), `scripts/kundenansicht-setup.mjs` (neu), Shop-/Unterkunft-Anfrage (Unternehmer aus Kundenart), Termine-Zustimmung ohne `gesundheit` im Abo-Modell | P5 | | `server/services/hourLedger.js` (Vorschuss), `server/routes/team/customers.js` (Stundenkonto-Endpunkt), `server/services/siteFeatures.js` (`adminActivate` mit Abo-Prüfung und Abrechnung), `server/routes/admin/funktionenAdmin.js`, `server/services/aboFunktionenAbgleich.js` (neu), Wechsel mit gemerkter Funktion in `planSwitch.js` (nur `details.funktion`, `completeSwitch`, `markSwitchFailed`) und `GET /api/abo` (`wechselFunktion`), Ticket-Texte zum Stundenkonto, neue Mailvorlagen K38–K41 und T24–T25, Anpassung K02/K03/K22/K29/K30/T16 nur für das Abo-Modell | P6 | | `docs/rechtliches/*-v6*.md` und neue Entwürfe, `public/vertragsunterlagen/v5/*` (Archiv), `public/neu/rechtliches/*` (neue Fassungen), `server/services/products.js` (Fassungstabelle), `config.abo.termsVersionNeu`, `scripts/build-legal-*`, `scripts/rechtliches-hochladen.mjs` (neu), `docs/rechtliches/kundenansicht-entscheidungen.md` (neu) | P8 | | WOMS-Repo komplett | P9 | | `scripts/kundenart-bestand.mjs` (neu), Umschalten, Aufräumen alter Dateien | P10 | | **geteilt:** `server/config.js` (P0: Abschnitt `kundenansichtNeu`; P5: Abschnitt `aboFunktionen`; P8: `abo.termsVersionNeu`; jeder nur sein eigener Abschnitt), `test/support/schema.js` (nur neue Collection-/Attribut-Quellen ergänzen), `scripts/setup-abo-collections.mjs` (nicht ändern; neue Attribute nur in `scripts/kundenansicht-setup.mjs`) | wie angegeben | Bestehende Testdateien ändert ein Paket nur dort, wo sein Plan sie ausdrücklich nennt, etwa für Pins auf Texte, die das Paket ändert. --- ## Global Constraints - **Alte Ansicht unverändert.** Bis P10 ist jede Verhaltensänderung für Kunden ohne neue Ansicht verboten. Neue Server-Logik prüft `neueAnsicht(req)`, ist rein additiv (neue Felder, neue Endpunkte) oder betrifft nur Datensätze, die erst über die neue Ansicht entstehen (`unlockedBy: 'abo' | 'projekt'`). - **Schalter:** `KUNDENANSICHT_NEU` ist `off | pilot | on`, Vorgabe `off`. `KUNDENANSICHT_NEU_PILOT_EMAILS` ist eine Komma-Liste, `*` heißt alle. Der Modus `off` schlägt jede Liste. - **Aussehen** (Spec 5.1, Werte wörtlich aus dem Ticketsystem `src/styles/global.css:7-51`): - Farben: `#fff` Fläche, Text `#1a1f36`, Zweittext `#5e6678`, Tertiär `#8a93a3` nur für Nebensächliches, Linien `#ebeef1` und `#d8dde3`, Hover `#f6f8fa`, Fläche `#f8f9fb`, Akzent `#111`. - Status-Pillen (Fläche/Schrift): grau `#ebeef1`/`#414552`, blau `#e0f2fe`/`#075985`, orange `#fcedb9`/`#a82c00`, violett `#efeafe`/`#5b21b6`, grün `#d7f7c2`/`#006908`, rot `#ffe7f2`/`#b3093c`. Fehler `#c0123c`. - Radien 6 und 12 px. Ring-Schatten `0 0 0 1px rgba(60,66,87,.16), 0 1px 1px rgba(0,0,0,.06)`. - Systemschrift `-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif`. Kein Gewicht 800, Zahlen `tabular-nums`. - Verboten: Karten, Verläufe, Glow, Punktraster, Glas als Deko, Dunkelmodus, Lila. - Ladezustände als Text („Wird geladen …“). Dialoge schließen nur per × und Esc. - **Anrede „du“** in allen neuen Texten der Oberfläche. Ausnahmen: Rechtsdokumente und bestehende Mails (Spec 5.7); die Bestellseiten bekommen „du“ erst mit Fassung 6 (C14, P7). - **Keine Preise für Funktionen** in der neuen Oberfläche, nirgends. - **Hashes:** Alle alten Hashes bleiben gültig (`VIEW_HASHES`, Aliase `#wartung`, `#abo`, `#fortschritt`). Die Query nach `?` gelangt unverändert ins Modul. Neue Hash-Namen nur aus `[a-z]` wegen der Login-Whitelist `/^\/dashboard\.html(#[a-z]+)?$/` (`public/app.js:466`, `public/login.html`). - **Rechtlich fest** (Spec 4): - Kerninfo und „Zahlungspflichtig bestellen“ stehen auf demselben Bildschirm. - Der Knopftext bleibt wörtlich. - „Verträge hier kündigen“ ist immer erreichbar. „Vertrag widerrufen“ steht immer im Konto. - `kuendigen.html` und `widerrufen.html` bleiben schlicht. - **Kundenart:** Der Server ist die einzige Quelle. Er nimmt nie die Kundenart vom Client. Eine leere oder unbekannte Kundenart gilt als `privat` (E4), überall über `kundenartWirksam()` (V4). - **Nachweise:** Wortlaut, Fassung, Zeit, IP, Browser und Konto werden weiter gespeichert, ab jetzt mit der Kundenart zum Klickzeitpunkt. - **Tests Portal** (aus dem jeweiligen Worktree): - Einzeldatei: `timeout 900 nice -n 10 node --max-old-space-size=1024 --test test/.test.js`. - Mehrere Dateien in einem Aufruf nur mit `--test-concurrency=1`. - Die ganze Suite nur in Phase E und P10: `timeout 2400 nice -n 10 systemd-run --scope -q -p MemoryMax=2500M -p MemorySwapMax=0 env NODE_OPTIONS=--max-old-space-size=1024 node --test --test-concurrency=2 test/*.test.js`. - Vor jedem schweren Lauf `free -m`. Bei weniger als 2000 MB verfügbar warten und den Controller melden. - **Tests WOMS:** `cd /home/webklar/apps/ticket.webklar.com/dev-kundenart && timeout 900 nice -n 10 systemd-run --scope -q -p MemoryMax=1500M -p MemorySwapMax=0 env NODE_OPTIONS=--max-old-space-size=1024 npm test`. - **Playwright** nur mit dem Werkzeug aus P0 (`run-browser.sh`, eine Instanz, Speicherbremse). `chromium-1234`, `playwright-core` 1.62.1. - **Commits:** Dateien einzeln hinzufügen. Nachrichten deutsch, mit der Zeile `Co-Authored-By: …` aus dem eigenen Systemhinweis der ausführenden Sitzung (kein fester Modellname, Nachtrag N50). - Kein Push, kein `--force`, kein Merge auf `main` oder `test`. Pushen tut nur der Inhaber. - Keine Änderungen in `build/`, weder im Portal noch im WOMS. - **Sicherheitsblock** in jedem Agent-Auftrag (Memory „feedback-destruktive-skripte“): - Kein `rm -rf` außerhalb des eigenen Scratch-Ordners, nie `/dev`, `/etc` oder `/` anfassen. - Kein `docker run`, keine globalen Installationen. - Das Vorschau-Skript nie mit Ziel aufrufen, nur `--pruefen`. Nie mit Pfaden wie `..` testen. - `TMPDIR` auf den eigenen Scratch-Ordner setzen. - Nach großen Läufen prüfen, dass `ls -l /dev/null` `crw-rw-rw-` zeigt. - **Prod-Daten:** Kein Paket schreibt in die Live-Datenbank. Ausnahmen sind die Setup-Skripte (`scripts/kundenansicht-setup.mjs`) und `scripts/kundenart-bestand.mjs`. Beide laufen nur auf ausdrückliche Anweisung des Controllers nach OK des Inhabers, vorher immer mit `--pruefen`. ## Review Focus 1. **Kundenart leer, unbekannt oder vom Client gefälscht:** Ohne gesetzte Kundenart sieht der Kunde das eine Häkchen, und der Server verlangt es auch. Ein Body-Feld `kundenart` vom Client wird ignoriert. Tests: P5 Task „kundenartWirksam“ und „Einschalten privat in Frist“, P7 Task „Bestellung Fassung 6“. 2. **Pilot-Schalter an den Rändern:** Groß-/Kleinschreibung und Leerraum der E-Mail, `*`, leere Liste, `off` mit `*`, Mitarbeiter-Kundenansicht (`viewAs` + `viewId`) im Pilot. Ohne neue Ansicht liefern die Abo-Modell-Endpunkte 404, und `/dashboard.html` ist die alte Seite. Tests: P0 Tasks „Schalter“ und „Dashboard-Weiche“, P5 Task „Einschalten nur in neuer Ansicht“. 3. **Alte Links nach dem Umbau:** `#termine?bestellt=1&projekt=x`, `#webshop?stripe=zurueck&projekt=x`, `#rechnungen`, `#stunden`, `#gutschrift`, `#wartung`, `#fortschritt`, `?abo=success`, Login mit `next=/dashboard.html#chat`. Jeder Link landet am richtigen neuen Ort, und die Query bleibt erhalten. Test: P1 Task „Routen“. 4. **Doppeltes oder widersprüchliches Einschalten:** Zwei Taps, schon aktiv, zweite Website, Hosting-Kunde bei einer Wartungs-Funktion (Wechsel mit gemerkter Funktion), gescheiterter Wechsel, Privatkunde in der Frist ohne Häkchen, Funktion per Schalter aus. Ergebnis: höchstens ein Ticket, klare 409-Codes, kein Zustand ohne Nachweis. Tests: P5 Task „Einschalten“, P6 Task „Wechsel mit Funktion“. 5. **Vorschuss statt Mehraufwand:** Nicht gedeckte Einrichtungsminuten erscheinen nie als Mehraufwand. Normale Arbeit über dem Guthaben bleibt Mehraufwand. Eine spätere Gutschrift gleicht den Vorschuss zuerst aus. Bei Vertragsende bleibt ein Restminus stehen, wird aber nicht berechnet. Gilt auch für Git-Push-Arbeitsblätter auf Einrichtungs-Tickets. Tests: P6 Task „Vorschuss im Stundenkonto“, P9 Task „Abo-Vorgabe bei Einrichtungs-Tickets“. --- ## Nachträge nach der Planrunde (verbindlich, 16.10.2026) Die Paket-Pläne wurden parallel geschrieben; ihre Vertragsfragen sind hier entschieden. Diese Nachträge gehen V1–V13 und der Zuständigkeitstabelle vor. Wo ein Paket-Plan davon abweicht, gilt dieser Abschnitt. Kürzel in den Fundstellen: `Task n` und `VFn` = Task bzw. Vertragsfrage n im genannten Paket-Plan (P0 bis P10), `Fn` = Frage n der Planrunde (`/root/reports/kundenansicht-neu-2026-10-15/planrunde.json`, Feld `vertragsfragen`). ### Schalter und Sitzung - **N1 Gültige Sitzung (V1).** Kunde heißt `customerId` und `appwriteUserId` ohne `role: 'admin'`; Kundenansicht heißt `viewAs === true` mit `viewId`. Alte Mitarbeiter-Sitzungen (`role: 'admin'` ohne `viewId`) sind in jedem Modus `false`, auch bei `*`. Eine `email`, die kein Text ist, zählt als leer. (P0 VF3; F12) - **N2 Schalter für Jobs ohne Cookie (V1).** `schalter.js` bekommt den vierten Export `export function neueAnsichtFuerKunde({ customerId, email }) // boolean`. Gleiche Modus- und Listenregeln wie `neueAnsichtFuerSession` (`off` false, `on` true, `pilot` bei `*` oder getrimmter, klein geschriebener `email` in der Liste), aber ohne Sitzungsprüfung und ohne Mitarbeiter-Regel; leere oder nicht-textliche `customerId` und Nicht-Objekte ergeben `false`. P5 `aboModellFuerKunde(customerId)` liest das Kundendokument und ruft sie; `runAboCheck` und P6 (Team-Aktivierung, Widerruf des Wechsels, Tagesjob) nutzen nur sie, nie `neueAnsichtFuerSession`. (P0 Task 2, VF10; P5 Task 13; F12) - **N3 Weiche (V1).** `server/services/kundenansicht/dashboardWeiche.js` ist eine Fabrik (`server/index.js` startet beim Import den Server). Der Direktaufruf `/neu/dashboard.html` wird für Sitzungen ohne neue Ansicht per 302 auf `/dashboard.html` geleitet; Module und CSS unter `/neu/` bleiben frei abrufbar. Bei Modus `on` liefert die Weiche von P10 (`UMSCHALT_SEITEN`, Zeile `/dashboard.html`) die neue Seite auch ohne Sitzung; `neueAnsichtFuerSession` bleibt unverändert. (P0 VF1, VF2; P10 Task 3, VF7; F12) - **N4 `/api/auth/me`.** `ansicht` steht nur in den beiden `authenticated: true`-Antworten von `GET /me`. P5 ändert in `auth.js` allein `sanitizeCustomer` (`kundenart`, `kundenartWirksam`); `test/kundenansichtMe.test.js` pinnt nur die Schlüssel der obersten Ebene, damit `customer` wachsen darf. (P0 Task 4, VF4; P5 Task 2; F13) - **N5 Platzhalter-Marker.** P0 Task 3 setzt im Platzhalter beide Marker (`` und `data-kn-platzhalter`) und pinnt in `istNeu` nur die Meta-Zeile. P1 Task 12 ersetzt die Seite, ohne einen P0-Test zu ändern; die Werkzeug-Ansichten `platzhalter` und `platzhalter-abgemeldet` bleiben, bis P1 sie ersetzt. (P1 VF1, Task 12/13; P0 Task 3; F11) ### Hülle, Routen, Reiter, Zähler - **N6 Routen (V2, „Pflichtinhalt von ROUTEN“).** Die Zeile „`vertraege`, `abo`, `wartung` → vertraege, reiter `abo`“ entfällt. Neu: `vertraege` → Bereich `vertraege` ohne `reiter` (die Ansicht wählt den Standardreiter, bei offener Rechnung `rechnungen`, sonst `abo`); `abo` und `wartung` → Bereich `vertraege`, reiter `abo`, kanonischer Hash `#abo` (`#wartung` wird `#abo`, `#vertraege` bleibt `#vertraege`); `?abo=success|cancel` → `#abo`. `waehleReiter` (P2) entscheidet nur über `route.reiter`; Reiterklicks navigieren mit `ersetzen: true` zu `#abo`, `#rechnungen`, `#stunden`, `#speicher`, und `ankerKette`/`elternVon` liefern weiter `#vertraege` als Bereichs-Anker. (P1 Task 3/4, VF4; P2 Task 16, VF8; F17) - **N7 Routenfelder und Exporte (V2).** Route-Felder zusätzlich `produkt` (`'hosting' | 'wartung'`) und `scroll` (`'aktivitaet'`); `query` ist immer ein String. Zusätzliche Exporte von `routen.js`: `hashFuerAnsicht(name)` (die `data-view`-Brücke der gemeinsamen Module), `startHash`, `ankerKette`, `elternVon`, `titelFuer`, `FUNKTIONS_ETAGEN`. Die Projekt-ID heißt einheitlich `#projekt?projekt=` (Lesen von `id=` bleibt Rückfall); ein `data-project` an einem `[data-view]`-Element wird zu `?projekt=`. (P1 Task 3, VF4; P2 VF4; F17) - **N8 Kleinere Festlegungen der Hülle (V2).** `api(…, { roh })` meint einen Rohdaten-Body (`application/octet-stream`) wie `fn-util.js request({ raw })`, nicht eine rohe Antwort; der V2-Kommentar ist so zu lesen. `?abo=` bleibt in `location.search`; P2 räumt mit `history.replaceState(history.state, '', location.pathname + location.hash)` auf, und P4 ändert die `history.replaceState`-Aufrufe der Module nicht (`stempeln()` aus P1 stempelt danach neu). `public/neu/manifest.webmanifest` hat dieselbe `id`, `start_url` und `scope` wie die alte; `sw.js` bleibt gemeinsam und wird in P1 Task 11 registriert. Ansichten rendern keine eigene `h1`; der Titel kommt aus `titelFuer(route)` oder `kn:titel` (P7: `meldeTitel`). (P1 VF7 bis VF11; P7 Task 10/11/13; F20) - **N9 Reiter-Baustein und Meldungen (V2, Hülle).** `baueReiter({ eintraege: [{ key, label, kurz? }], aktiv, art, ariaLabel, bei(key) })` in `public/neu/reiter.js` liefert direkt ein `HTMLElement` mit `setzeAktiv`, `setzeAnzeige`, `messen`; P2 und P3 passen sich an. Inhalte in Etage 2 melden Reiter und Titel ohne Import per `CustomEvent` mit `bubbles: true`: `kn:reiter` (`detail { eintraege, aktiv, bei }`, leere Liste = keine Reiter) und `kn:titel` (`detail { text }`). (P1 Task 5, VF5; P2 Task 16; P3 Task 5; F18) - **N10 Reiter der Funktionsmodule in Etage 2.** Terminplaner, Webshop, Buchungskalender und Analyse zeichnen im Abo-Modell keine eigene Leiste, sondern melden `kn:reiter` (die Analyse aus `mountCockpit` mit `bei: pickTab`: Übersicht, Heatmap, Einstellungen). Die Ausnahme „außer analyse“ im Gate `p4` entfällt; der Satz „die Analyse behält ihre eigene Leiste“ in F18 ist überholt. Kurzlabels stehen in `KURZLABELS` (`public/fn-reiter.js`: Team, Start, Recht, Preise), die Vorschau prüft sie (C2). (P4 Task 1/8/15, VF2; P1 VF5; F4, F18) - **N11 Zähler-Ereignis (V3).** `kn:zaehler-neu` wird am `document` gesendet (`bubbles: true`), `detail.was` ist `'chat' | 'vertraege' | 'alle'`. `zaehler.stand()` liefert nur `{ chat, vertraege }`; die Hülle setzt den Chat-Zähler beim Öffnen des Chats auf 0. (P1 Task 7/11, VF6; P2 VF2; F19) - **N12 Zeile „Rechnung offen“.** P1 zeichnet sie nicht und reicht kein `zaehler` an die Ansichten; `zeigeX(wurzel, { route, me, nav })` bleibt wie in V2. P2 liefert `public/neu/rechnung-offen.js` (`offeneRechnungen`, `rechnungOffenZeile`, `ladeOffeneRechnungen`, optional `ladeOffeneRechnungen(apiFn = api)`); P3 `zeigeFunktionen` lädt `GET /api/invoices` selbst über `abh.api` und zeichnet die Zeile in Meine und Entdecken. Fällt der Abruf aus, fehlt nur die Zeile. (P3 Task 5, VF5; P2 Task 3, VF3; P1 VF6; F1, F19) - **N13 Option `einschalten` für die Funktionsmodule (V2, Absatz „Funktionsmodule“).** Die Hülle ruft `initNews`, `initTermine`, `initShop`, `initBuchungen`, `initZeiten` als `init*(wurzel, { einschalten })` mit `einschalten(platz, { key, projectId, onFertig, viewAs? }) → Promise`, das lazy `zeigeEinschalten` aus `public/neu/funktion-einschalten.js` (P3) lädt (`MIT_EINSCHALTEN`, `einschaltenOption(lade)` in `huelle.js`); `initAnalyse(wurzel)` bleibt unverändert, die Projektwahl bleibt `wurzel.dataset.wunschProjekt`. `test/knHuelleStart.test.js` („Option einschalten“) pinnt die Verdrahtung; P4 Task 15 ersetzt seine Zeile `command grep -a -n einschalten public/neu/huelle.js` durch den Lauf dieses Tests. (P1 Task 11, VF12; P3 VF1; P4 VF1; F1, F21) - **N14 Overlay.** Ansichten dürfen `oeffneOverlay` und `beendeOverlay` aus `router.js` rufen (P3 für die Bestätigungsseite). Es ist nie mehr als ein Overlay offen; P3 und das Konto-Sheet schließen vor dem Öffnen ein offenes Overlay. (P3 Task 6, VF5; P1 R6; F22) - **N15 Chat-Composer.** Die neue Seite lädt `/neu/chat-composer.css`, eine lilafreie Kopie von `public/chat-composer.css` (Akzent `#111`/Grau, weder `#9d3dfe` noch Gewicht 800 noch `backdrop-filter`); `/chat-composer.js` bleibt gemeinsam, das alte CSS unverändert. P2 Task 7 legt die Kopie an und ändert dafür genau die zwei Composer-Zeilen in `dashboard.html`; `test/knHuelleSeite.test.js` akzeptiert `/neu/chat-composer.css` statt `/chat-composer.css`. (P1 VF3; P2 Task 7, VF10; F23) - **N16 Analyse-Bild und Alt-Klassen.** `public/neu/bilder/analyse.webp` erzeugt P4 über die Werkzeug-Ansicht `kn-modul-analyse` (Fall `m-analyse-aktiv`); bis dahin entfernt der `error`-Handler in „Entdecken“ das fehlende Bild. `public/neu/module.css` Abschnitt 0 (P4) definiert `kb-panel`, `kb-btn*`, `kb-empty`, `kb-link`, `kb-checks`, `abo-actions`, `abo-btn`, `hidden`, `muted`, `error`, außer eine Datei unter `public/neu/` tut es schon; P2 und P3 nutzen eigene `kn-*`-Klassen. (P3 VF2; P4 Task 9/14, VF3, VF5; F24) ### Katalog, Einschalten, Analyse - **N17 Katalogfelder (V5).** Jeder `katalog[]`-Eintrag trägt `consent: { version, knopf: 'Einschalten', items: [{ id, text, haken, nurPrivatInFrist? }] }` (`null` bei `analyse`) in der Reihenfolge `einschalten-zeile`, `hinweis`, `beginn` und dazu `hinweis: string | null`, den Text des Items `hinweis` (nur `shop`, `unterkunft`, `termine`: die Pflichten der alten Häkchen `verantwortung`, `stripe`, `gesundheit`). Der Server liefert beides, nie der Client; der Nachweis speichert den Hinweis als Text `hinweis` (`haken: false`). P3 `renderEinschalten` zeichnet `p.kn-fn-hinweis` direkt über dem Knopf (nach der Linkzeile, vor `beginn`), die P0-Fixtures und `vertragNeu` tragen beide Felder. (P5 Task 6/10/11, VF2; P3 Task 3; P0 Task 6/7, VF11; P8 VF3; F3, F27) - **N18 `anfrageStatus` (V5).** Der Pfad ist `projects[].features..anfrageStatus` (`'' | 'offen' | 'angenommen' | 'abgelehnt' | 'zurueckgezogen'`), gebaut von `anfrageStatusJeProjekt` in `server/services/aboKatalog.js`; P3 liest ihn nur über `anfrageStatusVon(projekt, key)`. (P0 VF9; P5 Task 11; P3 Task 1; F16) - **N19 Antwortcodes der Einschalt-Route (V5).** Die Prüfreihenfolge nach P5 Task 10: 400 `zustimmung_fehlt`; Projektprüfung (403, 409 archiviert); dann 409 `nicht_verfuegbar` (danach 503 `unterlagen_fehlen`), `abo_fehlt`, `nur_betrieb`, `bestellung_offen`, `beginn_noetig`, `bereits_eingeschaltet`, `bestellung_laeuft` (Zustand `ordered`), `abgeschaltet` (Zustand `disabled`), `fassung_veraltet`. 503 kommt zusätzlich bei nicht lesbarem Abo oder gescheitertem Ticket; P3 zeigt den Text des Servers. (P5 Task 10, VF3; P3 VF4; F27) - **N20 Probezeit (V5).** `aboDeckt` kennt zusätzlich den Grund `beginnt_spaeter` mit `ab` (ISO, = `trialEnd`) für `trialing` mit offener Probezeit (Privatkunde ohne Sofortbeginn-Häkchen). `abo_fehlt` trägt dann `body.ab`; der Text lautet „Dein Abo beginnt am TT.MM.JJJJ. Ab dann kannst du einschalten.“, und P3 (`TEXTE`, `fehlerAktion`) zeigt Text und Datum im Knopf. P6 (`adminActivate`) und P9 kennen den Grund; die Analyse-Eignung meldet in der Probezeit weiter `kein_abo`. (P5 Task 4/9/10, VF9; P3 VF4; F8) - **N21 Abrechnung am Funktionsdokument (V7).** Das Attribut `portalFeatures.einrichtungAbrechnung` (string 8, `'abo' | 'projekt' | ''`, angelegt in `scripts/kundenansicht-setup.mjs`) hält die Abrechnung am Dokument, damit `ticketNachlegen` und `holeZeitenNach` den Marker `workorders.einrichtungAbrechnung` erneut setzen. P6 nutzt es für `ticketNachlegen` (Dokumente mit `unlockedBy` `abo` oder `projekt` ohne Stripe-Sitzung heilen über `einschalten`) und für die Ticket-Zeilen in `termine/ticketDetails.js`, `shop/ticketDetails.js`, `unterkunft/ticketDetails.js`; K27 bleibt die Einschalt-Mail der Zeiten. (P5 Task 1, VF5; P6 VF3, VF4; F27) - **N22 Analyse im Abo-Modell.** In der neuen Ansicht verlangt `POST /api/funktionen/analyse/activate` `audienceConfirmed === true` (sonst 400 `zielgruppe_fehlt`, du-Text) und schreibt einen Nachweis in `featureConsents`: Linkzeile A1, Zielgruppen-Zeile A2 (`haken: true`), Knopf `Analyse einschalten`, Kundenart und Fassungen; bei einem Fehler wird alles zurückgenommen. P4 sendet `audienceConfirmed: true`, die alte Ansicht bleibt unverändert. (P5 Task 13, VF8; P4 Task 8, VF4; F29) ### Kundenart und Push-Reihenfolge - **N23 Kundenart-Pflicht beim Anlegen (V4; Entscheidung des Controllers, F2 und F28 widersprachen sich).** `createCustomerWithPortalAccess` verlangt `kundenart` nur bei `neueAnsichtModus() !== 'off'` (sonst 400 `kundenart_fehlt`); bei `off` ändert sich das Anlegen im WOMS und im Portal nicht. `updateCustomerWithPortalAccess` nimmt `kundenart` weiter optional mit gleicher Prüfung. Die unbedingte Pflicht aus F28 gilt nicht. (P5 Task 3, VF1; P9 VF3; F2, F28) - **N24 Push-Reihenfolge (Entscheidung des Controllers).** Der Inhaber pusht in dieser Reihenfolge: (1) `fix/akquise-sichtbar`, falls gewünscht; (2) WOMS-Tag `kn-p9-kundenart`; (3) Portal ab P5; (4) WOMS `kn-p9` zusammen mit P6. Vor dem Setzen von `KUNDENANSICHT_NEU=pilot` muss die WOMS-Kundenart (`kn-p9-kundenart`) live sein. Wird `fix/akquise-sichtbar` später gepusht, rebased P9 darauf, nur auf Anweisung des Controllers. Die Übergabe-Notiz nennt die Reihenfolge und vermerkt, dass die Abo-Vorgabe in WOMS zunächst nur für Pilotkunden wirkt (P6 wertet `abrechnung` nur mit `aboModellFuerKunde` aus). (P5 VF1, VF10; P9 VF3, VF4; F2, F28) ### Stundenkonto, Team, Mails - **N25 Antwortformen des Stundenkontos (V8).** `nextExpiry` ist `{ minutes, at } | null`; bei `produkt: 'hosting'` und ohne Abo ist `ledger` gleich `null`. `workorders.einrichtungFuer` und `einrichtungAbrechnung` sind für Mitarbeiter lesbar (Setup-Skript P5). T24 trägt den Block `vorschuss`, wenn beim Einschalten `balanceMinutes <= 0` gilt oder schon `vorschussMinutes > 0` besteht; eine Schätzung je Funktion gibt es nicht. (P6 Task 2/4, VF6; P9 VF2; F33) - **N26 Geltung der Team-Aktivierung (V10).** Das neue Verhalten (`aboDeckt`-Prüfung, `einschalten` mit Ticket) gilt nur, wenn `aboModellFuerKunde(customerId)` wahr ist; sonst wird `abrechnung` im Body ignoriert und alles läuft wie heute, mit `on` (P10) für alle. P9 sendet `abrechnung` immer. Die 409-Antwort bei fehlendem Abo ist flach: `{ error, code: 'abo_fehlt', grund, wechsel }` mit `grund` aus `kein_abo | falsches_abo | gekuendigt | nicht_aktiv | beginnt_spaeter`; WOMS zeigt eigene Texte, nie den Kundentext in „du“-Form. Erfolg wie bisher `projects[0].features[key]` mit `state: 'setup'` und `ticketWoid`. (P6 Task 7, VF2; P9 VF2; F32) - **N27 Abo-Modell-Dokument (V10, V11).** Ein Dokument gehört zum Abo-Modell, wenn `unlockedBy` in `abo | projekt` steht und `einrichtungAbrechnung` in `abo | projekt` (`istAboModell` in `server/services/aboModell.js`); die Gratis-Zeiten der alten Ansicht (`unlockedBy: 'abo'` ohne Marker) bleiben unberührt. V11 ändert sich: Der Widerruf des Wechsels beendet alle Abo-Modell-Dokumente, deren Schlüssel nur in `wartung` steckt, also auch `projekt`-Dokumente; die Analyse eines Hosting-Kunden bleibt. (P6 Task 5/13, VF4; F32) - **N28 Team-Einschalten „im Projekt“ ohne Abo (E9/E21; Entscheidung des Controllers, dem Inhaber in der Übergabe zur Bestätigung vorlegen).** Es gilt die Aufbauphasen-Regel von P6 (`server/services/aboAufbauphase.js`, `ohneAboBisher(customerId)`, P6 Task 7, 8, 10). `abrechnung: 'abo'` braucht ein deckendes Abo, sonst 409 `abo_fehlt`. `abrechnung: 'projekt'` braucht es auch, **außer in der Aufbauphase**: Hatte der Kunde noch nie ein Abo (abgebrochene Bestellungen zählen nicht), wird eingeschaltet und mit 200 plus `warnung: { code: 'abo_fehlt', grund: 'kein_abo' }` geantwortet, flach neben `projects`. Das boolesche `warnung: true` für ein fehlendes K16 bleibt für die alte Ansicht. WOMS zeigt die Warnung (P9), der Wortlaut sagt dem Team: „Wartungs-Abo vor dem Livegang vereinbaren“. Der Tagesabgleich lässt `projekt`-Dokumente in der Aufbauphase in Ruhe. Ab dem ersten Abo gilt die Abo-Regel wie für `abo`: Deckt das Abo die Funktion nicht (Hosting, beendet), endet sie mit K39. Hat der Kunde schon ein nicht deckendes Abo, bleibt es auch im Projekt bei 409. (P6 Task 7/8/10, VF8; P9 Task 10/11; F5) - **N29 Mailkennungen (V13).** Belegt sind K38 „Deine Funktion wird eingerichtet“ (Einschalten im Abo-Modell, nicht für `zeiten`, dort bleibt K27); K39 „Deine Funktion wurde beendet“ (News, Terminplaner mit Block `termine`, Webshop, Unterkunft; Platzhalter `funktion`, `projekt`, `loeschtag`); K40 und K41 (Zeiten: Abo-Ende mit „läuft noch bis {{frist}}“, abgeschaltet; ohne Preis, ohne Behalten); T24 ans Team mit Block `vorschuss`; K42 (Webshop-Zusage) und K43 (Buchungskalender-Zusage) im Abo-Modell ohne Preiszeile, ohne „€“ und ohne „bestell…“ (Builder `anfrageEntschiedenMail`, Weiche in `sendeEntscheidung` über `aboModellFuerKunde`). K24 und K34 bleiben bytegleich. K44 und T25 bleiben frei; eine Rückkehr-Mail gibt es nicht (die Rückkehr geschieht still, K39 kündigt sie an), außer bei Zeiten im bestehenden Modul. K02 und K03 bekommen den Block `funktionen_abo`; K22, K29, K30 und T16 bleiben bytegleich. Alle neuen Vorlagen in „du“-Form und im Layout von K16. (P6 Task 3/4/5/15, VF1, VF5; F31) - **N30 Behalten-Weg der Zeiten.** Für Abo-Modell-Dokumente ist `POST /api/funktionen/zeiten/behalten` gesperrt (409, P6). Die Route und die Mailtexte K29/K30 (Builder `k29Mail`, `k30Mail` auf K40/K41 umgestellt, Vorlagen-Dateien bleiben) räumt P10 Task 20/21 auf. Die WOMS-Texte (`zeitenStatusText`, Bestätigungstexte „Aktivieren (ohne Zahlung)“) ändert ein eigener WOMS-Nachzug nach P10 Task 22 (Controller, Push durch den Inhaber, geführt in der Checkliste Task 6). Alt-Zweige hinter `neueAnsicht(` bleiben als toter Code; ihre Entfernung entscheidet der Inhaber. (P6 VF7; P9 VF5; P10 VF11, VF12; F34) - **N31 Abo-Ende-Verhalten (Rechtstexte = Server).** Terminplaner, News, Webshop und Buchungskalender schalten spätestens am Folgetag ab (Tagesjob). Öffnungszeiten live läuft nach dem Abo-Ende 30 Tage unverändert weiter (Zeiten-Modul, K40, P4 Text Z2), danach 30 Tage gespeicherte Eingaben; kein Behalten gegen 30 €. Löschfristen: 30 Tage (Terminplaner, News, Öffnungszeiten), 90 Tage (Webshop, Buchungskalender), 60 Tage (Analyse-Messdaten); ein neues passendes Abo in der Frist holt die Funktion zurück, P6 baut den News-Löschjob. P8 passt Texte und Tests an (R8 „mit P4/P6 abgestimmt“). (P8 Task 4/7, VF5; P6 Task 8/10; P4 Text Z2; F39) ### Bestellseiten, Fassung 6, Nachweis - **N32 Pfade der Fassung 6 (V12).** `config.abo.termsVersionNeu = '2026-10-16-v6'` (Entwurfstag, bei der Freigabe wird das Datum gesetzt); `FUNKTIONEN_FASSUNG_V6 = '2026-10-16'`. Bis zum Umschalten gelten die Abo-Seite `/neu/rechtliches/abo-bedingungen.html`, das PDF `/neu/rechtliches/WEBklar-Abo-Vertragsunterlagen.pdf` und die Funktionsunterlagen `/neu/rechtliches/-2026-10-16.html|pdf`; `termsDocFor` liefert sie, `/abo-bedingungen-v6.html` aus V12 entfällt. P7-Fixtures und -Tests nutzen `termsDocFor(config.abo.termsVersionNeu).url` und `config.abo.termsVersionNeu` statt Literalen wie `'2026-10-30-v6'`. P10 Task 14 verschiebt Seite und PDF nach `public/` (`/abo-bedingungen.html`), stellt in `FASSUNGEN` v5 auf die `archiv`-Adressen (`public/vertragsunterlagen/v5/`) und v6 auf die Wurzel-Adressen, setzt 301-Weiterleitungen und passt `DOKUMENTE_V6`, den Modus `abo6` und die festen Links an. (P8 Task 1/5, VF1; P10 VF3; P7 Task 16; F35) - **N33 Fassung 6 verdrahten: ein Task, zwei Pläne (V5, V12).** P5 Task 15 und P7 Task 0 beschreiben dieselbe Arbeit und laufen einmal, als erste Aufgabe von P7 im Hauptzweig nach dem Merge von `kn/server` und `kn/recht`, mit den Dateien beider Tasks. Quellen sind `WORTLAUT_V6`, `FUNKTIONEN_FASSUNG_V6`, `FUNKTIONEN_UNTERLAGEN_V6` und `mitFassung` aus `server/services/products.js` (P8 liefert, verdrahtet nicht). Verdrahtet werden `config.aboFunktionen.fassung` und `texte.beginn` (Literale, `config.js` importiert keine Dienste), `consentAbo` mit dem Item `hinweis`, `aboKatalog.js` (`unterlagen`, `hinweis`), `aboUnterlagen.js` und `sichereAdresse` in `funktion-einschalten.js` (lässt `/neu/rechtliches/` zu). `bestelltexte.js` bleibt importfrei, `test/knBestelltexte.test.js` pinnt Linkzeile, Häkchen und Fassungsangabe gleich zu `WORTLAUT_V6` (eine einheitliche Form der Fassungsangabe), danach läuft `test/aboWortlautV6.test.js` grün. Die Analyse (`config.analyse.avvVersion`, `legalPaths`, K18-Weiche mit den PDFs aus `public/neu/rechtliches/`) folgt dieser Verdrahtung erst nach der Freigabe; wo P5 Task 15 sie sofort auf Fassung 6 stellt, gilt diese Regel. (P5 Task 15, VF7, VF10; P7 Task 0/1, VF12; P8 VF2; F3, F29, F30) - **N34 Vertragsnachweis (V12).** `speichereVertragsNachweis({ customerId, art, bezug = '', texte, fassungen, kundenart, req, at })` schreibt selbst mit `createDocument` und `texteHash` (aus `featureConsents.js`), weil `speichereZustimmung` `projectId = ''` ablehnt; `featureKey = art`, `projectId = ''`. `bezug` und `wortlaut` (`WORTLAUT_STATUS`) stehen als Schlüssel in `fassungen` (höchstens 1000 Zeichen), `texte` bleibt die reine Anzeigereihenfolge. `stripeSessionId` bleibt leer, weil der Nachweis vor der Stripe-Sitzung, dem Wechsel, der Speicheränderung und dem Antrag entsteht (kein Zustand ohne Nachweis); die Zuordnung läuft über Zeit, Konto und `bezug`. (P7 Task 2, VF3 bis VF5; P8 VF4; F36) - **N35 Fassung in den Bestellrouten.** P7 übernimmt die Stellen, die `config.abo.termsVersion` fest lesen: `stripeBilling.js:606-607` (`createAboCheckout`: Fassung aus `consent.termsVersion`, Vorgabe unverändert), `abo.js:236`, `:328`, `:388` (Prüfung) und `abo.js:201` (`GET /api/abo` liefert `termsVersion` über `aktuelleAboFassung(req)`). `fassungDerAnfrage(req, termsVersion)` prüft die Neu-Fassung zuerst; bei gleichen Werten von `termsVersion` und `termsVersionNeu` gilt `neueAnsicht(req) ? 'neu' : null`, sodass P10 Task 14 hier nichts ändert. `server/routes/takeoverCredits.js` `POST /` schreibt den Nachweis bei Fassung 6. (P7 VF2; P8 VF4; P10 VF4; F36, F42) - **N36 Speicher und Gutschrift (Fassung 6).** P7 übernimmt `ansicht-speicher.js` (Plus-Block) und `ansicht-gutschrift.js` (Zustimmungsblock) wie `ansicht-bestellen.js` und `ansicht-wechsel.js`: P2 baut sie 1:1 (Fassung 5), P7 baut sie auf das schlanke Muster um. Die Orakel-Vergleiche gegen `app.js` werden durch Pins auf die Fassung-6-Texte ersetzt; der Hash-Pin des Blocks `TAKEOVER_CONDITIONS` (`conditionsVersion` 2026-09-30) bleibt. Die Gutschrift-Bedingungen werden die eigene Ansicht `#gutschrift?bedingungen=1` mit unverändertem Rechtstext; `test/support/knWortlautOrakel.js` entfernt P10 Task 17 erst mit `app.js`. (P2 VF5; P7 VF1, VF8; P10 Task 17; F25) - **N37 Texte der Bestellseiten.** Fehlertexte der Dienste (`planSwitch.js`, `mailStorage.js`, `stripeBilling.js`, Abo-Routen) bleiben im Quelltext in Sie-Form und werden für Sitzungen mit neuer Ansicht über `server/services/duFehler.js` in du übersetzt; ein Quelltext-Test meldet jeden neuen Sie-Satz ohne Übersetzung. Die Zeilen „Anbieter“ und „Abrechnung“ entfallen aus dem Block der Bestellseiten (der Anbieter steht im Impressum im Konto) und gehen als Kanzlei-Frage in die Rechtsrunde (N46). K01 und K12 behaupten für Betriebe kein „ausdrücklich verlangt“ mehr (Block `sofortverlangen`). Bewusst der Mail-/Textrunde (Spec 5.7) überlassen: Validierungssätze in `takeoverCredits.js`, `custom_text` der Stripe-Zahlungsseite, die übrigen Mails. (P7 Task 6/14, VF6, VF7, VF11; F37) ### Öffentliche Seiten und Login - **N38 Kündigen und Widerrufen in du.** P7 Task 15 stellt `public/neu/oeffentlich/kuendigen.html` und `widerrufen.html` auf du um; `public/legal-form.js` und `public/auftrag-widerruf.js` zeigen du nur im Zweig `data-anrede="du"` (ohne Attribut unverändert). Die Seiten speichern keinen Wortlaut und keine `termsVersion`, es gibt also keine neue Version (C14). Text-Pins in `test/knOeffentlichDu.test.js` ersetzen für diese zwei Seiten den Paarvergleich aus P2 Task 17 (Feldattribute und Skripte werden weiter verglichen); P10 Task 17 stellt die Sie-Pins in `test/publicForms.test.js:1439-1450`, `test/auftraegeKundenportalWiderruf.test.js:9` und `test/auftraegeAblauf.test.js:288` auf diese du-Sätze um. (P7 Task 15, VF13; P2 Task 17; F6) - **N39 Neue öffentliche Seiten im Pilot.** Angemeldete Pilot-Sitzungen (`neueAnsicht(req)`) bekommen `/kuendigen.html` und `/widerrufen.html` schon im Pilot aus `public/neu/oeffentlich/`; ausgeloggte Besucher, der Login (`/login.html`) und `/offline.html` bleiben bis `on` alt. P0 setzt das mit zwei weiteren Zeilen in `dashboardWeiche.js` um (sie liefert nur, wenn die Datei existiert, sonst `next()`); P10 Task 3 übernimmt die Zeilen in `UMSCHALT_SEITEN` und passt den Test „off und pilot liefern die Datei von heute“ für diese zwei Seiten an. (P10 Task 3; P0 Task 3; P2 Task 17; F7) - **N40 Login, Service Worker, Auftragsseite.** P10 Task 16 (`public/neu/oeffentlich/login.js`) übernimmt `initLoginPage` samt Helfern (`app.js:421-568`) vor dem Löschen von `app.js`; die neue Hülle registriert `/sw.js` selbst (P1 Task 11), P10 zieht in `sw.js` die `OFFLINE_URL` mit. iOS-Fokuszoom: 16-px-Felder in P1/P2-CSS genügen, ein JS-Schutz nur im Login; der doppelte `beforeinstallprompt`-Hörer (Hinweis P2, Konto P1) darf P10 zusammenlegen. P10 nimmt `/auftrag.html` in `UMSCHALT_SEITEN` und in die Nachprüf-Marker auf (Marker: `/neu/oeffentlich/oeffentlich.css` im Quelltext, kein `/auftrag.css`); P2-Tests, die alte Dateien lesen (`app.js`, alte Seiten, `auftrag.css`, `chat-composer.css`), friert P10 vor dem Löschen ein oder entfernt sie. (P2 VF6; P10 Task 16/17, VF5; F26) ### Werkzeug und Vorschau - **N41 Werkzeug-Erweiterungen durch P1 bis P4 und P7.** Erlaubt sind nur `views.mjs`, `gates.mjs` (Gate `p`), `fixtures/endpunkte-p.mjs` mit je einem Import in `fixtures/endpunkte.mjs`, `fixtures/faelle-p.mjs` (P4: `fixtures/module.mjs`) mit einem Import in `fixtures/faelle.mjs`, `vertrag-endpunkte.mjs` und der Marker in `test/statik.test.mjs`. Neue Fälle hängen nur in eigenen Dateien hinter den vierzehn Kernfällen; P0 lockert den Pin „Fälle in fester Reihenfolge“ auf `FALL_NAMEN.slice(0, 14)` und den `boot.js`-Test auf diese vierzehn (`boot.js` bekommt `FALL_MUSTER = /^[a-z0-9-]{1,40}$/`, die Leiste zeigt nur Kernfälle). P4 (17 Fälle `m-*`) und P7 (`privat-ohne-abo`, `hosting-privat`) ändern Pin und `boot.js` nicht; ändert ein Paket eine Antwortform, ergänzt es den Vertragseintrag im selben Commit. (P0 Task 6/11, VF5; P1 VF2; P4 VF5; P7 Task 16, VF10; F14) - **N42 Strang-Umgebung und Ansicht-IDs.** Die Strang-Umgebung kommt nur über `KN_STRANG_ENV=…/tmp/strang-ui.env`, nie über `KW/strang.env`. Ansicht-IDs tragen das Paketpräfix (`kn-huelle-`, `kn-p2-`, `kn-funktionen-`, `kn-modul-`, `b7-`); wer fremde Ansichten aufruft, nennt die echten IDs (P7: `--only kn-p2-bestellen,kn-p2-bestellen-hosting,kn-p2-wechsel,kn-p2-email,kn-p2-email-plus,kn-p2-gutschrift,kn-p2-gutschrift-frage`). Fixtures für V5, V6 und V8 folgen den Nachträgen oben. Die Ansicht für `/auftrag.html` ergänzt P2 Task 19 mit einem Eintrag in `vorschau/seiten.mjs`. (P3 VF3; P2 VF7; P7 Task 16; F14) - **N43 Vorschau-Technik.** `dark-surface` erlaubt genau `#111` (Akzent) für Elemente bis 25 % der Viewport-Fläche; andere dunkle Flächen brauchen `ignorieren` je Ansicht. In der Vorschau hat `public/neu/oeffentlich/` Vorrang vor `public/`; `boot.js` feuert nach dem Laden einmal `DOMContentLoaded` und `load`, Code prüft trotzdem `document.readyState`. Der Arbeitsauftrag an den Inhaber (WOMS „Neues Projekt“, Subdomain `kundenansicht-neu`, Status `ready` oder `deployed`) steht in `KW/UEBERGABE-P0.md` und blockiert Phase B nicht; `KN_ERSTLAUF_FREMDES_OK=ja` nur auf ausdrückliche Anweisung des Controllers. (P0 Task 10/11/14, VF6 bis VF8; F15) - **N44 WOMS-Werkzeug.** „Playwright nur mit dem Werkzeug aus P0“ gilt für das Portal; für das WOMS gilt der eigene Strang `werkzeug-kundenart` (P9 Task 1). Ein kleiner Auftrag „WOMS-Werkzeug auf `test` heben“ (Aufträge/Projektkonten, Unterkunft, `arbeitsblaetter/abgerechnet`, Reiter „Abgesagt“) läuft vor Phase E. Beide Werkzeuge halten die Speicherbremse (`free -m` über 2000 MB) und starten nie mehr als einen Browser zugleich. (P9 Task 1, VF1; F41) ### Rechtsrunde - **N45 Rechtsrunden-Technik.** Die drei fehlenden WOMS-Karten (`leistungsbeschreibung-news`, `leistungsbeschreibung-website-analyse`, `avv-website-analyse`) legt `scripts/rechtliches-hochladen.mjs --karten-anlegen` mit OK des Inhabers vor dem ersten Upload an; `--probe` meldet fehlende als `STOPP: Karte fehlt: `, das Hochladen ohne Karte bricht ab. Die Karte der Analyse-Leistungsbeschreibung bekommt eine abgeleitete ID (`kartenId`, ihr Schlüssel hat 37 Zeichen, erlaubt sind 36), WOMS liest Karten über das Attribut `key`, und die drei Karten werden nicht in `setup-legal.mjs` eingetragen. Die Entwurfsseiten unter `/neu/rechtliches/` gehen mit dem normalen Push live (der Pilot braucht die Links), tragen Entwurfsmarke und `` (P8 Task 5 bis 10 ergänzen) und werden bei der Freigabe ersetzt. (P8 Task 11/13, VF6, VF7; F40) - **N46 Entscheidungsliste.** `docs/rechtliches/kundenansicht-entscheidungen.md` führt R1 bis R17 (statt R1 bis R15; `test/kundenansichtEntscheidungen.test.js` entsprechend): R16 ist die Du-Anrede der Seiten Kündigen und Widerrufen (N38), R17 sind die Zeilen „Anbieter“ und „Abrechnung“ im Block der Bestellseiten (N37), je mit Vorschlag, Alternative und Kanzlei-Frage. (P8 Task 11; P7 VF7; F6, F37) ### Livegang und P10 - **N47 Vor dem Pilot (Ablaufhinweis 4 ergänzt).** Zwei Schritte vor dem Push des Inhabers, beide mit Sicherung und OK des Inhabers: (1) Compose- und `.env`-Zeilen `KUNDENANSICHT_NEU` (Modus `pilot`) und `KUNDENANSICHT_NEU_PILOT_EMAILS` (Pilot-Liste) in `/home/webklar/apps/docker-compose.yml` (zwei Zeilen `- KUNDENANSICHT_NEU=${KUNDENANSICHT_NEU:-off}` und `- KUNDENANSICHT_NEU_PILOT_EMAILS=${KUNDENANSICHT_NEU_PILOT_EMAILS:-}` bei den `ZEITEN_*`-Zeilen) und `/home/webklar/apps/.env`, Sicherung `.bak--kundenansicht`, dann `docker compose up -d --no-deps kundenbereich-server`, nie `preview-portal`; (2) `scripts/kundenansicht-setup.mjs --pruefen`, nach OK des Inhabers ausführen (sonst scheitert jeder Nachweis ohne `featureConsents.kundenart` mit 500). `UEBERGABE-P0` und die Übergabe-Notiz tragen beide Punkte, P10 Task 9 prüft sie nur noch. (P0 UEBERGABE-P0; P7 VF9; P10 VF2, VF13, Task 9; Befund zur Übersicht; F9, F38) - **N48 Vor `on`.** P10 Task 1 und 9 prüfen: `WORTLAUT_STATUS` steht auf `'freigegeben'` (E20); die Gleichheitstests `test/knBestelltexte.test.js`, `test/knVerdrahtung.test.js` und `test/aboWortlautV6.test.js` sind grün; `scripts/rechtliches-hochladen.mjs --probe` meldet keine fehlende Karte und `--pruefen` endet mit Exit 0 (alle elf `documentKey` aus `DOKUMENTE_V6` haben eine aktuelle Version mit passender `source`, deshalb prüft Task 9 Step 2 mit `--pruefen` und nicht nur mit `--probe`); `scripts/kundenart-bestand.mjs` (P10 Task 8) und `scripts/kundenansicht-setup.mjs` sind gelaufen. (P7 VF9; P8 VF9; P10 VF9, VF13; F38) - **N49 Bestätigungen zu P10.** Die Alt-Zweige der P4-Dateien entfernt P10 Task 21, die Playwright-Skripte der alten Seite löscht Task 17. Zwischen Task 11 und Task 14 liegen sieben Tage Beobachtung; Task 14 ist der Punkt ohne Rückweg per Schalter und braucht ein eigenes OK des Inhabers. Der Rückbau (Task 20) beschränkt sich auf die beiden Kaufrouten und ihre Helfer; Alt-Preise in `config.js` und die Antwortfelder von `GET /api/funktionen` bleiben. (P10 VF4, VF6, VF8, VF10, VF11; F42) - **N50 Commit-Zeile (Global Constraints).** Jede ausführende Sitzung nutzt die `Co-Authored-By`-Zeile aus ihrem eigenen Systemhinweis. Commit-Beispiele in den Plänen mit `Claude Opus 5.5` und Formulierungen wie „wörtlich laut Übersicht“ stehen dafür als Platzhalter. (alle Pläne, Global Constraints „Commits“; P8 Schlussvermerk; F43) ### Pilot-Nachträge - **N52 Kurzinfo in „Meine“ (V5, Pilot-Feedback 2026-10-17, Strang kn/meine-pilot).** Mit neuer Ansicht und nur auf Anfrage (`GET /api/funktionen?kurzinfo=1`, sendet allein „Meine“; Etagen, Einschalt-Ablauf und Module fragen ohne den Parameter, dann fehlt das Feld und nichts wird gelesen; Runde 1, 17.10.) trägt jede Funktion unter `projects[].features.` zusätzlich `kurzinfo: string | null`, den Text der zweiten Zeile in „Meine“. Nur aktive Funktionen bekommen einen Text, alle anderen `null`; die Analyse trägt kein Feld (ihre Zahlen kommen aus dem Auswertungsdienst, nicht aus Appwrite). Quelle `server/services/kundenansicht/kurzinfo.js`: Terminplaner „3 Termine heute · nächster 10:30“ bzw. „Heute keine Termine“ (bestätigte und erledigte Termine ohne Sperren, Berliner Tag), Webshop „2 neue Bestellungen“ bzw. „Keine offenen Bestellungen“ (Status `bezahlt`), News „4 Beiträge sichtbar“ bzw. „Keine Beiträge sichtbar“ (`isHanging`), Buchungskalender „1 offene Anfrage“ (Status `neu`), sonst „Nächste Anreise 24.10.“ („Anreise heute“, „Anreise morgen“), sonst „Keine offenen Anfragen“, Öffnungszeiten live der Status der Website (`zeitenStatus.text`, ohne eigene Abfrage; ohne gespeicherte Zeiten „Noch keine Öffnungszeiten eingetragen“). Je Funktion höchstens zwei schmale Abfragen (`limit`, `select`, Filter auf Index-Felder), alle gleichzeitig; jede Funktion einzeln auf `kurzinfoDeps.limitMs` (1500 ms) begrenzt, Fehler oder Zeitüberschreitung ergibt `null`, eine volle Seite (Zahl unbekannt) ebenfalls. Die Texte enthalten nie Namen, Adressen oder Beträge. Die Oberfläche zeigt bei `null` „Aktiv“ und setzt die Zeile nur als Text (höchstens 80 Zeichen); eine Pille trägt nur noch eine Zeile, deren Funktion nicht läuft. Ohne neue Ansicht bleibt die Antwort bytegleich und es wird nichts gelesen. (Pilot-Feedback 2026-10-17; `test/kundenansichtKurzinfo.test.js`, `test/knFunktionen*.test.js`) ### Zuständigkeit - **N51 Zusätzliche Dateien und geteilte Stellen.** Die folgende Untertabelle ergänzt die Zuständigkeitstabelle um alle in Plänen und Vertragsfragen genannten Zusatzdateien und legt für die Hotspots eine feste Reihenfolge fest. Stubs aus P1 gehören nach der Anlage P2, P3 und P4; P7 übernimmt `ansicht-speicher.js` und `ansicht-gutschrift.js`; P0 besitzt `server/services/kundenansicht/` ganz. (P0 VF1; P1 VF3; P2 VF1; P4 Dateiübersicht; P5 VF4; P6 VF3; P7 VF1, VF2; P10 VF1; Befund zur Übersicht; F10) | Datei / Stelle | Besitzer | Reihenfolge, Hinweis | |---|---|---| | `server/services/kundenansicht/` ganz (`schalter.js`, `dashboardWeiche.js`), `test/kundenansichtSchalter.test.js`, `kundenansichtDashboard.test.js`, `kundenansichtMe.test.js` | P0 | andere Pakete legen dort nichts ab | | Werkzeug `views.mjs`, `gates.mjs`, `fixtures/*`, `vertrag-endpunkte.mjs`, `vorschau/seiten.mjs` (eigenes Git) | P0 legt an | P1 bis P4 und P7 ergänzen nur nach N41 | | `public/neu/manifest.webmanifest`, `test/support/knHuelle.js`, `test/knHuelle*.test.js` | P1 | P2 bis P4 dürfen die Testhilfe nutzen | | Stubs aus P1: `public/neu/ansicht-{website,chat,vertraege,passwort,gutschrift,wechsel,bestellen}.js`, `ansichten.css`; `ansicht-funktionen.js`, `funktionen.css`; `module.css` | P1 legt an; danach P2, P3, P4 | Besitz geht nach der Anlage an das Paket der Tabelle oben | | `public/neu/dashboard.html` | P1 | P2 ändert genau zwei Zeilen (Composer-Skript, Stil `/neu/chat-composer.css`) | | `public/neu/ansicht-hilfen.js`, `rechnung-offen.js`, `install-hinweis.js`, `website-details.js`, `vertraege-rechnungen.js`, `vertraege-abo.js`, `chat-composer.css`, `oeffentlich/oeffentlich.css`, `oeffentlich/auftrag.html`, `test/support/knDaten.js`, `knWortlautOrakel.js`, `test/knAnsicht*.test.js` | P2 | `knWortlautOrakel.js` friert P10 vor dem Löschen von `app.js` ein | | `public/neu/ansicht-bestellen.js`, `ansicht-wechsel.js`, `ansicht-speicher.js`, `ansicht-gutschrift.js` | P2 baut 1:1, P7 baut um | P7 erst nach dem Merge von UI und Recht | | `public/neu/funktion-einschalten.js`, `test/support/knFunktionenDaten.js`, `test/knEinschalten*.test.js`, `knFunktionen*.test.js` | P3 | P7 Task 0 ändert nur `sichereAdresse` | | `public/neu/bilder/*` | P3 | `analyse.webp` erzeugt P4 | | `public/fn-reiter.js` (neu), `test/support/modulAbo.js`, `test/kundenansichtNeuModul*.test.js`, Pins `test/shopWiring.test.js:31`, `test/unterkunftWiring.test.js:130` | P4 | | | `server/services/aboUnterlagen.js`, `aboKatalog.js`, `funktionEinschalten.js` (neu), `featureRegistry.js` (auch `consentAbo*`, `abowegAnalyse`), `test/featureSetup.test.js:191-192`, `test/aboFassung6Verdrahtung.test.js` | P5 | Verdrahtung der Fassung 6 siehe N33 | | P5, minimal in fremden Dateien: `server/routes/admin/customers.js` (`code`), `server/services/ticketAdmin.js` (`abrechnung`), `server/services/zeiten/einschalten.js` (`abrechnung`, `vorpruefung`), `server/services/analyse.js`, `server/services/shop/anfrage.js` (`stelleAnfrage`), `server/routes/shop.js` (Anfrage) | P5 | P6 vermeidet diese Funktionen | | `server/services/aboModell.js`, `aboFunktionenMails.js`, `aboFunktionenMeldung.js`, `aboAufbauphase.js`, `aboFunktionenAbgleich.js` (alle neu) | P6 | Aufbauphasen-Regel siehe N28 | | Mailvorlagen K38 bis K43, T24; Block `funktionen_abo` in K02 und K03 | P6 | K44 und T25 frei (N29) | | P6, minimal in fremden Dateien: `server/routes/admin/funktionenAdmin.js`, `termine/aboAbgleich.js`, `zeiten/aboAbgleich.js`, `zeiten/mails.js`, `zeiten/behalten.js`, `termine/ticketDetails.js`, `termine/einbauanleitung.js`, `shop/ticketDetails.js`, `unterkunft/ticketDetails.js`, `shop/anfrage.js` und `unterkunft/anfrageFunktion.js` (nur `sendeEntscheidung`) | P6 | nur die in Plan 07 benannten Stellen | | `server/services/bestelltexte.js`, `duFehler.js`, `vertragsNachweis.js`, `public/neu/bestell-bausteine.js`, `test/support/knBestellDaten.js`, `test/kn*.test.js` (Plan 08) | P7 | | | P7, minimal in fremden Dateien: `server/services/stripeBilling.js` (`createAboCheckout`), `server/routes/takeoverCredits.js`, `server/mail-templates/K12 Wechsel Wartungs-Abo.html` (Block `sofortverlangen`), `public/neu/oeffentlich/kuendigen.html`, `widerrufen.html`, `public/legal-form.js`, `public/auftrag-widerruf.js` (nur `data-anrede="du"`) | P7 | | | `test/aboWortlautV6.test.js`, `aboBedingungenV6.test.js`, `legalV6Build.test.js`, `funktionenRechtV6.test.js`, `kundenansichtEntscheidungen.test.js`, `rechtlichesHochladen.test.js` | P8 | | | `server/routes/umschaltSeiten.js`, `scripts/umschalten-nachpruefung.mjs`, `scripts/kauf-bestand-pruefen.mjs`, `docs/umschalten-kundenansicht.md`, `test/support/dateiListe.js`, `modulKlassen.json`, `oeffentlichStand.json`, `public/neu/oeffentlich/login.js`, `public/sw.js:5`, Tests `knUmschalt*`, `knFassung6Fest`, `knLoginModul`, `knAufraeumen`, `knKaufRueckbau`, `knAltZweigeWeg`, `knSchalterWeg`, `knOfflineCache`, `kundenartBestand`, `kaufBestandPruefen` | P10 | Rückbau der Kaufpfade und Alt-Zweige erst in Phase F | | **geteilt:** `server/routes/abo.js` | P6, dann P7 | P6: K02/K03, `POST /switch`, `GET /`, `withdrawOrder`, Widerruf des Wechsels; P7: nur die Pfade der Fassung 6 und `router.use` für `duFehler` | | **geteilt:** `server/services/planSwitch.js` | P6, dann P7 | P6: `details.funktion`, `completeSwitch`, `markSwitchFailed`; P7: `switchToWartung` (Z. 347-351, 388, 483), `performSwitch` (`details`, K12-Aufruf) | | **geteilt:** `server/services/siteFeatures.js` | P5, dann P6 | P5: `featureView`, `legeTicketAn`; P6: `adminActivate`, `ticketNachlegen` | | **geteilt:** `server/index.js` | je eine eigene Zeile | P0 Weiche vor `express.static`; P5 1 Import, 1 Mount; P6 1 Import, 1 Intervallblock; P10 eine Zeile direkt vor `app.use(express.static(publicDir))` | | **geteilt:** `server/services/products.js` | P8, dann P10 | P8: Fassungstabelle, `WORTLAUT_V6` und Konstanten; P10: Zeilen v6 und v5 der Tabelle | | **geteilt:** `server/config.js` | je eigener Abschnitt | P0 `kundenansichtNeu`, P5 `aboFunktionen`, P8 `abo.termsVersionNeu`; Ausnahme: die Literale `aboFunktionen.fassung` und `.texte.beginn` setzt die Verdrahtung (N33) | | **geteilt:** `server/services/featureRegistry.js`, `funktionEinschalten.js` | P5 | `consentAbo` ändert nur die Verdrahtung (N33); P6 fügt in `funktionEinschalten.js` genau einen Aufruf `meldeEingeschaltet` ein | | **geteilt:** `public/neu/ansichten.css` | P2, dann P7 | P7 hängt einen Abschnitt an | | **geteilt:** `test/mailTemplates.test.js` | P6, dann P7 | P6: Z. 9, 11, 718; P7: Z. 379 | --- ## Verträge ### V1 Pilot-Schalter (P0) `server/config.js`: ```js kundenansichtNeu: { mode: 'off'|'pilot'|'on', pilotEmails: string[] } // aus KUNDENANSICHT_NEU, KUNDENANSICHT_NEU_PILOT_EMAILS (commaList) ``` `server/services/kundenansicht/schalter.js`: ```js export function neueAnsichtModus() // 'off' | 'pilot' | 'on' export function neueAnsichtFuerSession(session) // boolean export function neueAnsicht(req) // = neueAnsichtFuerSession(req.session ?? parsePortalSession(req)) ``` Regeln für `neueAnsichtFuerSession`: - `off` → false. - `on` → true für jede gültige Kunden- oder Kundenansicht-Sitzung. - `pilot` → true, wenn eine dieser Bedingungen gilt: - `session.viewAs === true && session.viewId` (Mitarbeiter-Kundenansicht), - die Liste enthält `'*'`, - die Liste enthält die getrimmte, klein geschriebene `session.email`. - Ohne Sitzung → false. Auslieferung und Clients: - `app.get('/dashboard.html')` steht **vor** `express.static` (`server/index.js:154`). Gilt die neue Ansicht, liefert die Route `res.sendFile(public/neu/dashboard.html)` mit `Cache-Control: no-cache`. Sonst `next()`, und die alte Seite kommt wie heute. - `GET /api/auth/me` bekommt zusätzlich `ansicht: 'neu' | 'alt'`. - Die alte `app.js` lädt ohne Weiche. Die neue Seite lädt nur `/neu/*`-Dateien und die gemeinsamen Funktionsmodule. ### V2 Dateien und Module der neuen Oberfläche (P1, P2, P3) **Grundregeln** - Alle neuen Dateien liegen unter `public/neu/`. ES-Module (`