- 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]>
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]>
Data-integrity fixes found in a systematic review of ApiCore:
- Bottling booked the honey batch amount from a value read outside the
transaction and wrote an absolute result back, so two concurrent
bottlings (double tap, two devices) could both pass the check and one
deduction was lost. Bookings now use a conditional UPDATE inside the
transaction; corrections and deletions give amounts back atomically.
- Outgoing stock movements could drive stock below zero; the removal is
now a conditional decrement that refuses when stock is insufficient.
- Weak, conspicuous and queenless colonies were treated as "not active"
and could not be moved, split or merged - uniting a queenless colony
is the textbook merge. Only terminal statuses lock a colony now; merging
a colony into itself and dissolving twice are rejected.
- Moving a queen to another colony failed on the unique currentQueenId
because the old colony kept pointing at her. The old colony now releases
her (and becomes QUEENLESS); a queenless colony receiving a queen becomes
ACTIVE; dissolved colonies cannot receive a queen.
- Completing a task twice moved completedAt and re-sent the webhook;
reopening a task kept completedAt. Recurring templates created tasks
without a due date and caught up missed weeks one stale task at a time.
- Automation rules created a new task and warning on every trigger (every
stock removal below minimum). Tasks now record their source trigger and
at most one open task per rule and object exists. The varroa threshold
(mites per day) is only compared with natural-mite-fall rates, not with
sample-based counts in another unit.
- New opt-in Idempotency-Key support for record-creating endpoints, so
the offline queue can retry after a lost response without duplicates.
- Permission catalog sync crashed replicas booting concurrently on a
fresh database (unique violation); it now uses createMany/skipDuplicates.
- Server-side validation for inspection rating scales (1-5), unique
colony/harvest lists and net weight <= gross weight.
- Stale e2e expectations updated (permission count, trigger list, real PNG
fixture for the file-signature check).
Migration is additive only (nullable Task column, new table).
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
An inspection, treatment, feeding, varroa measurement or swarm control check
filed against the wrong colony could not be repaired: the colony was frozen at
creation time, and with no delete endpoint either, the only remedy was leaving
a wrong record in the colony's history. The same held for a harvest booked
against the wrong apiary or with a colony forgotten, for an extraction linked
to the wrong harvests, and for a honey batch whose number or source extraction
was mistyped.
Backend:
- The five colony-scoped update DTOs now cover the full create DTO, colonyId
included.
- Harvest update accepts apiaryId and colonyIds, extraction update accepts
harvestIds. Both validate the ids first and replace the whole join-row set
inside the existing transaction; the join rows carry no data of their own,
so replacing beats diffing.
- Honey batch update accepts batchNumber, extractionId and status. Status was
previously only ever set to BOTTLED by the bottling module, so SOLD and
CONSUMED were unreachable through the API.
- Colony and storage container codes, and a task's relatedType/relatedId, are
editable.
- apiaryId on a colony and storageContainerId on a honey batch stay out of
PATCH on purpose: both have dedicated endpoints that also write the history
entry (ColonyApiaryHistory, HoneyBatchRelocation) the traceability chain
depends on.
- AllExceptionsFilter now also translates Prisma P2003 to a 400 and P2025 to a
404. Services write straight against the foreign keys with no application
level existence check, so before this a stale or mistyped relation id came
back as a bare 500 and filled the log with fake server errors.
Frontend:
- New shared fields: ColonySelectField (used by all five colony-scoped edit
dialogs), HarvestSelectField (both extraction dialogs) and TaskRelatedField.
- Re-filing a record invalidates the lists of both the colony it left and the
one it joined, otherwise one of the two stays stale.
- The harvest dialog gained an apiary select and a colony checklist that keeps
already-linked colonies visible even when they moved or were dissolved; the
honey batch dialog gained batch number, source extraction and status.
- Extraction and harvest forms refuse to submit an empty relation set instead
of letting the backend reject it.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
None of these numeric business fields had a lower bound, so a
negative honey weight, jar count, feeding amount, mite count, or
stock quantity was silently accepted - each would corrupt whatever
total or statistic it later feeds into. Added @Min(0):
- Harvest: honeySuperCount, grossWeight, netWeight
- HoneyBatch: amount (+ @Max(100) on waterContent, a percentage)
- HoneyMeasurement: waterContent (+ @Max(100))
- Feeding: amount
- Extraction: amount (+ @Max(100) on waterContent)
- StockItem: quantity, minThreshold
- VarroaMeasurement: durationMinutes, miteCount, calculatedValue
Left temperature fields alone (extraction, inspection) - genuinely
can be negative (winter inspections, outdoor storage) and no
confident domain bound exists to apply instead. Update DTOs for these
entities all extend PartialType(PickType(Create...Dto, [...])) from
@nestjs/swagger, which preserves validation decorators, so no
separate change was needed there.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Ein zweiter, unabhaengiger Audit-Durchlauf (frischer Blick, keine
Vorkenntnis meiner eigenen Fixes) bestaetigte alle bisherigen Fixes als
korrekt verdrahtet und fand 3 LOW-severity Robustheitsluecken - keine
davon war ausnutzbar, aber alle sind jetzt behoben:
- HarvestsService.create/ExtractionsService.create pruefen jetzt, dass
referenzierte colonyIds/harvestIds tatsaechlich existieren, bevor die
Transaktion laeuft - vorher fuehrte eine unbekannte Id zu einem
ungefangenen 500 mit rohem Prisma-Fehler statt einer sauberen 400.
- Etiketten-Bildelemente durchlaufen jetzt denselben Magic-Byte-Check
wie Avatar-/Dokument-Uploads, statt pdfkits Bildparser ungepruefte
Bytes zu uebergeben.
- Webhook-Secret-Redaktion im Audit-Log nutzt jetzt explizites delete
statt sich auf Prismas JSON-Serialisierung zu verlassen, die
undefined-Werte verwirft (fragil, falls sich die Serialisierung mal
aendert).
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Replaces the fixed 5-value Role enum with a database-driven Role/Permission
model: admins can create custom roles with a precise per-resource/action
permission set (83-key code-owned catalog), instead of being locked to
ADMIN/BEEKEEPER/STAFF/READONLY/API_CLIENT.
- New Role/Permission/RolePermission tables; legacy Role enum renamed to
LegacyRole and kept as User.legacyRole so all existing @Roles() call
sites keep working unchanged during the migration.
- New RolesController/RolesService (CRUD, permission-set editing,
deactivate/reactivate) with lockout-prevention invariants: system roles
(ADMIN) can't be edited or deactivated, and a role still assigned to
users can't be deactivated.
- New PermissionResolverService (30s-cached, DB-fresh per user) and
PermissionsGuard + @RequirePermission(), registered as an additional
APP_GUARD alongside the legacy RolesGuard so both systems run
side-by-side until controllers are migrated in Phase 2.
- PermissionCatalogSyncService seeds any new catalog entries into the DB
on boot, so future features only touch the TS catalog.
- New GET /auth/me returns the caller's role and effective permissions.
- Migration hand-written (destructive column rename) with grants derived
from a fresh grep of every existing @Roles() call site, verified
against 5 controllers read in full for STAFF's exact write boundary.
- test/roles.e2e-spec.ts covers the new endpoints and invariants end to
end; test/authorization-coverage.e2e-spec.ts added but skipped until
Phase 2 gives every controller a @RequirePermission.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Bisher liessen sich diese acht Objekttypen nur anlegen, nie nachträglich
korrigieren – Aufgaben hatten zwar schon einen PATCH-Endpunkt, aber
keine Bearbeiten-UI, die übrigen sieben hatten weder Backend- noch
Frontend-Unterstützung. Jetzt bekommt jeder Typ einen PATCH :id
Endpunkt (UpdateXDto per PartialType(PickType(...)) nach demselben
Muster wie UpdateColonyDto/UpdateApiaryDto, mit Audit-Log-Eintrag
inkl. Vorher-/Nachher-Stand) sowie eine Bearbeiten-UI:
- Aufgaben: Dialog auf der Aufgabenliste
- Durchsichten/Behandlungen/Fütterungen/Varroa-Messungen: Dialog pro
Listeneintrag auf der Volk-Detailseite
- Ernten: Dialog auf der eigenen Ernte-Detailseite
- Schleuderungen: Dialog direkt in der Rückverfolgbarkeits-Karte der
Honigcharge (keine eigene Detailseite nötig); Erntezeilen dort
verlinken jetzt zur Ernte-Detailseite statt sie zu duplizieren
- Abfüllungen: Dialog auf der Abfüllungs-Detailseite, bewusst nur
Datum/MHD/Losnummer editierbar – Glasgröße/-anzahl bleiben
unveränderlich, weil sie bereits unumkehrbar die Honigcharge-Menge
reduziert haben
Relationsstiftende Fremdschlüssel (colonyId, apiaryId/colonyIds,
harvestIds, honeyBatchId) bleiben überall bewusst nicht editierbar,
konsistent mit dem bestehenden apiaryId-Ausschluss bei UpdateColonyDto.
HarvestsModule und ExtractionsModule als Fundament der
Rückverfolgbarkeitskette: eine Ernte kann mehrere Völker umfassen,
eine Schleuderung mehrere Ernten zusammenführen.