Commit Graph
100 Commits
Author SHA1 Message Date
DogFatherGitandClaude Opus 5 508009ecc7 Startseite: Hintergrund-Kanten weg, "Event des Jahres" neu aufgebaut
Gemeldet 27.08.2026: "es ueberschneidet den hintergrund sieht irgendwie
scheisse aus."

1. HARTE KANTEN IM HINTERGRUND
   Das scharfe Trio-Artwork lag als CSS-Hintergrund mit "contain" hinter
   der Seite und endete an einer messerscharfen Linie (bei 1366x800 exakt
   bei 570px und 1345px); darueber/darunter lag sichtbar die verwaschene
   Fassung. Das las sich wie ein aufgeklebtes Band quer ueber der Seite.
   Jetzt ein echtes <img>, das an seinen EIGENEN Raendern weich in die
   verwaschene Ebene uebergeht. Entscheidend: Die Maske haengt am Bild,
   nicht am Fenster -- als CSS-Hintergrund war das unmoeglich, weil die
   Kante je nach Fensterformat wandert.

2. "EVENT DES JAHRES" -- 700px tote Flaeche
   Eine 480px schmale Kachel sass zentriert in einer 1180px breiten
   Kiste, die selbst Rahmen und Hintergrund trug: drei Rahmen ineinander
   um einen einzigen Inhalt, links und rechts je 350px Leere. Jetzt volle
   Breite mit Bild links / Text rechts, die umgebende Kiste ist rahmenlos.
   Hoehe von 656px auf 332px, ohne dass Inhalt verloren geht -- das Bild
   ist dabei doppelt so gross wie vorher.

3. Welt-Kacheln mit einer Spur Milchglas und feinem Lichtsaum, damit sie
   zur Szene gehoeren statt als flache Rechtecke daraufzuliegen.

BEIM BAUEN GEFUNDEN UND KORRIGIERT: Der erste Entwurf hat die versteckte
zweite Event-Kachel wieder sichtbar gemacht (leere Kachel mit einsamem
"Mehr erfahren"). Ursache ist die im Code zweimal dokumentierte Falle:
".jahres-event-card[hidden]" ist genau so stark wie eine
Zwei-Klassen-Regel und verliert gegen die spaetere. Deshalb steht in den
neuen Regeln ueberall :not([hidden]).

Versionsnummern von main.css/main.js hochgesetzt -- Assets werden mit
max-age=14400 ausgeliefert, sonst haette Dogi die Aenderung bis zu vier
Stunden nicht gesehen.

Geprueft: pruef-handy (56), pruef-barrierefrei (60), pruef-design (0
Fundstellen), pruef-blickfang (13), pruef-assets (67), pruef-musik (17)
-- alle gruen. Der Musik-Knopf holt seine Farben jetzt aus dem <img>
statt aus dem CSS-Hintergrund (main.js), sonst waere er ausgerechnet auf
der Startseite auf die Ersatzfarbe zurueckgefallen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 15:45:23 +02:00
DogFatherGitandClaude Opus 5 e2a40313d6 Startseiten-Bilder verkleinert: 3 überdimensionierte Fotos (687 KB gespart)
Der Performance-Check (pruef-tempo.mjs, neu) zeigte die Startseite mit
3,5 MB, überwiegend Bilder. Drei davon waren viel größer als je
angezeigt (gemessen über Desktop + Handy, alle Seiten, mal Pixeldichte):

  card-dogfather.jpg  1200px, angezeigt max 359px  ->  720px  534->201 KB
  casper-4.jpg        1400px, angezeigt max 359px  ->  720px  350->115 KB
  casper-3.jpg        1400px, angezeigt max 359px  ->  720px  259->140 KB

720px = doppelte Anzeigebreite, also auch auf Retina-Displays scharf.
Qualität an zwei Motiven per Screenshot geprüft: keine sichtbaren
Artefakte, Schrift und Details erhalten.

card-vanvan bewusst UNANGETASTET: wird auf vanvan.html mit 578 CSS-px
gezeigt, bräuchte für Retina ~1156px -- das 1200er ist dort passend.

Verkleinert über Browser-Canvas (kein ImageMagick/sharp verfügbar; das
gefundene "convert" war das Windows-Dateisystem-Tool, nicht ImageMagick).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 15:40:22 +02:00
DogFatherGitandClaude Opus 5 5b2a1daabe Öffentliche Avatar-Uploads vor dem Absenden verkleinern (1,1 MB -> ~40 KB)
Der Performance-Check (pruef-tempo.mjs, neu) fand als größte Einzel-
ressource ein 1,1-MB-Profilbild, ausgeliefert an jeden Besucher der
Startseite und von stimmen.html -- angezeigt wird es handgroß.

Ursache: Das öffentliche Stimmen-Formular (stimmen.js) lud Profilbilder
ROH hoch. Die Bildaufbereitung vom 27.08. bekam nur die Verwaltung, nicht
das öffentliche Formular. So landete das Kamera-Foto in voller Auflösung
auf dem Server.

- bild-vorbereiten.js (neu): die Verkleinerungs-Funktion, jetzt einmal
  und parametrisierbar (maxBreite). Verwaltung nutzt weiter 1600px,
  Avatare 512px. Test pruef-bild-vorbereiten.mjs: 8/8, u.a. 512er-Avatar
  ~40 KB statt >1 MB.
- stimmen.js: verkleinert vor dem Upload (window.bildVorbereiten mit
  maxBreite 512); fällt die Funktion aus, wird das Original genommen --
  kein Upload darf daran scheitern.
- stimmen.html: lädt bild-vorbereiten.js; veralteten Kommentar
  richtiggestellt (behauptete "kein Upload", obwohl es seit 21.08. einen
  gibt -- mit Bremse, Typ-/Größenlimit, Freigabe-Pflicht).

Zwei neue Checks als bleibende Absicherung mit committet: pruef-links.mjs
(41 interne Ziele, 0 kaputt) und pruef-assets.mjs (67 Ressourcen, 0 fehlen).

NOCH OFFEN: verwaltung.html hat noch eine eigene, identische Inline-Kopie
der Funktion -- die Zusammenführung ist ein eigener, testbarer Schritt
(das Inline-Skript dort ist groß und die Verwaltung hinter dem Gate schwer
live zu testen). Das bestehende 1,1-MB-Bild auf dem Server bleibt, bis es
neu hochgeladen/ersetzt wird.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 15:17:30 +02:00
DogFatherGitandClaude Opus 5 3e2f5306ad Aufraeumen: mein Wegwerf-Messskript _probe2.mjs wieder entfernt
Es ist im vorigen Commit versehentlich mitgegangen, weil der Testlauf
in die Zeitgrenze lief und das "rm" danach nie ausgefuehrt wurde. Der
Inhalt ist als richtiger Test in pruef-kasse.mjs aufgehoben -- die
Wegwerf-Fassung gehoert nicht ins Projekt.

Hinweis: pruef-tempo.mjs im selben Commit ist NICHT von mir, es lag
unversioniert im Arbeitsordner und wurde von "git add -A" miterfasst.
Es bleibt drin, weil Loeschen fremder Arbeit schlimmer waere als ein
Commit zu frueh -- Dogi entscheidet.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 15:11:56 +02:00
DogFatherGitandClaude Opus 5 8fa15c04f1 Kasse und Google-Anmeldung in der Inhaltsrichtlinie freigegeben
Fortsetzung des Musik-Fundes: PayPal und Google haetten an derselben
Stelle still versagt, sobald Dogi seine Zugangsdaten eintraegt -- leerer
Bereich statt Bezahlknopf, ohne Fehlermeldung, ohne Protokolleintrag.

Vorgehen bewusst gemessen statt geraten: Erst die Angaben der Anbieter
(PayPal "Best Practices", Google CSP-Abschnitt der Setup-Anleitung),
dann im echten Browser mit PayPals offizieller Testkennung
"client-id=test" nachgemessen und die Verstoesse ueber das Ereignis
securitypolicyviolation eingesammelt. Die Messung hat zwei Dinge
gefunden, die in keiner Anleitung standen:

- www.sandbox.paypal.com (frame-src + connect-src). Beim Einrichten
  testet man mit Sandbox-Zugangsdaten; ohne diesen Eintrag haette die
  Kasse in genau dieser Phase nicht abgeschlossen werden koennen.
- accounts.google.com/gsi/style (style-src). 'unsafe-inline' deckt das
  NICHT ab -- es erlaubt nur Stile im Dokument, keine nachgeladene
  Stilvorlage. Der Anmelde-Knopf waere unformatiert erschienen.

Bewusst einzelne Adressen statt PayPals vorgeschlagener Platzhalter
(*.paypal.com): Was die Messung nicht gebraucht hat, steht nicht drin.
Skripte bleiben ohne 'unsafe-inline' -- die Pruefsummen-Loesung fuer die
eigenen Inline-Bloecke bleibt unangetastet, und ein zusaetzliches
'unsafe-inline' waere neben Pruefsummen ohnehin wirkungslos.

Neuer Test pruef-kasse.mjs (15 Pruefungen): laedt das echte PayPal-SDK,
baut die Bezahlknoepfe wirklich auf, rendert den echten Google-Knopf und
verlangt NULL Verstoesse. Bestandstest pruef-inhaltsrichtlinie.mjs (22)
und pruef-musik.mjs (17) bleiben gruen -- inklusive der Gegenprobe, dass
eingeschleuste Skripte weiterhin blockiert werden.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 15:11:27 +02:00
DogFatherGitandClaude Opus 5 87db17704a Test: kein Bild / keine eingebundene Datei fehlt
Prüft, was die öffentlichen Seiten laden: Bilder, og:image/twitter:image,
Favicon, Manifest. Eine fehlende Datei zeigt sich als leerer Kasten oder
kaputte Teilen-Vorschau, oft unbemerkt.

Ergebnis: 67 eigene Ressourcen, alle vorhanden, 0 fehlen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 15:07:55 +02:00
DogFatherGitandClaude Opus 5 3a8a52aa49 Test: kein interner Link zeigt ins Leere
Sammelt alle internen Links von 24 öffentlichen Seiten und prüft jedes
Ziel einmal gegen die echte Domain. Weiterleitungen (3xx, z.B. auf eine
Zugangswand) gelten als gültig, nur 4xx/5xx als toter Link.

Ergebnis: 41 Ziele, alle in Ordnung, 0 kaputt.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 15:06:36 +02:00
DogFatherGitandClaude Opus 5 1d99da8ef8 Musik-Knopf repariert: Inhaltsrichtlinie blockierte die Spotify-Player
Gemeldet am 27.08.2026 ("unten rechts laeuft was nicht richtig mit der
Musik"). Ursache lag nicht in der Seite, sondern in der am 26.08.2026
eingefuehrten Inhaltsrichtlinie: "frame-src 'none'" verbietet dem
Dokument jede Einbettung -- und die beiden Player (Hasidog, Van-Van)
sind genau das. Der Knopf reagierte, das Feld ging auf, die Player
blieben leer. Kein Absturz, keine Fehlermeldung auf der Seite; die
Begruendung stand nur in der Browser-Konsole.

- frame-src erlaubt jetzt genau eine Quelle: https://open.spotify.com.
  Kein Sternchen, kein 'unsafe-*'. Was im Spotify-Rahmen passiert,
  regelt Spotifys eigene Richtlinie.
- Neuer Test pruef-musik.mjs (17 Pruefungen). Er startet bewusst den
  ECHTEN server/index.js statt eines Datei-Servers -- ein einfacher
  Datei-Server erzeugt die Kopfzeile gar nicht und haette den Fehler
  nie gesehen. Geprueft wird im echten Browser, ob die Rahmen wirklich
  von open.spotify.com laden (statt einer Fehlerseite), dazu Tippziel,
  Position, Schliessen per Knopf und Escape, Handy-Layout.

Beim Nachsehen aufgefallen und NOCH OFFEN: PayPal-Kasse und
Google-Anmeldung auf abonnieren.html laden ihre Skripte und Rahmen
ebenfalls von fremden Adressen. Beide sind derzeit nicht scharf
(googleClientId/paypalClientId sind null), wuerden aber mit derselben
Richtlinie an derselben Stelle still ausfallen, sobald Dogi seine
Zugangsdaten eintraegt. Wird gesondert mit ihm besprochen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 13:52:36 +02:00
DogFatherGitandClaude Opus 5 887da7b09b Verwaltung: Gate-Text an die neue Anmelde-Regel angepasst
Unter der Ueberschrift stand weiterhin "gilt danach 24 Stunden" -- seit
heute gilt der Code aber nur, solange die Seite/App offen ist. Ein
Versprechen, das die Seite nicht mehr einhaelt, ist schlimmer als gar
keins: Dogi haette sich sonst darauf verlassen, morgen ohne Code
weiterzukommen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 13:29:03 +02:00
DogFatherGitandClaude Opus 5 7960b212c8 Verwaltung: Zugangscode wieder bei jedem Oeffnen noetig
Dogi hat die Lockerung vom 04.08.2026 ("Code nur einmal am Tag") heute
zurueckgenommen. Gewaehlte Auspraegung: Code beim OEFFNEN der Seite/App,
Neuladen im selben Tab wirft nicht raus, keine Abmeldung bei Untaetigkeit.

- admin-auth.js legt die Sitzung jetzt in sessionStorage statt in
  localStorage: gehoert zum Tab/App-Fenster, uebersteht F5 und Uploads,
  ist beim naechsten Oeffnen weg. Zweiter Tab = eigener Code.
- Alte localStorage-Sitzung wird beim ersten Laden einmalig entfernt --
  sonst waere Dogi trotz Umstellung mit dem alten Token weiter drin und
  ein Token laege monatelang im Browser herum.
- Versionsnummer des Skripts hochgesetzt: Caddy liefert Assets mit
  max-age=14400, der Browser haette die alte Anmelde-Logik sonst bis zu
  vier Stunden weiterbenutzt.
- Der 24-Std-Ablauf bleibt als Rueckfallsicherung fuer tagelang offene
  Tabs; der Server erzwingt dieselbe Grenze weiterhin selbst.
- Neuer Test pruef-verwaltung-anmeldung.mjs (12 Pruefungen: Gate beim
  Oeffnen, alte Sitzung wirkungslos, falscher/richtiger Code, F5 bleibt
  drin, neuer Tab verlangt Code, Abmelden raeumt auf, Untaetigkeit meldet
  NICHT ab).
- pruef-verwaltung-kacheln.mjs: echten Aufruf ans Live-Backend abgefangen
  und auf die Supporter-Zeilen gewartet -- der Test hatte dadurch
  gelegentlich falsch gemeldet.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 13:28:14 +02:00
DogFatherGitandClaude Opus 5 0465a7a14a Verwaltung: Stimmen als kompakte Kacheln, Supporter-Liste auf 4 gekuerzt
Beides auf Wunsch vom 27.08.2026 ein-/ausklappbar:

- Stimmen liegen jetzt in einem Raster (auto-fill, min. 290px) statt
  untereinander -- am Computer stehen 3-4 Kacheln nebeneinander, am Handy
  eine. Zugeklappt ist genau die erste Reihe sichtbar; wie viele Kacheln
  das sind, liest die Logik aus dem Raster selbst aus (getComputedStyle
  liefert bei auto-fill die gebauten Spuren), damit "erste Reihe" auf
  jeder Breite stimmt -- inklusive Neuberechnung beim Groessenwechsel.
- Supporter-Tabelle zeigt nur noch die 4 zuletzt Angemeldeten, Rest per
  Knopf. Knopftext nennt immer die Gesamtzahl, damit nichts versteckt
  wirkt; beim Zuklappen springt die Ansicht sauber zum Listenanfang.
- Neuer Test pruef-verwaltung-kacheln.mjs (18 Pruefungen, Computer +
  Handy, inkl. Groessenwechsel und Ueberlauf-Kontrolle).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 13:17:05 +02:00
DogFatherGitandClaude Opus 5 988a2fdc95 DEPLOY.md: interner Deploy verifiziert — npm-Warnungen eingeordnet, sleep 6
Der erste echte Lauf des internen Deploy-Blocks (28.08.2026) förderte zwei
harmlose, aber verunsichernde Punkte zutage:

- npm ci warnt "allow-scripts ... better-sqlite3": Die allowScripts-Sperre
  blockiert den nativen Build, aber prebuild-install liefert ein fertiges
  Binary. Dienst lädt danach die DB einwandfrei -> in Ordnung. Als Kontrolle
  dokumentiert.
- sleep 2 war zu kurz: Der health-Check lief, bevor der Dienst auf 4200
  hörte -> kurzzeitig 502 / "activating", obwohl gleich darauf alles läuft.
  Auf sleep 6 erhöht, mit Hinweis, wann ein 502 wirklich ein Problem ist.

Deploy selbst war erfolgreich: Bremse greift live (20 durch, 21. -> 429),
multer 2.2.0 + node-cron 4.6.0 installiert, better-sqlite3 lädt.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 12:41:38 +02:00
DogFatherGitandClaude Opus 5 5c546207e7 DEPLOY.md: interner Deploy-Block ohne führendes cd
Der Block scheiterte in der Praxis am ersten "cd /home/dogiintern/...":
Das Verzeichnis ist 700, selbst dogi kommt per cd nicht hinein, nur
dogiintern über sudo -u. Jetzt git -C statt cd, und das npm ci in einer
sudo -u dogiintern bash -c 'cd ... && npm ci'-Shell. Mit Warnhinweis,
warum kein cd davor stehen darf.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 12:03:52 +02:00
DogFatherGitandClaude Opus 5 68b421b79a Zahlungen-Bühne: besserer Bildausschnitt statt flacher Mitte
Das cryo-kristall-Motiv (Zahlungen) ist als einziges der sechs HOCHKANT
(702x941) statt quer (1672x941). Auf der breiten 16:9-Bühne zeigt "cover"
davon nur einen horizontalen Streifen -- der mittige (Standard center
center) traf die unruhigen Kristallsplitter und wirkte flach und
beschnitten.

Jetzt zeigt Zahlungen den oberen Streifen (background-position center
15%): die eleganten geschwungenen Kristallbänder rechts als Blickfang,
links ruhiger dunkler Raum für die Karten -- eine klare Komposition wie
bei den anderen Motiven. Nur für Zahlungen überschrieben, die übrigen
fünf bleiben mittig.

Nebenbei: Der Wert kommt aus --vw-blick, das für alle Bereiche längst
definiert, bisher aber nirgends ausgelesen wurde (toter Code). Die neue
.vw-buehne-Ausnahme wendet ihn für Zahlungen erstmals an.

Service-Worker-Cache v63 -> v64 (beide Stellen), damit Geräte mit der App
den neuen Ausschnitt bekommen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 12:02:26 +02:00
DogFatherGitandClaude Opus 5 50ff8e0f4c DEPLOY.md: voller Deploy-Block für den internen Dienst + Stand nachgezogen
- Vollständiger kopierfertiger Deploy-Block für server-internal MIT npm ci:
  pull -> npm ci -> Lade-Test der nativen Module -> restart -> Selbsttest,
  als eine &&-Kette, die bei jedem Fehler VOR dem Neustart abbricht (alter
  Dienst läuft dann unberührt weiter). Bisher stand dort nur pull + restart
  ohne npm ci -- genau die Lücke, durch die Code und Pakete auseinanderliefen.
- Wächter: 18 -> 19 Punkte (Sicherungsprüfung dazugekommen).
- Zwei offene Punkte ergänzt: ausstehender Server-Neustart (Kernel/OpenSSL
  liegen bereit) und der ausstehende server-internal-Deploy.
- Veralteten Datenschutz-404-Eintrag abgehakt (heute live 401 verifiziert).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 11:16:30 +02:00
DogFatherGitandClaude Opus 5 9bd9357185 Überschriftenordnung im Hauptinhalt: keine übersprungenen Ebenen mehr
Fünf Seiten hatten einen Sprung h1 -> h3 im Hauptinhalt (die Kachel-
Überschriften waren h3, ohne h2 dazwischen). Für Bildschirmleser fehlte
damit eine Ebene im "Inhaltsverzeichnis" der Seite (WCAG 1.3.1).

Pro Seite der passende, optik-erhaltende Weg -- jede Änderung mit
Screenshot bzw. gemessener Schriftgröße gegengeprüft:

- werte, kontakt, index: Kachel-h3 -> h2 (die Kacheln SIND die
  Hauptabschnitte unter der h1). Neue Regel .card > h2 hält die kompakte
  h3-Optik (1.62rem); Varianten .card-brand/.card-feature bleiben über
  ihre eigenen h3-Regeln unberührt. Gemessen: 25.92px vorher = nachher.
- bewerben: die schon sichtbaren Gruppenlabels (Community / Agentur)
  waren <span> -> jetzt <h2> mit derselben Klasse. Optik per Screenshot
  identisch (zentriert, uppercase, Zierstrich).
- links: die Kacheln nutzen Spezial-Varianten mit eigenen Größen, ein
  Tag-Wechsel wäre riskant -> stattdessen eine unsichtbare Gruppen-
  überschrift (.sr-only, neue Klasse nach WCAG-Standard). Ändert die
  Optik nicht, vervollständigt aber die Ordnung für Bildschirmleser.

Cache-Buster 20260828b (main.css geändert: .card > h2, .sr-only).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 11:08:37 +02:00
DogFatherGitandClaude Opus 5 8a2943df35 Fußzeilen-Spaltentitel h4 -> h2 (Überschriftenordnung, WCAG 1.3.1)
Die drei Fußzeilen-Spaltentitel (Filipe, Dogi&Hasi & Manager, Mehr)
waren <h4>, obwohl der Hauptinhalt bei h2/h3 endet -- ein Sprung in der
Überschriftenordnung (h2 -> h4) auf jeder Seite. Für Bildschirmleser ist
die Überschriftenliste das Inhaltsverzeichnis; eine übersprungene Ebene
stört die Orientierung.

Jetzt <h2> (eigenständige Abschnitte unter der Seiten-h1). Die Optik
bleibt exakt: Der CSS-Selektor .footer-grid h4 wurde zu .footer-grid h2
umgezogen, die kleine, gedämpfte Darstellung (.85rem, uppercase,
Akzentfarbe) überschreibt weiterhin die große globale h2-Größe.

Teil des Barrierefreiheits-Durchgangs (pruef-barrierefrei.mjs). Behebt
die reinen Fußzeilen-Fälle; die Hauptinhalt-Überschriften folgen separat.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 10:57:55 +02:00
DogFatherGitandClaude Opus 5 8a2d2b802a Wächter-Protokoll hält sich selbst klein (keine unbegrenzte Log-Datei)
waechter.log wuchs unbegrenzt: alle 5 Minuten eine Zeile, ~288/Tag. Ohne
Grenze irgendwann zu groß zum Durchsehen -- dann verliert das Protokoll
seinen Zweck.

Der Wächter kürzt jetzt selbst, statt logrotate: Das bräuchte eine Datei
in /etc (kein Schreibrecht) und einen Extra-Dienst. Vor dem Anhängen wird
nur die Größe abgefragt (billig); erst über 1 MB (~45 Tage) wird die Datei
einmal gelesen und auf die jüngsten 2000 Zeilen (~1 Woche) gestutzt.

Beim Bauen einen eigenen Fehler gefangen: statSync war in waechter.mjs
nicht importiert (beim Auslagern der Sicherungsprüfung mit entfernt worden).
node --check meldet das nicht -- es hätte erst zur Laufzeit im nächsten
Cron-Lauf gekracht. Import ergänzt.

Test pruef-protokoll-kuerzen.mjs: klein bleibt unangetastet, groß wird auf
die JÜNGSTEN Zeilen gestutzt (älteste fallen weg), Grenzfall und fehlende
Datei sauber. 8/8.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 10:40:03 +02:00
DogFatherGitandClaude Opus 5 91bf0755cb Test: alle Admin-Routen automatisch auf Zugriffsschutz prüfen
Liest alle 109 /admin/-Routen direkt aus index.js (nicht hartkodiert)
und spricht jede mit einem ungültigen Token an. Erwartet 401/403.

Der Sinn: Der Schutz sitzt in jedem einzelnen Handler statt als Sperre
vor der Gruppe. Wer eine neue Route hinzufügt und den Aufruf vergisst,
macht sie unbemerkt öffentlich -- das fällt beim Klicken nie auf. Weil
der Test die Routen aus dem Code liest, taucht eine künftig vergessene
Absicherung automatisch als Fehlschlag auf, ohne dass jemand den Test
pflegt.

Ungültiges Token statt gar keins ist der gefahrlose Weg, das auch für
schreibende Routen (anlegen, löschen) gegen die echte Domain zu tun:
Der Schutz greift vor dem Handler, es kann nichts verändert werden.

Ergebnis live: 109 geschützt, 0 auffällig.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 10:37:25 +02:00
DogFatherGitandClaude Opus 5 a612a0f0fd Anfragebremse für /submit und /testimonials/submit (die letzten zwei ungebremsten Routen)
Bei der Bestandsaufnahme als "keine Route hat eine Bremse" gemeldet --
das war falsch (grep suchte nach rateLimit/bremse, im Code heißen sie
Kontingent/Sperre/Fehlversuche). Fast alle öffentlichen Routen SIND
gebremst: Anmeldung, Supporter-Login, Upload, Kundenanfragen. Übrig
blieben genau zwei schreibende Routen: Bewerbungen und Stimmen.

Ohne Bremse könnte ein Skript die Datenbank mit Müll fluten. Kein
Sicherheitsleck (beide landen in einer Warteschlange, nichts wird
ungesehen veröffentlicht), aber eine sinnvolle Härtung.

Statt das vorhandene Muster ein drittes Mal zu kopieren: ein Baustein
lib/kontingent.js, den nun alle drei Routen nutzen. Zwei Verbesserungen
gegenüber dem Original in testimonials.js:
- getrennte Töpfe je Zweck (ein Bild-Upload verbraucht kein
  Bewerbungs-Kontingent)
- Selbstreinigung: die alte Zähler-Map ließ jede IP für immer im
  Speicher stehen (langsames Leck), die neue räumt abgelaufene Einträge auf

Grenzen: Uploads 10/Stunde/IP (belegen Plattenplatz), Text-Einreichungen
20/Stunde/IP (großzügig für geteilte Anschlüsse, stoppt Fluten).

Tests: pruef-kontingent.mjs (Baustein, 7/7), test-kontingent-routen.mjs
(echte Routen liefern 429 ab Grenze, getrennte Töpfe, IPs unabhängig,
4/4). Bestehende Tests unverändert grün (37/37).

NOCH NICHT LIVE: server-internal läuft aus /home/dogiintern (kein Zugriff),
wird mit den übrigen Server-Änderungen in einem Deploy live geschaltet.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 10:35:54 +02:00
DogFatherGitandClaude Opus 5 55775b53ea Beleg: die multer-DoS-Lücke ist in unserem Setup nicht auslösbar
Gestern als "kritisch, 5 Anfragen töten den Dienst dauerhaft" gemeldet.
Bei genauer Prüfung heute stimmt das nicht:

- index.js hat einen process.on("uncaughtException")-Handler, der genau
  das Prozess-Ende abfängt, das CVE-2025-7338 beschreibt. In drei
  Angriffsvarianten (roher Socket, chunked ohne Abschluss-Chunk,
  content-length-Lüge) blieb der Dienst am Leben.
- Die öffentliche Upload-Route hat eine eigene IP-Bremse (10/Stunde) und
  multer-Härtung; trust proxy ist gesetzt, die Bremse greift pro echter IP.

multer 1.4.5 hat die CVE trotzdem (Fakt) — das Update auf 2.x bleibt
richtig als Wurzelbehandlung, ist aber Hygiene, kein Notfall. Dieser Test
dokumentiert den Nachweis, damit die Einordnung nachvollziehbar bleibt.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 10:28:01 +02:00
DogFatherGitandClaude Opus 5 46563b02fc Waechter merkt jetzt, wenn die naechtliche Sicherung ausfaellt
Beim Nachweis des ersten automatischen Laufs gefunden: Der Cron-Eintrag
endet auf >/dev/null 2>&1 -- jede Fehlermeldung wird verworfen. Das
Sicherungsskript fuehrt zwar ein eigenes Protokoll, aber alles, was VOR
der ersten Protokollzeile schiefgeht (Skript geloescht, sqlite3 weg,
Platte voll, Cron gestoppt), passiert spurlos. Niemand haette es
gemerkt -- ausser in dem Moment, in dem man die Sicherung braucht.

Der Waechter prueft ab sofort die Datei selbst, nicht das Protokoll:
Ein Protokoll kann "erfolgreich" melden, waehrend die Datei fehlt.

In lib/ ausgelagert, weil waechter.mjs beim Import sofort seinen ganzen
Durchlauf startet -- testbar war die Funktion dort nicht.

Beim Testentwurf einen eigenen Fehler gefunden: readdirSync wirft
sowohl bei fehlendem Ordner (ENOENT) als auch bei fehlenden Rechten
(EACCES). Die erste Fassung behandelte beides als "kein Urteil" und
haette damit einen geloeschten Sicherungsordner verschwiegen. Jetzt
getrennt: ENOENT meldet, EACCES schweigt.

Die 26-Stunden-Grenze ist bewusst nicht enger: Kurz vor dem naechsten
Lauf ist die juengste Sicherung regulaer 24 Stunden alt. Genau dort
entstehen die Fehlalarme, nach denen man die Meldungen abschaltet --
und dann geht der eine echte mit unter. Als eigener Testfall abgesichert.

14 von 14 Pruefungen bestanden.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 03:02:55 +02:00
DogFatherGitandClaude Opus 5 f8f6a26cdf Tastatur-Test prüft die Wirkung des Sprungs, nicht sein Vorhandensein
Ein Sprunglink, der den Fokus nicht mitnimmt, sieht im Quelltext
korrekt aus und ist in der Benutzung wertlos. Der Test betätigt ihn
deshalb wirklich und misst, wo der nächste Tab landet.

Beim Nachmessen auf der Live-Seite fiel auf: Der Fokus liegt sofort
nach dem Sprung korrekt auf <main>, wandert rund 300 ms später aber
auf <body>. Drei Hypothesen einzeln geprüft und alle widerlegt --
sanftes Scrollen (aus: unverändert), Animationen (aus: unverändert),
Fensterfokus im Testbrowser (document.hasFocus() bleibt true). Ein
Abfangen sämtlicher focus()- und blur()-Aufrufe ergab keinen einzigen
Aufruf aus dem Seitencode. Es ist browserinternes Verhalten und
folgenlos: die Sprungmarke für die Tab-Reihenfolge bleibt gesetzt,
nachgewiesen auf allen fünf Seiten.

Zählung korrigiert: Die erste Fassung suchte nur Links und Knöpfe und
meldete auf Formularseiten "Nr. 0 von 67, -1 Punkte gespart", weil das
Ziel dort ein Eingabefeld ist. Jetzt dieselbe Liste wie beim Zählen,
und unsichtbare Elemente fliegen raus.

25 von 25 bestanden.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 01:26:35 +02:00
DogFatherGitandClaude Opus 5 efe98e32a7 Sprung zum Inhalt: Seite ohne Maus in einem Schritt bedienbar
Der Tastatur-Test (pruef-tastatur.mjs, neu) zeigte auf allen fünf
geprüften Seiten dasselbe Bild: jedes Bedienelement erreichbar, Fokus
durchgehend sichtbar, keine Tastaturfalle -- aber kein Sprunglink.

Ohne ihn muss sich jemand, der die Tastatur benutzt, auf JEDER Seite
erneut durch das komplette Menü tabben (31 bis 52 Punkte), bevor der
eigentliche Inhalt beginnt. WCAG 2.4.1 verlangt genau diesen Ausweg.

An einer Stelle gelöst statt in 35 Dateien: renderHeader() in main.js
setzt das Sprungziel und stellt den Link davor.

Das tabindex="-1" am <main> ist der Teil, der meistens fehlt: ohne ihn
verschiebt der Sprung in Chrome und Safari nur die Bildlaufleiste, der
Tastaturfokus bleibt in der Navigation -- der nächste Tab landet wieder
im Menü und der Sprung war wirkungslos.

Cache-Buster auf 20260827a (244 Stellen), sonst bekäme niemand die
geänderte main.js und main.css ausgeliefert.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 01:21:31 +02:00
DogFatherGitandClaude Opus 5 b99e3043b6 Bilder werden vor dem Hochladen aufbereitet
Das Bild "Event des Jahres" liegt als PNG mit 2524 KB auf dem Server
und macht damit zwei Drittel der gesamten Startseite aus. Angezeigt
wird es mit 246 bis 420 Punkten -- die Datei hat 1672. PNG ist fuer ein
Foto ausserdem das falsche Format.

Der Fehler passiert beim Hochladen: Ein Handy liefert Fotos in voller
Kameraaufloesung, und niemand denkt vorher ans Verkleinern. Es ist auch
nicht die Aufgabe dessen, der ein Bild aussucht -- sondern die der
Seite, die es entgegennimmt.

IM BROWSER, NICHT AUF DEM SERVER

Serverseitig braeuchte es eine Bildbibliothek mit nativem Code,
installiert in einem Verzeichnis, an das ich nicht herankomme. Der
Browser kann das ohnehin: Canvas skaliert und kodiert seit jeher.

Nebeneffekt: Schon der Upload wird kleiner. Wer vom Handy aus ein
8-MB-Foto hochlaedt, wartet sonst am Mobilfunknetz.

DREI ENTSCHEIDUNGEN

1. Kleine PNGs bleiben unangetastet. Ein Logo mit durchsichtigem
   Hintergrund wuerde als JPEG einen schwarzen Kasten bekommen. Die
   Grenze liegt bei 400 KB -- darunter ist es wahrscheinlich eine
   Grafik, darueber praktisch immer ein Foto.

2. Wird die Datei NICHT kleiner, bleibt das Original. Bei bereits gut
   komprimierten Bildern kann erneutes Kodieren sogar zulegen -- und
   Qualitaet kosten fuer nichts.

3. Schlaegt irgendetwas fehl, wird das Original hochgeladen. Ein
   misslungenes Verkleinern darf niemals einen Upload verhindern.

GEPRUEFT

Grosses PNG 3000px: 130 -> 55 KB, auf 1600px begrenzt.
Grosses JPEG 3000px: 269 -> 131 KB.
Kleines PNG: unveraendert, bleibt PNG.
Keine Skriptfehler.

Gilt fuer alle drei Upload-Stellen: Event-Bild, Team-Foto,
Stimmen-Bild.

Das vorhandene 2524-KB-PNG bleibt davon unberuehrt -- es liegt schon
auf dem Server. Ein einmaliges Neu-Hochladen ueber die Verwaltung
ersetzt es durch die aufbereitete Fassung.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 00:49:49 +02:00
DogFatherGitandClaude Opus 5 5deb920f39 Fuenf Bilder auf die tatsaechlich benoetigte Groesse gebracht
Die Startseite uebertrug 3,72 MB. Gemessen wurde, wie breit jedes Bild
tatsaechlich angezeigt wird -- in Handy- UND Desktop-Ansicht, denn die
groessere der beiden bestimmt, wie gross die Datei sein MUSS. Wer nur
am Handy misst und danach verkleinert, macht die Desktop-Ansicht
unscharf.

  casper-2.jpg            1050px -> 420px   245 KB -> 47 KB
  dogfather-casper-2.jpg  1050px -> 420px   182 KB -> 35 KB
  dogfather-portrait.jpg   933px -> 420px   122 KB -> 35 KB
  casper-1.jpg             720px -> 420px   153 KB -> 72 KB
  dogfather-casper-1.jpg   770px -> 400px   150 KB -> 63 KB

600 KB weniger. Die Zielbreiten liegen bewusst ueber dem gemessenen
Bedarf (408-411px gemessen, 420px gesetzt): Ein Bild kleiner zu machen
als noetig waere schlimmer als ein zu grosses -- Unschaerfe sieht man,
ein paar Kilobyte nicht.

Qualitaet vor dem Ersetzen angesehen, nicht nur die Zahl: Augen, Fell
und Kanten sind bei 420px unveraendert scharf.

NICHT ANGEFASST, WEIL ZU KLEIN

char-dogfather.jpg und char-hasidog.jpg haben 599px, brauchen auf
grossen Schirmen aber 954px. Sie sind also UNSCHAERFER als noetig --
das ist der umgekehrte Fall und gehoert getrennt betrachtet.

DER GROESSTE BROCKEN BLEIBT

Das Bild "Event des Jahres" ist ein PNG mit 2524 KB -- zwei Drittel der
ganzen Startseite. PNG ist fuer ein Foto das falsche Format; als JPEG
in der benoetigten Breite (840px) waeren es rund 150 KB. Die Datei
liegt unter /var/lib/dogfather-internal/uploads und ist ueber den
Verwaltungsbereich hochgeladen worden -- dort wird nicht umgewandelt.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 00:31:07 +02:00
DogFatherGitandClaude Opus 5 b037867a99 Tests beenden sich jetzt sauber (process.exitCode statt process.exit)
Der Absturz auf dem Server bestand nach dem ersten Fix fort:

  node::RemoveEnvironmentCleanupHook … Assertion failed: (env) != nullptr
  Statement::~Statement() … better_sqlite3.node
  Abgebrochen

DER ERSTE VERSUCH GING AN DER URSACHE VORBEI

Ich hatte db.close() entfernt -- naheliegend, weil der Aufrufverlauf
auf einen Statement-Destruktor zeigte. Es half nicht. Die Ursache liegt
eine Ebene tiefer: process.exit() beendet Node SOFORT, waehrend
better-sqlite3 noch offene Statements haelt. Deren Aufraeumhaken laeuft
dann ins Leere.

process.exitCode setzt nur den Rueckgabewert; Node beendet sich danach
von selbst, sobald nichts mehr aussteht -- und raeumt dabei in der
richtigen Reihenfolge auf.

WARUM DAS MEHR ALS EIN SCHOENHEITSFEHLER WAR

Der Absturz kam NACH allen Pruefungen und VOR der Zusammenfassung. Der
Test meldete einen Fehler, obwohl inhaltlich alles bestanden war. In
einer mit && verketteten Befehlsfolge blieb deshalb der anschliessende
Dienst-Neustart aus, und die neuen Endpunkte antworteten weiter mit
404. Gesucht habe ich bei den Endpunkten, beim Deploy, an der
Zugangswand -- die Ursache lag beim Beenden eines Testprozesses.

BEMERKENSWERT

Fuenf Tests im Projekt benutzten process.exitCode bereits. Das Muster
war also etabliert; meine neuen Dateien wichen davon ab, ohne dass es
jemandem auffiel. Sechs Tests sind jetzt angeglichen, alle geprueft:
Rueckgabewert 0, Zusammenfassung vollstaendig.

  test-push-kette 30, test-altabbruch 16, test-webdesign-anfragen 37,
  test-webdesign-portal 49, test-webdesign-paypal 28,
  test-personendaten 15

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 00:17:21 +02:00
DogFatherGitandClaude Opus 5 76b88c6b71 Tests: kein db.close() vor process.exit()
Auf dem Server brach test-personendaten.mjs ab:

  node::RemoveEnvironmentCleanupHook … Assertion failed: (env) != nullptr
  Statement::~Statement() … better_sqlite3.node
  Abgebrochen

better-sqlite3 raeumt seine Statements ueber einen Aufraeumhaken ab.
Wird die Verbindung unmittelbar vor dem Prozessende geschlossen, laeuft
dieser Haken ins Leere.

DIE FOLGE WAR SCHLIMMER ALS DER ABSTURZ

Der Absturz kam NACH allen Pruefungen, aber VOR der Zusammenfassung.
Der Test lieferte also einen Fehlercode, obwohl inhaltlich alles
bestanden war. In der mit && verketteten Befehlsfolge blieb deshalb der
anschliessende Dienst-Neustart aus -- und die neuen Endpunkte
antworteten weiter mit 404.

Man sucht dann den Fehler bei den Endpunkten, beim Deploy, an der
Zugangswand. Die Ursache lag beim Aufraeumen einer Testdatenbank.

Node schliesst die Verbindung beim Beenden ohnehin. Zusaetzlich faengt
das Loeschen der Testdateien jetzt Fehler ab: Ohne db.close() haelt der
Prozess sie noch offen, unter Windows scheitert das Loeschen dann mit
EBUSY -- und "force" hilft dagegen nicht, es unterdrueckt nur "Datei
nicht gefunden". Ein Test, der an seinem eigenen Aufraeumen scheitert,
meldet einen Fehler, den es fachlich nicht gibt.

Beide betroffenen Tests geprueft: Rueckgabewert 0, Zusammenfassung
vollstaendig.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-27 00:10:36 +02:00
DogFatherGitandClaude Opus 5 e6e7ab033e DEPLOY.md: die heutigen Werkzeuge und der Stand der offenen Punkte
Nach einem Tag mit vielen Aenderungen beschrieb die Anleitung weder,
was neu automatisch laeuft, noch stimmte ihre Liste offener Punkte.

NEU AUFGENOMMEN

- Zwei Zahlen beim Webdesign-Deploy: CACHE_NAME in sw.js UND die Nummer
  in der Registrierungsadresse. Mit Begruendung, warum eine allein nicht
  reicht -- Cloudflare ersetzt das "no-cache" des Servers durch vier
  Stunden, gemessen am 26.08.2026.
- Was per Cron laeuft: Sicherung (taeglich 03:15) und Waechter (alle
  fuenf Minuten), samt Probeschalter und der Grenze, die bleibt.
- Die Verwaltung als eigene App, und warum ihr Manifest in der
  Ausnahmeliste der Zugangswand stehen muss.

OFFENE PUNKTE NACHGEPRUEFT STATT ABGESCHRIEBEN

Der Eintrag "Hintergrundbilder liegen nur auf dem Server" stimmt nicht
mehr: Alle genannten Dateien und der Avatar-Ordner sind versioniert,
"git status --untracked-files=all -- assets/" meldet auf dem Server
null. Als erledigt gekennzeichnet, nicht geloescht -- eine Liste, in der
Erledigtes ungekennzeichnet steht, wird beim naechsten Mal gar nicht
mehr gelesen.

Der CORS-Punkt war schaerfer formuliert als die Lage: Die Antwort
enthaelt KEIN Access-Control-Allow-Credentials, und die Verwaltung
weist sich ueber "Authorization: Bearer" aus statt ueber ein Cookie.
Ein Browser schickt bei einer fremden Seite also weder Cookies noch das
Token mit; erreichbar sind nur die ohnehin oeffentlichen Endpunkte.
Sauberer waere eine feste Herkunftsliste -- das ist Haertung, keine
Reparatur, und gehoert als eigener Vorgang mit Live-Test angefasst.

Ergaenzt: express 5 ist vorbereitet aber bewusst nicht umgestellt, und
die Datenschutz-Endpunkte warten auf einen Pull in /home/dogiintern.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 23:48:21 +02:00
DogFatherGitandClaude Opus 5 40714b382e "Es fehlt: —." war ein Satz ohne Aussage
Beim optischen Durchsehen der Handy-Aufnahmen gefunden: Im Bereich
Zahlungen stand woertlich

    NOCH NICHT EINGERICHTET
    Es fehlt: —. Solange sagt das Kundenportal freundlich Bescheid …

Kommt die Liste der fehlenden Angaben leer zurueck -- etwa weil der
Server sie nicht mitschickt --, fiel der Text auf einen Gedankenstrich
zurueck. Man liest eine Fehlermeldung, die nicht sagt, was fehlt, und
sucht dann bei den Zugangsdaten statt bei der Antwort des Servers.

Jetzt zwei getrennte Saetze: einer, wenn bekannt ist WAS fehlt, und
einer fuer den Fall, dass nur bekannt ist DASS etwas fehlt.

Ein Rueckfallwert, der aussieht wie eine Angabe, ist schlechter als
gar keiner -- er beantwortet die Frage scheinbar und schickt einen in
die falsche Richtung.

Nebenbei geprueft und in Ordnung: Der Installationshinweis am unteren
Rand verdeckt nichts dauerhaft; er schafft sich seinen Platz ueber ein
gemessenes padding-bottom (steht seit dem 23.08.2026 so drin, damals
blockierte er den Annehmen-Knopf).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 23:43:23 +02:00
DogFatherGitandClaude Opus 5 504965e875 Push-Zustand macht der Uebersicht keinen Platz mehr streitig
Ein Gesamtdurchlauf aller Suiten nach dem heutigen Tag hat vier
Fehlschlaege in pruef-handy.mjs gezeigt -- eine Regression aus meiner
eigenen Arbeit.

DIE URSACHE

Die Karte zum Zustand der Benachrichtigung stand als ausgewachsener
Block ganz oben in der Uebersicht. Auf dem Handy schob sie die erste
Kennzahl auf 513 Punkte nach unten: Man oeffnete die Verwaltung und sah
zuerst einen Hinweis, die Arbeit erst nach dem Scrollen.

ZWEI ANLAEUFE, WEIL DER ERSTE ZU KURZ SPRANG

Zuerst wurde nur der GUTE Fall zu einer Zeile. Das half nichts -- im
Pruefbrowser sind Benachrichtigungen blockiert, also griff weiterhin
der Zweig mit der grossen Karte. Eine Aenderung, die nur den Fall
behebt, den man gerade vor Augen hat, ist keine.

Jetzt sind alle drei Zustaende eine Zeile mit Punkt und Kurztext, die
Erklaerung erst beim Aufklappen. Und der gute Zustand steht am ENDE der
Uebersicht statt oben: Eine Bestaetigung, dass nichts zu tun ist,
gehoert nicht an den Anfang. Oben bleibt nur, was Handlung verlangt.

Ergebnis: 513 -> 204 Punkte bis zum Ende des Kopfbereichs, Laenge
2635 -> 2028.

DREI RUNDUNGSFAELLE

.wd-btn--klein und zwei summary-Elemente kamen mit Rahmen und
Zeilenhoehe auf 43,x Punkte. Der Test vergleicht mit "< 44", gibt aber
gerundet "44" aus -- man sucht dann einen Fehler in einer Zahl, die
richtig aussieht. Jetzt 44.5 bzw. 46; optisch aendert das nichts.

ZWEI PRUEFUNGEN PRAEZISIERT, NICHT AUFGEWEICHT

1. "Erste Zahl im oberen Drittel" mass ab dem Seitenanfang und schlug
   damit auch bei einer BERECHTIGTEN Warnung an. Ein Test, der
   Warnungen als Mangel zaehlt, erzieht dazu, sie zu verstecken. Er
   misst jetzt den festen Kopfbereich (Titel, Suche, Reiter) -- also
   das, was man nicht wegbekommt.

2. Die Folgepruefung mass zunaechst Punkte-Abstaende. Das war fragil:
   Der Reiterstreifen klebt beim Scrollen oben fest, eine Messung
   lieferte -204. Sie zaehlt jetzt HINWEISBLOECKE statt Punkte --
   unabhaengig von Scrollposition und Schriftgroesse. Gemeint war
   ohnehin "hoechstens eine Meldung", nicht "hoechstens 140px".

56/56 in pruef-handy, alle uebrigen Suiten unveraendert gruen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 23:01:12 +02:00
DogFatherGitandClaude Opus 5 1c3b3808ba Express 5: geprueft, aber noch nicht umgestellt
Der Sprung 4 auf 5 entfernt Methoden, aendert die Pfadsyntax
grundlegend (path-to-regexp 8) und stellt Standardwerte um. Vieles
davon faellt nicht beim Start auf, sondern erst, wenn eine bestimmte
Adresse aufgerufen wird -- also im Betrieb, bei einem Kunden. Deshalb
zuerst pruefen statt installieren.

ZWEI PRUEFUNGEN, DIE SICH ERGAENZEN

pruef-express5.mjs durchsucht 89 Dateien nach den Bruchstellen aus dem
offiziellen Migrationsleitfaden: entfernte Aufrufe (res.send(zahl),
req.param, app.del, res.sendfile), die UMGEDREHTE Reihenfolge bei
res.redirect, geaenderte Pfadsyntax ("/*" ohne Namen, ":a?"),
schreibgeschuetztes req.query, entfernte static-Optionen.

server/test-express5.mjs startet einen echten Server mit genau unserem
Aufbau: Middleware-Kette mit eigenen Kopfzeilen, cookieParser,
express.json, eine umleitende Schranke, express.static, ein Router mit
Parameter, 404- und Fehlerbehandlung.

ERGEBNIS

15 von 15 Kategorien unbedenklich, 10 von 10 Laufpruefungen bestanden
gegen express 5.2.1. Der Umstieg waere ohne Codeaenderung moeglich.

Ein Fehlalarm lag dabei im Pruefer selbst: Er meldete drei
req.body-Zugriffe als ungesichert, die in Wahrheit innerhalb eines
"if (typeof req.body?.feld === 'boolean')" stehen -- der Rueckblick war
mit 60 Zeichen zu kurz fuer den Block. Ein Pruefer, der abgesicherte
Stellen anmahnt, kostet die Zeit, die er sparen soll, und beim
naechsten Mal glaubt man ihm auch die echten Funde nicht mehr.

NICHT UMGESTELLT

Bewusst. Der Bestand ist sicherheitstechnisch sauber (npm audit: 0),
express 4.22.2 wird weiter gepflegt, und der Nutzen waere gering
gegenueber dem Risiko, zwei laufende Dienste anzufassen. Die Vorarbeit
liegt vor -- wenn umgestellt wird, dann als eigener Vorgang mit
anschliessendem Live-Test, nicht nebenbei.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 20:19:25 +02:00
DogFatherGitandClaude Opus 5 c03793b25f Oberflaeche fuer Auskunft und Loeschung
Sitzt im Kunden-Bereich, zugeklappt. Ein eigener Reiter waere zu
prominent fuer etwas, das man vielleicht zweimal im Jahr braucht --
ganz weglassen hiesse, im Ernstfall von Hand durch ein Dutzend
Tabellen zu suchen, bei laufender Monatsfrist.

DREI SCHRITTE, IN DIESER REIHENFOLGE

  Auskunft erstellen      unschaedlich, jederzeit
  Loeschvorschau          zeigt, was betroffen waere
  Loeschen                nur nach Vorschau, mit doppelter Eingabe

Die Auskunft laesst sich als Datei herunterladen, nicht nur ansehen.
Man muss sie der Person schicken koennen, und zwar vollstaendig --
abtippen waere der sicherste Weg, etwas zu vergessen. JSON, weil
Art. 20 DSGVO ein "gaengiges, maschinenlesbares Format" verlangt.

Die Vorschau zeigt beides getrennt: was geloescht wuerde, und was
bleiben muss -- mit Grund und mit dem Datum, ab dem es weg darf. Ohne
diese Angabe muesste man bei jeder Anfrage neu nachschlagen, welche
Frist gilt.

Vor dem Loeschen wird die Adresse ein zweites Mal verlangt. Der
Unterschied zwischen .de und .com ist ein Buchstabe, die Folge
unwiederbringlich.

GEPRUEFT

Im Handy-Format mit nachgebildeten Antworten: Auskunft zeigt die
Bereiche samt Kennzeichnung "aufbewahrungspflichtig", der
Herunterladen-Knopf erscheint, die Vorschau trennt richtig, eine
abweichende Bestaetigung wird abgelehnt. Kein Ueberlauf, keine
Skriptfehler. Die Handy-Suite bleibt bei 25/25, keine Namenskollision.

Ein Fehler lag dabei im Test selbst: Playwright prueft die ZULETZT
registrierte Route zuerst, und meine allgemeine Auffangregel
ueberdeckte die spezifischen. Die Auskunft meldete "nichts
gespeichert", obwohl die Oberflaeche richtig arbeitete.

Die Endpunkte dazu liegen in server-internal und brauchen noch einen
Deploy durch Filipe (/home/dogiintern, Rechte 700). Bis dahin meldet
die Oberflaeche einen 404 -- mit dem Hinweis, dass der Server einen
Neustart mit dem neuen Stand braucht, statt nur "Fehler" zu sagen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 20:03:35 +02:00
DogFatherGitandClaude Opus 5 7b09d752f8 Auskunft und Loeschung nach DSGVO (Art. 15/17)
Schreibt jemand "Welche Daten haben Sie ueber mich?" oder "Bitte
loeschen Sie meine Daten", laeuft eine Frist von einem Monat (Art. 12
Abs. 3 DSGVO). Bisher haette man dafuer von Hand durch ein Dutzend
Tabellen suchen muessen -- und uebersieht man eine, ist die Auskunft
unvollstaendig, ohne dass man es ihr ansieht.

⚠️ LOESCHEN IST NICHT EINFACH LOESCHEN

Der naheliegende Weg waere ein "Alles loeschen". Das waere bequem und
doppelt falsch: Rechnungen und Buchungsbelege unterliegen einer
gesetzlichen Aufbewahrungspflicht -- § 147 Abs. 3 AO nennt acht Jahre
fuer Buchungsbelege, zehn fuer Handelsbuecher, gerechnet ab dem Ende
des Kalenderjahres (Abs. 4). Wer sie auf Zuruf loescht, verstoesst
gegen Steuerrecht, um Datenschutzrecht zu erfuellen.

Die DSGVO nimmt diesen Fall selbst aus (Art. 17 Abs. 3 lit. b). Fuer
das, was bleiben muss, sieht sie die Einschraenkung der Verarbeitung
vor (Art. 18) -- genau so wird es ausgewiesen, samt Datum, ab dem
geloescht werden darf.

Dasselbe bei Widerrufserklaerungen: Sie sind der Nachweis, DASS und
WANN widerrufen wurde. Wer sie loescht, vernichtet seinen eigenen Beleg
in genau der Sache, in der es spaeter Streit geben koennte.

EIN FEHLER, DEN DER TEST VOR DEM ERSTEN LAUF GEFUNDEN HAT

Die Tabelle wd_widerrufe fuehrt die Adresse nicht als "email", sondern
als "kontakt". Die erste Fassung suchte nach "email" -- sie haette dort
NIE einen Treffer geliefert, und die Auskunft haette trotzdem sauber
ausgesehen. Aufgefallen nur, weil die Testdaten gegen das echte Schema
angelegt wurden statt gegen die Annahme.

Deshalb steht in der Quellenliste jetzt die Spalte, nicht eine
Vermutung.

WEITERE ENTSCHEIDUNGEN

- Die Suche geht ueber die Adresse UND ueber die Kundenkennung. Vieles
  haengt nicht an der Adresse; ohne diesen Schritt bliebe die halbe
  Auskunft leer.
- Gross- und Kleinschreibung spielt keine Rolle -- sonst bekaeme
  jemand eine leere Auskunft, weil er seine Adresse anders schreibt.
- Die Loeschung verlangt die Adresse ein zweites Mal. Der Unterschied
  zwischen .org und .com ist ein Buchstabe, die Folge unwiederbringlich.
- Sie laeuft in einer Transaktion: Bricht sie ab, bliebe sonst ein
  halbgeloeschter Bestand, und niemand wuesste, welcher Teil weg ist.
- Der Vorgang wird protokolliert, aber OHNE die geloeschten Inhalte --
  sie dabei erneut zu speichern waere das Gegenteil des Zwecks.

15 Pruefungen gruen, mit Gegenprobe (fremde Adresse liefert nichts).

Die Oberflaeche in der Verwaltung folgt. Deploy braucht Filipe:
server-internal liegt unter /home/dogiintern mit Rechten 700.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 19:55:05 +02:00
DogFatherGitandClaude Opus 5 ac1d5c9916 Waechter: Rechte wurden gesetzt, aber wirkten nicht
Nachgemessen: /var/lib/dogfather-waechter stand auf 755 -- weltweit
lesbar, mit einer vollstaendigen Liste aller Dienste, Adressen und
ihres Zustands darin. Also genau das, was der Commit von heute
Nachmittag verhindern sollte.

Die Absicht stand im Code, die Wirkung fehlte. Zwei Gruende, beide
still:

1. "mode" bei mkdirSync gilt nur, wenn das Verzeichnis dabei NEU
   entsteht. Beim zweiten Lauf existiert es immer -- dann laesst
   mkdirSync die Rechte unberuehrt. Hier war es sogar schon vor dem
   Einbau der Zeile angelegt worden.
2. Selbst beim Neuanlegen zieht die umask des Prozesses Bits ab.

Der Unterschied zur Sicherung ist lehrreich: /var/backups/dogfather
steht korrekt auf 700, weil dort ein ausdrueckliches chmod im Skript
steht. Dieselbe Ueberlegung, einmal umgesetzt und einmal nur gemeint.

Jetzt chmod bei jedem Lauf, fuer Verzeichnis UND Dateien.

Aufgefallen ist es nur, weil die Rechte nachgesehen wurden statt
angenommen -- der Code sah richtig aus.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 19:37:45 +02:00
DogFatherGitandClaude Opus 5 6077c1700f server-internal: package-lock.json aufgenommen
Nach der Installation auf dem Server vom dortigen Stand uebernommen --
nicht hier erzeugt. Das ist der entscheidende Unterschied: Eine lokal
erzeugte Datei haette festgeschrieben, was HIER aufgeloest wird, nicht
was dort tatsaechlich laeuft. Genau diese Luecke sollte sie ja
schliessen.

Festgeschrieben sind jetzt:

  multer          2.2.0   (war 1.4.5-lts.1, veraltet)
  node-cron       4.6.0   (war 3.0.3)
  express         4.22.2  (package.json sagt ^4.21.2)
  better-sqlite3  11.10.0
  dotenv          16.6.1
  cors            2.8.6

express 4.22.2 zeigt, warum das noetig war: Die package.json erlaubt
alles unter 5.0, installiert war eine andere Fassung als die genannte.

uuid taucht in der Liste nicht mehr auf. Es kam ausschliesslich ueber
node-cron 3.x herein und war die einzige Luecke, die "npm audit"
gefunden hatte. Gegenprobe nach der Umstellung: "found 0
vulnerabilities". Auch die Veraltet-Markierung von multer ist weg.

Der Dienst laeuft stabil (PID unveraendert ueber mehrere Messungen,
keine zusaetzlichen Neustarts) und antwortet mit 401 -- er lebt also
und prueft Rechte.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 19:35:59 +02:00
DogFatherGitandClaude Opus 5 a2487212c8 multer 2.x und node-cron 4.x: geprueft, package.json angehoben
multer 1.4.5-lts.1 ist von den Betreuern ausdruecklich als veraltet
markiert. Die Meldung der Registry, woertlich:

  "Multer 1.x is impacted by a number of vulnerabilities, which have
   been patched in 2.x. You should upgrade to the latest 2.x version."

⚠️ BEMERKENSWERT: "npm audit" meldet dazu NICHTS. Die Pruefung nannte
nur eine Luecke in uuid, eingeschleppt ueber node-cron 3.x. Wer sich
allein auf audit verlaesst, haette multer fuer unbedenklich gehalten.
Die Deprecation-Meldung ist hier die eigentliche Warnung -- sie steht
aber an einer Stelle, an die man nur kommt, wenn man gezielt nachfragt.

2.0.0 behebt CVE-2025-47935 und CVE-2025-47944. Einzige dokumentierte
Breaking Change: Node ab 10.16. Auf dem Server laeuft 24.

node-cron 4.x behebt die uuid-Luecke. Die Fassung ist eine Umstellung
auf TypeScript, ohne dokumentierte API-Aenderung -- genau dabei aendert
sich aber gern die Art des Standard-Exports, und "import cron from
'node-cron'" wuerde danach beim START scheitern, nicht bei der
Installation.

GEPRUEFT STATT ANGENOMMEN

In einem eigenen Verzeichnis gegen multer 2.2.0 und node-cron 4.6.0
getestet, mit genau den Aufrufen aus index.js: diskStorage mit
destination/filename, limits, fileFilter, single/array/fields,
MulterError samt code, cron.schedule("* * * * *") und task.stop().
15 von 15 bestanden.

Die Pruefung liegt jetzt als server-internal/test-pakete.mjs im Projekt
-- die Frage "laeuft unser Code damit noch?" stellt sich bei jedem
Hauptversionswechsel neu.

Express bleibt bewusst bei 4.x. Der Sprung auf 5 ist ein eigener
Vorgang mit deutlich mehr Flaeche; ihn hier mitzunehmen wuerde zwei
unabhaengige Risiken in einem Schritt buendeln.

Die Installation selbst braucht Filipe: server-internal liegt unter
/home/dogiintern mit Rechten 700.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 19:30:58 +02:00
DogFatherGitandClaude Opus 5 f64403aea8 package-lock.json wird versioniert (server/)
Ohne Lock-Datei darf jede Installation andere Fassungen ziehen:
"^4.21.2" erlaubt alles unter 5.0. Auf dem Server laeuft deshalb
express 4.22.2, waehrend in der package.json 4.21.2 steht -- geprueft
wird also nie genau das, was ausgeliefert wird. Solche Unterschiede
fallen nicht beim Deploy auf, sondern im Betrieb, und dann sucht man
den Fehler im eigenen Code.

WARUM SIE AUSGESCHLOSSEN WAR

Ein "git pull" auf dem Server scheiterte daran: Git ueberschreibt keine
unverfolgte Datei -- unabhaengig davon, ob ihr Inhalt derselbe ist. Der
Ausschluss hat den Deploy repariert und dabei den Zweck der Datei
beseitigt.

Nachgemessen statt vermutet: Die Datei hier und die auf dem Server sind
Byte fuer Byte identisch (SHA-256 a50b028d…). Es gab also nie einen
inhaltlichen Konflikt, nur einen formalen. Er loest sich, indem die
Datei einmal vom Server entfernt und danach aus dem Repo geholt wird.

DEPLOY.md ergaenzt: auf dem Server "npm ci" statt "npm install". ci
loescht node_modules vorher und baut streng nach der Lock-Datei; es
schreibt sie nie um und bricht ab, wenn sie nicht zur package.json
passt -- statt still etwas anderes zu installieren.

server-internal/ folgt, sobald die dortige Lock-Datei vorliegt. Dieses
Verzeichnis liegt unter /home/dogiintern mit Rechten 700.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 19:26:40 +02:00
DogFatherGitandClaude Opus 5 ea11cefe17 Verwaltung auf dem Handy: vier Fehler behoben
Geprueft mit nachgebildeten Daten -- lange Firmennamen, lange
E-Mail-Adressen, mehrstellige Betraege. Eine leere Seite laeuft nie
ueber; die Fehler, um die es geht, entstehen erst mit Inhalt.

1. DAS RASTER DER KOPFZEILE WAR BREITER ALS SEIN BEHAELTER

Auf 390px summierten sich die beiden Spalten auf 372,7px, waehrend die
Leiste nur 358px breit ist. Rasterspalten schrumpfen von sich aus nicht
unter ihren Inhalt ("min-width: auto"). Alles darin schob sich nach
rechts hinaus: die Kopfzeile 8px, der Reiterstreifen 12px.

Sichtbar war das kaum -- der Streifen ist ohnehin wischbar, rechts
fehlte nur ein Stueck Rand. Genau deshalb blieb es liegen: ein Fehler,
der sich als Eigenart tarnt. Jetzt minmax(0, …) plus min-width: 0 an
den Kindern; lange Titel kuerzen mit Auslassungspunkten, statt zu
schieben.

2. DER INSTALLATIONSHINWEIS ZEIGTE DAS FALSCHE SYMBOL

Seit die Verwaltung eine eigene App ist, stand dort weiter der schwarze
Husky. Man las "Als App installieren" neben dem einen Bild und bekam
das andere auf den Startbildschirm. Das Symbol wird jetzt aus dem
Manifest abgeleitet, das die Seite tatsaechlich einbindet -- damit
stimmt es auch fuer jede kuenftige App, ohne dass jemand daran denken
muss.

3. DIE UEBERSICHT BRACH BEI UNVOLLSTAENDIGER ANTWORT AB

d.projekte und d.verlauf wurden ungeprueft mit .length angefasst,
waehrend drei andere Felder ausdruecklich geprueft wurden. Fehlte eine
der Listen, brach die Uebersicht mit "Cannot read properties of
undefined" ab -- und zwar NACH dem Aufbau der oberen Kacheln: Die halbe
Seite stand da, der Rest fehlte kommentarlos. Genau der Fall, den der
Kommentar daneben als "realistisch" beschreibt. Jetzt wird aufgefuellt
statt abgebrochen.

4. ZWEI FEHLALARME IM TEST SELBST

Der Test meldete den Reiterstreifen als Ueberlauf (er ist ein
Wischstreifen -- dass dort etwas ausserhalb liegt, ist sein Sinn) und
eine 1x1-Checkbox als zu kleines Tippziel (bedient wird sie ueber ihr
Label, 315x162 Punkte). Beides wuerde dazu verleiten, Funktionierendes
"zu reparieren". Der Test unterscheidet das jetzt.

Ebenso meldete er "Strg" und "Enter" mit 10,4px -- beide sind auf
schmalen Schirmen laengst ausgeblendet. getComputedStyle liefert auch
fuer verborgene Elemente eine Schriftgroesse; jetzt zaehlt nur
Sichtbares.

25 Pruefungen ueber sechs Bereiche gruen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 19:09:33 +02:00
DogFatherGitandClaude Opus 5 62cbd724e2 Manifest der Verwaltungs-App an der Zugangswand freigeben
Beim ersten Live-Test war die App nicht installierbar: Symbole 200,
Manifest 302.

Die Zugangswand hat eine Ausnahmeliste fuer App-Bausteine -- sw.js und
app.webmanifest stehen ausdruecklich darin, mit einer ausfuehrlichen
Begruendung aus dem Universe. Das neue Manifest fehlte schlicht.

Der Grund gilt hier sogar staerker als bei der oeffentlichen App: Die
Verwaltung liegt IMMER hinter der Schranke. Ohne Freigabe bekommt der
Browser statt des Manifests eine Umleitung auf zugang.html, also HTML
statt JSON -- und bietet "App installieren" gar nicht erst an.

Unbedenklich: Das Manifest enthaelt Name, Farben, Symbolpfade und drei
Verknuepfungen. Keine Kunden-, Projekt- oder Preisdaten. Wer es liest,
erfaehrt, dass es eine Verwaltung gibt -- was die Zugangswand ohnehin
verraet.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 16:13:40 +02:00
DogFatherGitandClaude Opus 5 1608521854 Verwaltung ist jetzt eine eigene App
Wunsch: "ich will die verwaltungsseite auch getrennt runter laden
koennen und installieren koenne."

Bisher gab es ein Manifest fuer den ganzen Webdesign-Bereich; die
Verwaltung war darin nur eine Verknuepfung. Jetzt hat sie ein eigenes
mit eigener "id" -- der Wert, an dem alles haengt: Ohne ihn halten
Browser beide fuer dieselbe App, und die zweite Installation
ueberschreibt die erste, statt danebenzustehen.

EIGENES SYMBOL

Zwei gleich aussehende Kacheln auf dem Startbildschirm waeren keine
Trennung. Das neue Symbol ist ein Ausschnitt des Eiskristall-Wappens --
dasselbe Motiv, das die Verwaltung ohnehin traegt, und klar
unterscheidbar vom schwarzen Husky der oeffentlichen App. Erzeugt aus
cryo-wappen.webp in drei Groessen, die beschneidbare Fassung weiter
herausgezoomt, damit Android die Spitzen des Wappens nicht abschneidet.

Als JPEG statt PNG: 76 statt 385 KB bei 512 Punkten. Bei einem
fotografischen Motiv hat PNG nichts zu gewinnen, und 385 KB fuer ein
Symbol waeren unverhaeltnismaessig.

⚠️ DER GELTUNGSBEREICH IST ABSICHTLICH WEIT

Naheliegend waere "/webdesign/verwaltung" gewesen. Das haette die App
unbrauchbar gemacht: Ohne gueltigen Ausweis leitet der Server auf
/webdesign/zugang.html um -- ausserhalb des Bereichs, und was
ausserhalb liegt, oeffnet der Browser in einem eigenen Fenster. Weil
der Zugang beim Schliessen endet, waere das bei fast jedem Start
passiert: Man tippt auf die App und landet im Browser. Nachgemessen:
verwaltung.html antwortet ohne Ausweis mit 302.

EIN STILLES VERSPRECHEN EINGELOEST

Die Verknuepfungen zeigen auf "?bereich=anfragen" und dergleichen --
und derselbe Parameter steht in den Push-Meldungen. Ausgewertet hat ihn
bisher NIEMAND. Man landete immer auf der Uebersicht, ohne dass etwas
kaputt aussah. Jetzt oeffnet die Seite den gewuenschten Reiter, ueber
einen ausgeloesten Klick auf den vorhandenen Reiter statt ueber
nachgebaute Logik: Daran haengen Signatur, Leuchtbalken, Nachladen und
der Wischstreifen auf dem Handy.

Auch hier hat erst der Test den Fehler gezeigt -- und danach einen in
der Pruefung selbst: Sie erreichte die Funktion gar nicht, weil diese
im gekapselten Bereich liegt. Jetzt nach aussen gegeben, wie schon
window.vwBalkenSetzen.

23 Pruefungen gruen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 16:12:35 +02:00
DogFatherGitandClaude Opus 5 92d2b568de Pruefung auf doppelte Funktionsnamen — und ein zweiter Fund
Die Kollision von heute war weder fuer den Browser noch fuer einen
Syntaxpruefer sichtbar: Zwei Funktionen desselben Namens sind erlaubtes
JavaScript, die spaetere gewinnt lautlos. Auch die
Oberflaechen-Tests schwiegen -- sie pruefen, ob Kacheln DA sind, nicht
ob sinnvoller Text darin steht.

pruef-namenskollision.mjs durchsucht jetzt alle 110 Dateien mit eigenem
JavaScript. Mit Gegenprobe, damit die Pruefung nicht selbst kaputtgehen
und dabei "sauber" melden kann.

ZWEITER FUND, AELTER ALS MEIN FEHLER

Sie meldete sofort eine weitere Kollision: tageSeit stand zweimal in
verwaltung.html. Beide rechneten dasselbe, mit einem Unterschied -- bei
fehlendem Datum gab die eine null zurueck, die andere 0.

Die spaetere (mit 0) gewann. Damit war die frueher definierte
wirkungslos, und mit ihr die Pruefungen "if (t === null) return ''" in
altersText und dringlichkeit: Ohne Datum stand dort "seit heute" statt
gar nichts.

Entfernt wurde die spaetere. Die beiden verbliebenen Aufrufer vertragen
null genauso wie 0 -- nachgeprueft, nicht angenommen: Math.max(x, null)
ergibt x, und null >= 7 ist falsch, gleich wie bei 0.

110 Dateien jetzt sauber.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 14:58:12 +02:00
DogFatherGitandClaude Opus 5 0e8c1447f2 DRINGEND: Namenskollision zerstoerte die Uebersicht
Alle Kacheln der Uebersicht zeigten "[object Object]" und "undefined".

Ursache war mein Push-Code von heute. Er brachte eine Hilfsfunktion
namens kachel(titel, inhalt, art) mit -- und weiter oben in derselben
Datei gibt es seit langem kachel(o), die die Uebersichtskacheln baut.

JavaScript kennt keine Ueberladung. Steht spaeter eine zweite Funktion
desselben Namens im selben Gueltigkeitsbereich, gewinnt sie. Ohne
Warnung, ohne Fehlermeldung, ohne dass irgendein Werkzeug anschlaegt.
Danach bekam jede Uebersichtskachel mein Objekt als ersten Parameter
und gab es als Zahl aus.

Besonders tueckisch: Die Push-Karte selbst funktionierte einwandfrei --
sie steht ja unmittelbar ueber den kaputten Kacheln. Der sichtbare
Schaden lag weit weg von seiner Ursache, und nichts deutete auf den
neuen Code hin.

Die Funktion heisst jetzt pushKachel und sagt damit, wozu sie gehoert.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 14:55:31 +02:00
DogFatherGitandClaude Opus 5 77b395e2b4 Test: laesst den Service Worker jetzt wirklich arbeiten
Die Luecke, durch die der Fehler von heute bis zum Benutzer
durchgerutscht ist.

Der Test hat 48 Seiten in einem echten Browser geoeffnet und nichts
bemerkt -- weil er den Service Worker nie hat arbeiten lassen. Er war
gruen und wertlos zugleich: Der entscheidende Weg wurde nicht
begangen.

Neu geprueft wird jetzt:
  - der Service Worker meldet sich an und wird aktiv
  - er darf tatsaechlich etwas abrufen (das war der kaputte Punkt)
  - dabei entsteht kein Richtlinien-Verstoss
  - sw.js traegt selbst KEINE Richtlinie, denn sie wuerde zu SEINER

Ausserdem umgedreht: Ein Test verlangte fuer Nicht-Seiten ausdruecklich
"default-src 'none'" -- und sicherte damit genau den Fehler ab, der die
App lahmlegte. Er haette den naechsten Versuch, es richtig zu machen,
als Fehler gemeldet. Jetzt wird geprueft, dass CSS, JavaScript, Bilder
und der Service Worker KEINE Richtlinie bekommen.

22 Pruefungen gruen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 14:47:10 +02:00
DogFatherGitandClaude Opus 5 0f253df134 Service Worker: Versionsnummer in der Adresse gegen fremden Cache
Bei der Fehlersuche zur lahmgelegten App kam ein zweiter, aelterer
Mangel zum Vorschein.

GEMESSEN

  Dienst selbst    Cache-Control: no-cache
  nach Cloudflare  Cache-Control: max-age=14400  (auch bei MISS)

Cloudflare ersetzt die Vorgabe des Servers durch vier Stunden. Ursache
ist eine feste "Browser Cache TTL" in den Einstellungen. Der Kommentar
in server/index.js behauptet das Gegenteil ("Cloudflare respektiert
laut Doku ein vom Origin gesetztes Cache-Control") -- fuer diese Domain
stimmt das nicht. Nachgemessen, nicht vermutet.

WARUM DAS MEHR ALS EIN SCHOENHEITSFEHLER IST

Jede Aenderung am Service Worker erreicht die Geraete bis zu vier
Stunden zu spaet. Solange alles laeuft, faellt das nicht auf. Ist der
ausgelieferte Stand aber fehlerhaft -- wie heute, als eine zu strenge
Inhaltsrichtlinie ihn lahmlegte und die App "Keine Verbindung" zeigte
--, sind es vier Stunden, in denen sich nichts reparieren laesst.
Genau dann, wenn Tempo zaehlt, ist man am langsamsten.

LOESUNG OHNE FREMDE EINSTELLUNGEN

Die Registrierung laedt jetzt "/webdesign/sw.js?v=56". Aendert sich die
Nummer, ist es eine andere Adresse -- dafuer kann kein Zwischenspeicher
einen alten Stand haben. Die Nummer wird zusammen mit CACHE_NAME
hochgezaehlt; beide gehoeren zusammen und stehen jetzt auf 56.

Das wirkt unabhaengig davon, wie Cloudflare eingestellt ist. Die
Einstellung selbst sollte trotzdem geprueft werden -- sie betrifft auch
CSS und JavaScript.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 14:43:45 +02:00
DogFatherGitandClaude Opus 5 c591925342 DRINGEND: Inhaltsrichtlinie legte die App lahm
Symptom: Die installierte App zeigte "Keine Verbindung", obwohl die
Seite online war, der Server lief und jede Pruefung Erfolg meldete.

Ursache war meine eigene Zeile von heute Vormittag. Fuer alles, was
keine Seite ist, setzte die Middleware "default-src 'none'" -- mit dem
Gedanken "kostet nichts und schadet nie". Der zweite Halbsatz war
falsch.

Ein Service Worker uebernimmt die Inhaltsrichtlinie, die beim
Herunterladen SEINER EIGENEN Skriptdatei gesetzt war, nicht die der
Seite, fuer die er arbeitet. sw.js ist keine Seite, bekam also 'none'
und durfte damit nichts mehr abrufen. Jede Anfrage scheiterte -- und
weil der Service Worker fuer genau diesen Fall eine Offline-Seite
bereithaelt, sah es aus wie ein Netzausfall beim Benutzer.

Fuer Bilder, Stylesheets und Schriften bringt eine Richtlinie ohnehin
nichts: Sie steuert, was ein DOKUMENT nachladen darf. Ein Bild laedt
nichts nach. Dem Schaden stand also nie ein Gewinn gegenueber.

Jetzt: Richtlinie nur noch fuer HTML-Dokumente.

Warum der Test das nicht gefunden hat, folgt gleich -- er hat die
Seiten geladen, aber nie den Service Worker arbeiten lassen. Genau die
Luecke, durch die es durchgerutscht ist.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 14:41:27 +02:00
DogFatherGitandClaude Opus 5 b2f4567cce Waechter: leere Geraeteliste und fehlende Tabelle unterscheiden
Der erste echte Probelauf meldete "kein Geraet angemeldet". Richtig --
aber dieselbe Meldung waere auch erschienen, wenn die Tabelle gar nicht
existierte, weil dbLesen in beiden Faellen "" zurueckgibt.

Das sind zwei voellig verschiedene Lagen: Einmal genuegt ein Klick in
der Verwaltung, einmal ist die Migration nicht gelaufen. Wer die falsche
Meldung liest, drueckt auf Einschalten, sieht keine Wirkung und sucht
dann beim Browser statt bei der Datenbank.

Genau die Sorte Verwechslung, gegen die dieser ganze Strang gebaut
wurde. Jetzt wird zuerst gefragt, ob die Tabelle da ist, und die Meldung
sagt im harmlosen Fall auch gleich, was zu tun ist.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 14:38:11 +02:00
DogFatherGitandClaude Opus 5 bcf6e01fa9 Waechter: engere Dateirechte und ein Probeschalter
Der erste automatische Lauf war erfolgreich (14:20, 18 Punkte, 0
auffaellig). Zwei Nachtraege:

RECHTE

Zustandsdatei und Protokoll lagen mit 644 in einem 755-Verzeichnis.
Personenbezogene Daten stehen dort keine -- wohl aber eine
vollstaendige Liste aller Dienste, Adressen und ihres Zustands. Fuer
jemanden, der einen Angriff vorbereitet, ist das eine bequeme
Landkarte. Jetzt 700/600. Kostet nichts, also gibt es auch keinen
Grund, es herzugeben.

PROBESCHALTER

  node waechter.mjs --probe

Verschickt eine Meldung, ohne dass etwas kaputt sein muss. Das ist die
einzige Moeglichkeit, den MELDEWEG zu pruefen, ohne auf eine echte
Stoerung zu warten.

Der Grund ist derselbe wie beim stillen "if (!url) return;", das diese
Reihe ausgeloest hat: Eine Ueberwachung, deren Zustellung
stillschweigend nicht funktioniert, ist schlimmer als gar keine. Man
haelt die Stille dann fuer "alles in Ordnung" -- dabei ist sie nur
Stille.

Steht bewusst VOR den Messungen: Wer den Meldeweg pruefen will, soll
nicht erst 18 Punkte abfragen muessen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 14:21:08 +02:00
DogFatherGitandClaude Opus 5 3a5805bfad Waechter: meldet Ausfaelle, statt sie unbemerkt zu lassen
Der Befund war besser als der Aufgabentitel: Alle Dienste haben
Restart=always und stehen nach einem Absturz von selbst wieder auf. Die
Luecke liegt woanders:

  - Dauerschleife: Startet ein Dienst und stuerzt sofort wieder ab, gibt
    systemd nach wenigen Versuchen auf. Dann bleibt er unten.
  - "active" heisst nicht "antwortet". Ein haengender Dienst gilt
    systemd als gesund.
  - Zertifikatsablauf und volle Platte machen keinen Dienst inaktiv,
    legen aber alles lahm.

Geprueft werden 18 Punkte: sechs Dienste, neun Adressen, Plattenplatz,
zwei Zertifikatslaufzeiten.

WARUM ALS ROOT PER CRON

Ein Waechter, der ueber den internen Dienst meldet, hat einen
Konstruktionsfehler mit Ansage: Ausgerechnet wenn DIESER Dienst das
Problem ist, kaeme keine Meldung durch. Also liest er .env und Datenbank
selbst und verschickt selbst -- unabhaengig davon, ob noch irgendetwas
laeuft. Ohne Fremdpakete, nur Node-Bordmittel, sqlite3 und systemctl.

NUR BEI ZUSTANDSWECHSEL

Gemeldet wird, wenn etwas kippt -- in beide Richtungen. Nicht alle fuenf
Minuten dasselbe. Wer staendig Meldungen bekommt, sieht irgendwann keine
mehr an und uebersieht die eine, auf die es ankam.

ZWEI EIGENE FEHLER, DIE DER TEST GEFUNDEN HAT

1. Zuerst stand je Adresse eine handgepflegte Liste erlaubter
   Antwortcodes. Der erste Lauf meldete VanVans Shop als ausgefallen --
   er war es nicht, er steht ebenfalls hinter einer Zugangswand und
   antwortet mit 302. Ich war damit genau in die Falle gelaufen, vor der
   der Kommentar an derselben Stelle warnte.

2. Danach galt "unter 400" als heil. Jetzt meldete das Postfach einen
   Ausfall, weil die geprueften Adresse 404 lieferte -- der Dienst lief
   einwandfrei, ich hatte die Adresse falsch gewaehlt.

Beide Male dieselbe Lehre: Eine Ueberwachung, die bei einer falsch
getippten Adresse "Ausfall" ruft, erzieht einen dazu, ihre Meldungen zu
ignorieren. Die Regel lautet jetzt "unter 500", denn der Waechter fragt
"lebt der Dienst?", nicht "ist der Inhalt richtig?". 401, 403 und 404
BEWEISEN, dass jemand da ist und zuhoert. Nur 5xx und Schweigen heissen,
dass dahinter nichts mehr laeuft. Ausnahme sind die beiden Pflichtseiten
Impressum und Widerruf -- dort ist alles ausser 200 bereits ein Mangel.

GEPRUEFT AM SERVER

Ausfall eingebaut: erkannt und gemeldet. Ausfall dauert an: still, keine
Wiederholung. Wieder erreichbar: Entwarnung. 18 Punkte, 0 Fehlalarme
ueber mehrere Laeufe.

⚠️ GRENZE, DIE BLEIBT

Ist der Server als Ganzes weg -- Netz, Strom, Hardware --, meldet auch
dieser Waechter nichts. Dagegen hilft nur eine Ueberwachung ausserhalb
der Maschine. Steht so im Kopf der Datei, damit sich niemand in falscher
Sicherheit wiegt.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 14:17:12 +02:00
DogFatherGitandClaude Opus 5 03a533f143 Inhaltsrichtlinie (CSP): mit Pruefsummen statt mit unsafe-inline
Fuenf Schutz-Kopfzeilen waren gesetzt, die wichtigste fehlte. Sie
entscheidet als einzige darueber, ob eingeschleuster Text zu
ausgefuehrtem Code wird oder sichtbarer Text bleibt.

WARUM NICHT DER BEQUEME WEG

Ueblich waere script-src 'self' 'unsafe-inline'. Eine Zeile, nichts geht
kaputt -- und der Schutz ist weg: Der Browser kann nicht unterscheiden,
ob ein Skript im Seitentext vom Entwickler stammt oder von einem
Angreifer. Das Ergebnis ist eine Kopfzeile, die gut aussieht und im
Ernstfall nichts tut.

Der Bestand liess den sauberen Weg zu: keine fremden Skriptquellen,
keine externen Schriften, ein Inline-Block je Seite. Von jedem Block
wird die Pruefsumme gebildet.

DIE PRUEFSUMMEN STEHEN BEWUSST NICHT IM CODE

Das waere hier eine Falle mit Ansage: Statische Dateien gehen per "git
pull" live, OHNE Neustart. Eine fest hinterlegte Summe waere nach der
naechsten Textaenderung falsch -- und die Seite wuerde ihr eigenes
Skript nicht mehr ausfuehren. Sichtbar erst im Browser des Besuchers,
nicht beim Deploy, und aussehend wie kaputtes JavaScript.

Deshalb liest die Middleware die Datei selbst und merkt sich das
Ergebnis, solange die Aenderungszeit gleich bleibt. Ein Test aendert
index.html im laufenden Betrieb und prueft, dass die Summe nachzieht
und die Seite weiterlaeuft.

WAS DER TEST GEFUNDEN HAT

Die erste Fassung haette die Startseite und stimmen.html beschaedigt:
Team-Fotos, Event des Jahres und die Stimmen kommen von der
postfach-Subdomain, img-src erlaubte nur 'self'. Der Deploy haette
Erfolg gemeldet, der Server waere gestartet -- und die Bilder waeren
weg gewesen. Gefunden, weil der Test alle 48 Seiten in einem echten
Browser oeffnet und mitschreibt, was blockiert wird.

Ein zweiter Fehlschlag lag am Test selbst: Er verlangte eine Pruefsumme
auf jeder Seite, auch auf denen ohne Inline-Block. Ein Test, der
Unmoegliches fordert, wird frueher oder spaeter abgeschaltet -- er
unterscheidet jetzt nach dem tatsaechlichen Inhalt der Datei.

DREI onclick-ATTRIBUTE ENTFERNT

Sie haetten 'unsafe-inline' erzwungen. Zweimal ein "Coming soon"-Knopf,
dessen onclick nur Klicks abfing -- ein <a> ohne href tut das von
selbst, ganz ohne Skript. Einmal ein Schliessen-Knopf im
Verwaltungsbereich, jetzt mit angehaengtem Zuhoerer.

GEGENPROBE

Ohne sie waere der Rest wertlos: Eine Richtlinie, die alles erlaubt,
blockiert auch nichts und besteht jede Pruefung. Der Test schleust
deshalb echten Code ein -- ein Inline-Skript und eines von fremder
Adresse -- und beide muessen scheitern.

style-src behaelt 'unsafe-inline': 587 style-Attribute im Bestand, deren
Umbau ein echtes Risiko fuers Aussehen waere, bei kleinem Gewinn. Ueber
Stile laesst sich verschleiern und ueberdecken, aber kein Code
ausfuehren. Bleibt als eigener Punkt auf der Liste.

15 Pruefungen gruen, 48 Seiten sauber, Portfolio-Suite unveraendert 35.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 14:05:42 +02:00
DogFatherGitandClaude Opus 5 094a69732b Portfolio: Spicy SafeAddress als sechstes Projekt
Die Rechtsplattform der Spicy Media Agency fehlte -- dabei ist sie das
technisch anspruchsvollste Stueck der Sammlung.

WAS IM TEXT STEHT UND WARUM SO

Die Projektdokumentation haelt ausdruecklich fest, dass die Wendung
"100 Prozent rechtssicher" dort nirgends verwendet wird. Ein Portfolio,
das mehr verspricht als die Sache selbst, waere genau der Fehler, den
das Projekt vermeiden will. Im Ergebnis steht deshalb, dass die
Plattform fertig gebaut und bewusst noch vollstaendig gesperrt ist und
auf die schriftliche Freigabe einer Fachkanzlei wartet -- nicht, dass
sie "im Einsatz" sei.

KEIN LIVE-KNOPF

Wie bei der Buchhaltung. Ein "Live ansehen", das auf eine Zugangswand
fuehrt, bricht das Versprechen, das der Knopf gibt. Stattdessen steht
dort ein Satz, der den Grund nennt.

GESICHTER UNKENNTLICH

Auf der Zugangswand stehen zwei Profilfotos. Eines davon gehoert einer
Geschaeftspartnerin, die dieser Veroeffentlichung nicht zugestimmt hat.
Beide sind in der Aufnahme weichgezeichnet, das Firmenzeichen bleibt
scharf. Begruendet im alt-Text und in einem eigenen Abschnitt -- genau
wie bei Projekt 5, wo derselbe Fall schon einmal auftrat.

Nebenbei: Die strenge Inhaltsrichtlinie der Seite (style-src 'self')
hat das eingefuegte Stylesheet fuers Weichzeichnen blockiert. Sie tut
also genau das, wofuer sie da ist. Ueber die Stil-Eigenschaft im Skript
ging es dann.

ZWEI STELLEN, DIE SONST FALSCH GEBLIEBEN WAEREN

Die Ueberschrift hiess "Fuenf Projekte, die man anfassen kann". Mit
SafeAddress steht dort erstmals ein Projekt, das man NICHT besuchen
kann -- die Ueberschrift haette also gleich doppelt daneben gelegen.
Jetzt: "Sechs Projekte, die es wirklich gibt", und die Einleitung sagt
ausdruecklich, dass eine der Seiten gesperrt ist.

Der Schlussaufruf lud dazu ein, "das sechste" Projekt zu werden. Bei
sechs vorhandenen waere das das Angebot gewesen, eines davon zu
ersetzen. Jetzt das siebte.

Beides in allen fuenf Sprachen, de-CH als echter Dialekt wie im Rest
dieser Datei.

TEST

Zwei Fehlschlaege, beide berechtigt: die Projektzahl und ein zu grobes
Suchmuster, das bei "zwei MENSCHEN" ansprang, obwohl es die veraltete
Aussage "zwei PROJEKTE" sucht. Das Muster laesst jetzt hoechstens ein
Wort zwischen Zahlwort und Substantiv zu. Mit Gegenprobe abgesichert:
faengt weiterhin "Two projects you can visit", ignoriert "Two people
work together on that site". Ein Fehlalarm, den man nur wegdrueckt,
faengt beim naechsten Mal auch den echten Fall nicht mehr.

35 Pruefungen gruen, i18n vollstaendig.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 12:19:50 +02:00
DogFatherGitandClaude Opus 5 bad5496adc Portfolio: Buchhaltungsseite mit dem aktuellen Stand zeigen
Das bisherige Bild zeigte eine fruehere Fassung der Anmeldeseite --
flaches Logo als blasses Wasserzeichen, Text darueber, keine
Anmeldekarte. Die Seite ist inzwischen ueberarbeitet: plastisches Logo,
Samthintergrund, eine eigene Anmeldekarte mit den beiden Zugaengen und
dem Hinweis auf verschluesselte Verbindung.

Ein Portfolio, das einen alten Stand zeigt, arbeitet gegen sich selbst
-- gerade wenn die neue Fassung die deutlich bessere ist.

Neu aufgenommen in 1200x750, also exakt den Massen der uebrigen
Portfolio-Bilder. Das steht auch als width/height im <img>; eine
Abweichung wuerde beim Laden ein Springen des Rasters ausloesen und die
Kacheln unterschiedlich hoch machen.

154 KB statt 49 KB. Der Aufschlag ist nicht zu vermeiden: Das Motiv ist
jetzt fotorealistisch, mit Samtfalten und Perlen -- lauter feine
Strukturen, bei denen JPEG wenig einsparen kann. Bei Qualitaet 76 waeren
es 132 KB gewesen, um den Preis sichtbarer Artefakte im Logo. Das Bild
laedt ohnehin verzoegert (loading="lazy").

Cache-Version auf v53: Ohne das bekaemen alle, die die Seite schon
einmal geoeffnet haben, weiterhin das alte Bild aus dem Zwischenspeicher.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 11:42:11 +02:00
DogFatherGitandClaude Opus 5 f435d89a16 Benachrichtigung bei neuer Anfrage: Push aufs Handy
BEFUND ZUERST, DENN ER WAR ANDERS ALS ERWARTET

Es gab sehr wohl eine Benachrichtigung -- ueber einen Discord-Webhook,
sauber gebaut, bewusst ohne Kundendaten im Text. Sie begann aber mit:

    const url = process.env.DISCORD_WEBHOOK_WEBDESIGN;
    if (!url) return;

Und diese Variable ist in .env.example nirgends aufgefuehrt. Eine
Variable, die niemand kennt, wird nicht gesetzt; dann kehrte die
Funktion wortlos zurueck. Kein Fehler, keine Protokollzeile. Es gab
eine Benachrichtigung, die nur im Quelltext existierte -- und niemand
konnte das bemerken, weil "funktioniert" und "kaputt" identisch
aussehen, solange nichts passiert.

WAS JETZT DA IST

Push aufs Handy, ohne Fremdpaket. Der interne Dienst liegt unter
/home/dogiintern mit Rechten 700 -- dort ist kein npm erreichbar, jede
Abhaengigkeit haette dauerhafte Handarbeit bedeutet. Node bringt P-256,
HKDF und AES-128-GCM selbst mit.

Die Schluessel erzeugt der Server beim ersten Start selbst und legt den
privaten Teil verschluesselt in der Datenbank ab (derselbe Weg wie die
PayPal-Zugangsdaten). Damit gibt es keinen Einrichtungsschritt, der
vergessen werden kann.

GEPRUEFT

Gegen RFC 8291 statt gegen ein Bauchgefuehl: alle fuenf Zwischenwerte
aus Anhang A stimmen (ECDH-Geheimnis, PRK_key, IKM, CEK, NONCE), und
die fertige Nachricht ist Byte fuer Byte die aus Abschnitt 5 der Norm.
Bei Kryptographie erzeugt ein Ableitungsfehler keinen Absturz, sondern
Bytes, die genauso zufaellig aussehen wie richtige.

Dazu ein Kettentest mit einem echten Empfaenger, der entschluesselt:
Kopfzeilen, Inhalt, keine Kundendaten in der Meldung, 410 loescht das
Geraet, 500 loescht es NICHT (sonst kostet eine einzelne Stoerung die
Anmeldung). 50 Pruefungen, alle gruen.

DREI ENTSCHEIDUNGEN

1. In der Meldung stehen nur Nummer und Paket. Sie erscheint auf einem
   Sperrbildschirm, den auch jemand sieht, der zufaellig danebensteht.

2. Ist der Schluessel unlesbar, wird KEIN neuer erzeugt. Das waere der
   bequeme Weg und der schlimmste: Ein neuer oeffentlicher Schluessel
   macht schlagartig jede Anmeldung wertlos, ohne dass jemand erfaehrt,
   warum nichts mehr ankommt.

3. Die Verwaltung zeigt den Zustand an und hat einen Testknopf. Genau
   das fehlte dem Discord-Weg. Bei verweigerter Erlaubnis erscheint
   kein Knopf, der nichts bewirkt, sondern der Weg ueber die
   Browsereinstellungen.

Das stille "if (!url) return;" ist ersetzt: Jeder Weg wird einzeln
protokolliert -- auch und gerade, wenn er uebersprungen wird.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 10:50:33 +02:00
DogFatherGitandClaude Opus 5 75ab3f713f Sicherung: Kundendaten nicht mehr fuer jedes Konto lesbar
Der erste echte Lauf hat einen Mangel sichtbar gemacht, den das Skript
selbst verursacht hat: root legt Dateien standardmaessig mit 644 an,
also welt-lesbar. Nachgemessen als gewoehnliches Konto, ohne sudo: Die
Sicherung liess sich nach /tmp kopieren und daraus Kundennamen,
E-Mail-Adressen und Paketwahl auslesen; das Upload-Archiv ebenso. Auf
dieser Maschine bestehen fuenf Konten.

Eine Sicherung buendelt an einer Stelle, was sonst verstreut liegt --
sie muss enger geschuetzt sein als das Original, nicht lockerer.

umask 077 fuer alles Neue; fuer die bereits angelegten Verzeichnisse
zusaetzlich ausdruecklich 700 bzw. 600, denn umask wirkt nur auf neu
Erzeugtes.

Der gleiche Mangel besteht beim Original selbst (644 dogiintern) --
das kann ich nicht aendern, es gehoert nicht mir. Wird gemeldet.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 00:34:05 +02:00
DogFatherGitandClaude Opus 5 bf6c105bc5 Zeilenenden im Repo festlegen
Beim Einchecken der Sicherungsskripte meldete Git, es werde LF durch
CRLF ersetzen. Diesmal ging es gut -- im Repo landet LF, und auf dem
Server kam die Datei sauber an. Verlassen sollte man sich darauf nicht:
Faellt bei einer .sh-Datei ein Wagenruecklauf in die erste Zeile, sucht
Linux ein Programm namens "/bin/bash\r" und meldet "bad interpreter"
unter Nennung eines Pfades, der voellig richtig aussieht. Dieser Fehler
kostet erfahrungsgemaess mehr Zeit als er verdient.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 00:25:52 +02:00
DogFatherGitandClaude Opus 5 d62ff53062 Datensicherung: taeglicher Stand und geprueft Wiederherstellung
Bisher gab es keine Sicherung. Ein Plattenfehler oder ein falsches
DELETE haette Kunden, Projekte, Zahlungen und Widerrufsnachweise
endgueltig gekostet.

sicherung.sh legt taeglich einen Stand an -- mit SQLites eigenem
".backup", nicht mit "cp". Der Grund ist messbar: Die Datenbank ist
778 KB gross, ihr WAL 4,1 MB. Eine Kopie der .db allein waere also
nicht bloss veraltet, sondern weitgehend leer. Ein Gegentest mit einer
frisch beschriebenen Datenbank zeigte 4 KB in der .db gegen 2,1 MB im
WAL.

Jeder Stand wird sofort nach dem Anlegen geprueft (integrity_check und
Mindestzahl an Tabellen) -- eine Sicherung, die niemand geoeffnet hat,
ist keine. 14 taegliche Staende, sonntags zusaetzlich ein Wochenstand,
8 davon: Eine still fortschreitende Verfaelschung faellt manchmal erst
nach Wochen auf, wenn alle taeglichen Staende sie schon enthalten.

wiederherstellen.sh geht den Weg zurueck: Sicherung erst pruefen, dann
Dienst anhalten, bisherigen Stand beiseiteraeumen statt loeschen,
einspielen, Dienst starten und nachsehen, ob er laeuft.

Am Server geprueft: 44 Tabellen gegen das Original verglichen, 0
Abweichungen; 7 von 7 Uploads im Archiv; Rotation 17 -> 14 entfernt
genau die aeltesten; Wiederherstellung spielte 100 Kunden ueber 300 und
rettete die 300 nach beiseite.

Eine Annahme wurde dabei widerlegt und der Kommentar entsprechend
korrigiert: Ein zurueckgelassenes WAL vermischt NICHT zwei Staende --
SQLite erkennt an der Kennung, dass es nicht dazugehoert, und verwirft
es. Der echte Schutz ist das Anhalten des Dienstes.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 00:25:11 +02:00
DogFatherGitandClaude Opus 5 2e2172202e Rechtstexte in fuenf Sprachen
Impressum, AGB, Widerrufsbelehrung und Datenschutzerklaerung gibt es
jetzt auf Deutsch, Schweizer Hochdeutsch, Englisch, Franzoesisch und
Portugiesisch. 95 Textbloecke, rund 13.000 Zeichen je Sprache.

DIE MASSGEBLICHKEITSKLAUSEL STEHT IN JEDER SPRACHE

Nur die deutsche Fassung ist verbindlich; die Uebersetzung dient dem
Verstaendnis. Ohne diesen Hinweis waere die Uebersetzung ein Risiko --
ein Kunde koennte sich auf die franzoesische Fassung berufen. Der
Hinweis stand schon auf der Seite, aber selbst nur auf Deutsch: Genau
die Leute, fuer die er gedacht ist, konnten ihn nicht lesen.

WIDERRUFSBELEHRUNG UND MUSTERFORMULAR SIND AMTLICH

Sie folgen dem Wortlaut aus Anhang I der Richtlinie 2011/83/EU in der
jeweiligen Amtssprache -- nicht einer eigenen Uebersetzung. Der
Gesetzgeber hat diese Saetze selbst formuliert; wer sie neu uebersetzt,
verliert den Schutz der Musterbelehrung. Angepasst ist nur die Person:
Die Muster sprechen von "wir/uns", hier schreibt ein Einzelunternehmer
"ich/mir" -- diese Anpassung sieht das Muster ausdruecklich vor.

SCHWEIZERDEUTSCH GIBT ES HIER BEWUSST NICHT

Auf allen anderen Seiten spricht "de-CH" echten Dialekt. In
Rechtstexten waere das unserioes -- es gibt keine Widerrufsbelehrung auf
Mundart. Die Schweiz schreibt Hochdeutsch, nur ohne Eszett. Genau das
erzeugt eine Zeile Code aus dem deutschen Text, statt 95 von Hand
gepflegter Doppeleintraege, die beim naechsten Aendern auseinanderlaufen
wuerden.

DER FEHLER, DER FAST LIVE GEGANGEN WAERE

Beim Aufbau wurden zwei Bloecke uebersprungen: eine reine Formularlinie
und eine zweite "Fassung vom"-Zeile. Ab dort war jede Schluesselnummer
um eins verschoben.

Im Musterformular haette dadurch ueber jedem Feld die falsche
Beschriftung gestanden -- "Datum" wo "Name" hingehoert. Und in fuenf
Sprachen waere es niemandem aufgefallen, denn jede Sprache war ja
vollstaendig gefuellt: Eine Vollstaendigkeitspruefung ist gegen diese
Art Fehler wehrlos.

Die Schluessel sind deshalb jetzt ueber den TEXT zugeordnet, nicht ueber
eine laufende Nummer. Ein eigener Durchlauf vergleicht jeden deutschen
Text im Woerterbuch mit dem an derselben Stelle im HTML.

WAS DER NEUE DURCHLAUF SONST PRUEFT

- Greift die Umschaltung in jeder der fuenf Sprachen, folgt das
  lang-Attribut, bleibt kein Textblock leer?
- Gegenprobe, dass die Umschaltung ueberhaupt etwas tut: Englisch,
  Franzoesisch und Portugiesisch MUESSEN sich vom Deutschen
  unterscheiden. Ohne diese Probe waere ein Woerterbuch, das ueberall
  denselben deutschen Text zurueckgibt, gruen durchgelaufen.
- Kein Eszett in der Schweizer Fassung, sonst wortgleich.
- Die Massgeblichkeitsklausel nennt in jeder Sprache die deutsche
  Fassung.

Geprueft: 1303 Pruefungen gruen (Browser 737, Server 566), i18n ohne
Fehler, WCAG ohne Fundstellen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-25 22:28:52 +02:00
DogFatherGitandClaude Opus 5 c42fed7b27 Temporaere Hilfsdatei aus dem Projekt entfernt
patch15.py war ein Wegwerf-Skript zum Anpassen einer Testdatei und ist
versehentlich mit eingecheckt worden. Solche Dateien gehoeren nicht ins
Projekt: Sie beschreiben einen einmaligen Umbau, nicht den Zustand --
und wer sie spaeter findet, haelt sie fuer etwas, das noch gebraucht
wird.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-25 17:35:07 +02:00
DogFatherGitandClaude Opus 5 bd0346f785 Verwaltung wird fluessig: von 4,5 auf 60 Bilder pro Sekunde
Bei der Systemabnahme gemessen und fuer untragbar befunden: Die
Verwaltung lief mit 4,5 Bildern je Sekunde, sobald die Maus bewegt
wurde. Die Startseite lag bei 60. Ein Arbeitswerkzeug, das bei jeder
Mausbewegung ruckelt, ist kein Gewinn an Schoenheit.

WAS GEMESSEN WURDE, STATT GERATEN

Jeder Effekt einzeln abgeschaltet, mit echter Mausbewegung:

  alles an                6,4 Bilder/s
  ohne Glas               9,7
  ohne Lampe              9,2
  ohne Kachel-Neigung     6,4   (kostet NICHTS)
  ohne Glas UND Lampe    16,4

Kein einzelner Schuldiger -- es war die Wechselwirkung. Die Lampe
verschiebt den Untergrund, woraufhin JEDE Glasflaeche darueber ihre
Rueckseiten-Unschaerfe neu berechnen muss. Beide zusammen kosteten mehr
als beide einzeln.

Die Neigung ist gratis: Sie laeuft auf der Grafikkarte.

Ein zweiter Posten kam dazu: Solange die Kacheln durchscheinend sind,
muss das bildschirmfuellende Buehnenbild bei jeder Neuzeichnung
mitgerechnet werden. Ohne Buehnenbild stieg die Rate von 24 auf 34,6.

DREI EINGRIFFE

1. Glas nur noch auf dem Detailblatt. Davon gibt es immer genau eines,
   und es liegt gross ueber der Seite -- dort faellt die Rechenzeit
   einmal an, nicht pro Listeneintrag. Kacheln, Reiter und Knoepfe
   bekommen stattdessen eine dichte Flaeche. Optisch kaum ein
   Unterschied, weil die Struktur des Bildes ohnehin verschwindet.

2. Die Vollbild-Zeigerlampe ist abgeschaltet. Das Licht, das dem Zeiger
   folgt, gibt es weiterhin -- auf den Kacheln selbst. Das ist der
   Effekt, der zaehlt, und er ist billig: Er betrifft nur die Kachel
   unter dem Zeiger statt des ganzen Bildschirms.

3. Die Kachelflaeche ist dichter (.92/.96 statt .52/.68). Die Buehne
   bleibt rings um die Kacheln voll sichtbar, durch die Kachel selbst
   schimmert sie nur noch als Ahnung.

ERGEBNIS

  in Ruhe (lesen)        55,6 -> 60,6 Bilder/s
  beim Scrollen          54,0
  Zeiger bewegt           4,5 ->   28

DIE BILDRATE IST JETZT SELBST EIN PRUEFPUNKT

Ohne ihn kaeme jederzeit ein weiterer huebscher Effekt dazu, der die
Seite still wieder zaeh macht -- und niemand wuesste, welcher es war.
Der Durchlauf verlangt jetzt ueber 45 Bilder je Sekunde in Ruhe.

NEBENBEFUND

Die Regel fuer das Detailblatt stand unter "#vw-bereich" -- das Blatt
liegt aber in einer eigenen Ueberlagerung. Die Regel griff also nie:
Ausgerechnet die eine Flaeche, die eine Unschaerfe wirklich verdient,
hatte als einzige keine. Gefunden hat das der neue Test.

Geprueft: 1281 Pruefungen gruen (Browser 715, Server 566), i18n ohne
Fehler, WCAG ohne Fundstellen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-25 17:34:53 +02:00
DogFatherGitandClaude Opus 5 0ef64ba222 Systemabnahme: fuenf Altlasten geschlossen
Vollstaendiger Durchlauf ueber alle Pruefungen. Dabei kamen fuenf Dinge
ans Licht, die teils seit Wochen offen standen.

1. WCAG-KONTRAST -- der einzige Punkt, der echte Besucher betraf

Die lila Schilder erreichten nur 4,08:1, verlangt sind 4,5:1 fuer
normalen Text. 17 Fundstellen, alle dieselbe Ursache: Die Schrift nutzte
den vollen Ton Aurora Violet.

Gemessen wurde nicht geschaetzt: Untergrund rgb(38,41,81) aus dem
echten Bild ausgelesen, Schrift 12,5 Punkte -- und fett zaehlt erst ab
18,5 Punkten als "grosser Text".

Neu ist --wd-lila-hell (#C197FF, 6,02:1). Bewusst NICHT der knappste
Wert: #B380FF haette mit 4,91:1 gereicht, aber dasselbe Schild steht
auch ueber der Buehne im Verwaltungsbereich. Ein Ton, der nur an einer
Stelle knapp besteht, faellt beim naechsten Hintergrund wieder durch.
Dasselbe Muster wie --wd-blau-hell, das genau deshalb existiert.

2. GEHEIMNIS-TEST -- der Test war veraltet, nicht der Code

Er verlangte ".env hat Vorrang", die Umsetzung macht das Gegenteil. Die
Begruendung im Code ueberzeugt: Wer PayPal-Daten im Formular eintraegt,
erwartet, dass sie gelten. Andernfalls koennte ein alter Wert in der
.env sie stumm ueberstimmen -- und man sucht stundenlang.

Der Test prueft jetzt die tatsaechliche Reihenfolge, dazu neu, dass ein
gleichzeitig vorhandener Serverwert auch angezeigt wird.

Ausserdem endete der Lauf trotz gruener Pruefungen mit einer
Fehlermeldung: Unter Windows haelt eine offene SQLite-Datei eine Sperre,
das Aufraeumen scheiterte mit EPERM. Auf Linux waere es
durchgelaufen -- dasselbe Skript mit unterschiedlichem Ergebnis je
Rechner. Jetzt wird erst geschlossen, dann geloescht, und das Aufraeumen
kann den Lauf nicht mehr zum Scheitern bringen.

3. PORTAL-TEST -- ebenfalls veraltet

Er suchte "1500,00" und schlug fehl, seit der Formatierer den
Tausenderpunkt setzt. "1.500,00 EUR" ist die korrekte deutsche
Schreibweise; gerade ab vier Stellen macht der Punkt eine Zahl auf einen
Blick lesbar.

4. ZWEI SEITEN OHNE WOERTERBUCH -- eine Entscheidung, keine Luecke

"diagnose" ist ein Betriebswerkzeug. "rechtliches" ist der heiklere
Fall: Rechtstexte durch eine ungepruefte Uebersetzung zu schicken ist
gefaehrlicher, als sie einsprachig zu lassen. Ein Fehler in einer
Widerrufsbelehrung wirkt gegen den Verfasser.

Die Pruefung meldete beides als "Datei fehlt" -- das las sich wie ein
Versehen und stand deshalb dauerhaft in der Fehlerliste, ohne dass
jemand etwas tat. Jetzt sind beide benannt und begruendet.

5. TAG-UNGLEICHGEWICHT -- eine Fehlmessung

Die Pruefung zaehlte auch HTML-Schnipsel, die als Zeichenketten im
JavaScript stehen. Dort steht ein oeffnendes <div> regelmaessig in einer
anderen Zeichenkette als sein </div>, weil die Teile erst beim
Zusammensetzen ein Ganzes ergeben.

Gegengeprueft im Browser: geparster Baum einwandfrei, kein
Skriptfehler, Verschachtelung unauffaellig. Es war nie ein
Strukturfehler. Die Meldung stand aber monatelang als "2 Fehler" da und
haette jede echte Meldung entwertet, die dazugekommen waere.

STAND

  Browser  717 Pruefungen   0 offen
  Server   566 Pruefungen   0 offen
  i18n     keine Fehler
  WCAG     0 Fundstellen (vorher 17)

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-25 17:13:42 +02:00
DogFatherGitandClaude Opus 5 686e8924bb Portfolio: Kacheln einer Reihe sind gleich gross
Die Projekttexte sind unterschiedlich lang, also waren auch die Kacheln
unterschiedlich hoch. Jetzt bekommen alle Kacheln einer Reihe dieselbe
Hoehe, naemlich die der hoechsten.

Dafuer waren drei Dinge noetig, und nur das erste ist offensichtlich:

1. "align-items: start" musste weg. Es liess jede Kachel ihre
   natuerliche Hoehe behalten -- genau das war der Fehler.

2. Der Inhalt muss die zusaetzliche Hoehe auch aufnehmen koennen.
   Ohne Flex-Aufbau in Kachel und Inhalt waere die Kachel zwar hoeher,
   ihr Inhalt bliebe aber oben kleben und darunter entstuende ein
   leeres Feld.

3. Das letzte Element sitzt am unteren Rand.

DER PUNKT, DER FAST DURCHGERUTSCHT WAERE

Gleich hohe Kacheln heissen NICHT automatisch, dass der Inhalt buendig
steht. Nach Schritt 1 und 2 waren die Kacheln exakt gleich hoch -- und
die Knoepfe standen trotzdem auf verschiedener Hoehe.

Grund: Die Abstaende standen als style-Angabe direkt im HTML
(style="margin-top:1rem"). Eine solche Angabe schlaegt jede Regel aus
dem Stilblatt, das "margin-top: auto" lief also ins Leere. Fuenf
Vorkommen entfernt.

Danach blieben 33 gegen 48 Punkte Abstand zum Kachelboden: Ein Absatz
bringt einen eigenen Abstand nach unten mit, eine Knopfreihe nicht.
Auch das ist jetzt vereinheitlicht.

Die Regel greift ueber :last-child statt nur ueber die Knopfreihe --
die Buchhaltungs-Kachel hat naemlich gar keinen Knopf, sondern einen
Hinweistext an dieser Stelle. Der soll genauso unten stehen.

DIE PRUEFUNG MISST BEIDES GETRENNT

Einmal "jede Reihe ist in sich gleich hoch", einmal "die letzten
Elemente stehen auf einer Linie". Ein einzelner Test auf die Hoehe
haette den Knopf-Versatz nie bemerkt -- die Kacheln waren ja bereits
gleich hoch, als die Knoepfe noch verrutscht waren.

Gemessen: 1129/1129, 881/881, 831 -- und ueberall 33 Punkte Abstand zum
Kachelboden.

Geprueft: 313 Pruefungen gruen (Portfolio 34, Bewegung 34, Verwaltung 62,
CRYONOVA 53, Blickfang 13, System 5, Abmelden 16, Abbruch 42,
Angebot 54).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-25 13:28:31 +02:00
DogFatherGitandClaude Opus 5 4d39b0e989 Portfolio: zwei Projekte nebeneinander
Die Kacheln nahmen bisher die volle Breite ein -- ueber 1180 Punkte pro
Stueck. Jetzt stehen zwei nebeneinander, jede etwa 574 Punkte breit.

ZWEI UMWEGE, DIE NICHT FUNKTIONIERT HABEN

Zuerst stand hier auto-fit mit einer Mindestbreite von 420 Punkten. Das
klang flexibel, hatte aber zwei Haken: Auf einem Tablet mit 900 Punkten
blieb es einspaltig, weil zwei Spalten plus Abstand knapp nicht mehr in
den Container passten (869 gegen 828 verfuegbare Punkte).

Auf 360 gesenkt, wurden daraus auf einem breiten Schirm dann DREI
Spalten -- auto-fit fuellt eben so viele, wie hineinpassen. Gewuenscht
sind ausdruecklich zwei, also steht die Zahl jetzt fest, mit einem
Umbruch auf eine Spalte unter 760 Punkten.

minmax(0, 1fr) statt nur 1fr: Ohne die Null als Mindestbreite bekommt
eine Rasterspalte automatisch die Breite ihres breitesten Inhalts als
Untergrenze. Ein langer Projektname ohne Leerzeichen wuerde die Spalte
dann aufblaehen und das Raster aus dem Container schieben.

WAS SICH NEBENBEI VON SELBST ERLEDIGT

Der Kippwinkel haengt an der Kachelgroesse. Halb so breite Kacheln
kippen dadurch automatisch etwas lebendiger, ohne dass hier ein Wert
nachgestellt werden muesste -- die Portfolio-Kachel war ja gerade
deshalb die traegste von allen.

Das Bild ist von 16:10 auf 16:9 geflacht: Bei halber Breite waere ein
16:10-Bild sehr hoch geworden und haette das Verhaeltnis von Bild zu
Text in der Kachel gekippt.

Der Abstand kommt jetzt vom Raster statt von einem Aussenabstand unten
-- sonst saehen die Kacheln einer Zeile ungleich hoch aus.

GEPRUEFT UEBER SECHS BILDSCHIRMBREITEN

  1920px  2 Spalten  Kachel 574px
  1440px  2 Spalten  Kachel 574px
  1200px  2 Spalten  Kachel 538px
   900px  2 Spalten  Kachel 403px
   700px  1 Spalte   Kachel 644px
   390px  1 Spalte   Kachel 359px

Jeweils mit Gegenprobe, dass nichts seitlich ueberlaeuft. Ein Test auf
nur einer Breite haette den Dreispalten-Fall nie bemerkt.

Geprueft: 310 Pruefungen gruen (Portfolio 31, Bewegung 34, Verwaltung 62,
CRYONOVA 53, Blickfang 13, System 5, Abmelden 16, Abbruch 42,
Angebot 54).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-25 13:19:45 +02:00
DogFatherGitandClaude Opus 5 81c7d40274 Grosse Kacheln kippen jetzt weniger als kleine
Auf der Portfolio-Seite war die Bewegung zu stark. Der Grund liegt nicht
am Winkel, sondern an der Groesse: Bei gleichem Winkel legt eine grosse
Kachel an ihren Ecken viel mehr Weg zurueck als eine kleine.

Eine Preiskachel auf der Startseite ist 282 Punkte lang, eine
Portfolio-Kachel 1503. Fuenf Grad sehen bei der einen beilaeufig aus und
bei der anderen wie das Kippen des halben Bildschirms -- obwohl in
beiden Faellen exakt derselbe Wert im Stilblatt steht.

Der Winkel haengt jetzt an der Kachelgroesse. Bezugswert sind 420
Punkte: Kacheln bis dahin kippen voll, groessere anteilig weniger. Nach
unten bei 1,4 Grad begrenzt, damit auch die groesste Kachel noch
erkennbar reagiert und der Effekt nicht einfach ausfaellt.

Gemessen ueber alle Seiten:

  index        282px   Faktor 5,00   2,45 Grad
  ueber        588px   Faktor 3,57   1,76 Grad
  portal       562px   Faktor 3,74   1,83 Grad
  ablauf      1130px   Faktor 1,86   1,19 Grad
  leistungen  1206px   Faktor 1,74   1,14 Grad
  portfolio   1503px   Faktor 1,40   0,92 Grad

Der Durchlauf sammelt diese Werte jetzt und prueft das Verhaeltnis: Die
grosse Kachel MUSS weniger kippen als die kleine, und der Faktor darf
nie unter 1,4 fallen. Ein blosser Test auf "hoechstens neun Grad" haette
den Unterschied nicht bemerkt -- beide Faelle lagen ja deutlich
darunter, und trotzdem war einer davon zu viel.

Geprueft: 298 Pruefungen gruen (Bewegung 34, Portfolio 19, Verwaltung 62,
CRYONOVA 53, Blickfang 13, System 5, Abmelden 16, Abbruch 42,
Angebot 54).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-25 13:11:53 +02:00
DogFatherGitandClaude Opus 5 52f5d31199 Die farbige Oberkante der Kacheln ist zurueck
Aufgefallen an einem Screenshot: Bei einer Kachel lag oben ein lila
Streifen, bei den anderen fehlte jede Farbe.

Die Ursache war meine eigene Aenderung von gerade eben. Der
Glanzstreifen kam ins ::before -- dort wohnt aber schon die farbige
Oberkante (.wd-karte--kappe), und zwar seit dem urspruenglichen Bau der
Seite.

Ein Element hat nur ZWEI Pseudoelemente, und beide waren belegt: ::after
traegt den Lichtkegel, ::before die Kante. Der Glanz hat sich das
::before genommen und die Kante damit still ueberschrieben.

Warum es nur teilweise auffiel: ".wd-karte--lila.wd-karte--kappe::before"
hat zwei Klassen und damit mehr Gewicht als mein
".wd-karte::before" -- die lila Farbe blieb also stehen, die blaue
verschwand. Deshalb sah es aus wie ein Zufall statt wie ein Fehler.

DIE LOESUNG

Glanzstreifen und Lichtkegel teilen sich jetzt das ::after -- als zwei
Hintergrundebenen desselben Pseudoelements. Das ::before ist wieder
frei fuer die Oberkante.

Beides funktioniert unveraendert: Der Glanz wandert weiterhin mit der
Neigung (er nimmt --wd-nx in seinen Winkel auf), der Lichtkegel
weiterhin mit dem Zeiger.

DIE PRUEFUNG DAZU

Ein neuer Abschnitt in pruef-bewegung.mjs misst alle acht Kacheln mit
Oberkante: 5 Punkte Hoehe, ganz oben sitzend, Farbe vorhanden -- und
ausdruecklich BLAUE UND LILA zusammen. Haette der Test nur eine Sorte
angesehen, waere genau dieser Fehler wieder durchgerutscht, denn die
lila Fassung war ja nie kaputt.

Dazu die Gegenprobe, dass der Glanz nicht einfach verlorengegangen ist:
Das ::after muss beide Verlaeufe tragen.

Geprueft: 296 Pruefungen gruen (Bewegung 32, Portfolio 19, Verwaltung 62,
CRYONOVA 53, Blickfang 13, System 5, Abmelden 16, Abbruch 42,
Angebot 54).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-25 12:41:30 +02:00
DogFatherGitandClaude Opus 5 e4216a2c7a Kachel-Effekte gelten jetzt auf der ganzen Seite, nicht nur intern
Neigung, Glanzstreifen und Lichtkante lagen bisher nur im
Verwaltungsbereich. Sie stehen jetzt im Grundsystem und gelten damit
ueberall: Startseite, Leistungen, Ablauf, Portfolio, "Ueber mich",
Kundenportal.

Die Farbe kommt aus --wd-lumen. Jede Kachel bringt die ohnehin mit
(Token-Regel 02), der Verwaltungsbereich ueberschreibt sie mit der Farbe
des jeweiligen Bereichs. Dadurch braucht es keine einzige Sonderregel.

DREI FEHLER, DIE DIE REGRESSION GEFUNDEN HAT

1. Die Einblendung hat die Neigung geloescht.

   ".wd-bereit .wd-auf.wd-sichtbar" setzt "transform: none" und hat mit
   drei Klassen die hoehere Gewichtung. Sichtbar war das nur auf Seiten,
   deren Kacheln eine Einblendung tragen: Auf "ablauf" kippte die
   Kachel, auf "leistungen" nicht -- und der Wert kam in beiden Faellen
   korrekt an. Die Einblendung nutzt jetzt "translate".

   Das ist zum dritten Mal dieselbe Falle nach Buehne und Kacheln im
   Verwaltungsbereich. Merksatz: Wer "transform" animiert oder
   zuruecksetzt, blockiert es fuer alles andere.

2. Die Perspektive hat die Buehne zerlegt.

   "perspective" auf dem Abschnitt macht diesen zum Bezugsrahmen fuer
   position:fixed in seinem Inneren -- genau wie "transform" oder
   "filter". Die bildschirmfuellende Buehne im Verwaltungsbereich lag
   danach nicht mehr am Fenster, sondern am Abschnitt: gemessen 472
   statt 900 Punkte Hoehe.

   Die Perspektive steckt jetzt als Funktion im transform der Kachel
   selbst. Der gemeinsame Fluchtpunkt benachbarter Kacheln entfaellt
   damit, was bei hoechstens fuenf Grad niemand sieht.

3. Kachel-Neigung und Bild-Parallaxe haben sich aufgeschaukelt.

   Die kippende Kachel schiebt das Bild unter dem Zeiger weg, der landet
   dadurch auf einem Nachbarelement, das Bild springt zurueck auf null --
   und beim naechsten Zucken von vorne. Messbar war das als "an einer
   Ecke sauber, an der anderen dauerhaft 0".

   Klare Arbeitsteilung: Ein Bild INNERHALB einer Kachel bekommt nur den
   Zoom, die Kachel kippt darum herum. Freistehende Bilder behalten ihre
   eigene Gegenbewegung.

NEUER DURCHLAUF ueber alle Seiten (pruef-bewegung.mjs)

Weil die Effekte im Grundsystem liegen, reicht eine Pruefung auf einer
Seite nicht: Genau so ist Fehler 1 entstanden und waere unbemerkt
geblieben. Der Durchlauf geht sechs Seiten ab und prueft je Seite
Neigung, Winkel unter neun Grad, Zuruecksetzen beim Verlassen und
Skriptfehler.

Zwei Stolpersteine stecken darin dokumentiert: Die Startseite legt beim
Laden einen Vorhang ueber alles (wer zu frueh misst, trifft den Vorhang
statt der Kachel), und eine Portfolio-Kachel ist ueber 1400 Punkte hoch
-- ein fester Anteil ihrer Hoehe landet ausserhalb des Bildschirms.

Geprueft: 291 Pruefungen gruen (Bewegung 27, Portfolio 19, Verwaltung 62,
CRYONOVA 53, Blickfang 13, System 5, Abmelden 16, Abbruch 42,
Angebot 54).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-25 12:12:31 +02:00
DogFatherGitandClaude Opus 5 76aea6ac3f Bilder bewegen sich unter dem Zeiger
Faehrt der Zeiger ueber ein Bild, tritt es leicht naeher und wandert ein
Stueck GEGEN die Zeigerrichtung -- als schaue man durch ein Fenster und
lehne sich zur Seite.

Warum gegen die Richtung: Bewegt sich das Bild MIT dem Zeiger, wirkt es
wie ein Aufkleber, der verrutscht. Nur die Gegenbewegung liest das Auge
als Tiefe hinter dem Rahmen. Derselbe Grund wie bei der Buehne im
Verwaltungsbereich, eine Ebene kleiner.

Der Effekt liegt im GRUNDSYSTEM, nicht in einer einzelnen Seite. Er gilt
damit ueberall: Portfolio, Startseite, "Ueber mich", Zugangswand und die
Vorschaubilder im Verwaltungsbereich. Eine Sonderloesung je Seite waere
beim naechsten neuen Bild wieder vergessen worden.

Zwei Zahlen, die bewusst klein sind:

- Der Ausschlag liegt bei hoechstens 14 Punkten (gemessen 13,9).
- Der Zoom bei 1,055.

Zusammen ergibt das Bewegung, ohne dass ein Bild beim blossen
Vorbeifahren seinen Ausschnitt merklich aendert. Ein groesserer Wert
waere kein Effekt mehr, sondern ein Bildsprung.

Umgerechnet wird auf die Groesse des jeweiligen Bildes (-1 bis +1), nicht
in festen Bildpunkten. Sonst wanderten ein Vorschaubild von 1200 Punkten
Breite und ein Logo von 80 gleich weit -- beim kleinen saehe das aus wie
ein Ruck.

Zwei Faelle, die im Code ausdruecklich abgefangen sind:

- Der Zeiger liegt ueber einem Element, das das Bild UEBERDECKT (etwa
  einem Textblock in derselben Kachel). Ohne Behandlung bliebe das Bild
  stehen, sobald man den Rahmen verlaesst, aber die Kachel noch nicht.
- Der Zeiger steht neben dem Bild, aber noch in der Kachel. Die Werte
  liefen dann weit ueber 1 hinaus und das Bild schoesse aus dem Rahmen.
  Deshalb wird auf -1 bis +1 begrenzt.

Beim Verlassen stellt sich alles zurueck -- sonst bliebe das Bild
verschoben stehen, nachdem der Zeiger laengst weg ist.

Bei prefers-reduced-motion ist der Effekt aus.

Geprueft: 265 Pruefungen gruen (Portfolio 20, Verwaltung 62, CRYONOVA 53,
Blickfang 13, System 5, Abmelden 16, Abbruch 42, Angebot 54). Der Test
prueft ausdruecklich die RICHTUNG der Bewegung, nicht nur, dass sich
ueberhaupt etwas tut.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-25 09:48:17 +02:00
DogFatherGitandClaude Opus 5 ae8c498672 Verwaltung: die Effekte liegen jetzt auf den ECHTEN Kacheln
Der Grund, warum von allem bisher nichts zu sehen war.

Saemtliche Effekte -- Glas, Neigung, Prisma-Kante, Lichtkegel,
Glanzstreifen, Facetten, gestaffeltes Auftauchen -- lagen auf
".wd-karte". Diese Klasse kommt im Verwaltungsbereich aber fast nicht
vor. Die Listen bestehen aus ".vw-karte", die Meldungen der Uebersicht
aus ".vw-meld".

Gebaut, gemessen, geprueft, deployt -- und alles auf Elementen, die es
dort gar nicht gibt.

Der Test hat das nicht gefunden, weil er sich seine Probekachel selbst
gebaut hat: als .wd-karte. Er hat also eine Attrappe geprueft und war
zurecht gruen, waehrend auf den echten Kacheln nichts ankam. Ein Test,
der seinen eigenen Pruefgegenstand erfindet, kann diese Sorte Fehler
grundsaetzlich nicht sehen.

WAS JETZT ANDERS IST

Alle Effekte gelten fuer .vw-karte und .vw-meld:
- Glas mit Rueckseiten-Unschaerfe
- raeumliche Neigung zum Zeiger, hoechstens 7 Grad
- Lichtkegel und Prisma-Kante, die dem Zeiger folgen
- Glanzstreifen, der mit der Neigung wandert
- ungleich geschliffene Ecken
- gestaffeltes Auftauchen beim Bereichswechsel
- Projektnummer und Name stehen vor der Flaeche (translateZ)

Dazu setzt der Verwaltungsbereich die Lichtposition jetzt selbst. Der
Verfolger im Grundsystem (wd-core.js) sucht ausdruecklich nur
".wd-karte" -- auf .vw-karte waere der Lichtkegel bei seinem Startwert
oben mittig kleben geblieben, selbst nachdem alles andere stimmte.

DER TEST BAUT JETZT DIE ECHTE STRUKTUR NACH

.vw-karte mit .vw-karte-nr, .vw-karte-mitte und .vw-karte-rechts, genau
wie das Skript sie erzeugt. Zwei Stueck statt einer, damit auch die
Staffelung an echten Geschwistern gemessen wird.

Geprueft: 260 Pruefungen gruen (Verwaltung 62, Portfolio 15, CRYONOVA 53,
Blickfang 13, System 5, Abmelden 16, Abbruch 42, Angebot 54).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-25 09:33:47 +02:00
DogFatherGitandClaude Opus 5 494946386a Verwaltung: die Kacheln werden zu geschliffenen Glasplatten
Buehne und Zeigerlicht bleiben unveraendert. Diesmal geht es nur um die
Kacheln selbst.

SIE LIEGEN IM RAUM, NICHT AUF DER SEITE

Faehrt der Zeiger darueber, neigt sich die Kachel ihm entgegen -- als
wuerde man eine echte Glasplatte kippen. Beim Ueberfahren kommt sie dem
Betrachter zusaetzlich entgegen und wirft einen laengeren Schatten. Erst
das macht aus der Neigung ein Objekt im Raum statt eines schraegen
Bildes.

Der Ausschlag liegt bei hoechstens 7 Grad (gemessen 6,7). Alles darueber
verzerrt die Schrift sichtbar, und auf diesen Kacheln wird gearbeitet,
nicht nur geschaut.

DER INHALT STEHT VOR DER FLAECHE

Ueberschriften weiter vorn als Fliesstext. Dadurch entsteht beim Neigen
echte Staffelung statt einer flachen Ebene, die sich mitdreht -- und der
Text bleibt scharf, obwohl die Flaeche unter ihm schraeg liegt.

DAZU EIN GLANZSTREIFEN UND UNGLEICHE ECKEN

Ein schmales Licht laeuft ueber die Platte und wandert mit der Neigung.
Die Ecken sind diagonal weit und diagonal knapp gerundet: Eine
gleichmaessig gerundete Kachel liest sich als Knopf, die ungleiche nimmt
die Facetten der Motive auf.

DIE FALLE, DIE ICH SCHON KANNTE

Die Auftauch-Animation der Kacheln nutzte "transform" und haelt ihren
Endwert fest -- eine Animation schlaegt jede normale Regel, die Neigung
waere also wirkungslos geblieben. Genau dieselbe Falle wie zuvor bei der
Buehne, nur eine Ebene tiefer. Die Animation nutzt jetzt "translate" und
"scale" als eigene Eigenschaften; "transform" bleibt der Neigung
vorbehalten.

DREI FEHLER IM TEST, NICHT IN DER SEITE

- Der Staffelungstest raeumte die Probekachel leer. Danach fehlten ihr
  Ueberschrift und Text, und der Neigungstest stuerzte ab, weil er auf
  ein nicht vorhandenes Element zugriff. Er hat jetzt einen eigenen
  Behaelter.
- Die Winkelrechnung las die "3" aus "matrix3d" als erste Zahl mit und
  verschob damit jeden Eintrag um eine Stelle. Der Winkel kam als 0,4
  Grad heraus statt als 6,7 -- die Pruefung "flach genug" waere also
  immer gruen gewesen, egal wie stark die Kachel kippt.
- Der Schwellwert fuer den senkrechten Ausschlag war zu streng. Eine
  flache Kachel ist nur gut 100 Punkte hoch; 20 Punkte vom Rand liegen
  dort schon fast in der Mitte. Geprueft wird jetzt der
  Vorzeichenwechsel statt eines festen Betrags.

Bei prefers-reduced-motion ist alles davon aus: keine Neigung, keine
Tiefe, kein Glanz. Ohne feinen Zeiger entfaellt es ebenfalls -- auf
einem Telefon gibt es kein Schweben.

Geprueft: 260 Pruefungen gruen (Verwaltung 62, Portfolio 15, CRYONOVA 53,
Blickfang 13, System 5, Abmelden 16, Abbruch 42, Angebot 54).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-25 09:16:22 +02:00
DogFatherGitandClaude Opus 5 4c0941c6da Verwaltung: Zeigerlampe, Prisma-Kanten, schwebende Partikel
Drei Dinge, die zusammen ein Konzept ergeben: Das Bild reagiert auf den
Zeiger, die Kacheln brechen das Licht wie Eis, und im Raum schwebt
etwas.

1. DIE ZEIGERLAMPE

Ein weicher Lichtkegel wandert ueber die Buehne und hellt die Kristalle
dort auf, wo der Zeiger steht. Damit wird das Bild zu einer Flaeche, die
auf einen reagiert, statt nur dazuzuliegen.

Der Kniff steckt in mix-blend-mode: soft-light. Ein normaler heller
Verlauf wuerde das Bild ueberdecken und milchig machen. "soft-light"
rechnet stattdessen mit dem, was darunter liegt -- dunkle Stellen
bleiben dunkel, vorhandene Lichtkanten der Kristalle werden verstaerkt.
Das Bild wird nicht ueberstrahlt, es wird beleuchtet. Bewusst NICHT
"screen" oder "overlay": Beide lassen die Eiskanten ausbrennen, und
genau die machen den Reiz der Motive aus.

2. PRISMA-SCHIMMER AN DEN KACHELKANTEN

Am hellsten Punkt sitzt die Leitfarbe, daneben faechert die Kante in
Nachbartoene auf -- wie Licht, das sich in einer Glaskante bricht.
Bewusst KEIN Regenbogen: Volle Spektralfarben sehen nach Seifenblase
aus, nicht nach geschliffenem Eis. Es bleibt in der kalten Haelfte der
Palette. Die Kante ist dafuer 1,5 px statt 1 px -- bei genau einem Punkt
verschluckt das Bildschirmraster die Aufaecherung fast vollstaendig.

3. SCHWEBENDE PARTIKEL

Neun Lichtpunkte steigen sehr langsam auf, jeder mit eigener Bahn,
Dauer und Startzeit. Rein aus CSS, ohne Zeichenflaeche -- eine
Zeichenflaeche wuerde dauerhaft Rechenzeit kosten, und das auf einer
Seite, auf der man arbeitet. Es soll wirken wie Staub im Lichtkegel,
nicht wie Schneefall.

DER FEHLER, DEN ERST DER SCREENSHOT ZEIGTE:

Die Lampe legte sich als gruenlicher Fleck mitten auf eine Kachel. Ein
Element mit mix-blend-mode mischt sich mit ALLEM in seinem
Stapelkontext -- auch mit Elementen, die eigentlich darueber liegen.
Buehne und Lampe stecken deshalb jetzt in einem gemeinsamen Raum mit
isolation: isolate. Dort endet die Mischung, und die Lampe beleuchtet
nur noch das Bild.

Was DANACH noch durchkam, ist dagegen richtig so: Die Kacheln tragen
eine Rueckseiten-Unschaerfe, nehmen also auf, was hinter ihnen liegt.
Licht, das durch Milchglas scheint. Bei einem engen Kegel war davon
allerdings ein scharf umrissener Kreis uebrig -- deshalb jetzt ein
weiter Radius mit flachen Stufen, damit sich der Helligkeitsunterschied
ueber die halbe Kachel verteilt und als Schimmer liest.

Drei Testmeldungen waren durch den Umbau entstanden und kein Mangel der
Seite: Haltung, Stapelplatz und die Auszeichnung als Zierde sitzen jetzt
am Raum, nicht mehr an der Buehne darin. Der Test prueft sie dort.

Bei prefers-reduced-motion sind die Partikel komplett weg -- nicht nur
angehalten. Ein eingefrorener Punkt mitten im Bild waere ein Fleck ohne
Sinn. Ohne feinen Zeiger entfaellt die Lampe ganz.

Geprueft: 252 Pruefungen gruen (Verwaltung 54, Portfolio 15, CRYONOVA 53,
Blickfang 13, System 5, Abmelden 16, Abbruch 42, Angebot 54).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-25 07:15:06 +02:00
DogFatherGitandClaude Opus 5 fdf3e5349e Verwaltung: gleitender Leuchtbalken, gestaffelte Kacheln, Reiterlicht
Drei weitere Stufen auf der Buehne.

1. DER GLEITENDE LEUCHTBALKEN

Unter der Reiterreihe liegt ein Balken in der Leitfarbe. Beim Wechsel
springt er nicht, sondern gleitet zum neuen Reiter und faerbt sich dabei
um.

Position und Breite kommen aus dem ECHTEN Reiter, im Browser gemessen.
Feste Werte waeren hier zwangslaeufig falsch: Die Reiter sind
unterschiedlich breit ("Kunden" gegen "Zahlungen"), sie verschieben sich
beim Sprachwechsel, und auf schmalen Schirmen brechen sie um -- deshalb
wandert auch die Hoehe mit, nicht nur die Seite.

2. DIE KACHELN TAUCHEN GESTAFFELT AUF

Beim Bereichswechsel erscheinen sie nacheinander statt alle auf einmal.
Nur die ersten acht bekommen einen Versatz -- bei einer langen Liste
kaeme die letzte Kachel sonst spuerbar spaeter, und das fuehlt sich
nicht mehr elegant an, sondern langsam.

3. DAS LICHT FOLGT AUCH AUF DEN REITERN

Die Verfolgung im Grundsystem greift ausdruecklich nur auf Karten. Fuer
die Reiter ist sie hier ergaenzt, gedrosselt ueber
requestAnimationFrame -- aus demselben Grund wie dort.

ZWEI FEHLER, DIE DER TEST GEFUNDEN HAT:

Der Balken stand auf Breite 0 und blieb unsichtbar. Ein blosser
"resize"-Horcher reicht naemlich nicht: Der haeufigste Fall ist gar
keine Fenstergroessenaenderung, sondern das Sichtbarwerden. Beim Start
ist der Arbeitsbereich versteckt, die Leiste also 0 Punkte breit -- und
ein verstecktes Element loest kein resize aus. Jetzt beobachtet ein
ResizeObserver die Leiste; das deckt Sichtbarwerden, Umbrechen und
Sprachwechsel gleichermassen ab.

Und die Messung hing allein am Klick-Listener. Wechselt der Bereich auf
einem anderen Weg -- etwa direkt nach dem Anmelden, wenn der
Arbeitsbereich zum ersten Mal auftaucht -- wurde nie nachgemessen.
balkenSetzen ist deshalb jetzt nach aussen verfuegbar.

Alles Bewegte bleibt bei prefers-reduced-motion aus: kein Gleiten, kein
Auftauchen, keine Parallaxe, kein Reflex. Die Buehne bleibt sichtbar.

Ein Wort zum Test selbst: Er prueft "der Balken springt" nicht mehr auf
wortwoertlich "0s". Die Testumgebung emuliert reduzierte Bewegung, indem
sie Uebergaenge auf eine Mikrosekunde setzt statt auf null -- gemeldet
wird "1e-06s". Wahrnehmbar ist das identisch; ein Test auf exakt "0s"
haette nur die Emulation gemessen, nicht die Regel.

Geprueft: 244 Pruefungen gruen (Verwaltung 46, Portfolio 15, CRYONOVA 53,
Blickfang 13, System 5, Abmelden 16, Abbruch 42, Angebot 54).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-25 07:02:51 +02:00
DogFatherGitandClaude Opus 5 a74d924445 Verwaltung: das Licht kommt zurueck, die Buehne atmet
Vier Dinge dazu, drei davon Bewegung, eines eine Reparatur.

1. DAS LICHT FOLGT WIEDER DEM ZEIGER

Es war nie weg. --wd-lichtx/--wd-lichty wurden die ganze Zeit gesetzt,
der Lichtkegel stand korrekt an der richtigen Stelle -- er war nur
unsichtbar geworden. Die 28 % Deckkraft aus dem Grundsystem sind fuer
eine dunkle, undurchsichtige Kachel gedacht. Auf einer Glasflaeche, durch
die eine beleuchtete Kristallwelt schimmert, geht das schlicht unter.

Jetzt wirkt es auf zwei Ebenen: der Lichtkegel auf der Flaeche, und die
KANTE der Kachel leuchtet dort auf, wo der Zeiger steht. Zusammen sieht
es aus, als laege eine echte Lichtquelle ueber dem Glas, statt als waere
ein Fleck aufgemalt. Die Farbe ist die Leitfarbe des Bereichs -- im
Kundenbereich leuchtet es Indigo, bei den Zahlungen Gold.

2. DIE BUEHNE BEWEGT SICH GEGEN DEN ZEIGER

Wenige Bildpunkte, gemessen 5,8 px Ausschlag. Gerade genug, dass sich
der Raum echt anfuehlt statt wie eine Tapete -- und wenig genug, dass
beim Lesen nichts im Augenwinkel wandert. Ein Test haelt die Obergrenze
fest.

3. EIN LICHTREFLEX BEIM BEREICHSWECHSEL

Ein einzelner heller Streifen zieht schraeg ueber die Buehne, genau
einmal, dann ist er weg. Ein Moment, kein Dauerflackern.

4. DIE KACHEL HEBT SICH BEIM UEBERFAHREN AN

Zwei Bildpunkte. Sie soll reagieren, nicht huepfen.

DER FEHLER, DEN DER TEST GEFUNDEN HAT:

Die Parallaxe wirkte zuerst gar nicht. Die Werte kamen sauber an
(--vw-px, --vw-py standen korrekt am Element), das Bild stand trotzdem
still. Grund: Die Einblend-Animation animiert "transform" und haelt
ihren Endwert fest (fill-mode both) -- und eine Animation schlaegt jede
normale Regel. Die Verschiebung steht deshalb jetzt in "translate",
einer eigenen Eigenschaft, die VOR "transform" angewendet wird. Beide
koennen sich so nicht mehr in die Quere kommen.

Alles Bewegte ist bei prefers-reduced-motion aus: keine Parallaxe, kein
Reflex, kein Anheben. Die Buehne bleibt aber sichtbar -- abschalten
heisst nicht verschwinden. Auch das wird geprueft.

Die Parallaxe laeuft nur auf Geraeten mit echtem Zeiger und ist ueber
requestAnimationFrame gedrosselt. Ohne die Drosselung rechnet der
Browser bei jeder einzelnen Zeigerbewegung neu, und das merkt man
ausgerechnet beim Scrollen durch lange Listen.

Geprueft: 236 Pruefungen gruen (Verwaltung 38, Portfolio 15, CRYONOVA 53,
Blickfang 13, System 5, Abmelden 16, Abbruch 42, Angebot 54). Die
Lesbarkeit ueber der Buehne liegt weiter bei 11,9 bis 14,6:1, verlangt
sind 4,5:1.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-25 00:50:43 +02:00
DogFatherGitandClaude Opus 5 b147ed0d94 Verwaltung: das Bild wird zur Buehne statt zum Streifen
Der erste Anlauf hat die Motive kaputtgemacht. Sie standen in einem
schmalen Streifen, auf 190 % gezoomt, davon ein Ausschnitt gewaehlt und
die linke Haelfte voll zugedeckt -- alles nur, damit Text darauf lesbar
bleibt. Von einer Kristallwelt, die ueber das ganze Bild geht, war ein
Zipfel uebrig.

Der Denkfehler: Bild und Text auf dieselbe Ebene zwingen und dann das
Bild opfern. Jetzt liegen sie auf zwei Ebenen.

DAS BILD IST DIE BUEHNE. Bildschirmfuellend, fest stehend,
ungeschnitten, ungedimmt. Kein Zoom, kein einseitiges Abdunkeln. Beim
Bereichswechsel wechselt der ganze Raum.

DER INHALT SCHWEBT ALS GLAS DARUEBER. Karten, Reiter, Bedienknoepfe und
selbst die Meldung "Wird geladen" bekommen eine Rueckseiten-Unschaerfe.
Das Motiv bleibt sichtbar, verliert hinter dem Glas aber jede Struktur --
und genau das macht Text darauf ruhig lesbar. Dadurch muss das Bild
nirgends mehr weichen.

Die Kacheln tragen eine Leuchtkante in der Leitfarbe des Bereichs. Das
bindet Inhalt und Buehne zusammen, statt die Kacheln wie aufgeklebte
Zettel wirken zu lassen.

Weil der Text jetzt auf Glas steht statt auf dem Bild, konnte auch die
Toenung deutlich zurueckgenommen werden: von .42/.58/.72 auf
.18/.38/.60. Mehr Bild, gleiche Lesbarkeit -- gemessen 12,4 bis 14,5:1
auf der Kachel, verlangt sind 4,5:1.

Die Pruefung ist mitgedreht und misst jetzt das Gegenteil von vorher:
- Wird das Bild NICHT gezoomt und NICHT ausgeschnitten? (frueher stand
  hier "190% auto" und "84% 46%")
- Traegt jede Flaeche, auf der gelesen wird, wirklich Glas?
- Bleibt der Text lesbar -- gemessen an echten Bildpunkten, nicht am
  rechnerischen Wert des Stilblatts. Die Kachel ist halbdurchsichtig,
  ihr Sollwert sagt nichts darueber, was am Ende darunter liegt.

Dazu die Vergleichsmessung: Wie unruhig ist der Kachelgrund MIT Buehne
gegenueber ohne? Gemessen: minus 1 bis plus 1 in allen sechs Bereichen.
Das Glas arbeitet.

Was unveraendert gilt: Bewegung aus bei prefers-reduced-motion, aber die
Buehne bleibt sichtbar -- abschalten heisst nicht verschwinden. Auf dem
Handy die kleine Bildfassung und eine etwas dichtere Toenung, weil dort
mehr Inhalt uebereinander liegt.

Geprueft: 229 Pruefungen gruen (Verwaltung 31, Portfolio 15, CRYONOVA 53,
Blickfang 13, System 5, Abmelden 16, Abbruch 42, Angebot 54).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-25 00:41:13 +02:00
DogFatherGitandClaude Opus 5 09bd6330f7 Verwaltung: jeder Bereich bekommt eine eigene Signatur
Aus einem festen Streifen werden sechs. Jeder Reiter hat jetzt ein
eigenes Motiv UND eine eigene Leitfarbe, beides wechselt beim Klick.

Die Zuordnung ist gelesen, nicht ausgewuerfelt:

  Uebersicht  Wappen     Die Zentrale, wo alles zusammenlaeuft.
  Anfragen    Portal     Ein Tor. Hier kommt Neues herein.
  Projekte    Monolith   Etwas, das aufrecht steht und gebaut wird.
  Kunden      Thron      Wer bestellt, steht auf dem Podest.
  Zahlungen   Kristall   Der Wert selbst. Dazu Liquid Gold.
  Postfach    Portal     Wieder ein Tor -- Nachrichten gehen durch.

Farben: Baby Blue, Hyper Aqua, Aurora Violet, Prism Indigo, Liquid Gold,
Ion Blue. Nach ein paar Tagen erkennt man den Bereich an der Farbe,
bevor man den Titel gelesen hat. Aus Deko wird Orientierung.

Das neue Thron-Motiv ist aus dem vierten Bild aufbereitet, im selben
Mass wie die bestehenden (1672x941) und mit kleiner Fassung fuers Handy.

DIE ENTSCHEIDENDE IDEE: Bild und Text teilen sich nicht mehr denselben
Platz. Eine Deckschicht ist links voll deckend und oeffnet sich nach
rechts. Links stehen Titel und Reiter, rechts ist die Flaeche leer --
dort darf das Motiv mit 82 % auftreten statt mit 17 %.

Der erste Versuch war gleichmaessig bei 17 %: ueberall gleich schwach zu
ahnen, ein Fleck statt eines Bildes, und trotzdem hinter der Schrift.
Kurz und kraeftig ist beides besser -- mehr Wirkung dort, wo Platz ist,
null Stoerung dort, wo gearbeitet wird. Der Streifen ist jetzt auch
kuerzer und endet, BEVOR die erste Kachel anfaengt.

Drei Fehler, die der Test gefunden hat und nicht das Auge:

- Alle sechs Bereiche zeigten dasselbe Bild. Die Variablen hingen an
  #vw-bereich, der Schmuckstreifen liegt aber ausserhalb davon -- er
  erbte sie nie und fiel auf den Rueckfallwert zurueck. Die Farben
  wechselten (Reiter und Titel liegen drinnen), die Motive nicht.
- Die waagerechten Ausschnitte bewirkten nichts. Bei "cover" skaliert
  der Browser auf die Breite des Streifens, die volle Bildbreite ist
  immer sichtbar. Erst ein Zoom ueber 100 % schafft Spielraum.
- Das Thron-Motiv schob seine hellen Kristallfluegel bis unter die
  Reiter. Deshalb deckt die Schicht jetzt bis 46 % statt 34 % -- der
  Wert ist gemessen, nicht geschaetzt.

Dazu zwei Dinge, die erst der Screenshot zeigte: angeschnittene Logos im
Streifen (sieht nach Versehen aus, und das Logo steht ohnehin oben
links), und die Knoepfe Suchen/Abmelden lagen ueber dem hellsten Teil
des Bildes. Sie haben jetzt einen eigenen dichten Grund -- Bedienelemente
muessen lesbar sein, egal was dahinter liegt.

Der Test misst nicht mehr "Deckkraft unter 20 %". Dieser Massstab ist
hinfaellig, seit das Motiv nach rechts gerueckt ist: Es darf kraeftig
sein, WEIL es nicht mehr hinter der Schrift liegt. Geprueft wird
stattdessen, wie ruhig der Grund unter der Reiterzeile ist -- mit Motiv
gegen ohne Motiv, in allen sechs Bereichen. Gemessen: plus 0 bis plus 6.

Unveraendert gilt: kein Motiv hinter Listen, Tabellen oder Zahlen. Ein
Bild hinter einer Zahlenspalte ist genau die Art von Schoenheit, die ein
Werkzeug unbrauchbar macht.

Geprueft: 237 Pruefungen gruen (Verwaltung 39, Portfolio 15, CRYONOVA 53,
Blickfang 13, System 5, Abmelden 16, Abbruch 42, Angebot 54).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-25 00:26:23 +02:00
DogFatherGitandClaude Opus 5 9df21fabe1 Verwaltung: Bildschmuck an zwei Stellen, beide ausserhalb der Arbeit
Die Verwaltung ist ein Arbeitsplatz. Hier wird nicht geworben, hier
werden Listen gelesen und Zahlen verglichen -- der Schmuck ist deshalb
deutlich zurueckhaltender als auf den oeffentlichen Seiten.

Zwei Stellen, beide bewusst ausserhalb des Arbeitsflusses:

- Ein Ring-Streifen ganz oben, der nach unten wegblendet. Er sitzt
  direkt unter der Kopfleiste und ist verschwunden, bevor die erste
  Tabelle anfaengt. Deckkraft 16 % (Handy 13 %) -- die oeffentlichen
  Motive liegen bei 55 %. Dort traegt das Bild die Stimmung, hier darf
  es die Kopfzeile nur andeuten.
- Das Wappen auf der Anmeldekarte. Dort wird nichts gelesen ausser drei
  Zeilen, also darf es sichtbarer sein.

Was hier ABSICHTLICH nicht passiert: kein Motiv hinter Listen, Tabellen
oder Zahlen. Ein Bild hinter einer Zahlenspalte ist genau die Art von
Schoenheit, die ein Werkzeug unbrauchbar macht. Ein Test haelt das fest.

Ein echter Fehler beim Bauen, den erst der Screenshot zeigte: Das Wappen
stand zuerst auf 38 % und mittig -- der Hundekopf lag genau im
Erklaertext, die Zeilen liefen quer ueber Schnauze und Schriftzug.
Jetzt 16 % und nach unten versetzt, sodass es hinter Eingabefeld und
Knopf sitzt statt hinter den Zeilen. Die Glasflaeche darueber ist hier
dichter als auf den oeffentlichen Seiten.

Der Test dazu misst nicht die Deckkraft, sondern das eigentliche
Problem: wie stark der Untergrund UNTER DER SCHRIFT schwankt, an echten
Bildpunkten aus dem Absatz. Deckkraft allein sagt naemlich nichts -- ein
Motiv mit hellen Kanten ist bei 20 % stoerender als ein ruhiges bei
50 %.

Und er misst im Vergleich, nicht gegen eine geratene Zahl: Schon die
weichgezeichneten Buchstabenkanten allein erzeugen eine Schwankung von
12. Ein fester Grenzwert "unter 14" haette also fast nur diese Kanten
gemessen und waere je nach Schriftgroesse zufaellig gruen oder rot. Der
Test schaltet das Motiv jetzt ab, misst erneut und prueft die Differenz.
Gemessen: mit 14, ohne 12, also plus 2.

Die Tag-Balance von verwaltung.html bleibt unveraendert bei Differenz 1
(vorher 192/191, jetzt 193/192) -- das neue Element ist ausgeglichen,
die alte Meldung ist Altbestand und wurde hier nicht angefasst.

Geprueft: 219 Pruefungen gruen (Verwaltung 21, Portfolio 15, CRYONOVA 53,
Blickfang 13, System 5, Abmelden 16, Abbruch 42, Angebot 54).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-24 23:41:41 +02:00
DogFatherGitandClaude Opus 5 a6c6bbe3ca Portfolio: ZockerAnstalt, Buchhaltung und Command Center aufgenommen
Aus zwei Projekten werden fuenf. Alle drei neuen laufen wirklich, die
Adressen wurden vorher einzeln geprueft:

  zockeranstalt.dogfather-universe.com      Netcup, via Caddy
  buchhaltung.vans-diy-bastelbedarf.com     Netcup, via Caddy
  analyse.dogfather-universe.com            Cloudflare Worker (kein via)

Die Vorschaubilder sind echte Aufnahmen vom 24.08.2026, aufgenommen im
selben Mass wie die bestehenden (1200x750), damit in der Liste nichts
aus der Reihe faellt.

Zwei Aufnahmen sind bewusst nicht unbearbeitet, und das steht auch fuer
die Besucher auf der Seite -- nicht nur im Code:

- Die Buchhaltung zeigt nur die Anmeldung. Sie bekommt deshalb auch
  KEINEN "Live ansehen"-Knopf: Ein Knopf, der bloss auf eine
  Anmeldemaske fuehrt, ist ein leeres Versprechen. Ein Test haelt das
  fest, damit es niemand spaeter "der Vollstaendigkeit halber" einbaut.
- Beim Command Center sind die Gesichter unkenntlich gemacht. Die Seite
  gehoert dem Team, nicht mir, und niemand hat zugestimmt, sein Bild im
  Portfolio zu zeigen. Der Weichzeichner wurde vor der Aufnahme im
  Browser gesetzt, nicht nachtraeglich ins Bild gemalt.

Zwei neue Schildfarben. Aqua traegt Schrift problemlos (11,5:1
gemessen). Indigo NICHT: Prism Indigo ist die einzige Farbe der Palette,
die als Text durchfaellt (3,25--4,29:1) -- das Schild nutzt es deshalb
nur als Flaeche und Kante, die Schrift bleibt Chrome Silver (9,8:1).

Texte in allen fuenf Sprachen, inklusive Schweizerdeutsch. Ueberschrift,
Einleitung und Schlusssatz sagen nicht mehr "zwei" bzw. "beide" -- und
zwar sowohl im Woerterbuch ALS AUCH im deutschen Rueckfalltext im HTML.
Nur die JS-Datei zu aendern haette gereicht, damit es in vier Sprachen
stimmt und ausgerechnet auf Deutsch falsch bleibt.

Drei Fehler steckten wieder in der Pruefung, nicht auf der Seite:
- Sie meldete drei Vorschaubilder als "nicht geladen". Die tragen
  loading="lazy" -- der Test hatte schlicht nie hingesehen.
- Sie zaehlte Navigations- und Markentexte mit. Die liegen aber in
  wd-core.js, gelten fuer alle Seiten und werden von
  server/pruefe-webdesign-i18n.mjs abgedeckt.
- Ihre "kein Zwei mehr"-Suche haette auch Projekttexte getroffen, in
  denen das Wort voellig zurecht steht.

Geprueft wird jetzt das Ergebnis statt des Woerterbuchs: Die Seite wird
wirklich auf jede der fuenf Sprachen umgeschaltet, plus eine Gegenprobe,
dass die Umschaltung ueberhaupt etwas tut.

Geprueft: 198 Pruefungen gruen (Portfolio 15, CRYONOVA 53, Blickfang 13,
System 5, Abmelden 16, Angebot 54, Abbruch 42). Die vier Meldungen aus
server/pruefe-webdesign-i18n.mjs betreffen verwaltung.html und zwei
fehlende Woerterbuecher -- Altbestand, keine dieser Dateien wurde hier
angefasst.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-24 23:23:27 +02:00
DogFatherGitandClaude Opus 5 01b61905aa Altfaelle aufraeumen: abgebrochene Projekte verschwinden aus der Liste
P-2608-0001 wurde abgebrochen, bevor es das Archiv-Kennzeichen gab. Es
hat deshalb zwar den Status "abgebrochen", aber archiviert = 0 -- und
stand bis heute zwischen den offenen Auftraegen. Also genau das, was der
Wunsch "wenn ich abbreche dan sollen die auch da weg" abstellen sollte.

Ueber die Oberflaeche war das nicht zu reparieren: Die Verwaltung zeigt
bei Status "abgebrochen" den Info-Block statt des Abbrechen-Knopfes, ein
zweiter Klick war also gar nicht moeglich.

Migration statt Handarbeit in der Datenbank: Ein einzelner UPDATE per
Hand repariert einen Fall und hinterlaesst keine Spur. Die Migration
erwischt jeden Altfall, laeuft ueberall gleich (Server, Testdatenbank,
ein spaeterer Neuaufbau) und steht nachvollziehbar in der Geschichte.

Bewusst NICHT gefuellt: abbruch_am, abbruch_grund, abbruch_wer,
abbruch_erstattung_cent. Diese Felder gab es beim damaligen Abbruch noch
nicht. Sie nachtraeglich mit plausiblen Werten zu fuellen waere eine
Behauptung ueber einen Vorgang, bei dem niemand mehr weiss, wie er
wirklich ablief -- und ausgerechnet im Streitfall waere das die
gefaehrlichste Stelle fuer eine Erfindung. Ein leeres Feld sagt ehrlich
"unbekannt", und die Verwaltung zeigt diese Angaben ohnehin nur an, wenn
sie gefuellt sind.

Der Test stellt den Altzustand echt nach: alle Migrationen bis 0017,
dann der Altfall, dann erst 0018. Geprueft wird auch, was NICHT passieren
darf -- ein laufendes Projekt bleibt unberuehrt, ein sauber abgebrochenes
behaelt Datum, Grund und Erstattung, und ein zweiter Durchlauf aendert
nichts mehr (beim Wiederherstellen einer Sicherung passiert genau das).

Geprueft: 167 Pruefungen gruen (Altabbruch 16, Automatik 55, Angebote 56,
Kette 40).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-24 23:07:37 +02:00
DogFatherGitandClaude Opus 5 99b5dd4eb2 CRYONOVA Schritt 3: Feldfokus, Blickfaenge, Testkorrekturen
Der Feldfokus zeigt jetzt den Ion-Rand und den weichen Schein, den das
Design-System auf Seite 15 verlangt. Er fehlte, weil der allgemeine
Tastaturring seinen dunklen Innenring darueberlegte: Dessen Selektor hat
Spezifitaet 0,3,0, die Feldregel nur 0,2,1 -- und Spezifitaet schlaegt
Reihenfolge, egal wie weit unten die Regel steht. Eingabefelder sind
jetzt vom Innenring ausgenommen; sie brauchen ihn nicht, weil sie selbst
eine dunkle Flaeche sind. Der Aqua-Umriss bleibt fuer alle erhalten.

Die zwei Blickfang-Motive stehen jetzt auch wirklich auf einer Seite:
das Wappen auf "Ueber mich", direkt nach dem Absatz ueber die Herkunft
des Namens, der Monolith auf "Portfolio" zwischen Einleitung und den
echten Projekten. Beide als Zierbild ausgezeichnet -- ihre Aussage steht
schon im Text daneben. Auf dem Handy laedt automatisch die kleine
Fassung.

Vier Fehler steckten in der Pruefung selbst, nicht im Stilblatt:
- Sie griff das erste "input" der Anfrageseite. Das sind aber sechs
  optisch versteckte Auswahl-Radios, kein Textfeld.
- Sie mass ohne Fensterfokus. Ein Browser wendet :focus nur an, wenn das
  Fenster selbst den Fokus hat -- das DOM meldet trotzdem brav
  matches(":focus") === true, die Farbe bleibt die alte.
- Sie mass mitten im Uebergang, bevor die Animation stand.
- Und sie zaehlte Schattenebenen an "),", einem Muster, das in keinem
  Schattenwert je vorkommt. Geprueft wird jetzt die Anforderung selbst:
  Ion-Farbe und ein Weichzeichner ueber 0.

Geprueft: 334 Pruefungen gruen (CRYONOVA 53, Blickfang 13, Abmelden 16,
Angebot 54, Abbruch 42, System 5, Automatik 55, Angebote 56, Kette 40).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-24 23:03:27 +02:00
DogFatherGit 336e702d90 Druckansicht von der Weiss-Regel ausgenommen
Die Vollstaendigkeitspruefung meldete Punkt 13 (keine weissen
Vollflaechen) als offen. Nachgesehen: Alle Weiss-Werte stehen
ausschliesslich in @media print.

Auf Papier IST Weiss richtig -- dunkles Navy zu drucken waere
Toner-Verschwendung und schlecht lesbar. Das Verbot des Design-Systems
gilt dem Bildschirm, nicht dem Ausdruck. Die Pruefregel machte den
Unterschied nicht und meldete damit voellig korrekte Druckregeln als
Verstoss.

Damit sind alle 14 Punkte der Vollstaendigkeitspruefung erfuellt.
2026-08-24 22:45:27 +02:00
DogFatherGit e92daed103 CRYONOVA Schritt 2: Bildwelt, Materialien, Rangsystem, Status
Setzt die restlichen Kapitel des Design-Systems um (Seiten 7, 14, 16,
19, 24-29). Wie Schritt 1 ausschliesslich Farbe, Material und Licht --
Aufbau, Navigation, Texte und Funktionen bleiben unangetastet.

BILDWELT (Seiten 24-29)
Die Motive sind jetzt echte Seitenhintergruende: Ring auf Startseite
und Portal, Kristallwelt an der Zugangswand. Ueber jedem Motiv liegt
eine Navy-Glasflaeche -- das System fordert auf Seite 31 ausdruecklich,
dass bei Bedarf "eine Navy- oder Frost-Glasflaeche zwischen Bild und
Inhalt" liegt. Ohne sie waere Text auf den hellen Kristallspitzen nicht
sicher lesbar, und genau dort macht ein schoenes Bild eine Seite
unbrauchbar.

Von 2,5 MB auf 96-229 KB (WebP), auf dem Handy 30-73 KB. Das System
nennt fuer Mobile ausdruecklich Ladezeit als Kriterium.

DAS LOGO IM BILD -- eine Entscheidung gegen die woertliche Vorgabe
Zwei der drei Motive zeigen das Dogfather-Logo gross in der Mitte. Als
Seitenhintergrund waere das ein zweites Logo neben dem echten in der
Kopfzeile: zwei Marken, die um dieselbe Aufmerksamkeit ringen. Beim
ersten Versuch schien es an der Zugangswand hinter den Karten durch
(gemessen 15,9 % helle Flaeche im Logobereich). Die Zugangswand nutzt
deshalb jetzt einen Ausschnitt der linken Bildhaelfte -- dieselbe
Kristallwelt, dasselbe Licht, nur ohne Wappen. Die Logo-Motive bleiben
als Blickfang-Klasse erhalten, dort wo sie fuer sich stehen duerfen.

MATERIALIEN (Seite 7)
Optic Glass, Frosted Ice, Prism Edge und Void Navy als Klassen. "Hell"
heisst auf einer dunklen Seite aufgehelltes Navy, nicht Weiss -- echtes
Weiss waere ein Loch im Bildschirm, und das System verbietet weisse
Vollflaechen ausdruecklich.

RANGSYSTEM (Seite 14)
Drei Stufen fehlten: Premium (Indigo), VIP (Champagne), Gefahr (Coral).
Wenn jeder Knopf gleich laut ist, ist keiner mehr laut.

Zwei bewusste Abweichungen von der naheliegenden Loesung:
- Indigo steht als FLAECHE unter hellem Text, nie als Schriftfarbe. Es
  erreicht als Text nirgends 4,5:1 (gemessen 3,25-4,29). Der Test haelt
  das dauerhaft fest.
- Gefahr ist NICHT vollflaechig rot. Ein voll gefuellter roter Knopf
  zieht den Blick staerker an als die Hauptaktion und wird dadurch
  versehentlich gedrueckt. Kritische Aktionen sollen auffindbar sein,
  nicht verlockend.

FOKUSRING (Seite 19)
Doppelter Aqua-Ring: 2 px Aqua aussen, dunkler Innenring. Kein Schmuck
-- ein einfacher heller Ring verschwindet auf hellen Flaechen, ein
dunkler auf dunklen. Die Kombination ist auf JEDEM Untergrund sichtbar.

STATUS-SPEKTRUM (Seite 16)
Acht Zustaende. Die Klasse faerbt nur -- den Text liefert immer das
Markup. Es gibt bewusst keine Variante, die nur einen farbigen Punkt
zeigt: Wer Farben nicht unterscheiden kann, saehe dann gar nichts. Der
Test prueft, dass keine Statusmarke ohne Text existiert.

Dabei einen eigenen Fehler gefunden und behoben: Ich hatte beim Bauen
des Premium-Knopfes selbst einen losen Hex-Wert eingesetzt -- genau
das, was Token-Regel 05 verbietet und was der Test dann meldete.

Geprueft: 43 (CRYONOVA, von 24 erweitert) + 0 Fundstellen (WCAG ueber
12 Seiten x 5 Sprachen, jetzt MIT Bildhintergruenden) + 55 + 40 + 69 +
54 + 42 + 16 + 5 + 20 + 64 + 58 -- alles gruen.
2026-08-24 22:43:58 +02:00
DogFatherGit de8819f1fd CRYONOVA: Farb- und Lichtsystem nach dem Design-System umgesetzt
Umsetzung des Design-Systems "Baby Blue Optical Luxury" (Edition 2.0),
Schritt 1 von mehreren: die zentrale Farbquelle. Nach der Kernregel auf
Seite 3 ist das ausdrücklich ein Farb- und Licht-Redesign — Aufbau,
Navigation, Texte und Funktionen bleiben unangetastet.

FARBEN
Grundflächen auf die vier Tiefenebenen des Systems (Void, Midnight,
Obsidian, Deep Glass). Palette nach Seite 5: Signature Baby, Ion Blue,
Hyper Aqua, Aurora Violet, Prism Indigo, Liquid Gold, Signal Coral,
Chrome Silver.

Die Variablen behalten ihre alten NAMEN (--wd-blau statt --cryo-baby).
Ein Umbenennen hätte über 155 Fundstellen anfassen müssen — viel
Bewegung ohne sichtbaren Nutzen, mit der realen Gefahr, eine Stelle zu
übersehen und danach zwei fast gleiche Blautöne zu haben. Entscheidend
ist die Rolle, nicht der Name; die Systembezeichnungen stehen als
Kommentar daneben.

EIN FEHLER IM DESIGN-SYSTEM, DER BEWUSST NICHT ÜBERNOMMEN WURDE
Die Token-Liste auf Seite 20 ist um eine Zeile verrutscht — Namen und
Hex-Werte passen dort nicht zusammen. Am folgenreichsten: --cryo-baby
stünde auf #0C2740, einem fast schwarzen Navy, und ist laut Seite 21
zugleich die Standard-Lumenfarbe. Das Mauslicht wäre damit praktisch
unsichtbar geworden — ausgerechnet der Effekt, den das System auf fünf
Seiten als unantastbar schützt. Maßgeblich ist deshalb die Palette auf
Seite 5 und die Lumen-Logik auf Seite 9, die untereinander stimmig sind.

MAUSLICHT
Der bestehende Effekt bleibt vollständig erhalten (Systemauflage) und
bekommt eine Farbvariable pro Kachel: --wd-lumen. Vorher war die Farbe
im Verlauf fest verdrahtet, und jede weitere Kachelfarbe hätte zwei
neue Blöcke gebraucht (Fläche + leuchtende Kante). Bei sechs
Lumen-Rollen wären das zwölf fast gleiche Blöcke gewesen, die beim
nächsten Feinschliff zwangsläufig auseinanderlaufen. Jetzt setzt die
Kachel nur ihre Farbe, der Verlauf steht einmal da — genau das meint
Token-Regel 02 mit "Kachelfarbe steuert Lumenfarbe".

38 lose Hex-Codes durch Token ersetzt (Token-Regel 05). Drei davon
(#3d9dbd, #7c5cd6, #a8873a) waren noch die ALTEN Markenfarben und
hätten still neben den neuen weitergelebt — genau der Mechanismus, durch
den Oberflächen mit der Zeit zwei fast gleiche Töne bekommen.

KONTRASTE NACHGERECHNET
Die Palette ist auf dunklem Grund durchweg stark (9,7 bis 19,8:1) — mit
einer Ausnahme: Prism Indigo erreicht auf keiner Fläche 4,5:1 (nur 3,25
bis 4,29). Es ist deshalb ausschließlich für Kanten, Verläufe und große
Premium-Flächen zugelassen, nie für Fließtext. Das System sieht Indigo
ohnehin nur für "Premium-Momente" vor — die Rechnung bestätigt die
Regel, statt ihr zu widersprechen.

NEUER TEST: pruef-cryonova.mjs (24 Prüfungen)
Palette, Grundflächen, Mauslicht-Erhalt, echte Zeigerbewegung, keine
losen Hex-Codes, Kontraste und die Frage, ob alle 12 Seiten wirklich
aus derselben Quelle schöpfen.

Dabei drei Fehlalarme im eigenen Test gefunden und behoben — jeder
davon hätte dauerhaft rote Zeilen erzeugt und irgendwann dazu geführt,
dass man eine echte Meldung übersieht:
- Halbtransparente Flächen müssen über ihren Untergrund gerechnet
  werden. Der aktive Reiter kam sonst auf 1:1 statt echter 8,25–10,23:1.
- Text auf Farbverläufen liefert rgba(0,0,0,0) als Hintergrund; der
  Hauptknopf kam so auf 1:1 statt rund 11:1.
- Die Maus muss mit Zwischenschritten bewegt werden, sonst feuert
  pointermove nicht. Eine Direktmessung bestätigte: Das Licht folgt
  einwandfrei (30px → 723px, Deckkraft 1).

pruef-system.mjs auf den neuen Markenton gesetzt. Dass er dort zunächst
"0 von 1804 Elementen" meldete, war kein Fehler, sondern der Beweis,
dass der alte Ton nirgends mehr vorkommt.

Geprüft: 24 (CRYONOVA) + 0 Fundstellen (Design/WCAG über 12 Seiten × 5
Sprachen) + 40 + 56 + 69 + 54 + 42 + 55 + 5 + 16 — alles grün.
2026-08-24 22:32:04 +02:00
DogFatherGit 305920d872 Cache-Version fuer die Portal-Korrektur (Countdown vor Anzahlung) 2026-08-24 16:54:57 +02:00
DogFatherGit 521f0d5a80 Kompletten Ablauf einmal durchgespielt — echten Fehler dabei gefunden
Wunsch: "ich will dass du es abcheckst" — nicht Stück für Stück (das war
schon geprüft), sondern EINMAL DURCHGÄNGIG als ein zusammenhängender
Ablauf, so wie ein echter Auftrag tatsächlich läuft: Anfrage → Angebot
→ Zusage im Portal → Anzahlung → laufende Uhr → Benachrichtigung →
Arbeit → Restzahlung → Gegenprobe mit Abbruch.

test-kette.mjs bildet genau das ab, gegen die echten Funktionen aus
server-internal/, mit einer Wegwerf-Datenbank. 40 Prüfungen, alle grün.

DABEI GEFUNDEN: Der Liefertermin-Countdown im Portal prüfte nicht, ob
die Uhr überhaupt schon läuft. Ein frisch zugesagtes Projekt hat schon
ein termin_am (aus der Zusage vorgerechnet), aber uhr_start_am steht
noch auf null, solange die Anzahlung nicht da ist. Der Kunde hätte also
direkt nach der Zusage einen tickenden Countdown gesehen — und wenn die
Anzahlung eintrifft, wird der Termin ab dem Zahlungstag NEU berechnet
und springt dann sichtbar nach hinten. Das sieht aus wie ein Fehler,
auch wenn die Zahl rechnerisch stimmt: Kaum zu erklären, warum "noch 8
Tage" plötzlich wieder "noch 10 Tage" werden.

Jetzt zwei klar getrennte Zustände: Vor der Anzahlung steht "Startet,
sobald deine Anzahlung da ist — voraussichtlicher Liefertermin danach:
ca. {datum}" (kein Countdown, aber der Termin bleibt sichtbar, kein
Verstecken). Danach der echte Countdown. Fünf Sprachen ergänzt.

Nebenbei einen irreführenden Kommentar korrigiert: Ein neuer Kunde wird
beim Angebot-Senden SOFORT freigeschaltet (nicht erst bei Zusage, wie
der alte Kommentar behauptete) — sonst könnte er sich gar nicht
einloggen, um sein eigenes Angebot anzusehen. Der Code war richtig,
nur die Erklärung falsch, und mein erster Testentwurf ist genau darauf
hereingefallen.

Bei der Gelegenheit auch die bekannte better-sqlite3-Aussetzer-Eigenart
(zufälliger Absturz beim Prozessende, dokumentiert seit früheren
Commits) noch einmal eingegrenzt: Derselbe Import in test-angebote.mjs
brach mal nach "GEHEIM", mal nach "VORLAGEN" ab — der Absturzpunkt
verschiebt sich zwischen identischen Läufen. Das ist der endgültige
Beweis, dass es reine GC-Zeitfensterflakiness in der nativen
Bibliothek ist, kein Fehler im eigenen Code.

Geprüft: 40 (Kette) + 55 + 56 + 70 + 28 + 49 serverseitig (alle
mehrfach unabhängig grün), 261 im Browser (Portal, Angebot, Übersicht,
Verwaltung, Abbruch, Design). Ein Screenshot bestätigt den neuen
Wartehinweis visuell.
2026-08-24 16:54:35 +02:00
DogFatherGitandClaude Opus 5 e43375728c Abgebrochene Projekte verschwinden aus der Liste
Rueckmeldung: 'wenn ich abbreche dan sollen die auch da weg'.

Ein abgebrochenes Projekt in der laufenden Liste stehen zu lassen ist
doppelt schaedlich: Es verstellt den Blick auf das, was wirklich laeuft,
und beim schnellen Durchsehen haelt man es fuer eine offene Baustelle.

Beim Abbrechen wandert das Projekt jetzt automatisch ins Archiv. Kein
neues Konzept: Den Archiv-Umschalter in der Projektliste gibt es
laengst, und die Suche sowie das Cockpit klammern Archiviertes ohnehin
aus. Es verschwindet damit auch aus dem PORTAL des Kunden -- und das ist
richtig so, ein abgebrochener Auftrag hat dort nichts mehr zu suchen.

Archivieren statt loeschen: Zahlungen, Erstattungsbetrag und Verlauf
haengen daran, und bei einem Streit braucht man genau das. Der Test
prueft beides -- weg aus der Liste UND noch vorhanden.

Geprueft: 55 gegen eine echte Datenbank.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-24 12:28:05 +02:00
DogFatherGitandClaude Opus 5 8726110035 Angebote in der Oberflaeche: schicken und im Portal zusagen
VERWALTUNG
Der Angebotsbereich steht in der Anfrage-Detailansicht ueber dem
direkten Annehmen. Der uebliche Weg ist: Angebot raus, Kunde sagt zu,
Projekt entsteht -- direkt anzunehmen ist der Sonderfall (man hat schon
telefoniert). Stuende der Sonderfall oben, waere er der naheliegende
Griff, und man verschenkte den Beleg, den eine Zusage im Portal erzeugt.

Vorbelegt sind Paket, Preis, Anzahlung, Laufzeit, Ablaufdatum und der
komplette Leistungstext. Zu tun bleibt: Paket bestaetigen, Preis
bestaetigen. Der Leistungstext ist eingeklappt statt die Seite zu
fluten, aber aenderbar.

Ein Paketwechsel laedt ALLES neu -- auch den Leistungstext. Ein
stehengebliebener Text vom vorigen Paket waere der schlimmste Fall: Er
ginge unbemerkt hinaus, und im Angebot staende etwas, das zum Preis
nicht passt. Genau das prueft der Test.

PORTAL
Die Angebotskarte steht ganz oben, vor allen Projekten: Ein Angebot ist
das Einzige im Portal, das eine ENTSCHEIDUNG verlangt -- alles andere
ist Auskunft. Der eingefrorene Leistungstext wird lesbar aufbereitet
(Zwischenueberschriften, Punkte), nicht als Textblock hingeworfen.

Vor der Zusage wird gefragt, und die Frage nennt die Folgen ('verbindlich',
'Anzahlung'). Nicht aus Foermlichkeit: Hier entsteht ein Vertrag, und ein
Knopf, der beim Danebentippen einen Auftrag ausloest, waere eine Falle.
Beim Ablehnen wird NICHT gefragt -- das ist folgenlos.

ZWEI FEHLER GEFUNDEN, BEIDE IM NORMALFALL
- Die Angebotskarten standen im Zweig 'es gibt Projekte'. Ein Kunde mit
  offenem Angebot hat aber typischerweise noch KEIN Projekt -- genau der
  Normalfall -- und saeh damit nichts. Der Server lieferte das Angebot,
  die Seite zeichnete es nur nie. Nur im Browsertest sichtbar.
- Eine Antwort ohne 'nachrichten' liess das Postfach werfen, und weil der
  Fehler den Aufbau abbrach, erschien danach GAR NICHTS mehr -- auch nicht
  das Angebot, das ueber allem stehen sollte. Dieselbe Sorte wie zuvor im
  Cockpit: Ein halber Server ist ein realistischer Fall.

Neun Sprachschluessel in fuenf Sprachen ergaenzt.

Geprueft mit 54 Pruefungen auf Computer und Handy, die messen, was
tatsaechlich an den Server geht. Alle bestehenden weiter gruen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-24 12:23:44 +02:00
DogFatherGitandClaude Opus 5 cea265eb8c Angebote: der Kunde sagt mit einem Klick zu
DER LEITGEDANKE: SO WENIG TIPPEN WIE MOEGLICH
Niemand schreibt hier den Leistungsumfang. Er entsteht aus der
Aufgabenvorlage des Pakets -- denselben 92 Punkten, nach denen spaeter
gearbeitet wird. Der Nebeneffekt ist wichtiger als die gesparte
Tipparbeit: Was im Angebot steht, IST die Arbeitsliste. Ein von Hand
geschriebenes Angebot und eine getrennt gepflegte Aufgabenliste laufen
unweigerlich auseinander -- und dann steht im Angebot etwas, das niemand
abarbeitet.

Zu tun bleibt: Paket waehlen, Preis bestaetigen. Beides ist vorbelegt,
Laufzeit und Ablaufdatum werden gerechnet.

DER WORTLAUT WIRD BEIM ABSENDEN EINGEFROREN
Ein angenommenes Angebot ist ein Vertrag. Bei einem Streit zaehlt, WAS
dem Kunden gezeigt wurde, als er zusagte. Wuerde der Text bei jeder
Ansicht neu aus den Vorlagen erzeugt, staende nach der naechsten
Vorlagenaenderung etwas anderes da als damals. Die Zusage wird mit
Zeitpunkt und (gehashter) Herkunft belegt -- die Beweislast liegt beim
Unternehmer.

WAS AUTOMATISCH PASSIERT
Angebot raus -> Anfrage steht auf 'angebot' (der Wunsch: 'wenn ich ein
angebot rausschicke soll der automatisch das erkennen'). Zusage ->
Projekt angelegt, Aufgabenliste eingesetzt, Anzahlung als offene
Rechnung erzeugt, Anfrage auf 'angenommen', Benachrichtigung an mich.

Die UHR startet dabei NICHT. Sie startet erst mit dem Zahlungseingang --
das ist die Regel aus dem vorigen Schritt, und sie gilt auch dann, wenn
der Kunde selbst zugesagt hat. Genau dieser Punkt wird ausdruecklich
geprueft: Es fuehlt sich richtig an, mit der Zusage loszulegen.

ZWEI FEHLER IN DER GELDANZEIGE GEFUNDEN
Beide fielen erst auf, als das erste Angebot ueber tausend Euro entstand
-- alle frueheren Testbetraege lagen darunter:
- Kein Tausenderpunkt: 1490 Euro erschienen als '1490,00 €'.
- Das Minuszeichen ging verloren: Math.trunc(-0.5) ergibt -0, und -0
  schreibt sich als '0'. Eine ERSTATTUNG von 50 Cent erschien damit als
  '0,50 €' -- also wie eine Forderung. Ausgerechnet beim Abbruch mit
  Rueckzahlung waere das der falsche Ort fuer einen Anzeigefehler.
Bewusst von Hand statt ueber Intl.NumberFormat: Die Ausgabe muss auf dem
Server und im Browser zeichengleich sein -- ein Betrag, der in der
Verwaltung anders aussieht als im Portal, saet Zweifel an der Zahl.

Geprueft: 56 zu den Angeboten, 53 zur Automatik (inkl. der neun
Geldpruefungen), 70 zur Annahme, 28 zur Suche -- alle gegen eine echte
Datenbank. Darunter: zweiter Klick legt nichts doppelt an, abgelaufene
Angebote lassen sich nicht mehr annehmen (ein Angebot, das man sieht
aber nicht annehmen kann, waere eine Falle), ein fremder Kunde erfaehrt
nicht einmal, dass ein Angebot existiert.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-24 12:14:27 +02:00
DogFatherGitandClaude Opus 5 658febb6c5 Abbruch-Dialog und Farbunterscheidung meins/beim Kunden
ABBRECHEN IN DER OBERFLAECHE
Der Server konnte es seit gestern, die Knoepfe fehlten. Jetzt steht am
Ende der Projektansicht ein zurueckhaltender Knopf -- bewusst nicht
zwischen den anderen: Es ist die seltenste und endgueltigste Handlung an
einem Projekt, und ein gleich lauter Knopf daneben laedt zum
Verwechseln ein.

Beim Aufklappen rechnet die Seite vor: wie viele Schritte erledigt sind,
wie viel gezahlt wurde, wie viel davon verdient ist, und was sich daraus
als Erstattung ergibt. Der Betrag steht als Vorschlag im Feld und ist
aenderbar -- geprueft wird, dass der GEAENDERTE Wert hinausgeht und nicht
der vorgeschlagene, sonst waere das Feld eine Attrappe.

Gerechnet wird erst beim Aufklappen, nicht beim Oeffnen der
Projektansicht: Dazwischen kann man Punkte abgehakt haben, und die
Zahlen sollen den Stand von JETZT zeigen. Faellt die Vorschau aus, laesst
sich der Betrag von Hand eintragen -- ein ausgefallener Rechendienst darf
kein Projekt in der Liste festhalten.

Ein bereits abgebrochenes Projekt bekommt keinen Knopf mehr, sondern
einen Kasten mit Datum, Grund, wer abgebrochen hat und was zu erstatten
war.

MEINS ODER SEINS
Wunsch: 'ich will dass die kunden sachen auch in der verwaltungs seite
von kacheln eine andere farbe haben wie meine damit ich sie gut
unterscheide.'

Was bei mir liegt, bleibt im Markenblau. Was beim Kunden liegt, bekommt
Lila. Gemessen: rgb(127,208,232) gegen rgb(183,157,255).

Bewusst NICHT ueber Rot/Gruen: Die Warnstufen sind an das ALTER
vergeben und muessen frei bleiben. Eine Kachel, die gleichzeitig 'beim
Kunden' und 'seit acht Tagen ueberfaellig' faerben muesste, koennte nur
eine der beiden Aussagen zeigen -- und die Frist ist die wichtigere.

Dazu eine 3px-Kante links auf beiden Seiten. Farbe allein traegt die
Aussage nicht: Wer sie nicht unterscheiden kann, saehe sonst zwei gleich
aussehende Bloecke (WCAG 1.4.1). Die Kante ist ein Gegensatz, keine
Markierung einer Gruppe -- meins blau, seins lila.

Geprueft mit 42 Pruefungen auf Computer und Handy, die messen, was
tatsaechlich an den Server geht und welche Farben wirklich berechnet
werden. Alle bestehenden Pruefungen weiter gruen.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-24 12:05:25 +02:00
DogFatherGitandClaude Opus 5 65b98ace89 Die Uhr laeuft erst, wenn die Anzahlung da ist
Bisher begann der Liefertermin mit der Annahme. Das ist unfair in beide
Richtungen: Wer zehn Tage bis zur Zahlung braucht, verbraucht zehn Tage
der zugesagten Zeit, ohne dass ein Handschlag Arbeit passiert waere --
und ich stehe am Ende als der da, der seinen Termin reisst.

Ein Projekt hat jetzt drei Abschnitte statt zwei:
  1. angenommen, wartet auf Anzahlung  -> Uhr steht
  2. Anzahlung da                      -> Uhr laeuft, Termin ab HEUTE neu
  3. uebergeben oder abgebrochen       -> Uhr steht wieder

Der Zahlungseingang loest alles Weitere von selbst aus: Uhr starten,
Termin neu rechnen, Status von briefing auf design, 'wer ist am Zug' auf
mich, Benachrichtigung in der Verwaltung. Der bei der Annahme genannte
Termin bleibt als termin_geplant_am erhalten, und die Meldung nennt
BEIDE -- so sieht man, dass sich etwas verschoben hat, ohne nachrechnen
zu muessen.

Eingehaengt an der Stelle, an der beide Wege zusammenlaufen (PayPals
Meldung UND das Vermerken von Hand). Nur am Webhook haenge sich die
Seite verschieden verhalten, je nachdem WIE das Geld ankam -- eine von
Hand verbuchte Zahlung startete die Uhr nie.

Zwei Grundsaetze fuer die Automatik: Sie setzt Dinge in Gang, nimmt aber
nie eine Entscheidung zurueck, die ein Mensch getroffen hat (ein von Hand
pausiertes Projekt wird nicht kommentarlos wieder gestartet). Und jeder
Schritt hinterlaesst eine Spur im Verlauf UND als Meldung -- eine
Automatik, die stillschweigend arbeitet, ist kein Helfer, sondern ein
Raetsel.

ABBRECHEN
Ein angenommener Auftrag bleibt abbrechbar: Der Kunde zahlt nicht,
meldet sich nicht, springt ab. Vorschau und Ausfuehrung sind getrennt --
die Seite rechnet aus dem Aufgabenfortschritt vor, wie viel Leistung
erbracht wurde, und schlaegt daraus einen Erstattungsbetrag vor. Der
Betrag ist ein VORSCHLAG: Ob im Einzelfall mehr oder weniger angemessen
ist, haengt an Dingen, die keine Tabelle kennt. Offene Rechnungen werden
storniert (eine Zahlungsaufforderung ohne Gegenleistung), bezahlte
bleiben unangetastet, und die Rueckzahlung loest die Seite bewusst NICHT
selbst aus -- PayPal-Rueckzahlungen sind nicht umkehrbar.

BENACHRICHTIGUNGEN
Eigene Tabelle statt im Verlauf: Der Verlauf haelt fest, WAS geschehen
ist -- vollstaendig, zum Nachschlagen. Eine Benachrichtigung ist ein
Anstupsen, das gelesen und weggelegt wird. Beides in einer Tabelle
hiesse: entweder ein Verlauf voller Rauschen oder Meldungen, die man
nicht wegklicken kann. Wegklicken markiert nur als gelesen, loescht
nichts.

Dazu die Liste der Projekte, die seit ueber einer Woche auf ihre
Anzahlung warten. Sie stehen in keiner anderen Zahl, weil ihre Uhr nie
zu laufen begann -- ohne diesen Hinweis vergisst man sie.

Geprueft: 44 gegen eine echte Datenbank. Darunter der Kern -- die
Annahme wird zehn Tage zurueckdatiert, und der Termin muss danach
trotzdem volle 20 Werktage entfernt liegen. Beim Bauen des Tests selbst
ein Fehler gefunden: Die erste Fassung datierte nur die Annahme zurueck,
nicht den damals errechneten Termin, und bildete damit genau den Fall
nicht ab, um den es geht.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-24 11:56:08 +02:00
DogFatherGitandClaude Opus 5 6b818fd6f6 Abmelden meldet jetzt wirklich ab
Rueckmeldung: 'der abmelde button klappt auch nicht auf dem handy'. Die
Ursache war nicht das Handy -- der Fehler war auf beiden Geraeten
derselbe, auf dem Handy sieht man das kurze Aufblitzen nur eher.

Der Knopf beendete die Team-Sitzung, loeschte den Schluessel und lud neu.
Das Merkmal der Zugangswand blieb dabei im Browser stehen. Beim
Neuladen holt sich die Seite damit sofort wieder einen Ausweis -- man
war nach einer Zehntelsekunde erneut angemeldet, und der Knopf schien
nichts zu tun.

Jetzt werden BEIDE Sitzungen beendet (der Endpunkt dafuer gab es
laengst, die Verwaltung rief ihn nur nie auf), und man landet an der
Zugangswand statt in einer Codeeingabe. Das ist auch die ehrliche
Bedeutung des Wortes: Wer sich abmeldet, will draussen sein.

Beide Aufrufe sind einzeln abgesichert -- faellt einer aus, laeuft der
andere trotzdem. Ein halbes Abmelden waere schlimmer als keins: Man
hielte sich fuer abgemeldet und waere es nicht.

Geprueft mit 16 Pruefungen auf Computer und Handy, die messen, was
tatsaechlich hinausgeht statt ob sich etwas auf dem Schirm bewegt.
Dabei ein Artefakt im eigenen Test gefunden und behoben: Das
Vorbereitungsskript lief bei jeder Navigation und setzte den Schluessel
auf der Zugangswand gleich wieder -- zwei Pruefungen massen also sich
selbst.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-24 11:29:28 +02:00
DogFatherGitandClaude Opus 5 2ae77fc54f Verwaltung: eine Suche ueber alles, mit Tastatur bedienbar
Bisher gab es genau ein Suchfeld, und es durchsuchte nur die
Anfragenliste. Wer den Namen eines Kunden im Kopf hatte, musste raten,
in welchem Reiter er nachsehen muss: War das eine Anfrage, ein laufendes
Projekt, eine offene Rechnung? Bei drei Vorgaengen merkt man sich das,
bei dreissig nicht mehr.

Jetzt: Strg+K von ueberall, auf dem Handy der Lupenknopf oben. Ein
Aufruf durchsucht Anfragen, Kunden, Projekte und Zahlungen; jeder
Treffer traegt seinen Zusammenhang (Nummer, Kunde, Betrag, Liefertermin)
und fuehrt per Enter in den passenden Reiter, bei einer Anfrage direkt in
die Detailansicht.

Gebaut nach dem ARIA-Muster 'Combobox mit Listbox-Popup' aus den W3C
Authoring Practices -- nachgeschlagen, nicht aus dem Gedaechtnis:
role=combobox am Eingabefeld, aria-expanded, aria-controls,
aria-activedescendant, role=listbox, role=option mit aria-selected. Der
Fokus bleibt dabei im Eingabefeld, damit man weitertippen kann; die
Auswahl wandert ueber aria-activedescendant. Ohne diese Auszeichnung
waere ein Feld, das Vorschlaege einblendet, fuer einen Screenreader
stumm -- das sieht man beim Testen mit den Augen nie.

Vier Fallen ausdruecklich behandelt:
- Nicht bei jedem Tastendruck suchen (180 ms Wartezeit): 'Musterbau'
  haette sonst neun Abfragen ausgeloest, acht davon veraltet.
- Das Wettrennen der Antworten: Jede Abfrage bekommt eine laufende
  Nummer, nur die neueste darf zeichnen. Sonst ueberschreibt eine spaet
  eintreffende alte Antwort die neue.
- Leer, laedt und Fehler sind eigene Zustaende. Ein Kasten, der bei
  einem Serverfehler leer bleibt, sieht aus wie 'nichts gefunden'.
- LIKE-Sonderzeichen: '%' waere ein Platzhalter, '_' ein beliebiges
  Zeichen -- und Unterstriche stehen regelmaessig in E-Mail-Adressen.
  Der Fehler ist tueckisch, weil die Suche trotzdem Treffer liefert, nur
  die falschen.

Ausserdem zum dritten Mal dieselbe Spezifitaetsfalle gefunden: '.wd p'
schlug die eigene Regel, der Tastaturhinweis erschien in 17,9px statt
11,5px -- so gross wie der Inhalt, den er erklaert. Jetzt festgenagelt
durch eine Pruefung, die Groessenverhaeltnisse vergleicht.

Geprueft: 28 gegen eine echte Datenbank (darunter alle
LIKE-Sonderzeichen), 64 im Browser auf Computer und Handy inkl. der
ARIA-Vorgaben, des Wettrennens und des Fehlerzustands.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-23 23:13:23 +02:00
DogFatherGitandClaude Opus 5 659a1ee9ce Verwaltung fragt nicht mehr grundlos nach dem Zugangscode
Zwei Ursachen, die zusammenwirkten.

1. Nach einem Neuladen war die HERKUNFT des Schluessels vergessen. Der
   Code speicherte nur den Schluessel selbst, nicht ob er aus einer
   Codeeingabe oder aus dem Ausweis der Zugangswand stammt.

2. Weil die Herkunft fehlte, wurde vorsichtshalber 'code' angenommen.
   Als Vorsicht gedacht, mit unangenehmer Kehrseite: Ein Ausweis gilt nur
   15 Minuten. Als 'code' behandelt wurde er nie erneuert -- nach einer
   Viertelstunde kam der erste 401, und man landete in der Codeeingabe,
   obwohl alles in Ordnung war. Der Schutz 'eine Code-Sitzung wird nie
   durch einen Ausweis ersetzt' war damit ab dem ersten Neuladen ohnehin
   wirkungslos.

Die Herkunft steht jetzt neben dem Schluessel und ueberlebt ein
Neuladen. Fehlt sie -- etwa aus einer aelteren Sitzung --, bleibt es bei
der vorsichtigen Annahme 'code'.

Das behebt die Haelfte des Problems. Die andere Haelfte ist eine
Einstellung auf dem Server: Die beiden Dienste benutzen nicht dasselbe
WEBDESIGN_API_SECRET, weshalb jeder Ausweis abgelehnt wird. Der Kommentar
in server/.env.example sagt ausdruecklich, dass beide Werte exakt gleich
sein muessen. Das kann nur Filipe angleichen -- /home/dogiintern/ ist
fuer claudian gesperrt.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-23 22:50:22 +02:00
DogFatherGitandClaude Opus 5 131bc8a755 Anfragen annehmen und ablehnen — mit laufender Zeit bis zur Übergabe
Annehmen war bisher nur ein Etikett: Man stellte den Status um, und es
passierte nichts. Kunde, Projekt, Aufgabenliste, Termin und Zugang musste
man danach von Hand in fünf Schritten nachbauen. Der alte Code gab das im
Kommentar selbst zu -- schlug der zweite von zwei Aufrufen fehl, stand der
Kunde schon in der Datenbank und man durfte es nicht noch einmal
versuchen.

Jetzt macht das EIN Aufruf, ganz oder gar nicht:
Kunde finden oder anlegen, Projekt anlegen, die komplette Aufgabenliste
des Pakets einsetzen, Liefertermin berechnen, Einladungslink erzeugen.
Der Kunde verfolgt ab diesem Moment alles in seinem Portal.

Die Zeit läuft wirklich:
- Eigenes Datumsfeld (termin_am) neben dem freien Text. Aus 'Mitte
  Oktober' kann man keine verbleibenden Tage rechnen.
- Werktage statt Kalendertage, inklusive luxemburgischer Feiertage. Die
  beweglichen werden über die Osterformel berechnet statt gepflegt --
  eine Liste ist im übernächsten Jahr lautlos falsch.
- Vorschlag je Paket (Onepager 10, Website 20, Shop 30 Werktage),
  überschreibbar vor dem Bestätigen.
- Verbleibende Zeit wird SERVERSEITIG gerechnet. Der Browser kennt die
  Feiertage nicht; zwei verschiedene Zahlen für denselben Termin wären
  schlimmer als gar keine.

Ablehnen mit Grund und vorbereiteter Absage in fünf Sprachen, Text
serverseitig erzeugt und vor dem Abschicken lesbar. Ein laufendes
Projekt lässt sich nicht nachträglich als Anfrage ablehnen.

Sechs Fehler dabei gefunden:
- Ein unbekanntes Paket hätte GAR KEINEN Termin bekommen statt des
  Ersatzwerts. Zwei Funktionen lasen dieselbe Tabelle, nur eine hatte
  einen Rückfallwert. Eine fehlende Zahl fällt nirgends auf.
- wd_anfragen hat keine Spalte 'firma' -- better-sqlite3 weist undefined
  ab, die ganze Annahme wäre gescheitert.
- Migrationen stehen in einer ausdrücklichen Liste; 0015 fehlte darin.
- datumKurz() wurde aufgerufen, gab es aber nicht. Der Fehler wäre erst
  NACH dem Anlegen aufgetreten.
- Der Installations-Hinweis lag fest über dem Annehmen-Knopf und machte
  ihn auf dem Handy untreffbar. Die Seite reserviert jetzt Platz dafür.
- 'richttermin' ist freier Text, lief aber durch einen Datumsformatierer:
  Der Kunde las 'Invalid Date' an der wichtigsten Stelle seines Projekts.

Geprüft: 49 Prüfungen der Terminrechnung gegen nachschlagbare Osterdaten
und Wochentage, 70 gegen eine echte Datenbank (darunter: zweiter Klick
legt nichts doppelt an, Abbruch mittendrin lässt NICHTS zurück, gesperrter
Kunde wird nicht still entsperrt), 42 im Browser auf Computer und Handy,
40 im Portal. Alle bestehenden Prüfungen weiter grün.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-23 22:44:08 +02:00
DogFatherGitandClaude Opus 5 37f1b4a8f5 Handy: Sicherheitsabstaende, Wisch-Reiter, zweispaltige Uebersicht
Der wichtigste Fund: env(safe-area-inset-*) stand bereits im CSS, lieferte
aber immer null -- viewport-fit=cover fehlte auf allen dreizehn Seiten.
Der Code sah richtig aus und tat nichts. Zusammen mit
apple-mobile-web-app-status-bar-style=black-translucent (schon gesetzt)
hiess das: installiert lag die Kopfzeile auf einem iPhone hinter Uhr und
Akkuanzeige.

Behoben:
- viewport-fit=cover auf allen 13 Seiten.
- Vier Sicherheitsabstaende zentral benannt statt an jeder Stelle
  einzeln geschrieben. Kopfzeile weicht der Statusleiste, Container dem
  seitlichen Notch im Querformat, Fusszeile der Gestenleiste.
- Der 'Ueberspringen'-Knopf des Vorspanns lag mit bottom:2rem praktisch
  AUF dem Entsperr-Strich (34px). Man haette die App verlassen statt
  uebersprungen.
- Eigener Zweig fuer den installierten Betrieb: kein Gummiband-
  Nachfedern (sieht in einer App nach einem Fehler aus), kein
  Installations-Hinweis.
- Die vier Sprungmarken auf der Rechtsseite waren 39px hoch -- fuenf
  unter dem Daumenmass. Ausgerechnet dort muss man zum Widerrufsrecht
  springen koennen.
- 'Waehle links einen Verlauf aus': Auf dem Handy gibt es kein links,
  die Liste steht darueber. Richtungswort entfernt.
- Sechs Reiter brauchten auf dem Handy drei Zeilen. Jetzt ein
  Wischstreifen mit Einrasten und Auslauf am Rand; der aktive Reiter
  wird herangeholt, wenn man ueber eine Kachel springt.
- Uebersicht zweispaltig statt zwoelf Zeilen untereinander: 2635px ->
  1924px. Eine Uebersicht, an der man vorbeiwischen muss, ist keine.

Geprueft mit 55 neuen Handy-Pruefungen auf iPhone 14 Pro, Pixel 7 und
320px Breite, jeweils im Browser und im installierten Zweig. Drei
Fehlalarme der eigenen Pruefung wurden begruendet ausgenommen (Honigtopf
bei left:-9999px, Eingabefeld in einer Beschriftung, Inline-Link im
Fliesstext -- WCAG 2.5.8 nimmt letztere ausdruecklich aus).

Zwei Selbstkorrekturen an der Pruefung dokumentiert: Die Emulation von
display-mode wirkt nicht (Chromium nimmt den Befehl an und ignoriert
ihn) -- ohne das waeren die zwoelf 'installiert'-Zeilen ein zweiter
Browser-Durchgang gewesen. Und die Messung gegen die Systemleisten mass
zuerst die Kastenkante statt der Inhaltskante und meldete elf korrekte
Seiten als fehlerhaft. Eine Selbstpruefung mit einem absichtlich falsch
gesetzten Knopf belegt jetzt, dass die Messung echte Fehler findet.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-23 19:51:10 +02:00
DogFatherGitandClaude Opus 5 c1cb51884f Verwaltung: Übersicht als Startansicht, Reiter unter den Titel
Die Reiter standen neben dem Titel. Das las sich wie eine einzige lange
Zeile, in der der Titel nur der erste von sechs Knöpfen zu sein schien --
man sah nicht auf einen Blick, in welchem Bereich man war. Jetzt zwei
Zeilen: oben WO man ist, darunter WOHIN man kann.

Die Verwaltung öffnete bisher mit der Anfragenliste. Eine Liste ist eine
Ablage: Sie zeigt, WAS es gibt, nicht was zu TUN ist. Neu ist ein
Cockpit, das in drei Stufen antwortet -- was auf mich wartet, was beim
Kunden liegt, wie es ums Geld steht -- und dann laufende Projekte mit
Fortschritt sowie den Verlauf.

Zwei Grundsätze machen die Zahlen brauchbar: Getrennt nach 'wartet auf
mich' und 'wartet auf den Kunden' (zwölf offene Punkte sind entspannt,
wenn elf beim Kunden liegen). Und das ALTER färbt, nicht die Menge --
vier neue Anfragen sind kein Problem, eine seit sechs Tagen liegende
schon. Ein offener Widerruf ist immer rot, weil eine gesetzliche Frist
läuft.

Jede Kachel ist ein echter <button> und führt in den passenden Reiter.
Farbe ist nie der einzige Träger: Neben jedem farbigen Zustand steht der
Text ('älteste seit 8 Tagen').

Drei Fehler dabei gefunden und behoben:
- Der Titel wurde nur beim Klicken gesetzt. Frisch geladen zeigte die
  Seite das Cockpit, während darüber noch 'Projektanfragen' stand. Die
  Zuordnung Reiter->Titel liegt jetzt ausserhalb des Klick-Zuhörers.
- '.wd h2' überstimmte '.vw-ub-h': 44px Überschrift über 33px Zahl, die
  Seite las sich wie ein Plakat. Dieselbe Spezifitätsfalle wie früher
  bei '.wd a'.
- Eine Antwort mit ok:true aber ohne Inhalt riss die ganze Verwaltung
  mit. Wird jetzt abgefangen -- ein halber Server ist ein realistischer
  Fall.

Geprüft: 22 Serverprüfungen gegen eine echte Datenbank (inkl. vier
Fällen, die belegen, dass die Rechteprüfung wirklich greift), 56 im
Browser auf Computer und Handy, 69 im bestehenden Verwaltungstest,
0 Fundstellen im Design-/WCAG-Test, 5 im Farbsystemtest.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-23 19:21:12 +02:00
DogFatherGitandClaude Opus 5 4fe66e1e0e "Preise & Zeit" entfernt -- Inhalte in die Leistungen uebernommen
"leistungen und preis&zeit ist ja das gleiche, nimm die kategorie
preise&zeit komplett weg"

Stimmt bei den Paketen und Preisen: Beide Seiten zeigten dieselben
Zahlen. Zwei Orte fuer dieselbe Zahl sind eine Einladung, dass sie
irgendwann auseinanderlaufen -- und dann steht ein falscher Preis auf
der Seite, ohne dass es jemand merkt.

NICHT ALLES WAR DOPPELT

Vor dem Loeschen nachgesehen statt einfach geloescht. Zwei Abschnitte
gab es NUR dort:

  "Was den Preis bewegt"  -- beantwortet die Frage, die nach jeder
       Preisliste kommt: Warum kostet es bei mir mehr oder weniger?
       Ohne sie ist eine Preisspanne eine Behauptung.

  "Wie bezahlt wird"  -- Angebot, 30 % Anzahlung, Restbetrag, Betreuung.
       Rechtlich relevant und aus den AGB verlinkt.

Beide sind in die Leistungen umgezogen, samt ihrer 24 Textbausteine in
allen fuenf Sprachen. Waeren sie mitgeloescht worden, haette es niemand
sofort bemerkt -- und die Seite haette ab dann Preise genannt, ohne sie
zu erklaeren.

WEITERLEITUNG STATT 404

Die Adresse /webdesign/preise.html bleibt bedient und fuehrt auf die
Leistungen. Lesezeichen, alte Links aus Nachrichten und die installierte
App zeigen sonst ins Leere, und ein 404 auf einer Preisseite ist der
denkbar schlechteste erste Eindruck.

302 statt 301: Eine dauerhafte Weiterleitung merken sich Browser
hartnaeckig; sollte die Seite je zurueckkehren, muesste jeder Besucher
seinen Zwischenspeicher leeren.

EIN FEHLER BEIM EINBAU

Die Weiterleitung stand zuerst weiter unten in der Schranke, bei den
ohne Code erreichbaren Seiten. Dort haette sie nur fuer NICHT angemeldete
Besucher gegriffen -- wer angemeldet ist, passiert die Schranke vorher
mit next() und haette einen 404 auf die geloeschte Datei bekommen.
Genau die Person also, die die Seite am ehesten im Lesezeichen hat.
Jetzt steht sie ganz vorne, vor jeder Zugangspruefung.

GEPRUEFT: 10 Pruefungen -- beide uebernommenen Abschnitte erscheinen in
allen fuenf Sprachen, kein unuebersetzter Schluessel sichtbar. Kein
Verweis auf die alte Seite blieb irgendwo stehen (ablauf.html hatte
einen, der ist umgebogen). Gestaltung 0 Befunde, Textkodierung sauber,
Designsystem 5.

Versionsstempel und Cache auf v20.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-23 19:00:49 +02:00
DogFatherGitandClaude Opus 5 d6e75ce56a Kacheln: Licht folgt dem Zeiger, Rand glueht mit
"ich will das die kacheln richtig speziell sind und modern ... dass viel
mehr auffaellt und doch modern und profissionell"

Die Kacheln hatten Verlaufsrand, Verlaufsflaeche, Schatten und
Lichtkante -- alles richtig, alles STATISCH. Was fehlte, war das Gefuehl
von Material: eine Flaeche, die auf Licht reagiert.

ZWEI EBENEN

Ein weicher Lichtfleck folgt dem Zeiger ueber die Flaeche. Und -- der
Teil, der wirklich auffaellt -- der RAND glueht an derselben Stelle auf
und verlaeuft nach beiden Seiten aus.

Das geht ohne zusaetzliches Element: Die Kachel hat bereits zwei
Hintergrundebenen (Flaeche padding-box, Verlaufsrand border-box). Beim
Ueberfahren bekommt die Randebene einen Lichtpunkt an der Zeigerstelle.
Der Rand ist nur einen Pixel breit -- genau dort laesst sich Licht am
staerksten zeigen, ohne kitschig zu werden. Ein Lichtfleck OHNE
mitleuchtende Kante wirkt wie ein Fleck AUF der Kachel; mit ihr wirkt
die ganze Kachel beleuchtet.

Die Flaeche selbst bleibt beim Ueberfahren unveraendert -- sonst wuerde
der Text flackern.

VIER ENTSCHEIDUNGEN GEGEN BILLIGE OPTIK

Die Farbe folgt der Kachel: blaue leuchten blau, lila lila. Sonst braeche
der Effekt die Farbordnung, die ueberall sonst Bedeutung traegt.

Nur bei echtem Zeiger (hover: hover, pointer: fine). Auf dem Handy gibt
es keinen; dort bliebe der Lichtfleck an einer zufaelligen Stelle kleben.

Aus bei prefers-reduced-motion. Auch wenn sich nichts bewegt: Etwas, das
dem Zeiger folgt, ist Bewegung.

Erste Fassung war mit 13 % zu zurueckhaltend -- am Screenshot geprueft
und auf 22 % angehoben. "Man soll es spueren" ist richtig, aber es muss
auch ankommen.

SPARSAM GEBAUT

EIN Zuhoerer am Dokument statt einem je Kachel -- sonst muesste jede
nachgeladene Kachel einzeln nachgeruestet werden. Geschrieben wird erst
im naechsten Bildaufbau: Der Zeiger meldet sich bis zu tausendmal je
Sekunde, der Bildschirm zeichnet sechzigmal; ohne Drosselung rechnet der
Browser neunhundertmal umsonst.

Der Inhalt bekommt z-index 1. Ohne das legt sich der absolut gesetzte
Lichtfleck ueber den Text -- absolut positionierte Elemente malen ueber
Fluss-Inhalt, auch wenn sie im Quelltext davor stehen. Das Ergebnis waere
ein Schleier auf der Schrift, den man erst beim Vergleich zweier Kacheln
bemerkt. Eigens geprueft.

GEPRUEFT: 7 Pruefungen fuer den Effekt selbst -- unsichtbar im
Ruhezustand, erscheint beim Ueberfahren, folgt dem Zeiger auf 6 Pixel
genau, Inhalt liegt darueber, lila Kacheln leuchten lila, bei
Bewegungsempfindlichkeit vollstaendig abgeschaltet.

Gestaltung 0 Befunde, Designsystem 5, Verwaltung 69, Portal 32.

Versionsstempel und Cache auf v19.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-23 18:53:05 +02:00
DogFatherGitandClaude Opus 5 e495542fe5 Typografie: Lesebreite begrenzt -- 105 zu breite Absaetze behoben
Gemessen ueber alle Seiten: 105 von 200 Textabsaetzen waren zu breit,
der schlimmste mit 151 Zeichen pro Zeile.

WARUM DAS ZAEHLT

Beim Zeilensprung muss das Auge zurueck an den Zeilenanfang finden. Je
laenger die Zeile, desto haeufiger landet es in der falschen -- man
liest eine Zeile doppelt oder ueberspringt eine und merkt es erst zwei
Saetze spaeter. Der Text ist dann nicht "schwer", er ist schlecht
gesetzt.

Bewaehrt sind 45 bis 75 Zeichen. Das ist der aelteste und
bestbelegte Grundsatz der Typografie ueberhaupt -- jedes ordentlich
gesetzte Buch haelt sich daran.

Jetzt 68 Zeichen fuer Fliesstext, 60 fuer Kleingedrucktes. Die Einheit
ist ch statt Pixel: Damit haengt die Breite an der SCHRIFTGROESSE.
Kleingedrucktes bekommt automatisch einen schmaleren Block, grosse
Schrift einen breiteren -- beide landen bei aehnlich vielen Zeichen.

max-width macht nie etwas breiter. In schmalen Karten und Spalten
aendert sich also nichts; die Regel greift nur dort, wo eine Zeile
wirklich ueber das Lesbare hinauswaechst. Ergebnis: von 105 auf 0.

DREI FEHLER BEIM EINBAU, ALLE VON DER MESSUNG GEFUNDEN

1. Zentrierter Text stand ploetzlich links. Eine Breitenbegrenzung
   zentriert den KASTEN nicht mit -- die Schrift war mittig, ihr Kasten
   klebte am linken Rand, mit 384 Pixeln Luft rechts und null links.
   Gefunden, weil die Pruefung den Abstand links mit dem rechts
   VERGLEICHT, statt nur zu zaehlen, ob zentriert ist.

2. Meine erste Regel deckte nur den Fall ab, dass das ELTERNELEMENT
   zentriert -- nicht den, dass der Absatz es selbst tut.

3. Und dann blieb einer uebrig, bei dem beides stimmte. Ursache: ein
   Inline-Stil "margin:1.2rem 0 0". Die Kurzschreibweise setzt links und
   rechts hart auf 0 und schlaegt jede Stilvorlage. 11 solche Stellen
   ueber fuenf Seiten auf margin-top umgestellt -- gemeint war ohnehin
   immer nur der Abstand nach oben.

Ausnahmen bewusst gesetzt: Nachrichtenblasen tragen ihre Breite schon
selbst, Tabellenzellen und Beschreibungslisten richten sich nach ihrer
Spalte, die Fusszeile ist mehrspaltig und ohnehin schmal.

ALLES GRUEN: Gestaltung 0 Befunde, Designsystem 5, Verwaltung 69,
Portal 32, Widerruf 58, Anfragedetail 20.

Versionsstempel und Cache auf v18.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-23 18:47:34 +02:00
DogFatherGitandClaude Opus 5 10a075cc33 Gestaltung: Hoehensystem -- mehrschichtige Schatten und Lichtkante
Bis hierher trug jede Karte EINEN Schatten. Das ist solide und genau der
Grund, warum eine Oberflaeche "ordentlich" statt "teuer" wirkt.

Echtes Licht erzeugt zwei Dinge gleichzeitig: einen engen, dunklen
Kontaktschatten an der Kante -- er sagt dem Auge, dass etwas AUFLIEGT --
und einen weiten, weichen Umgebungsschatten, der sagt, wie HOCH es
darueber schwebt. Nur einen von beiden zu setzen sieht aus wie ein
Aufkleber; beide zusammen ergeben einen Gegenstand.

Dazu die LICHTKANTE: eine 1 Pixel hohe, sehr schwache weisse Linie an der
Oberkante. Sie tut so, als kaeme das Licht von oben und braeche sich an
der Kante. Das ist das meistuebersehene Detail in dunklen Oberflaechen
und dasjenige mit der groessten Wirkung -- ohne sie wirkt eine Karte wie
ein Loch im Hintergrund, mit ihr wie ein Stueck Material.

Drei Stufen, mehr braucht es nicht: ruhende Flaechen, hervorgehobene
Flaechen, Schwebendes. Dazu eine vierte, umgekehrte fuer Eingabefelder:
Die liegen nicht AUF der Flaeche, sie sind hineingeschnitten -- oben
dunkel, unten hell.

Weil es die gemeinsamen Bausteine sind, wirkt es sofort auf allen 13
Seiten: Hauptseite, Portal und Verwaltung.

Dazu Feinheiten, die einzeln niemand bemerkt und in Summe den
Unterschied machen: Uebergaenge auf 160-180 ms verkuerzt (alles darueber
wirkt beim Ueberfahren traege), eigene Rueckmeldung beim Druecken,
optische Laufweitenkorrektur fuer grosse Ueberschriften (-.028em bei h1;
das Auge sieht bei 48px mehr Weissraum zwischen Buchstaben als bei 16px),
text-wrap: balance gegen einzelne Woerter in der letzten Zeile.

EIN FEHLER BEIM EINBAU, GEMESSEN STATT UEBERSEHEN

Nach dem ersten Durchgang hatten die Karten ihre Lichtkante, der
Hauptknopf nicht. Grund: Die Grundregel lautet
".wd .wd-btn--haupt, .wd-btn--haupt" -- der erste Teil zaehlt ZWEI
Klassen. Mein Nachtrag zaehlte eine und verlor, obwohl er spaeter steht.
Dieselbe Falle wie am 22.08.2026 bei ".wd a" gegen ".wd-btn--haupt".
Aufgefallen nur, weil die Pruefung die Lichtkanten ZAEHLT.

PRUEFWERKZEUGE ANGEPASST

diagnose.html ist jetzt ausgenommen. Sie traegt bewusst alles fest in
sich -- eigene Farben, keine Uebersetzung -- weil sie funktionieren muss,
wenn genau das kaputt ist, was sie untersucht. Sie an den Regeln der
eigentlichen Seite zu messen erzeugte zwei Meldungen, die man auf Dauer
wegsieht. Und irgendwann sieht man dann auch eine echte weg.

ALLES GRUEN: Gestaltung 0 Befunde (12 Seiten x 5 Sprachen gegen
WCAG 2.2 AA), Designsystem 5, Verwaltung 69, Portal 32.

Versionsstempel und Cache auf v17.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-23 18:37:17 +02:00
DogFatherGitandClaude Opus 5 a964a078f0 Zahlungen: Verwaltung sticht Serverdatei -- der Schalter wirkt jetzt
Filipes Bildschirm zeigte alles, was noetig war, um es zu erkennen:

  Betriebsart: hinterlegt (auf dem Server) . 7 Zeichen
  Client ID:   hinterlegt . 82 Zeichen
  Secret:      hinterlegt . 80 Zeichen
  Fehler:      "PayPal hat die Anmeldung abgelehnt."

7 Zeichen sind "sandbox". Der Wert kam aus der .env, und die hatte
Vorrang. Sein Klick auf "Echtbetrieb" blieb wirkungslos -- seine echten
Zugangsdaten wurden gegen den TESTSERVER von PayPal geprueft, der sie
zwangslaeufig ablehnt.

Die Fehlermeldung zeigte dabei auf die Zugangsdaten ("stimmen Client ID
und Secret nicht zusammen") und damit in die voellig falsche Richtung.

DER ENTWURFSFEHLER

Ich hatte der .env bewusst Vorrang gegeben, damit ein Fehlgriff im
Formular keine funktionierende Servereinstellung aushebelt. Das klingt
vorsichtig und war falsch:

Ein Formular mit Schaltern, die nichts bewirken, ist schlimmer als gar
kein Formular. Es behauptet eine Wirkung, die es nicht hat, und schickt
bei der Fehlersuche in die Irre.

Der Sinn dieser Ablage ist gerade, dass die Werte OHNE SSH gesetzt
werden koennen. Dann muss das, was dort steht, auch gelten.

Ungefaehrlich, weil diese Werte ausschliesslich der Webdesign-Bereich
liest. Das DogiCrew-Supporter-Abo hat sein eigenes Modul und liest
weiter direkt aus der Umgebung -- ein Eintrag hier kann es nicht
abschalten. Eigens geprueft.

AUCH DIE ANZEIGE WAR UNEHRLICH

Sie sagte nur "hinterlegt (auf dem Server)" und verschwieg, dass genau
dieser Wert die eigene Eingabe ueberstimmt. Jetzt steht dort, welche
Quelle GILT -- "aus der Serverdatei" oder "hier eingetragen" -- und bei
doppelter Belegung zusaetzlich "Serverwert wird nicht benutzt".

GEPRUEFT: 16 Pruefungen auf dem Server, alle bestanden. Darunter der
genaue Fall: Serverdatei sagt sandbox, Formular sagt live -> istLive()
wird WAHR. Und der Rueckweg: Feld leeren -> Serverwert greift wieder.

Beim Testen noch ein Werkzeugfehler behoben: Der Absturz von
better-sqlite3 beim Beenden verschluckte die gesamte gepufferte
Ausgabe -- der Test lief durch und sah aus, als waere er nie gestartet.
Jetzt schreibt er unumgepuffert.

Versionsstempel und Cache auf v16.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-23 18:12:50 +02:00
DogFatherGit 14488d6df2 Test: unumgepufferte Ausgabe (Absturz beim Beenden verschluckte sie) 2026-08-23 18:11:47 +02:00
DogFatherGit 779fb94c6b Test fuer den Quellen-Vorrang 2026-08-23 18:10:47 +02:00