Files
notify-bridge/RELEASE_NOTES.md
T
alexei.dolgolyov e2c17dd343
Release / test-backend (push) Successful in 2m16s
Release / release (push) Successful in 1m3s
chore: release v0.9.0
2026-05-28 15:33:00 +03:00

8.2 KiB

v0.9.0 (2026-05-28)

A feature + observability release. The headline additions are per-receiver Telegram options (silent send and forum-topic routing), an oversized-video fallback that bypasses Telegram's 50 MB sendVideo cap by switching to sendDocument, partial-failure visibility on the dashboard via a new dispatch_summary block on every EventLog row, and an admin diagnostic-mode panel for temporary per-module DEBUG logging with auto-revert. End-to-end correlation IDs (dispatch_id, X-Request-Id) now tie log lines to the database rows they produced. No breaking changes; no migrations required.

User-facing changes

Features

  • Per-receiver Telegram options. Each Telegram chat receiver can now be configured to send silently (disable_notification — no sound or vibration on the recipient) and to route into a specific forum topic (message_thread_id) on supergroups with topics enabled. A new cog-icon button on the receiver row opens an inline editor; active options surface as a bell-off icon and a #thread-id chip on the receiver header. The plumbing uses a ContextVar bound at the public send entry points, so every internal payload builder (sendMessage, sendPhoto/Video/Document, sendMediaGroup, cache-hit POST) picks them up without a signature change (6a8f374)
  • Send oversized videos as documents. A new per-target toggle, send_large_videos_as_documents, falls back to sendDocument when a video would exceed Telegram's 50 MB sendVideo limit. Useful for archival use cases (Immich library shares with users on free Telegram accounts) where the video would otherwise be silently dropped or noisily 413'd. Pairs with the existing send_large_photos_as_documents toggle. Translated copy lives under targets.sendLargeVideosAsDocuments (6a8f374)
  • Diagnostic mode for temporary DEBUG logging. A new Diagnostics cassette on the Settings page lets an admin flip one module to DEBUG for a bounded window (1 minute to 4 hours) with an automatic revert. Useful for investigating a specific dispatch failure without flooding stderr; the revert reads the current DB-configured log_levels at expiry so a setting change made during the window is honored. State is in-memory only — a restart wipes overrides, and setup_logging() re-applies the DB baseline at boot, so a forgotten override cannot silently survive a deploy (6a8f374)
  • Partial-delivery visibility in the dashboard. Every event-, watcher-, scheduled-, deferred-, action-, and command-dispatch path now writes a dispatch_summary block onto EventLog.details: per-target succeeded/failed counts, Telegram media delivered_count / skipped_count / failed_count, and a truncated list of error strings. Partial outcomes (one target out of three failed, two of ten assets dropped) are no longer indistinguishable from a clean success (6a8f374)
  • Inbound request correlation IDs. A new middleware accepts X-Request-Id from the inbound request (so an upstream proxy with its own correlation system can propagate its id) and echoes the value back on the response. Values are sanitised to a bounded charset to prevent CR/LF injection into log lines. The id is bound into log context for every request and copied onto any EventLog row written during the same request, so the SPA can surface it for bug reports (6a8f374)

Bug Fixes

  • sendMediaGroup byte-budget enforcement. Telegram's sendMediaGroup envelope tops out near 50 MB total (multipart bytes including form overhead). Previously the per-item budget admitted items that, when summed, busted Telegram's request cap and 413'd. A new 45 MB total-bytes budget (TELEGRAM_MAX_GROUP_TOTAL_BYTES) splits groups before the overhead pushes us over, with safety margin (6a8f374)
  • Per-item fallback inside sendMediaGroup. A stale file_id reference (cache poisoning by Telegram's GC, or a video that's no longer downloadable from the cached URL) used to silently lose the cached asset because the failed chunk had no re-download path. Each cached item now retains its source_url + download_headers so the per-item fallback can re-fetch and retry as a single send when its file_id POST returns transient errors (6a8f374)

Development / Internal

Observability

  • Shared dispatch_id across log lines and EventLog rows. A disp:<12 hex> correlation id is bound at the top of every dispatch entry point (dispatch_provider_event, check_tracker, dispatch_scheduled_for_tracker, _process_row in deferred dispatch, run_action, command handler, HA status logger) via ContextVar. Nested dispatcher calls reuse the bound id instead of generating their own, so a single dispatch's log lines and the EventLog.details.dispatch_id field share one searchable id (6a8f374)
  • enrich_details_with_correlation() helper. New helper in notify_bridge_core.log_context merges the bound dispatch_id and request_id onto EventLog.details dicts at write time without overwriting caller-provided keys. Every EventLog insertion site has been migrated to use it (6a8f374)

Architecture

  • split_media_by_upload_size retired. Per-item upload accounting moved onto the new _MediaItem dataclass (upload_bytes property) and the splitter logic moved into _send_media_group, where the byte budget and per-item fallback live (6a8f374)
  • API endpoints for diagnostic mode. New routes under /api/app-settings/diagnostic-mode (GET list, POST activate, DELETE /{module:path} revert) with admin-auth requirement and a curated module allowlist that blocks the root logger (6a8f374)

Tests

  • New suites: test_diagnostic_mode.py, test_dispatch_summary.py, test_request_correlation.py, test_telegram_media_group_partial.py, test_telegram_per_send_options.py. Total: 294 tests passing (up from 283 in v0.8.2) (6a8f374)
  • Test scaffolding fix: _reset_state() in test_diagnostic_mode.py now also clears the _bg_tasks set so a long-window schedule from one test doesn't pollute len(_bg_tasks) in the next test's assertion. Cross-loop .cancel() is intentionally skipped — the prior test's loop is closed and cancelling there raises RuntimeError on the next test's setup (9aada75)

Documentation

  • Six-axis production-readiness review (/.claude/reviews/) covering backend, frontend, security, performance/DB, UI/UX, and bugs+features. Snapshots the v0.8.1 codebase; informed several items shipped in v0.8.2 and this release (6a8f374)
  • Functional review of Telegram / Immich / Logging subsystems (.claude/docs/functional-review-2026-05-28.md). Captures what's in place, in-flight work, and ranked gaps for each subsystem; pairs with the existing feature backlog (6a8f374)

All Commits
  • 6a8f374feat: observability, per-receiver Telegram options, oversized-video fallback (alexei.dolgolyov)
  • 9aada75fix(tests): clear diagnostic_mode _bg_tasks between cases (alexei.dolgolyov)