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]>
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]>
- 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]>
- 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]>
- 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]>
- 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]>
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]>
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]>
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]>
Auth flow and guard order as built now, new architecture decisions
(user-management escalation guard, SSO identity binding, sealed backups),
a section on API security with the remaining known gaps, and the audit
table with commits, new env variables and steps after the update.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- WinterCheck per colony, winter and season (autumn/spring), every field
editable; loss causes limited to the COLOSS monitoring categories.
- Summary per winter and apiary: loss rate lost/wintered, marked
provisional while colonies are not yet checked in spring - an unchecked
colony never counts as survived.
- A loss marks the colony deceased only on explicit request, in the same
transaction and audited.
- Permissions winter-checks:read/create/update granted by migration to
beekeeper and staff roles; winter checks included in backups.
- Page "Überwinterung": next action first, one button per row until the
colony is wintered, German number format.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Treatment records supplier, purchase proof and prescribing vet, and
keeps the apiary the colony stood at on the treatment day.
- Holder name and address in site settings (never in public output;
regression test in public.service.spec).
- treatment-log.pdf: one sheet per apiary, identical treatments grouped,
missing mandatory fields printed as FEHLT, uncertain locations marked.
- Shared PDF table helpers extracted from the record card.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Integrations such as the Wabenhain shop sync no longer need a service
account with a password (and without MFA). An admin creates a key under
Verwaltung > Automatisierung > API-Schlüssel.
- Key format ack_<8>_<43>; only its SHA-256 hash is stored, the key is
shown exactly once. Optional expiry, "last used" timestamp, revocation
takes effect immediately; create/revoke are audit-logged without the key.
- A key is not a user: JwtAuthGuard sets request.apiKey, PermissionsGuard
admits it only to routes whose permission is among its scopes and never
to user-only routes. Scopes come from a short allowlist (shop-sync:read).
- Managing keys requires the new permission api-keys:manage.
- Keys are excluded from backups on purpose; a restore means issuing a new
key.
Additive migration (new table).
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Only Durchsicht had an offline fallback (queue to IndexedDB, sync on
reconnect); Behandlung, Fütterung, Varroamessung, Schwarmkontrolle and
Ernte just failed outright with no signal - exactly the situation a
beekeeper is in at the actual hive. Generalizes lib/offline/db.ts and
sync.ts to six queues (version-bumped IndexedDB schema, existing queued
inspections preserved), wires the same "server rejected it vs. no
network" fallback into all five remaining */new pages, and updates the
sync badge to count/drain all six.
Also: doc-hygiene pass (ARCHITECTURE.md/TASKS.md) to replace the stale
pg_dump/CronJob backup plan with the in-app BackupModule that actually
shipped earlier this session.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
One Entra enterprise application now covers both interactive login and
the calendar sync's client-credentials Graph access, instead of
requiring a second app registration. GraphClientService reads
OIDC_ISSUER_URL/OIDC_CLIENT_ID/OIDC_CLIENT_SECRET (same as SSO) and
derives the tenant ID from the issuer URL's path, rather than three
dedicated M365_CALENDAR_* env vars. The app registration just needs the
"Calendars.ReadWrite" Application permission added alongside its
existing delegated login scopes.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Adds a one-way push (ApiCore -> M365) of Aufgaben/Durchsichten/
Behandlungen/Fütterungen/Ernten/Königinnen-Zuordnungen into the calendar
of a Microsoft 365 group, via the Graph API.
- CalendarSyncModule (app-only client-credentials Graph auth, separate
from the interactive SSO app registration; new env vars
M365_CALENDAR_TENANT_ID/_CLIENT_ID/_CLIENT_SECRET).
- Outbox + minute-interval cron worker (CalendarSyncOutboxEntry) instead
of a synchronous push, since Graph calls are too slow/flaky to block a
domain create request on; failed syncs retry up to 5 times.
- Per-source-type toggle plus a group-ID field, configurable in
Verwaltung > Automatisierung > Kalender-Sync (Verwaltung > Backup, an
env var still wins on those fields where one applies to related config).
- Hooked into Tasks/Inspections/Treatments/Feedings/Harvests/Queens at
their existing create (and Task update/complete) points; entries render
as all-day Graph events to avoid timezone ambiguity.
- Documents the design decision in ARCHITECTURE.md (Annahme 9), since
this is a net-new capability the lastenheft doesn't scope.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Bisher wurde nirgends automatisch ein erster Admin-User angelegt -
prisma/seed.ts existierte, war aber weder im Dockerfile-CMD noch sonst
irgendwo verdrahtet. Auf einer frischen Datenbank haette also niemand
sich einloggen koennen.
- Dockerfile-CMD: migrate:deploy && seed && node dist/main.js
- ts-node/typescript von devDependencies zu dependencies verschoben
(seed.ts laeuft als rohes TypeScript zur Laufzeit, kein
Kompilierschritt), tsconfig.json zusaetzlich ins Runtime-Image kopiert
- seed.ts gegen Unique-Constraint-Race bei mehreren gleichzeitig
startenden Pod-Replicas gehaertet (P2002 abgefangen statt Absturz)
- Login-Banner im Log nur beim tatsaechlichen Erst-Anlegen (URL aus
FRONTEND_ORIGIN, Benutzername, Passwort) - bewusst nicht bei jedem
Neustart, sonst wuerden veraltete/geaenderte Zugangsdaten weiter
geloggt und Credentials landen unnoetig dauerhaft in den Logs
Seed ist bereits idempotent per E-Mail-Check (auch bei deaktivierten
Usern), UsersService.assertNotLastSystemRoleUser verhindert ohnehin
das Deaktivieren des letzten aktiven Admins - kein Aenderungsbedarf
dort, siehe Konversation.
Verifiziert: tsc/eslint clean, 297/297 Tests, Build clean. Migration
und Schema live gegen die lokale DB bestaetigt (ueber WSL direkt, da
Windows->WSL-Portweiterleitung weiterhin kaputt ist); der eigentliche
Seed-Lauf selbst konnte nicht End-to-End gegen die DB verifiziert
werden (gleiches Verbindungsproblem), Banner-Ausgabe isoliert
smoke-getestet.
Kein separater Kubernetes-Job mehr. Dockerfile-CMD fuehrt jetzt
`npm run migrate:deploy` aus, bevor `node dist/main.js` startet -
Tabellen werden beim ersten Verbinden automatisch angelegt. Sicher bei
mehreren gleichzeitig startenden Pod-Replicas (replicas: 2), da Prisma
migrate deploy einen DB-seitigen Advisory-Lock nutzt. deployment.yaml
bekommt eine startupProbe (failureThreshold 30 x periodSeconds 10 = bis
zu 5 Min), damit liveness/readiness den Container waehrend der
Migration nicht neu starten. ARCHITECTURE.md entsprechend aktualisiert,
migrate-job.yaml entfernt.
Bisher lief keine Migration automatisch: dem Runtime-Image fehlten
Prisma-CLI, prisma/migrations/ und prisma.config.ts (nur dist+generated
wurden kopiert, prisma war devDependency). Jetzt:
- prisma von devDependencies zu dependencies verschoben (wird fuer
migrate:deploy im --omit=dev Runtime-Image gebraucht)
- neues npm-Script migrate:deploy (prisma migrate deploy)
- Dockerfile kopiert prisma/, prisma.config.ts zusaetzlich ins Runtime-Image
- deploy/k8s/backend/migrate-job.yaml: Job, der dasselbe Backend-Image mit
ueberschriebenem command nutzt statt eines eigenen Migrations-Images,
laeuft einmalig vor dem Rollout (Reihenfolge/kubectl-Befehle als
Kommentar in der Datei)
Verifiziert: tsc, 253/253 Unit-Tests, Build clean, `npx prisma --version`
laedt prisma.config.ts korrekt.
Architektur-Entscheidung geaendert: PostgreSQL laeuft jetzt selbst im
Cluster statt auf einer bestehenden externen Instanz. Neu:
deploy/k8s/postgres/ mit StatefulSet (postgres:18-alpine,
volumeClaimTemplates, pg_isready-Probes), headless Service
(apicore-postgres) und Secret-Vorlage. Backend-DATABASE_URL zeigt auf
den neuen In-Cluster-Service. CLAUDE.md/ARCHITECTURE.md aktualisiert:
Backup-Verantwortung liegt jetzt beim Projekt, nicht mehr beim
externen Cluster-/DBA-Betrieb (Backup-CronJob als offener Punkt in
TASKS.md ergaenzt).
Migrations-, Backup- und Restore-Vorgehen in ARCHITECTURE.md
Abschnitt 8 festgehalten. migrate deploy/dev bereits einziger
Migrationsweg (kein db push), /health/ready prueft DB bereits.