feat: T4b/T4c wave 8 - weather history, phenology, honey chain, Telegram base

- 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]>
This commit is contained in:
Nick SzaunigandClaude Opus 5.5 committed 2026-10-14 20:00:46 +02:00
1 parent 22382d53a2
commit 1971b2d9c4
90 files changed
+8431 -155

No files matched your search

@@ -6,6 +6,7 @@ import type {
} from '../domain-events/domain-event-types';
import { OnDomainEvent } from '../domain-events/on-domain-event.decorator';
import { Prisma } from '../../generated/prisma';
import { honeyBatchInclude } from '../honey-batches/honey-batch-include';
import { WebhookEvent } from './webhook-events';
import { WebhooksService } from './webhooks.service';
@@ -20,6 +21,7 @@ export const DOMAIN_EVENT_WEBHOOKS: Readonly<
HarvestRecorded: 'harvest.created',
BatchBottled: 'batch.updated',
BottlingCorrected: 'batch.updated',
HoneyBatchRecorded: 'batch.created',
StockChanged: 'stock.changed',
TaskCreated: 'task.created',
TaskCompleted: 'task.completed',
@@ -88,6 +90,13 @@ async function webhookData(
return tx.honeyBatch.findUnique({
where: { id: event.payload.honeyBatchId },
});
// The new honey batch with the relations the API shows, as the legacy
// path sent it after the create.
case 'HoneyBatchRecorded':
return tx.honeyBatch.findUnique({
...byAggregate,
include: honeyBatchInclude,
});
case 'StockChanged':
return stockChange(event.payload);
case 'TaskCreated':