* chore: repo-wide bloat sweep, remove dead code and fold duplicate helpers
Remove 33 dead files, ~270 unreferenced exports/types, 13 dead i18n
namespaces and 4 unused dependencies; fold byte-identical helper copies
into one canonical home each (lib/utils chunk/sleep/utcDateStamp,
lib/dates/iso, lib/invariants/uuid, lib/xml/escape, lib/reports/sru/format,
lib/pdf/number-text, lib/browser/panel-request, lib/api/v1/body +
v1ValidationError rolled out to ~55 v1 routes, booking-template schemas).
No behaviour change: v1 bodies and status codes, MCP tool schemas, DB
writes and money math are untouched. Naive ore rounding was deliberately
not swapped for roundOre; see DECISIONS.md 2026-09-02 for the full list
of things left alone on purpose.
tsc, lint, 19588 unit tests and check:guards green; antipattern baseline
ratcheted (naive-ore-round 622 -> 620, hand-rolled-invariant 115 -> 113).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* test(transactions): import RawTransaction from @/types after the ingest re-export removal
CI's type ratchet (check:types, full tsconfig) caught the one test file
that still imported the type through lib/transactions/ingest.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
---------
Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
* feat(notifications): opt-in daily "nytt att bokföra" email digest
Users asked for an email when new work arrives: bank transactions that
synced overnight and documents that landed in the inbox. Adds a daily
05:45 UTC cron (after the 05:00 bank sync) that emails opted-in users a
per-company summary with counts only, no amounts (data-minimization
stance of the kvittens/skattekonto mails).
- notification_settings.email_digest_enabled, NOT NULL DEFAULT false:
strictly opt-in via a new toggle in the notification settings panel
- notification_log type 'bookkeeping_digest' with the claim-then-send
partial unique index pattern: one mail per user per company per day
- counts unbooked transactions and unprocessed inbox items created in
the last 24h; empty digests are never sent
- brand-aware sender + link base via lib/email/brand-sender
- docker crontabs regenerated from vercel.json
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0122MznXxrLRyT96fGhfyzD4
* fix(notifications): digest review findings in one pass
Skeptic + bot findings on PR #2078, resolved together:
- Count queries now match the canonical worklist anchors: ignored
transactions excluded; inbox items already booked directly or matched
to a transaction (created_journal_entry_id / matched_transaction_id)
no longer counted (skeptic: spurious digests).
- Memberships sweep and member-email lookups chunk .in() id lists at 150
ids to stay under proxy URL limits (HTTP 414 at ~350 opted-in users).
- Recoverable claim lifecycle (CodeRabbit): claim inserts as 'pending',
flips to 'sent' only after the provider accepted the mail; a stale
pending claim is atomically taken over by a later run, so a worker
death mid-send no longer swallows the day's digest. New migration
20260831110000 admits 'pending' to the delivery_status CHECK.
- Company name sanitized against CRLF header injection before the mail
subject (compliance swarm ASVS V1.2.5), with test.
- RoPA entry for the new processing activity in .compliance/ropa.yaml
(compliance swarm GDPR Art. 30).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0122MznXxrLRyT96fGhfyzD4
* fix(types): admit 'pending' to NotificationLog delivery_status union
Matches migration 20260831110000; surfaced by fix re-verification.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0122MznXxrLRyT96fGhfyzD4
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>