feat: T4 wave 10 – bottling quality, phenology and honey chain UI, Telegram link/receive and ops

- 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]>
This commit is contained in:
Nick SzaunigandClaude Opus 5.5 committed 2026-10-14 22:47:17 +02:00
1 parent 16cc557462
commit fab7f7e12f
91 files changed
+8463 -104

No files matched your search

+12 -1
View File
@@ -348,7 +348,10 @@ describe('Notifications (e2e)', () => {
.set(as('bare'))
.expect(200);
expect(response.body).toEqual({
const { telegram, ...settings } = response.body as {
telegram: Record<string, unknown>;
};
expect(settings).toEqual({
taskDueEnabled: true,
digestEnabled: true,
digestTime: '06:30',
@@ -357,6 +360,14 @@ describe('Notifications (e2e)', () => {
telegramCategories: ['TASK_DUE', 'DIGEST'],
telegramDetail: true,
});
// configured / botUsername follow the environment of the process.
expect(telegram).toMatchObject({
linked: false,
firstName: null,
linkedAt: null,
enabled: false,
});
expect(typeof telegram.configured).toBe('boolean');
});
it('PATCH notification-settings stores digest time, quiet hours and Telegram categories', async () => {