Der Decoder-Container mountet /work/in schreibgeschützt und nur
/work/out beschreibbar (Policy-Test); ingest ignoriert Ergebnisse zu
Aufnahmen außerhalb des Manifests (Test). ARCHITECTURE Annahme 106
beschreibt das getragene Restrisiko (DNS im Pod-Netz, Sicht auf den
eigenen Lauf) und stärkere Optionen für später.
Refs #27
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
ApiCore-Whisper: transcribe.py liest das Manifest im RAM-Arbeitsordner,
dekodiert mit festem Demuxer je Endung, misst die Länge vor dem
Abschreiben (TOO_LONG > 240 s, DECODE_FAILED bei Länge 0), sperrt das
Netz vor dem Modell-Laden und schreibt nur Zähler auf stdout. Image:
python:3.12-slim per Digest, Nutzer 1000, alle Pakete mit Hash
(faster-whisper 1.2.1, av 18.1.0 wegen Inkompatibilität mit av 19),
Modell faster-whisper-small auf fester Revision mit SHA-256 je Datei.
CI-Workflow nur bei Änderungen unter ApiCore-Whisper/, Actions per SHA.
deploy/k8s/components/voice: CronJob alle 2 Minuten mit fetch, whisper,
ingest (gleiche UID, getrennte /tmp-Volumes, damit der Decoder dem
Container mit den Secrets nichts unterschieben kann; whisper ohne
Secrets und Umgebungsvariablen außer WHISPER_CPU_THREADS), Overlay mit
Platzhalter-Digest, ConfigMap-Werte für Sprache, Policy-Tests (55).
Die Anbindung des Anthropic-Schlüssels bleibt offen (Ruling 9).
Refs #27
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Wartende Sprachnotizen werden nach Einträgen und Fotos hochgeladen, je
Notiz ein fester Idempotency-Key; endgültige 4xx markieren die Notiz
mit festem deutschem Text, 401/408/429/5xx/Netz warten auf den nächsten
Lauf, „wird bearbeitet“ wird übersprungen. Warteschlangen-Sheet mit
Abschnitt „Sprachnotizen“ (Verwerfen je Notiz nur für den Besitzer,
alle verwerfen mit Rückfrage, Hinweis bei zu vielen offenen Notizen),
Offline-Band zählt nur wartende Notizen, Abmelden fragt bei wartenden
Notizen nach. Zählen liest keine Audiodaten.
Gates: Frontend lint, typecheck, 2886 Tests.
Refs #27
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
checkout, setup-node, setup-buildx, login, metadata und build-push
laufen jetzt auf dem Commit, auf den der bisherige Major-Tag am
2026-10-16 zeigte (Tag als Kommentar). Ein verschobener Tag kann so
keinen fremden Code mit dem Registry-Token ausführen.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
labels.spec prüft, dass jedes Backend-Ereignis einen Text hat; die
neuen Ereignisse VoiceCaptureReady und VoiceCaptureFailed fehlten.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Recorder (webm/opus oder mp4, 24 kbit/s, Warnung bei 150 s, Stopp bei
180 s oder im Hintergrund; Mikrofon und Wake-Lock werden in jedem Fall
freigegeben, auch bei Fehlern und ausgestecktem Mikrofon) und
IndexedDB v6 mit Store pending-voice (Index ownerUserId); volle
Speicherquote ergibt VoiceStorageFullError ohne Datenverlust; Abmelden
löscht nur die eigenen Notizen.
Refs #27
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Task 8: POST /inspections nimmt voiceCaptureId; die Notiz wird zuletzt
in derselben Transaktion bestätigt (jeder offene Status, Ruling 1),
Inhalt, Audio und Claim gelöscht; fremde oder geschlossene Notiz 409
VOICE_CAPTURE_GONE, anderes Volk 409 VOICE_CAPTURE_COLONY_MISMATCH;
Wiederholung mit gleichem Schlüssel idempotent.
Task 9: Ereignisse VoiceCaptureReady/VoiceCaptureFailed (nur IDs und
Endcodes), Meldungen „Sprachnotiz für … ist fertig“ bzw. Fehlerhinweis
mit nächstem Schritt, Betreiberhinweis bei abgeschalteter Auswertung;
Handler auch im Worker registriert (sonst gingen die Ereignisse
ungehört durch); keine Weiterleitung an Webhooks.
Task 11: Job voice-transcribe mit Phasen fetch (Audio nur im Speicher
entschlüsselt in ein RAM-Arbeitsverzeichnis 0700/0600) und ingest
(Ergebnis gegen Claim geprüft, Transkript versiegelt, Audio gelöscht,
Auswertung geweckt; Stille/zu lang enden FAILED); Ereignisse genau
einmal in derselben Transaktion. Aufbewahrung: Audio 24 h, Inhalt bis
contentExpiresAt, deaktivierte Besitzer, Zeilen 90 Tage; im täglichen
Cleanup inkl. leerer Job-Läufe (Ruling 10).
Gates: Backend lint, typecheck, 4471 Unit-Tests, build; e2e 103 Suites
(einziger Fehler: Wetter-Test gegen echte API).
Refs #27
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
scripts/check-3d-budget.mjs misst den three.js-Chunk (Marker
WebGLRenderer und __THREE__) mit gzip -9 und bricht den Image-Build ab,
wenn er 320 kB übersteigt oder nicht gefunden wird. Heute 299 kB.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Gemeinsame Helfer statt vier Kopien der Teilsuche, zwei Vektor-
Subtraktionen und zwei Zufallsgeneratoren; hasPartSheet an einer
Stelle; ungenutzte setOpacity und points-Ref entfernt. Verhalten
unverändert (1050 Tests).
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Upload (Audio nur per Magic Bytes, versiegelt, 2 MiB), Text-Weg,
Liste, Abruf, Wiederholen, Verwerfen, alle verwerfen, Konfiguration und
Zähler unter /voice-captures. Rechte voice-captures:use plus
inspections:create (eigener Guard, API-Schlüssel ausgeschlossen),
Zähler nur mit system:manage; jede Abfrage auf den Besitzer begrenzt.
Grenzen je Benutzer unter Advisory-Lock (10 offen, 30 je Tag, 10 je
60 s), Wiederholen ebenfalls begrenzt und gedrosselt, Audio für QUEUED
nur unter 24 h. Verwerfen löscht Inhalt und Claim sofort. Keine Inhalte
in Log oder Audit. Wächter gegen Sprachinhalte in Suche, Export und
Berichten erweitert.
Refs #27
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- zweiter Finger während Druck oder Ziehen bricht ab (kein Ablegen)
- Druck auf ein Rähmchen im Editor erreicht die Zarge nicht mehr
- Canvas-Rechteck bei Scroll neu gemessen, Motion-Ziele nur bei
Abstand oder bewegtem Teil neu berechnet, toter Re-Export entfernt
Refs #21
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Teile aus der Palette in die Szene ziehen: Stapel öffnet sich, erlaubte
Plätze leuchten, Vorschau rastet ein; erlaubte Plätze per
applyHiveOps auf einer Kopie (gleiche Regeln wie der Server)
- Vorhandene Teile verschieben, auf „Entfernen“ ziehen; Einsätze aus
offenen Zargen ziehen, dünne Teile mit Mindest-Trefferhöhe
- Drücken auf ein Teil dreht das Modell nie (Prüfung erst beim Dreh-
Beginn, passend zur Ereignisfolge von R3F); Abbruch mit Esc, Fenster-
wechsel oder Maus ohne Taste
- Zoom 0,6–2,5 per Knöpfen (Fokus bleibt an den Grenzen), Rad und Pinch
- Verdunster verschieben fragt nicht mehr nach Behandlungsende;
Ansage „… verschoben“; gemeinsame Helfer fresh-part und feeder-slots
- Neuzeichnen nur bei Zeigerbewegung, geteilte Materialien
Gates: Frontend lint, typecheck, 2804 Tests, build; 3D-Chunk 292 kB gzip.
Review (Opus) und Browserprüfung Maus/Touch.
Refs #21
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
npm run export:voice-vocabulary schreibt ApiCore-Whisper/vocabulary.txt
aus whisperInitialPrompt(); ein Wächter vergleicht Datei und Quelle im
Monorepo (im Backend-Image-Build ohne Nachbarordner übersprungen).
Datei mit LF über .gitattributes; scripts/ aus dem Nest-Build
ausgeschlossen.
Refs #27
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Task 5: Schlüsselbund-Zweck voice (VOICE_ENCRYPTION_KEY), AES-256-GCM-
Hilfen für Text und Bytes mit AAD capture:<id>:<art>, voice-config
(Modell, Tageslimit, Aufnahme, Whisper-Grösse) und anthropic-config
(nie geloggt, nur worker/all), Startprüfung verlangt den Schlüssel in
Produktion in jeder Rolle, rekey versiegelt offene Sprachnotizen neu
(Audio einzeln gelesen, CAS mit Status), Secret-Vorlagen und
ensure-secrets.sh ergänzt.
Task 6: Text-Normalisierung und Bereinigung (Steuer- und Formatzeichen,
Codepoint-Kürzung), Zitatprüfung mit Dezimalzahlen als ein Wort
(Ruling 7), Felderliste passend zu CreateInspectionDto, versiegeltes
Transkript mit Grenzen, Fachwörter und Skalen mit Paritätstests.
Rollout: Ein Image mit diesem Stand startet in Produktion nur mit
VOICE_ENCRYPTION_KEY in apicore-backend-secret (ARCHITECTURE Annahme 22).
Gates: Backend tsc, eslint, 922 Unit-Tests, e2e 100 Suites / 1004 Tests.
Refs #27#28
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
RetryPolicy mit retryDelayMsFor (bestehende Aufrufer unverändert),
Phasen transcribe/extract mit eigenen Wartezeiten (5/15 min bzw.
30 s/2 min, 3 Versuche) und Stale-Fenstern (30 bzw. 5 min). Claims per
FOR UPDATE SKIP LOCKED mit claimToken als Compare-and-Set, nur
Notizen mit gültigem Inhalt; Erfolg setzt den nächsten Status und nach
dem Abschreiben attempts = 0; Fehler meldet 'lost', wenn die Notiz
inzwischen bestätigt oder verworfen wurde; Aufschub mit Grund;
Stale-Freigabe transaktionsfähig mit den beendeten Notizen als
Rückgabe. Eine Quelle für die 14 Fehlercodes.
Refs #27
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
VoiceCapture und VoiceCaptureAudio nach Spec §3 mit CHECKs
(audioBytes 1..2 MiB, durationSec 1..240, CONFIRMED genau mit
inspectionId, audioMime nur bei AUDIO), Recht voice-captures:use für
ADMIN, BEEKEEPER und STAFF, Sperrreihenfolge mit VoiceCapture am Ende,
Ausschluss aus dem Backup mit Begründung, Wächter gegen Suche und
Export.
Refs #27
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Umbauen: Fokus nach Rückgängig/Wiederholen am Ende auf den anderen
Verlaufsknopf, nach Entfernen auf die Nachbarzeile (sonst Überschrift)
- Umbauen: Hinweis „Beute wurde inzwischen geändert“ zuverlässig, der
Stand wird bei Fokus/Sichtbarkeit und alle 30 s neu geladen, solange
ein Entwurf besteht; Editor-Wurzel ist <main>
- Verlauf: Futtertaschen-Füllstand als „Flüssigfutter 1,5 l“, Entfernen
als „Futtertasche entfernt“; Gliederung ohne leere Werte („Boden · /“)
- Durchsicht: Rückgängig der letzten Änderung behält den Schalter
„Liste“ und setzt den Fokus auf die Überschrift der Änderungen
Refs #21#22
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Beute L3 Runden 2+3 und Review-Fixes:
- Glas-Holo ohne warmes Leuchten, Wanderboden-Fuss, Fokusflug mit
Geist-Ansicht, Inhalte im Fokus, Rähmchen mit Art-Farbe beim Anheben
- Hinweis-Geometrie je Zarge nur einmal je Layout gebaut; Drehen mit
Pointer-Capture, endet auch bei Loslassen ausserhalb (rig-pointer.ts)
- Tiefenunschärfe ab 55 fps, Leistungsprobe bei jeder Bewegungsstufe,
Context-Loss-Listener aufgeräumt, Trefferboxen als skalierte Einheitsbox
Beute L4 Umbauen (/colonies/[id]/hive): Entwurf mit Basis-Schnappschuss
beim ersten Schritt, Rückgängig/Wiederholen, Hinweis bei Serverstand-
Änderung, Konfliktblatt.
Beute L5:
- Durchsicht-Abschnitt „Beute“: Aufbau-Änderungen in der Durchsicht,
Blatt-Formulare stoppen das Submit-Bubbling durch Portale (sonst
speicherte „Speichern“ im Blatt die ganze Durchsicht), Fokus nach
Rückgängig, Folgevorschläge beim Speichern
- Offline: Beute im Offline-Paket, Warteschlange pending-hive-changes
(DB v5), Abarbeiten in Reihenfolge, keine Online-Änderung überholt
wartende, Hinweis bei abgelehnter Änderung mit „Warteschlange öffnen“
T7 L1 Diktat: Spracherkennungs-Kapsel (Web Speech API), Einwilligung
standardmässig aus, Diktat-Knopf an Freitextfeldern, Seite
„Sprache & Datenschutz“; axe-core als exakte devDependency.
Backend: NotificationsService liest Empfänger und schreibt in einer
Transaktion mit FOR KEY SHARE (paralleles Löschen eines Benutzers
führte zu P2003 und verzögerten Vorschlägen; Ursache der wackeligen
Endtests). Zwei e2e-Prüfungen gegen geteilten DB-Zustand entschärft.
Gates: Frontend lint, typecheck, 2740 Tests, build; Backend lint,
typecheck, 4266 Unit-Tests, build; parallele e2e 3x grün (978).
Browser-Prüfung 1280/390 px, Tastatur, offline, reduced motion.
Refs #19#20#21#22#27#32
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
The seed checked only SEED_ADMIN_EMAIL. After the real admin changed
their address, every start created a second ADMIN with a random
password and wrote that password to the log. The seed now skips as
soon as any user exists and never prints an operator-supplied
password. seedAdmin() is exported for tests; main() runs only when the
file is executed directly.
Refs #45
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- 3D dependencies (three, react-three-fiber, drei, postprocessing,
motion), exact versions, supply-chain checked; scene loaded only on
demand on the colony page (283 kB gzip), SVG stays as fallback
- glass hologram per owner design 2026-10-16: true Liebig Zander
shapes (grips, rebate, Wanderboden foot), cool blue edge glow,
click any part -> it flies to the front while the rest stays as a
faint ghost; focus shows content (feed levels, Bausperre, pockets);
frames rise on hover and take their type look (colour + comb pattern)
- full-bleed card with frosted overlays and glass buttons
- Futtertasche (FRAME_FEEDER): new part kind occupying frame slots
with a fill level, catalog row, validator rules in both ports,
outline, sheets, follow-ups, settings
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- lib/hive: frontend port of the hive domain (ops, validator, facts,
layout, follow-ups, describe texts, reason texts) running the backend
fixtures, API client
- hive card on the colony page (same height as Stammdaten): SVG view,
accessible outline tree, frame/part sheets with optimistic save,
conflict sheet, history with past states, init from template or
another colony, inspection draft mode
- follow-up toasts prefilling feeding and treatment forms (treatment
end only with an open treatment)
- settings page "Beutensysteme" (hive-systems:manage)
- NumberStepper decimal steps, reactive motion level hook, holo tokens
- spec: owner design decision 2026-10-16 (glass hologram, focus mode)
The SVG view is the fallback; the 3D scene follows in L3.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Between 22:00 and 24:00 UTC the UTC day and the Berlin day differ, so
the specs failed at night.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Neues README im Root mit Zweck, Aufbau, Start, Prüfbefehlen, Betrieb und
Projektstand (Issues #44, #43). Backend- und Frontend-README ersetzen die
Framework-Vorlagen. CLAUDE.md: Ordnerstruktur und echte Arbeitsweise
(main, gebündelt pushen, Rollout nur mit Freigabe). ARCHITECTURE §7/§8:
Live-ConfigMap, ensure-secrets.sh, korrektes Admin-Seed-Verhalten.
.env.example um BACKUP_MAX_IMPORT_BYTES und SEED_ADMIN_* ergänzt.
Statuszeilen in Plänen Rückgrat, T3–T6 und Specs Fahrplan, T7.
Refs #42
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Each colony can now hold its hive setup (Ist-Zustand) as validated
JSON with an append-only change log:
- catalog: hive systems and part types with dimensions, placement
rules and state fields; Liebig-Zander 10 seeded with fixed ids
(owner's equipment incl. Bausperre, Futterzarge, Nassenheider)
- pure domain module (operations, validator, derived facts) with
shared JSON fixtures the frontend runs too
- API: catalog (hive-systems:manage), setup create/change/history/
state at a day (colonies:read/update), 409 HIVE_CONFLICT with the
current state, idempotent writes, HiveChanged event
- inspections can carry hive ops saved in the same transaction
- backup export/import of the new tables (backup wins per colony)
- docs: ARCHITECTURE section 13, Annahmen 95-100
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Owner wish 2026-10-15: colony page shows the hive as an animated
holographic 3D model of its current state, editable in the card, in a
full-screen editor and through inspections. Covers part 1 (data) and
part 2 (3D view); AI parts follow with their own specs. Reviewed
against the codebase (permissions, locks, offline drain, backup).
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- api-client accepts 2xx answers without body (revoke showed an error)
- authority notices: sentOn in the future is rejected (Berlin day) on
create, mark-sent and PATCH; dialog defaults to the Berlin day
- service worker never caches /api/v1/site-settings
- treatments/new no longer overflows at 390 px (min-w-0 on form
wrappers); dose with more than 3 decimals is an error, never rounded
- medicines: expired lots flagged, disposal over the rest warns
- label designer: side panel at lg, sample values instead of
placeholders, compliance panel reopens on lg, labelled zoom buttons
- skip link to #main-content, headings on no-access pages
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
The frontend image build runs ~240 spec files on a loaded runner; a first
render in apiary-colonies-card.spec missed the default 1 s findByRole
window in CI run 711 while it passes locally. 5 s stays well below the
15 s test timeout, so real hangs still show.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Treatment form and bulk entry: catalogue medicine or free text, lot with
oldest preselected on create, lot fields from the lot, dose with at most
3 decimals, catalogue prefill with explicit take-over of the withdrawal
period on a medicine switch, ?editTreatment deep link.
- Authority notices: list with year filter, create with draft dedupe link,
detail steps with PDF download and mark-sent, settings with MM-TT check,
digit-rule editor and "Annahme – bitte prüfen" notes; COMPLIANCE label
in notifications and Telegram categories.
- Restricted zone: page with active/planned/ended, create and end dialogs
(endsOn inclusive), red band at the apiary, guards on move, merge, edit
and new colony, override only with compliance:manage, harvest warning.
- ARCHITECTURE.md §12.11, Annahmen 91–94; TASKS.md T6 done with open items.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Values were passed as kubectl -p argument and via environment variables,
both visible to other processes. Node now generates the values and writes
the patch into a private temp file (umask 077, removed on exit); kubectl
reads it with --patch-file. Only key names and modes are on the command
line. Also fixes the first key being dropped (node -e argv offset).
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- overlays/encry configmaps now carry the live cluster values (runbook
step 1) plus OIDC_CLIENT_ID; kubectl diff against the cluster is empty.
- deploy/k8s/scripts/ensure-secrets.sh adds missing secret keys without
touching existing ones (random encryption keys, empty Open-Meteo key,
TELEGRAM_BOT_TOKEN=dummy) and never prints values.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
The frontend image build only sees ApiCore-Frontend, so reading
../ApiCore-Backend/test/fixtures/placeholder-keys.json failed with ENOENT
in CI. Same pattern as the labels and bulk specs: compare in the monorepo
checkout, skip inside the image build.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Readiness: all 18 checks (medicine lots, treatment log gaps, labels,
harvest withdrawal), medicine subjects masked without
veterinary-medicines:read, report PDF (counts only), summary cached 60 s,
409 codes READINESS_*.
- Frontend: /compliance page with acknowledge/revoke, dashboard line and
nav; veterinary medicine catalogue, lots, movement book with inventory
and disposal; label designer check panel, print card with gap list and
proof print, food business master data, product legal name.
- Service worker: cacheability is path-based (the backend sends no-store
on every answer); compliance, medicine, restriction, notice and label
check/print paths and restricted document kinds are never cached.
- ARCHITECTURE.md §2, §12.10–12.11, Annahmen 86–90.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Authority notice PDFs: pure model plus renderer, TSK count in A4
landscape, FEHLT for missing particulars, stored totals with correction
note; GET /authority-notices/:id/pdf with compliance:read.
- Compliance notifications in category COMPLIANCE: herd change and TSK
reference day, at most one unread per topic and user (advisory lock);
daily rule TskReferenceDayReached with Berlin day and catch-up; handlers
in API and worker.
- Readiness core: live evaluation of seven pure rules, fingerprinted
acknowledgements that reopen when revoked or stale, subject masking,
read-only GET paths; READINESS_RULES injectable; kept out of the worker.
- e2e hygiene: no unscoped deletes, seeded settings rows saved/restored.
- ARCHITECTURE.md §12.8–12.10, §10 handler rows, Annahmen 78–85.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Treatments book their dose from a medicine lot (delta booking on edit,
bulk all-or-nothing, idempotent); Bestandsbuch columns from the lot,
FEHLT exactly where treatmentLogGaps reports a gap; Bestandsbuch export
needs veterinary-medicines:read.
- Label printing: 409 LABEL_PARTICULARS_MISSING on gaps, proof print with
watermark, PRINT audit before the PDF, check endpoints; digit rules only
from the seeded ComplianceSettings row (fail closed).
- Restricted zone (Berlin day): create, update, move, merge and CSV import
blocked inside an active restriction; override with reason and
compliance:manage, audited, never stored on the colony; split, harvest
and dissolve stay free; restriction details only with compliance:read.
- Authority notices: settings, herd snapshot, registration/herd change/TSK
notices with one open draft per kind under the settings lock.
- Label check refinements (geometric reading order, number|unit pairs).
- ARCHITECTURE.md §12.6–12.8, Annahmen 71–77; spec §5.6 Berlin day.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Veterinary medicine catalogue, lots and append-only movement ledger
(rest always computed; corrections as counter-bookings; inactive
medicines take no new stock; unit must match on lot moves).
- Documents of medicine lots, authority notices and restrictions need the
domain right on top of documents:* (403 before any lookup).
- Label particulars: pure checkLabel with six mandatory gaps and four
warnings, AFM font metrics, Berlin-day dates, geometric reading order
for split addresses, new master-data fields with row-locked updates.
- Herd events ColonyCreated, ColonyMoved, ApiaryChanged and
TskReferenceDayReached (ids only, never forwarded to webhooks); CSV
import emits ColonyCreated with audit.
- ARCHITECTURE.md §12 Compliance (T6), Annahmen 59–70.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Migrations (additive): veterinary medicines with lots and movement
ledger, treatment medicine/lot/dose links, label particulars, authority
notices, apiary restrictions, compliance settings and acknowledgements;
notification category COMPLIANCE, added to non-empty Telegram lists.
- Permissions veterinary-medicines:* and compliance:* with role grants.
- LOCKABLE_TABLES: Apiary locks before Colony; T6 tables appended.
- Backup exports and restores the new tables in FK order; unsealed files
skip them and report dropped medicine links; an open TSK draft in the
target wins.
- describeNullContracts shared helper; Telegram backlog e2e no longer
depends on the time of day.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Honey batch create omits an empty batch number (was 400); product form
labels the legacy total weight, detail shows jar weight and shelf life;
bottling hints next to their fields, values cleared on product change,
post-save water-content wording; withdraw reason placeholder not
selectable; honey batch actions offline-disabled with reason, 44 px.
- Offline signal: one slow request no longer flips the app offline; two
consecutive timeouts do, any HTTP answer clears it, aborts never count.
- Phenology sheet as a side card on desktop with focus return; confirm
dialogs as alertdialog; step-up texts in du-form ("Das Passwort stimmt
nicht."); backup import gives its disabled reason; missing event labels
plus a guard test; profile buttons 44 px.
- GET /phenology/state returns intervalDays via the shared
inspectionIntervalOf (autopilot, digest, season state use one source).
- T6 plan aligned with the code after T4, plus owner decisions of
2026-10-15 (Berlin day for restrictions, COMPLIANCE category, override
without step-up).
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Bottling form: jar size and best-before from the product with source,
differs-hints before saving, prefill status, water-content confirmation
naming the measured value, idempotency key renewed on body change;
product form gets shelf life (months) and whole-gram jar weight.
- Telegram frontend: link card with step-up dialog, t.me-only deep link,
status polling, unlink; system Telegram card with receiver/sender state
and confirmed retry; StepUpDialog shared with the backup import.
- Reauth calls never refresh-and-retry on 401 (no double lockout count);
German texts for expired session and 429; service worker never caches
Telegram, notification settings and system responses.
- Telegram backend round 2: shared link removal on every disconnect path,
lease released on shutdown, connection errors not counted as poison.
- ARCHITECTURE.md §2, §6 (Annahmen 56–58), §10, §11.12, §11.13; TASKS.md T4.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Bottling quality: water content above the legal limit (strict >) answers
409 CONFIRMATION_REQUIRED bound to the measured value; prefill for jar
size, best-before (shelfLifeMonths, month-end clamping) and water content;
impossible dates give 400; water-content checks in src/common/quality.
- Frontend phenology: "Entwicklung melden" sheet, IndexedDB v4 queue,
season header on the apiary page, confirmed revoke for all apiaries.
- Frontend honey chain: "Vorschlag aus …" hints with explicit take-over,
prefill status note, movement ledger with withdraw/correct dialogs.
- Telegram: link tokens (hashed, single use), private-chat receiver with
lease and offset CAS, bounded poison-update skip, Retry-After, pending
deliveries skipped on disconnect; system API for deliveries and retry;
worker-only secret wiring and manifest policy tests.
- ARCHITECTURE.md §7, §9, §10, §11.11–11.13, Annahmen 51–55.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Phenology reports API with corrections and revokes; recompute per
apiary via PhenologyRecomputeRequested, allowBackward for corrections;
season changes are announced in the morning digest (changes since the
previous digest, each once)
- Season autopilot A4-A10 with per-apiary sync (Apiary before Colony
locks) and WinterCheckRecorded
- Honey batch ledger at runtime: initial amount, BOTTLING bookings,
manual movements, one gram rounding, untracked bottlings never book
- Telegram delivery: planning, Berlin delivery times incl. DST, quiet
hours, permission re-check and stale skip at send time, rate limits,
cleanup of old deliveries
- ARCHITECTURE.md: assumptions 47-50 and updated sections 11.x
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Weather history from Open-Meteo behind an adapter: worker loop
phenology-weather with backoff, Retry-After, User-Agent, chunked
writes and forecast-to-archive correction
- Phenology: growing degree sum per DWD, phase from reports, GTS and
calendar, calibration from previous years, daily job step,
SeasonService reads the stored state
- Honey chain: prefill for extractions and batches (proposals only),
plausibility and withdrawal warnings, Extraction lock, chain events
and batch.created webhook from HoneyBatchRecorded
- Telegram base: schema, NotificationDelivery outbox, hardened bot
client, startup check without crash, no-network-outside-loops guard
- ARCHITECTURE.md: sections 11.11-11.13, assumptions 42-46, env table
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Phenology reports, apiary phenology state and weather days with
permissions for system roles
- Honey movement ledger: BOTTLING movements and initialAmount backfilled
from existing bottlings with the same gram rounding; the migration
reports counts and aborts if the ledger invariant fails
- Shelf life and event catalog columns; backup export/import of the new
tables, old bundles keep importing, invariant check after imports
- e2e: test timeout 20 s (app start in hooks exceeded 5 s under load)
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
A userEvent-heavy colony form spec took 5.1 s inside the Docker build
and hit the 5 s default.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
CI builds have no .env file; the handler discovery test compiled the API
module without JWT_ACCESS_SECRET when no earlier suite in the same jest
worker had set it.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Withdrawal wording from one backend helper everywhere ("bis
einschließlich TT.MM. – ab TT.MM. wieder erntefähig", year when it
crosses New Year); withdrawal-status returns lastBlockedDay and the
ready text, the frontend no longer computes dates
- Calendar events name colony or apiary, withdrawal events carry
product and detail; compact withdrawal rows on mobile
- One bottling warning per colony and treatment; pre-save hint
- Offline: online-only actions disabled with a reason, cached data
shows its timestamp, open sheets block submit offline
- Mobile: no horizontal scroll at 360 px, touch targets >= 44 px,
toasts and the add button no longer cover content
- Cleanup sheet without destructive preselection; natural sort of
colony codes; collapsible automation rule cards; varroa list newest
first with chart axes; text fixes
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- In-app reminders: task due (skips rescheduled tasks, catch-up wording)
and a per-user morning digest step in the daily job
- Frontend: "Als Nächstes" on the colony page, "Heute" on the dashboard,
inspection-due card, season chip, apiary overview fields, cleanup
sheet for ended colonies
- Frontend: plausibility warnings that stay until dismissed, withdrawal
spans in the calendar, notification bell with categories and links,
notification settings in the profile
- ARCHITECTURE.md: T4a sections 11.1-11.10, assumptions 31-41, handler
table and daily job step order
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Colony end releases the queen in the same transaction; open
suggestions become obsolete and a CLEANUP suggestion lists open
tasks and the queen; apply-cleanup validates evidence and blocks
reactivated colonies
- Withdrawal chain: plausibility warnings on harvest and bottling,
conflict notifications also when a treatment is entered or corrected
after the harvest (naming bottled lots), withdrawal spans in the
calendar and M365 sync, Berlin-day end events
- Next 3 steps per colony and the "Heute" endpoint with ranking and an
explicit merge table by intent
- Inspection-triggered rule suggestions follow the colony's latest
inspection (resolve when the finding is gone, refresh when it stays)
- Frontend: colony card takeover in the inspection form, feed status as
condition, occupied frames 0-40; legacy offline entries keep syncing
- Notifications and preferences are deleted with their user
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Automation rules propose suggestions instead of creating tasks;
notify per rule config; corrected varroa measurements refresh or
resolve their suggestion
- Inspection values can be taken over into the colony card
(occupiedFrames 0-40, strength deprecated); warning counts use the
same visibility rule as the notification list
- Biotech chains: swarm check after queen cells, queen acceptance
check, split schedules (raise own, queen cell, queen)
- Season autopilot core with A1 inspection due, A2 varroa measurement,
A3 recheck after treatment; colony sync and daily step
- Frontend: suggestions page with groups, bulk accept, undo, paging,
focus management and permission gating
- Frontend: varroa traffic light from seasonal bands, sample size,
one range request for the colony chart
- Notifications: mark-read answers 404 for rows the user may no
longer see
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Schema III: per-user notifications (broadcast fan-out migration,
category, link, requiredPermissions filter), notification preferences
and settings endpoints, calendar withdrawal switch, occupiedFrames
- Suggestions API: list/summary with permission visibility and paging,
accept/dismiss/reopen incl. bulk, SuggestionView on all responses
- Varroa traffic light by season and method, young colonies, treatment
effect window, sample-based percentage; shared withdrawal-window helper
on Berlin calendar days; GET /varroa-measurements/thresholds
- ColonySplit and QueenAssigned domain events with calendar and webhook
handlers
- Outbox claims compare against now() rounded to timestamp(3); fixes the
flaky e2e claims of rows written with SQL now()
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Rule configs with typed parsers and registry; Berlin day helpers
- Schema: T4 enums, Suggestion table, feed vocabulary (feedStatus as
ColonyCondition with legacy text mapping), split queen source,
varroa sample size; migration harness for data migrations
- SuggestionsService: propose/refresh/resolve/obsolete/expire,
accept/dismiss/reopen incl. bulk, lock order, concurrency e2e
- SeasonService: phenology-based segments, inspection/varroa due dates,
queen age; TimeEventsStep with JobPartialFailure
- Frontend: per-rule settings forms with visible value sources
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Brings the ERPNext inventory export (T8) into main after its review:
strict seit format, settle window on the DB clock, BatchMovement
(createdAt, id) index, one scope per new API key and the failed-attempt
lockout on the shared throttler storage. Conflicts resolved in the
permission catalog (System and ERP-Anbindung groups) and ARCHITECTURE.md
(lockout is Annahme 30).
Gates on the merged state: backend lint, typecheck, 1909 unit, 471 e2e,
build, no schema drift; frontend 929 tests, lint, typecheck, build.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Platform backbone (plan 2026-10-13-plattform-rueckgrat), all tasks reviewed:
- webhooks with delivery table, retries and stable delivery id
- calendar handlers; a finished task removes its Outlook event
- tasks, automations, inspections, varroa, treatments, feedings,
harvests, colonies, wintering, stock and bottling record domain events
in the same transaction; legacy post-commit calls removed
- row locks through one helper (lockRowForUpdate, lockRowsForShare,
lockRowsForUpdate)
- job framework with JobRun, cli exit codes, cleanup, task templates,
daily evaluation, hourly app-backup and the rekey job
- system API and page "Verwaltung > System" with retry
- @nestjs/schedule removed, guard tests against in-process schedulers
Field capture T3 (plan 2026-10-13-t3-erfassung-am-stand), all tasks reviewed:
- inspection photo offline without metadata
- bulk capture for treatments, feedings and varroa (backend + frontend)
- offline stand mode with service worker, offline pages and packs
- offline session, cold start and logout on shared devices
Docs: ARCHITECTURE.md assumptions and sections updated, TASKS.md.
Gates: backend lint, typecheck, 1770 unit, 420 e2e, build, no schema
drift; frontend lint, typecheck, 913 tests, build. Not rolled out.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Brings the ERP inventory export up to date with the platform backbone and
field capture work. Conflicts in ARCHITECTURE.md, the API key service and
the API key settings tab resolved by keeping both sides:
- read and write keys stay separate; erp-inventory:read is read-only and
cannot be combined with a write scope
- the failed-attempt lockout counts in the shared ThrottlerHit table so it
holds across API pods
- the UI offers only scopes the user holds and creates one scope per key
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Platform backbone (plan 2026-10-13-plattform-rueckgrat), reviewed tasks:
- run roles api/worker/job/all with shared startup checks (Task 1)
- schema and migration for DomainEvent, deliveries, JobRun, ThrottlerHit (Task 2)
- audit, notifications and domain events written in the same transaction (Task 3)
- dispatcher with SKIP LOCKED claims, retries and stale release (Task 4)
- worker building blocks: tick, LISTEN/NOTIFY wake-up, health (Task 5)
- worker process and entry points (Task 6)
- throttler counters in Postgres (Task 19)
- separate keys per purpose with key ring (Task 20)
- public origin page without exact location (Task 22)
- shop keys split into read and write (Task 23)
- Kustomize base, components, overlay encry, CI validation (Task 26)
Field capture T3 (plan 2026-10-13-t3-erfassung-am-stand), reviewed tasks:
- brood frames, read endpoints, apiary overview and offline pack
- network basics, offline queue v3, navigation sheets, form building blocks
- new inspection with last-time hints and weather, varroa per day,
treatment prefill, apiary page with round
- inspection photo (Task 10) is implemented, review pending
Not rolled out. Remaining tasks follow in later commits.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Backend
- Backup covers every Prisma model (bundle v3); a guard test fails for any
new model that is neither backed up nor explicitly excluded. Unsealed
bundles may only add rows to 27 legacy master/field tables and never
overwrite; counters, ledgers, settings, roles etc. need a sealed file.
Shared import limit BACKUP_MAX_IMPORT_BYTES (default 50 MB, max 256 MB)
with warnings in log and UI; replaced document files are removed after
commit.
- Post-commit side effects (automation, calendar sync, webhooks) run via
runAfterCommit and never turn a committed write into a 500, so retries
with the same Idempotency-Key no longer create duplicates.
- Calendar outbox is multi-replica safe: per-entry claims with stale
takeover, revision-based settle, new Graph event only on 404/410,
transactionId on create, 30 s Graph timeout, URL-encoded ids, GUID
validation for groupId, "run now" in background and resets FAILED.
- Automation tasks are deduplicated under a row lock on the rule.
- Update DTOs follow one null rule: omitted = unchanged, null clears a
nullable field, null on a required field is a 400.
- Varroa mites/day is recomputed on correction; withdrawal status is
checked against the harvest date (?at=), bounded colonyIds; inspection
create is audited; updates re-enqueue calendar entries.
- Dashboard, statistics and apiary/colony guards treat WEAK, CONSPICUOUS
and QUEENLESS colonies as living.
- Prisma errors and body-parser errors are logged without query values or
request bodies.
- Migration 20261012100000_calendar_sync_claims; schema index name aligned
with the existing database object.
Frontend
- Sticky save bar sticks again (no scroll container ancestors, card uses
overflow-clip), safe-area insets, scroll-padding, FAB hidden on forms,
sticky desktop sidebar.
- Inspections record yes / no / not checked instead of silent defaults.
- All living colonies are selectable; shared RecordSelect, EnumSelect,
ChoiceGroup and TriStateField replace per-page copies; selects show
names instead of ids; labels point at real controls.
- Emptied optional fields are sent as null (emptyToNull) so every field
can be cleared; dates default to the local day and are sent via
toApiDate.
- Harvest withdrawal warning uses the harvest date; backup page warns when
a backup outgrows the import limit and lists skipped tables.
Build and docs
- Docker builds fail unless lint, typecheck and unit tests pass.
- ARCHITECTURE.md updated (assumptions 9-12, 19-21, section 8) and spec
docs/superpowers/specs/2026-10-12-plattform-rueckgrat-und-fahrplan-design.md
for the backbone (api/worker/job roles, domain event outbox) and the
feature roadmap T3-T7.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Der Server lehnt Rechte ab, die der Anlegende nicht selbst hat. Das UI
bietet jetzt auch nur diese an (z. B. kein shop-sync:write ohne eigenes
Recht) und erklärt, wenn gar keines übrig bleibt.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Befunde aus dem Sicherheitsreview des Branches:
- MEDIUM: Die Sperre lief vor der Schlüsselsuche und zählte auch falsch
aufgebaute Tokens. Wer die gemeinsame Adresse (Cluster-Ausgang, Proxy)
nutzt oder per X-Forwarded-For vortäuscht, hätte ERPNext und den Shop
15 Minuten aussperren können. Jetzt: erst Suche über den Hash; ein
gültiger, nicht widerrufener, nicht abgelaufener Schlüssel kommt immer
durch; 429 nur für unbekannte, widerrufene und abgelaufene Schlüssel;
falsch aufgebaute Tokens sofort 401 ohne Zählung. Beim Start warnt
ApiCore, wenn TRUST_PROXY in Produktion fehlt (im Cluster gesetzt).
- LOW: Bei vollem Speicher verdrängt der Limiter zuerst nicht gesperrte
Einträge, gesperrte erst zuletzt.
- LOW: Bewegungs-Cursor mit Jahr außerhalb 2000-2100 ergab 500, jetzt 400.
- LOW/INFO: Vertrag legt fest, dass ERPNext Produkte und Chargen bei jedem
Lauf vollständig neu liest (Voll-Spiegel) und Bewegungen inkrementell;
remainingQuantity ist live, Bewegungen kommen 5 Minuten verzögert.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Wie der Throttler seit 886b4e7: die Sperre nach abgelehnten Schlüsseln
zählt über rateLimitSubject, damit wechselnde Adressen im eigenen
IPv6-/64 das Budget nicht zurücksetzen. Im Log steht weiter die Adresse.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Bisher vergab die Oberfläche fest shop-sync:read. Jetzt sind alle
freigegebenen Rechte ankreuzbar (Shop lesen, Shop-Buchungen,
Inventar-Spiegel für ERPNext); vorausgewählt bleibt wie bisher nur
Shop lesen, ohne Haken lässt sich nichts anlegen. Die Liste zeigt, was
ein Schlüssel darf, und der Hinweis nach dem Anlegen nennt das Zielsystem.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Datenquelle für den nächtlichen Inventar-Abgleich in ERPNext (ERPNext-
Ausbau T8, Entscheidungen E2/E3). Neues Recht erp-inventory:read, auch als
API-Schlüssel-Scope; es öffnet nur diese vier GET-Endpunkte:
- /api/v1/export/inventar/produkte: Artikel (ohne Beschreibung)
- .../chargen: Abfüllungen und Herstellungschargen mit Losnummer, MHD,
Ausgangsmenge, Rest aus dem Bewegungsbuch, Füllmenge g und Sorte
- .../bewegungen?seit=&nach=&limit=: Shop-Verkauf, Retoure, Entnahmen mit
Grund (HOFLADEN = Vertrauensschrank, EIGENBEDARF, BRUCH, PROBE,
SONSTIGES) und Inventur; Cursor über (createdAt, id), Bewegungen erst
nach 5 Minuten Wartezeit, damit der Cursor keine offene Buchung überholt
- .../ernten?jahr=: Nettogewicht kg je Kalenderjahr, wie die Statistik
Alle Listen stabil sortiert, seitenweise (1-500), Parameter mit
class-validator geprüft, Cursor je Endpunkt streng validiert. Feld-
Allowlists: keine Notizen, Benutzer, Standorte, Rohstoffe. Die Gründe gab
es im Datenmodell schon - keine Migration; das Recht legt der Katalog-
Sync beim Start an und gibt es der Rolle Administrator.
Vertrag mit Feldern, Einheiten, Fehlercodes und Beispielen:
docs/erp-inventar-export.md.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Nach 20 abgelehnten API-Schlüsseln (fehlerhaft, unbekannt, widerrufen,
abgelaufen) in 15 Minuten antwortet ApiCore der Absender-IP mit 429, auch
für richtige Schlüssel - vor jeder Datenbankabfrage. Gleiches Budget wie
der ERP-Export des Shops. Die Zählung liegt wie der Throttler im Speicher
des Pods und ist auf 10.000 Adressen begrenzt.
Abgelehnte Schlüssel werden mit Grund, öffentlichem Präfix (ack_xxxxxxxx)
und IP protokolliert, nie mit dem Schlüssel. Die Suche bleibt über den
SHA-256-Hash; neu sind Unit-Tests für authenticate (fehlend, unbekannt,
widerrufen, abgelaufen, Scope-Filter, IP-Allowlist, Sperre).
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Die cloudflare-only-Annotation ist eine Traefik-Annotation und war mit
ingressClassName nginx wirkungslos. Produktion laeuft auf RKE2 mit Traefik
(Ingresses apicore-backend/-frontend), die Vorlagen nennen jetzt dieselbe
Klasse.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Die live gesetzte Annotation traefik.ingress.kubernetes.io/router.middlewares
fehlte in den Manifesten (Backend und Frontend). Sie schuetzt vor
Direktzugriff am Origin vorbei, nicht vor fremden Cloudflare-Zonen.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Die Kommentare beschrieben noch einen einzelnen Reverse-Proxy-Hop. Sie
beschreiben jetzt die Cloudflare/Traefik-Liste. Beim Start wird einmal
auf info-Level geloggt, ob Hop-Zahl, Liste oder der Default aktiv ist
(nur Anzahl, keine Adressen), damit eine fehlende Produktionseinstellung
auffaellt. Das Standardverhalten bleibt unveraendert.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Der Standard-ThrottlerGuard trackt die volle req.ip. Ein IPv6-Client
kann innerhalb seines /64 beliebig Adressen rotieren und so die
Login-/Refresh-/SSO-Limits umgehen. Neuer ClientIpThrottlerGuard
liefert IPv4 unveraendert, IPv4-mapped als IPv4 und IPv6 als /64-Praefix.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
LAN-Adressen als vertrauenswuerdige Hops waren unnoetig weit: Traefik
verwirft X-Forwarded-For von LAN-Clients ohnehin, sie sind selbst Absender.
Vertraut werden nur noch 10.42.0.0/16 (Traefik-Pods) und die Cloudflare-Netze.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Mit TRUST_PROXY=1 (Standard) war req.ip die Cloudflare-Edge, weil Traefik
sie als letzten X-Forwarded-For-Eintrag anhaengt. Rate-Limits trafen damit
alle Clients derselben Edge gemeinsam, und IP-Freigaben von API-Schluesseln
sahen nie die echte Adresse.
Vorlage und .env.example nennen jetzt "loopback, uniquelocal" plus die
Cloudflare-Netze: Express nimmt den ersten Eintrag von rechts ausserhalb
dieser Netze, also den Client, den Cloudflare angehaengt hat. Vom Client
mitgeschickte Eintraege links davon erreicht die Auswertung nie.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Documents, avatars and the encrypted app backups lived in ./uploads and
./backups inside the container and were lost on every restart. The
backend now mounts the apicore-data volume at /data (configmap paths
/data/documents and /data/backups).
Both deployments pin the image to a CI build (sha-<commit>) with
imagePullPolicy IfNotPresent instead of :latest, run as uid 1000 with
seccomp RuntimeDefault, no privilege escalation and all capabilities
dropped. The frontend gets a liveness probe.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
CronJob apicore-db-backup dumps the database to the Storage Box
(StorageClass smb-backup, SMB 3.1.1 with seal). Keeps 14 daily and
8 weekly dumps.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Requests are set to an explicit 0 instead of being removed, because
Kubernetes defaults a missing request to the limit, which would reserve
even more. Limits stay as an upper bound.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
The floating 18-alpine tag with IfNotPresent kept an old 18.x image
cached on the nodes, so minor releases never reached the cluster.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
With {ART} in the raw material number format (default {ART}-{YYYY}-{SEQ:4}),
beeswax and propolis each count on their own: WA-2026-0001, PR-2026-0001.
Counters live in the new AutomationSequence table (one row per rule and
scope). Without {ART} the rule keeps its single shared counter, since
separate counters behind one prefix would hand out duplicate numbers.
Resetting a rule clears all of its counters.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- RAW_MATERIAL_LOT_NUMBER_FORMAT / PRODUCTION_BATCH_NUMBER_FORMAT fill in the
lot number when it is left empty, like honey batches and bottlings.
- RAW_MATERIAL_LOW: task when the total of a raw material kind drops below
config.minGrams after consumption, withdrawal or inventory count.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Follow-up to a7609ff for the whole frontend: all 30 API helpers in
src/lib now pass path values through encodeURIComponent, so an id taken
from the URL can never leave its own path segment and make the page call
another endpoint with the viewer's token.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Security review finding (client-side path traversal): ids taken from the
URL were interpolated into API paths unencoded, so a crafted link like
/raw-materials/..%2Fusers made the page call another endpoint with the
viewer's token. Ids are now encodeURIComponent-ed; a regression test pins
"../users" to its own path segment.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Pages for raw material lots (beeswax, propolis): list, create in grams
with optional apiary, detail with stock card (harvested / used in
production / withdrawn / remaining) and "used in" batches.
- Production batch form takes the raw material used (lot + grams) and the
batch page shows what it was made from - traceability both ways.
- Sidebar entry "Rohstoffe".
- Removes backend files that were committed into ApiCore-Frontend/src by
mistake in e52a605 (copies of ApiCore-Backend/src/raw-materials).
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Security review findings on the raw material commit:
- Creating a production batch with inputs books raw material off other
lots; production-batch:create alone no longer suffices, the user also
needs raw-material:withdraw (403 otherwise).
- Correcting the quantity of a raw lot or production batch below what was
already consumed, sold or withdrawn is refused (row lock + check in the
same transaction) instead of silently driving the remaining stock
negative; shortfalls go through an inventory correction.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- RawMaterialLot (beeswax, propolis) in grams with origin (apiary,
harvest); stock in the same movement ledger: withdrawals, inventory
counts, and the new movement type CONSUMPTION.
- Production batches take inputs (raw lot + grams); batch and consumption
are one transaction, never more than a lot has left. Deleting a
mistaken batch gives the material back as its own movement.
- Traceability both ways: batch -> inputs, raw lot -> usedIn.
- Movement belongs to exactly one of bottling / production / raw lot
(CHECK); raw material changes do not trigger the shop webhook.
- Permissions raw-material:*, granted to roles that maintain production
batches.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- New article page /products/[id]: remaining stock summed over all
production batches (or bottlings for honey), each batch linked to its
own page for withdrawals and counts; "Bestand ansehen" in the list.
- Production batches can be corrected (lot number, date, quantity,
best-before date, note) via PATCH; the article stays fixed.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Bottling detail shows Abgefüllt / Webshop verkauft / Entnommen / Rest
(negative rest flagged "Prüfen") and the full movement list; buttons
"Entnehmen" (reason, note) and "Inventur korrigieren" (counted
quantity, difference booked).
- Rest column in the bottling list.
- New pages for production batches (list, create, detail with the same
stock card), sidebar entry with its own permission.
- Products get "Art" (honey vs. manufactured goods) and the webshop
category; bottlings only offer honey articles.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
One remaining quantity per batch, kept in ApiCore and always derived:
initial quantity (jarCount / produced quantity) plus the signed movements
in the new BatchMovement ledger, which is never deleted.
- Production batches for MANUFACTURED articles (candles, propolis
products, ...); articles get a kind and a shop category.
- Withdrawals (farm shop, own use, breakage, sample) and inventory
corrections on bottlings and production batches, never more than the
remaining stock; every write locks the bottling or the article row.
- Shop bookings via POST /shop-sync/movements (new scope
shop-sync:write): idempotent per externalId, articles allocated oldest
best-before first, returns mirrored onto the batches of the sale,
invalid entries rejected one by one.
- GET /shop-sync/stock and remainingCount in the shop feed; webhook
stock.changed after every change (ids only, receivers fetch numbers).
- Bottlings and production batches with movements cannot be deleted.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
One remaining quantity per batch, kept in ApiCore: movement ledger for
bottlings and new production batches, shop write-back of paid sales and
returns, withdrawals in ApiCore reflected in the shop via webhook plus a
10-minute reconcile.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>