Bot commands were the only user-initiated path that didn't surface in
the dashboard. They now produce ``command_handled`` /
``command_rate_limited`` / ``command_failed`` rows in ``EventLog``
alongside tracker and action events.
Backend
- ``EventLog`` gains nullable ``command_tracker_id`` / ``telegram_bot_id``
FKs plus deletion-snapshot name columns (idempotent migration).
- New ``_log_command_event`` helper emits one row per invocation at the
three branches in ``handle_command``. Logging failures are swallowed
so they cannot block the user-visible reply.
- Telegram ``from`` is captured in poller and webhook, whitelisted to
identity fields by ``_normalize_issuer`` (drops ``language_code`` and
any future PII), persisted under ``details.issuer``.
- ``/api/status`` resolves live ``CommandTracker`` / ``TelegramBot``
names (mirroring the action pattern) and exposes ``tracker_id``,
``command_tracker_id``, ``telegram_bot_id`` so the frontend can
deep-link.
Frontend
- Event rows are now clickable and open a detail modal with full
provenance (bot → chat → issuer → provider), raw ``details`` JSON,
and per-entity action buttons.
- Buttons use the existing ``requestHighlight`` + ``goto`` crosslink
pattern, so clicking lands on the entity's list page with that
specific card scrolled into view and pulsing.
- Auto-refresh dropdown (Off / 10s / 30s / 1m / 5m) persisted in
``localStorage``; ticker pauses while the tab is hidden.
- Event-type filter, dashboard verb labels, and gradients extended for
the three new ``command_*`` types.
- Filled in pre-existing missing i18n keys (``common.hide`` /
``common.show`` / ``commandConfig.noCommandsForProvider``).
Tests
- New ``test_command_event_logging.py`` covers subject formatting,
issuer normalization, the three event branches, and graceful failure
when the DB is unreachable. ``pytest packages/server/tests/`` → 96/96.