Commit Graph
14 Commits
Author SHA1 Message Date
ScarriffleandClaude Opus 5 fa5a2bc4fa Angebrochene Packungen, Teilverbrauch und Hausmasse (Backend)
Eine Teilmenge auszulagern ging rechnerisch schon, aber es blieb keine Spur
davon: Eine Charge mit 900 von urspruenglich 1000 ml war von einer frisch
eingelagerten 900-ml-Charge nicht zu unterscheiden, und eine geoeffnete
Milch galt weiter bis zum aufgedruckten MHD.

Anbrechen spaltet jetzt eine Packung ab. Aus 4 x 1 l wird eine offene
Charge mit 1 l und eine ungeoeffnete mit 3 l - sonst truegen die
restlichen 3 l das kurze Verbrauchsdatum mit, obwohl sie noch zu sind. Der
Gesamtbestand aendert sich dabei nicht, es ist eine Umbuchung wie das
Aufteilen und schreibt wie dieses keine Bewegung.

Verbrauchen geht in einem Schritt: Wer "100 ml verbraucht" eintraegt,
bricht die Charge dabei an (abschaltbar ueber auto_open). Gerechnet wird
vor dem Anbrechen - scheitert die Einheit, soll keine Packung grundlos
offen stehen.

MHD und "zu verbrauchen bis" stehen nebeneinander und bleiben beide
sichtbar; gewarnt wird nach dem FRUEHEREN (Lot.faellig_am). Dafuer musste
in /expiring die Vorbedingung "best_before IS NOT NULL" weg, sonst fehlte
ausgerechnet die angebrochene Packung ohne aufgedrucktes MHD. ExpiringItem
traegt beide Daten plus warn_date und warn_source, und best_before ist dort
jetzt optional.

Die Frist kommt aus drei Ebenen: Artikel schlaegt Kategorie, Kategorie
schlaegt ueber ihre Oberkategorien hinweg den globalen Wert. Der Helfer
dafuer (services/vererbung.py) liest die Kategoriekette von
services/fields.py rueckwaerts - dort werden Felder ALLER Vorfahren
gesammelt, hier gewinnt der naechste. Dieselbe Mechanik traegt die
Hausmasse.

Hausmasse sind der zweite Teil: "2 EL Mehl" liess sich bisher nicht
buchen, weil eine globale Einheit "Essloeffel" bei Mehl und Zucker
zwangslaeufig gleich schwer waere. Der Name ist deshalb global, die Menge
haengt an Kategorie und Artikel. to_base loest sie nur mit hausmasse=True
auf - beim Auslagern und Verbrauchen ja, beim Einlagern und bei
Mindestbestaenden ausdruecklich nicht, denn ein Loeffel ist ein
Schaetzwert. Verwaltete Einheiten werden zuerst geprueft, damit ein Mass
namens "Liter" die echte Einheit nicht verdeckt.

Zwei Stellen waeren sonst still kaputtgegangen:

- Die Konsolidierung haette die offene Packung beim naechsten Einlagern am
  selben Ort einkassiert und ihr Verbrauchsdatum verschluckt. Angebrochene
  Chargen werden deshalb nie zusammengefasst - in consolidate_lot_group
  und im Start-Aufraeumer, beide bisher ohne jeden Test. Das Netz dafuer
  ist jetzt da (test_konsolidierung.py).
- FEFO sortiert nach dem wirksamen Datum statt nur nach dem MHD. Eine
  offene Packung kommt damit meist von selbst zuerst, aber eine
  ungeoeffnete, die morgen ablaeuft, geht vor - Verderb schlaegt
  Bequemlichkeit. Erst bei gleichem Datum wird das Angebrochene vorgezogen.

Dashboard bekommt "angebrochen" als fuenften Zustand, mit Vorrang
abgelaufen > bald > angebrochen > in Ordnung: Was offen ist und morgen
ablaeuft, gehoert in die Warnung, nicht in den ruhigeren Zustand.

Oberflaechen in Web und App folgen in eigenen Commits.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-02 11:25:36 +02:00
ScarriffleandClaude Opus 5 467ef3c804 Einzelstuecke verleihen: eigener Vorgang statt Lagerort-Wert
Der Lagerort bleibt beim Verleihen stehen - er sagt weiter, wohin das
Stueck nach der Rueckgabe zurueckgehoert. Ein Lagerort-Wert "verliehen"
haette diese Information vernichtet. Stattdessen eine eigene Tabelle
item_loans mit Entleiher und Grund als Freitext, Verleihdatum und einem
optionalen Rueckgabedatum: "kurz mal mitgenommen" soll nicht an einem
Datumszwang scheitern.

Zurueckgegebene Vorgaenge bleiben stehen, damit die Tabelle gleichzeitig
die Verleih-Historie je Stueck ist. Ein Teilindex auf die offenen
Vorgaenge haelt fest, dass ein Stueck nicht zweimal gleichzeitig
verliehen sein kann - wie bei den "Ueberall"-Mindestbestaenden, weil
Postgres NULLs in UNIQUE als verschieden zaehlt.

Verliehen heisst nicht im Bestand: Ein Stueck, das nicht im Haus ist,
zaehlt nirgends mehr mit - weder am Lagerort noch im Gesamtbestand.
Entschieden wird das an einer Stelle (services/loans.py::nicht_verliehen),
die alle fuenf Zaehlungen benutzen. Sonst behauptete "Was liegt hier?"
weiterhin, die Kamera liege im Regal. Folge: Faellt ein verliehenes
Stueck unter den Mindestbestand, erscheint der Artikel auf der
Einkaufsliste.

Verleihen und Zuruecknehmen darf jeder angemeldete Benutzer - es ist eine
Buchung wie Ein- und Auslagern, keine Stammdatenaenderung - und wird mit
dem Konto protokolliert. Nur das Loeschen eines Vorgangs bleibt Admins
vorbehalten, weil es Historie vernichtet.

Bewusst keine Movement-Bewegung je Verleih: Der Verlauf rechnet den
Bestand rueckwaerts aus vorzeichenbehafteten Bewegungen, ein Abgang ohne
Gegenbuchung wuerde diese Kurve verfaelschen.

GET /loans?due_in= liefert die faelligen Verleihe und ist damit schon die
Datenquelle fuer die spaetere Rueckgabe-Erinnerung der App. Oberflaeche in
Web und App fehlt noch.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 09:16:47 +02:00
ScarriffleandClaude Opus 5 eaacfd03e5 Gruppen: Obergruppen + Mindestbestaende nur noch je Lagerort (Backend)
Zwei zusammenhaengende Umbauten, weil sie dieselben Stellen betreffen.

Obergruppen: Gruppen bilden jetzt einen gerichteten azyklischen Graphen statt
einer flachen Liste. Eine Gruppe darf unter MEHREREN Obergruppen haengen -
"Grillwurst" unter "Wurst" UND unter "Grillgut"; mit einem einzelnen parent_id
waere genau das nicht abbildbar. Bestand und Mindestbestand einer Gruppe zaehlen
den gesamten Untergraphen, wobei eine ueber zwei Wege erreichbare Untergruppe
nur einmal zaehlt (services/gruppen.py arbeitet durchgaengig mit Mengen).
Product.group_id bleibt unveraendert - ein Artikel haengt weiter an genau einer
Gruppe.

Mindestbestaende: der separate Gesamt-Mindestbestand entfaellt. Er wird zur
Zeile mit location_id NULL ("Ueberall") und ist damit die Wurzel ueber allen
Lagerorten - dieselbe Verrechnung wie bei verschachtelten Orten greift jetzt
auch zwischen Ueberall und Kueche, wodurch derselbe Artikel nicht mehr doppelt
in der Einkaufsliste steht. Alle Werte liegen einheitlich in Basiseinheiten
statt in drei verschiedenen Einheiten nebeneinander; das Umrechnen beim
Umschalten der Erfassungseinheit entfaellt dadurch ersatzlos.

_netted_topups nimmt die Hierarchie jetzt als Parameter und faltet damit
Lagerort-Baum und Gruppen-Graph. Verrechnet wird zwischen zwei Gruppen nur,
wenn die zaehlenden Artikel der Untergruppe eine Teilmenge der Obergruppe sind -
zaehlt die Obergruppe in Kilogramm und die Untergruppe in Stueck, kommt ein Kauf
dort oben nicht an.

Die vierfach kopierte Bestandssumme wandert in Sammelabfragen
(summe_bestand_base), sonst vervielfacht der transitive Teilgraph die Abfragen.

Einmalige Datenwanderung beim Start (Merker in den Einstellungen), 18 neue
Tests - darunter Doppelzaehlung ueber zwei Wege, Ringschutz und die bewusst
offene Grenze bei zwei Obergruppen mit gemeinsamer Untergruppe.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 21:48:14 +02:00
ScarriffleandClaude Opus 4.8 ecc1570100 Gruppen-Gebinde: Mindestbestand/Bestand in Packungen (Backend)
Gruppen bekommen ein eigenes Richt-Gebinde (package_size/label) plus
min_stock_in_packages. Weil die Produkte einer Gruppe unterschiedlich
große Packungen haben können (Pesto 99 g vs. 160 g), legt die Gruppe einen
gemeinsamen Richtwert fest (1 Glas ≈ X g).

- Neuer Helfer group_min_context() zentralisiert, in welcher Einheit der
  Gruppen-Mindestbestand zaehlt (Gebinde ODER verwaltete Einheit).
- Einkaufsliste (gesamt + je Ort), Dashboard-Bedarf und GroupOut nutzen ihn;
  need.text kommt so als 'x Glaeser (y g)'.
- update_group rechnet bestehende Werte beim Umschalten der Einheit um, damit
  der physische Bedarf gleich bleibt.
- Migration: groups.package_size/package_label/min_stock_in_packages.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-07 18:38:12 +02:00
ScarriffleandClaude Opus 4.8 9b0771ae0e Kategorien-Anteile: einstellbare Kategorie-Tiefe (Unterkategorien zusammenfassen)
/dashboard/by-category bekommt depth: rollt jede Artikel-Kategorie auf ihren
Vorfahren der gewuenschten Stufe hoch (1 = oberste Ebene), ohne depth zaehlt
jede zugeordnete Kategorie einzeln (feinste). Neue Karten-Konfig select
Kategorie-Tiefe (Feinste / oberste / bis 2. / bis 3. Stufe). KartenConfig zeigt
jetzt auch bei select den Hinweis.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-30 17:39:30 +02:00
ScarriffleandClaude Opus 4.8 a70a99b387 Dashboard zaehlt Einzelstuecke mit; Baum-Dropdown scrollbar
Auswertungen (by_category, expiry_split, stats) iterierten nur Lots. Gegenstaende
mit Menge-je-Lagerort liegen als Lots und tauchten auf – Einzelstuecke liegen
aber als Items und fehlten dadurch komplett (Dashboard zeigte z.B. nur die
Powerbank, nicht Kamera/Kabel). Neuer Helfer _bestand_beitraege deckt beide
Speicherformen ab: jedes Item zaehlt als ein Stueck ohne MHD. Regressionstest
ergaenzt; volle Suite gruen (120).

CategorySelect: Scrollen IM aufklappbaren Menue schloss es, weil jeder Scroll als
Seiten-Scroll gewertet wurde. Jetzt schliesst nur ein Scroll ausserhalb des
Menues; die lange Liste laesst sich scrollen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-26 12:09:25 +02:00
ScarriffleandClaude Opus 4.8 f62289f975 Mehrere Dashboards und dieselbe Karte mehrfach
Zwei Grenzen sassen tief im Datenmodell. Erstens war "i" im gespeicherten
Layout zugleich die Kartenart; da react-grid-layout eindeutige Kennungen
verlangt, ging jede Art genau einmal - zwei Artikel gleichzeitig beobachten war
damit unmoeglich. Zweitens war dashboard_layouts.user_id unique, also genau ein
Dashboard je Benutzer.

Jetzt ist "i" die Kennung dieser einen Karte und "type" ihre Art, dazu kommt
"props" fuer das, was nur diese Karte angeht (etwa welcher Artikel). Aeltere
Anordnungen haben kein "type"; fuer sie gilt die alte Kennung als Art, sodass
gespeicherte Startseiten unveraendert weiterlaufen.

Die Tabelle bekommt Name und Reihenfolge, das unique faellt. Sein Name haengt
davon ab, wie die Tabelle entstanden ist, deshalb wird er in pg_constraint
nachgeschlagen statt geraten.

Eine Feinheit beim Anlegen: Wer noch auf der Admin-Vorgabe sitzt und sein
erstes eigenes Dashboard anlegt, wuerde die Vorgabe schlagartig verlieren -
sobald eigene Zeilen da sind, zaehlen nur noch die. Deshalb wird sie vorher
uebernommen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 19:11:49 +02:00
ScarriffleandClaude Opus 4.8 26f7ed21d8 Ein- und Auslagerungen als Wasserfall- bzw. Brueckendiagramm
Bisher waren es zwei Linien mit der Anzahl der Vorgaenge. Gemeint war eine
Bruecke: die Bestandslinie, und schwebende gruene/rote Balken, die jeden ihrer
Spruenge erklaeren.

Damit Balken und Linie ueberhaupt zusammen in ein Bild duerfen, zaehlen die
Balken jetzt Mengen in Artikeleinheiten statt Vorgaengen - beides liegt so auf
derselben Achse. Zwei Skalen in einem Diagramm bleiben ausgeschlossen.

Neuer Endpunkt GET /dashboard/flow liefert je Abschnitt Anfangsbestand, Zugang
und Abgang aus einer Abfrage. Getrennt abgefragt koennten die Abschnitte von
timeline und activity um eine Schrittweite auseinanderliegen - dann stuende ein
Balken neben dem Sprung, den er erklaert.

/dashboard/activity bleibt fuer externe Zugriffe erhalten.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 12:45:59 +02:00
ScarriffleandClaude Opus 4.8 d6a717e39c Ablaufkarten trennen: "Bald ablaufend" zeigt kein Abgelaufenes mehr
Eine Karte mit dieser Ueberschrift, in der bereits Ueberfaelliges steht,
ist irrefuehrend. KarteAblauf bekommt daher einen Modus soon/expired/alle
statt des bisherigen Schalters "nurAbgelaufen".

Neu ist die Karte "Ablauf-Uebersicht" (alle), die beides zusammen zeigt.
Sie steht auch in der eingebauten Vorgabe, damit dort ohne Zutun weiterhin
Ueberfaelliges sichtbar ist.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 12:24:30 +02:00
ScarriffleandClaude Opus 4.8 1fd1e89872 Zeitraum im Kartenkopf; Verlauf feiner als ein Punkt pro Tag
Zeitraum-Auswahl:
Sie stand ueber dem Diagramm und nahm ihm Hoehe weg. Jetzt sitzt sie klein im
Kartenkopf neben dem Titel - die ganze Kartenhoehe gehoert dem Diagramm. Die
Wahl wird in der Anordnung mitgespeichert, bleibt also nach dem Neuladen
erhalten; ausserhalb des Bearbeitungsmodus geschieht das still im Hintergrund.

Feinere Aufloesung:
Ein Punkt je Tag verbarg, WANN ein- und ausgelagert wurde. Die Abtastrate
richtet sich jetzt nach dem Zeitraum:
  bis  2 Tage  -> stuendlich
  bis 14 Tage  -> alle 6 Stunden
  bis 120 Tage -> taeglich
  darueber     -> woechentlich
So bleibt die Punktzahl immer zwischen etwa 25 und 105 - fein genug zum
Erkennen, grob genug zum Zeichnen. Ein Test haelt diese Spanne fest.

Die Zeitachse traegt entsprechend Uhrzeit statt Datum, wenn stuendlich
abgetastet wird. Punkte heissen jetzt "at" (Zeitpunkt) statt "date".
Zeitstempel aus SQLite kommen ohne Zeitzone zurueck und werden vereinheitlicht,
sonst schluege die Differenzbildung fehl.

Auswahl erweitert: 24 Stunden, 2 Tage, 7, 30, 90 Tage, 6 Monate, 1 Jahr.

Geprueft: "npm run build" laeuft durch. pytest weiterhin nicht ausfuehrbar -
kein Python auf diesem Rechner; die Tests wurden an die neue Aufloesung
angepasst.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 10:36:33 +02:00
ScarriffleandClaude Opus 4.8 97a6e94ea1 Kurzmeldungen als Overlay, Liniendiagramm fuellt die Karte, Zeitraum ab 1 Tag
Meldungen:
"Gespeichert", "Anordnung gespeichert" und die uebrigen Erfolgsmeldungen standen
als Leiste mitten in der Seite - beim Speichern sprang dadurch das ganze Layout
ein Stueck nach unten. Sie erscheinen jetzt als Overlay am unteren Rand und
verschwinden nach fuenf Sekunden von selbst; ein Kreuz blendet sie sofort aus.
Umgestellt in Uebersicht, Einstellungen, Gefahrenbereich, Benutzer,
Produktformular sowie Ein- und Auslagern.

Bewusst NICHT umgestellt: Fehlermeldungen. Ein Fehler, der nach fuenf Sekunden
verschwindet, ist genau dann weg, wenn man ihn noch braucht - der bleibt an
seiner Stelle stehen.

Rollbalken beim Liniendiagramm:
Den hatte ich mir mit der Zeitraum-Auswahl selbst eingebaut. Das Diagramm hatte
eine feste Hoehe von 200 px; zusammen mit der Auswahl darueber passte das nicht
mehr in die Karte. Jetzt wird auch die Hoehe gemessen, das Diagramm fuellt den
verbleibenden Platz.

Zeitraum "1 Tag":
Ein einzelner Tag ergibt genau einen Messpunkt - eine Linie aus einem Punkt
waere unsinnig. Das Diagramm zeigt in dem Fall die blanke Zahl. Die
Backend-Begrenzung laesst jetzt einen Tag zu.

Geprueft: "npm run build" laeuft durch; kein Verweis mehr auf die entfernten
Meldungszustaende.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 10:23:53 +02:00
ScarriffleandClaude Opus 4.8 aa58914374 Kartengroessen korrigiert, Vorschau im Menue, Zeitraum-Auswahl bei Verlaeufen
Zu kleine Karten (Konstruktionsfehler):
Bei Schnellzugriff, Status und den Kennzahlen war die Mindesthoehe gleich der
Standardhoehe - und beide zu klein. Bei 96 px Zeilenhoehe blieben nach Kartenkopf
und Innenabstand nur 23 px fuer den Inhalt, eine Kennzahl braucht allein rund
45 px. Deshalb erschien schon in der Grundeinstellung ein Rollbalken, und
kleiner ging es nicht.
- Zeilenhoehe von 96 auf 40 px: die Hoehe laesst sich jetzt fein abstufen.
- Jede Karte hat eine Mindesthoehe, in die ihr Inhalt nachweislich passt
  (Rechnung steht als Kommentar am Verzeichnis).
- Gespeicherte Anordnungen aus der alten Fassung werden beim Laden auf die
  Mindestgroesse angehoben, statt zu flach zu bleiben.

Vorschau im Menue "Karte hinzufuegen":
Der Name allein verriet nicht, was eine Karte zeigt. Jede Karte hat jetzt eine
kleine Skizze (Tabelle, Ring, Balken, Linie, Kennzahl) und eine Beschreibung.
Bewusst gezeichnete Skizzen statt echter Vorschau - sonst muesste das Menue fuer
jede Karte Daten laden.

Zeitraum bei den Verlaufskarten:
Bestandsverlauf, Artikelverlauf und Ein-/Auslagerungen haben eine Auswahl
7 Tage bis 1 Jahr.

Ringdiagramme:
Der Ring klebte oben in der Karte, darunter blieb viel Leerraum. Er fuellt jetzt
die Karte und sitzt mittig.

Geprueft: "npm run build" laeuft durch.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 10:09:05 +02:00
ScarriffleandClaude Opus 4.8 4246e0d98f Fix Startabbruch: 204-Route in dashboard.py brach den Backend-Start ab
AssertionError beim Import: "Status code 204 must not have a response body".

Ursache ist eine Wechselwirkung, die nur in dieser Datei auftritt: dashboard.py
nutzt "from __future__ import annotations". Dadurch ist die Rueckgabeangabe
"-> None" eine Zeichenkette, die FastAPI zu NoneType aufloest und als
Antwortmodell wertet. Zusammen mit status_code=204 - das keinen Rumpf haben darf -
schlaegt die Pruefung beim Registrieren der Route fehl und das Backend startet
gar nicht. Andere Router im Projekt haben dieses __future__-Import nicht, deshalb
funktioniert dasselbe Muster dort.

Die Route folgt jetzt dem bereits bewaehrten Muster aus branding.py: Der
Statuscode steht am Response, nicht im Dekorator. Der Grund steht als Kommentar
an der Funktion, damit die Falle nicht erneut zuschnappt.

Geprueft: Alle uebrigen Routen der Datei haben ein explizites response_model im
Dekorator und sind davon nicht betroffen. Ein Start des Backends konnte ich hier
nicht pruefen - auf diesem Rechner ist weder Python noch Docker vorhanden.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 09:58:01 +02:00
ScarriffleandClaude Opus 4.8 84ce596446 Modulare Startseite: Kartenraster, eigene Anordnung je Benutzer, Auswertungen
Die Uebersicht war fest verdrahtet und fuer alle gleich. Jetzt besteht sie aus
Karten in einem 12-Spalten-Raster, die sich ziehen, in der Groesse aendern und
einrasten lassen (react-grid-layout). "Anordnen" schaltet das frei, gespeichert
wird ausdruecklich - Probieren bleibt folgenlos.

Anordnung:
- Neue Tabelle dashboard_layouts; user_id = NULL ist die Admin-Vorgabe.
- Jeder Benutzer hat seine eigene Anordnung. Admins speichern eine Vorgabe fuer
  alle und koennen sie ueber die Einstellung dashboard_enforced verbindlich
  machen; dann lehnt das Backend das Speichern eigener Anordnungen ab.
- "Auf Vorgabe zuruecksetzen" verwirft die persoenliche Anordnung.

15 Karten (Verzeichnis web/src/dashboard/cards.jsx, neue Karte = ein Eintrag):
Schnellzugriff, Status, Artikelzahl, Artikeleinheiten, Bald ablaufend,
Abgelaufen, Einkaufsliste, letzte Bewegungen sowie sechs Auswertungen
(Ablauf-Ring, Kategorien-Ring, Kategorien nach Zustand, Bestandsverlauf,
Verlauf eines Artikels, Ein-/Auslagerungen).

Diagramme als eigene SVG-Komponenten statt Diagramm-Paket:
- Zustandsfarben (ok/bald/abgelaufen) sind Statusangaben aus den Tokens und
  stehen nie ohne Beschriftung; Kategorien nutzen eine gepruefte Farbreihe in
  fester Ordnung, ab sieben Kategorien wird zu "Andere" gebuendelt.
- Eine Werteachse, duenne Marken, zurueckhaltendes Gitter, Fadenkreuz mit
  Kurzinfo. Hell und Dunkel haben eigene Farbstufen.

Mengen durchgehend in Artikeleinheiten (Glaeser, Packungen, Stueck): Gramm und
Stueck lassen sich nicht addieren, Gebinde schon. Der bisher dreifach
vorhandene Helfer liegt jetzt einmal in services/conversion.py::article_unit.

Der Verlauf wird rueckwaerts vom heutigen Bestand aus den Bewegungen
rekonstruiert. Bekannte Ungenauigkeit, im Code und in der Roadmap vermerkt:
Die Umrechnung nutzt die heutige Packungsgroesse.

Geprueft: "npm run build" laeuft durch. Die neuen pytest-Tests
(backend/tests/test_dashboard.py) konnten hier nicht laufen - auf diesem
Rechner ist kein Python installiert.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 09:48:16 +02:00