feat: Центр уведомлений DC v3 (замена Novu) [C28-22…27] — код-комплит, 6/6 эпиков #60

Merged
ant merged 2 commits from feat/notification-center-dc-v3 into dev 2026-06-08 08:37:31 +00:00
Owner

Зонтичный PR трека notifications (C28-22…C28-27): вырезан централизованный Novu, в контроллер каждого кооператива поставлен собственный лёгкий Центр уведомлений (почта / in-app / веб-пуш) с журналом доставок и переотправкой. Каждый кооператив доставляет сам своим подписчикам напрямую (federation-инвариант: coopname обязателен). Источник требований — ARCH-6 fe-arch-tsentr-uvedomleniy-dc-v3.

Нейминг: единый порт NotificationPort (без Novu/Center в идентификаторах).

Статус: все 6 эпиков КОД-КОМПЛИТ

Весь код написан и закоммичен. Ветка смержена с актуальным dev — конфликт (@coopenomics/notifications/package.json: version+description) устранён.

  • Эпик 1 (C28-22) — ядро: порт NotificationPort.notify, типы входа, channel-порты, роутер NotificationService, схема БД (notification_outbox + notification_deliveries), идемпотентность (sha256 + ON CONFLICT DO NOTHING).
  • Эпик 2 (C28-23) — три канала: SMTP (nodemailer) / веб-пуш (прямая доставка на endpoint подписки) / in-app (персист в notification_inbox); шаблонизатор из каталога; нет кредов → канал тихо disabled, наружу не стучится.
  • Эпик 3 (C28-24) — outbox-worker: @Interval-поллинг по индексу (status, scheduledAt), claim-and-send, retry с backoff [5s, 30s, 2m, 10m, 1h], реклейм зависших SENDING, enum-статусы.
  • Эпик 4 (C28-25) — миграция consumer-сервисов: 28 call-sites triggerWorkflow в 15 сервисах переключены на notify(); @OnEvent/cron-подписки сохранены; cross-plugin порт chatcoop мигрирован.
  • Эпик 5 (C28-26) — удаление Novu: @novu/api + infrastructure/novu/* + config + sync-сервис вырезаны (grep -ri novu по controller/src = 0); subscriber_hash HMAC переведён на SERVER_SECRET; пакет @coopenomics/notifications репурпослен (только каталог типов + шаблоны, sync удалён); desktop @novu/js снят.
  • Эпик 6 (C28-27) — стол председателя + личный инбокс: GraphQL getNotifications/getNotification/resendNotification (роли chairman/member, guard по coopname), инбокс пайщика (колокол на нативном доменном NotificationCenter + поллинг unreadCount), страница «Журнал уведомлений» по канону mono-desktop.

Осталось — операторская приёмка (runtime, в CI/фоне недоступно)

Эти шаги требуют запуска на остановленном/живом контроллере, поэтому не закрываются автоматически (отсюда красный typecheck в CI: без кодогена FE/SDK не компилируются — это ожидаемо):

  1. Кодоген: pnpm generate-schema + pnpm generate-client (controller) + pnpm build (sdk) — backend-резолверы эпика 6 поменяли GraphQL-схему; локально нельзя (вешает контейнер), нужно на остановленном controller или в CI.
  2. Boot: контроллер стартует без NOVU_* env → grep ERROR (DI-граф tsc не ловит).
  3. E2E: решение совета / платёж / анонс собрания → реальная доставка по каналам (SMTP-письмо, веб-пуш в браузер, in-app инбокс); переотправка со стола председателя создаёт новую попытку в журнале.

После кодогена typecheck станет зелёным.

🤖 Generated with Claude Code

Зонтичный PR трека **notifications** (C28-22…C28-27): вырезан централизованный Novu, в контроллер каждого кооператива поставлен собственный лёгкий Центр уведомлений (почта / in-app / веб-пуш) с журналом доставок и переотправкой. Каждый кооператив доставляет сам своим подписчикам напрямую (federation-инвариант: `coopname` обязателен). Источник требований — ARCH-6 `fe-arch-tsentr-uvedomleniy-dc-v3`. Нейминг: единый порт `NotificationPort` (без Novu/Center в идентификаторах). ## Статус: все 6 эпиков КОД-КОМПЛИТ ✅ Весь код написан и закоммичен. Ветка смержена с актуальным `dev` — конфликт (`@coopenomics/notifications/package.json`: version+description) устранён. - [x] **Эпик 1 (C28-22) — ядро**: порт `NotificationPort.notify`, типы входа, channel-порты, роутер `NotificationService`, схема БД (`notification_outbox` + `notification_deliveries`), идемпотентность (sha256 + ON CONFLICT DO NOTHING). - [x] **Эпик 2 (C28-23) — три канала**: SMTP (nodemailer) / веб-пуш (прямая доставка на endpoint подписки) / in-app (персист в `notification_inbox`); шаблонизатор из каталога; нет кредов → канал тихо `disabled`, наружу не стучится. - [x] **Эпик 3 (C28-24) — outbox-worker**: `@Interval`-поллинг по индексу `(status, scheduledAt)`, claim-and-send, retry с backoff `[5s, 30s, 2m, 10m, 1h]`, реклейм зависших `SENDING`, enum-статусы. - [x] **Эпик 4 (C28-25) — миграция consumer-сервисов**: 28 call-sites `triggerWorkflow` в 15 сервисах переключены на `notify()`; `@OnEvent`/cron-подписки сохранены; cross-plugin порт chatcoop мигрирован. - [x] **Эпик 5 (C28-26) — удаление Novu**: `@novu/api` + `infrastructure/novu/*` + config + sync-сервис вырезаны (`grep -ri novu` по `controller/src` = 0); `subscriber_hash` HMAC переведён на `SERVER_SECRET`; пакет `@coopenomics/notifications` репурпослен (только каталог типов + шаблоны, sync удалён); desktop `@novu/js` снят. - [x] **Эпик 6 (C28-27) — стол председателя + личный инбокс**: GraphQL `getNotifications`/`getNotification`/`resendNotification` (роли chairman/member, guard по `coopname`), инбокс пайщика (колокол на нативном доменном `NotificationCenter` + поллинг `unreadCount`), страница «Журнал уведомлений» по канону mono-desktop. ## Осталось — операторская приёмка (runtime, в CI/фоне недоступно) Эти шаги требуют запуска на остановленном/живом контроллере, поэтому **не закрываются автоматически** (отсюда красный typecheck в CI: без кодогена FE/SDK не компилируются — это ожидаемо): 1. **Кодоген**: `pnpm generate-schema` + `pnpm generate-client` (controller) + `pnpm build` (sdk) — backend-резолверы эпика 6 поменяли GraphQL-схему; локально нельзя (вешает контейнер), нужно на остановленном controller или в CI. 2. **Boot**: контроллер стартует без `NOVU_*` env → `grep ERROR` (DI-граф tsc не ловит). 3. **E2E**: решение совета / платёж / анонс собрания → реальная доставка по каналам (SMTP-письмо, веб-пуш в браузер, in-app инбокс); переотправка со стола председателя создаёт новую попытку в журнале. После кодогена typecheck станет зелёным. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
claude added 4 commits 2026-06-02 12:26:26 +00:00
Порт notify(NotifyInput) + типы входа (coopname обязателен как federation-инвариант,
workflowId ссылается на каталог @coopenomics/notifications, NotificationChannel enum
email/in_app/push). Старый NovuWorkflowPort сохраняется до миграции call-sites (эпик 4),
удаляется в эпике 5. Часть истории 1.1 эпика 1 (C28-22).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Сущности notification_outbox (очередь recipient×channel, hot-path индекс
(status, scheduledAt), unique idempotency_key) и notification_deliveries
(append-журнал попыток, индексы по coopname/recipient для стола председателя
и личного инбокса). Статусы — enum, не строки. Фундамент для роутера (1.3),
worker'а (эпик 3) и стола председателя (эпик 6).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
NotificationService implements NotificationPort: резолвит каналы по типу из
каталога @coopenomics/notifications (steps[].type, только email/in_app/push),
для каждой пары получатель×канал пишет строку в notification_outbox идемпотентно
(ON CONFLICT DO NOTHING по unique idempotencyKey). Опциональный EntityManager —
запись в транзакции вызывающего (story 1.3). idempotencyKey = sha256(coopname|
workflowId|subscriberId|channel|stable(payload)) с детерминированной сериализацией
payload (story 1.5). email-канал — только при подтверждённом email (gate).
Модуль зарегистрирован в app.module, экспортирует NOTIFICATION_PORT.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
[C28-22][@ant] feat: порты каналов доставки — Email/InApp/WebPush
Typecheck / desktop (pull_request) Successful in 14m44s
Typecheck / controller (pull_request) Successful in 13m20s
c783ce322b
Контракты EmailChannelPort/InAppChannelPort/WebPushChannelPort + ChannelMessage/
ChannelDeliveryResult + DI-токены. Только интерфейсы — реализации в эпике 2;
worker эпика 3 зависит от абстракций, не от провайдеров. Имена без I-префикса
(канон портов репо).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
claude added 1 commit 2026-06-02 12:32:44 +00:00
[C28-23][@ant] feat: каналы email + web-push + шаблонизатор
Typecheck / desktop (pull_request) Successful in 13m37s
Typecheck / controller (pull_request) Successful in 13m16s
8e6e45eba5
EmailChannelAdapter (nodemailer, SMTP из config.email; disabled при пустом
SMTP_HOST, наружу не стучится — NFR11). WebPushChannelAdapter: контроллер
доставляет сам своим подписчикам напрямую через WebPushService на endpoint'ы
из web_push_subscriptions по username (фикс пушей — точки доставки, не ключи);
410 → деактивация подписки. Шаблоны рендерятся из каталога @coopenomics/
notifications (controlValues.subject/body), mustache-интерполяция {{payload.x}}
без liquidjs (лишняя зависимость для MVP). Порты EMAIL_CHANNEL_PORT/
WEB_PUSH_CHANNEL_PORT в NotificationCenterModule.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
claude added 1 commit 2026-06-02 12:46:27 +00:00
[C28-23][@ant] feat: канал in-app — персист уведомлений в инбокс кооператива
Typecheck / desktop (pull_request) Successful in 13m24s
Typecheck / controller (pull_request) Successful in 13m18s
0e98ae5538
InAppChannelAdapter за InAppChannelPort: доставка in-app = запись
отрендеренного из каталога уведомления в notification_inbox. Новая
сущность notification_inbox (isRead/readAt, индексы под ленту + unreadCount).
ChannelMessage расширен outboxId + actorSubscriberId (worker даёт из outbox).
Live-подписка/unreadCount/mark-read — read-side инбокса, в эпик 6.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
claude added 1 commit 2026-06-02 12:51:06 +00:00
[C28-24][@ant] feat: outbox-worker — надёжная доставка с retry и статусами
Typecheck / desktop (pull_request) Successful in 13m42s
Typecheck / controller (pull_request) Successful in 13m14s
5b3a28d56c
OutboxWorkerService @Interval(3000): поллит notification_outbox по индексу
(status, scheduledAt), шлёт через порт канала, исход пишет в журнал
notification_deliveries. at-least-once: backoff-ретрай (5s→30s→2m→10m→1h,
maxAttempts), после исчерпания — терминальный failed (переотправка на столе
председателя). Реклейм зависших SENDING после рестарта. Строка = один канал →
независимые попытки по каналам. Статусы — enum. ScheduleModule.forRoot()
подключён в app.module. Параметры worker'а из env с дефолтами.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
claude added 1 commit 2026-06-02 13:08:09 +00:00
[C28-25][@ant] feat: миграция consumer-сервисов с Novu на единый порт уведомлений
Typecheck / desktop (pull_request) Successful in 13m32s
Typecheck / controller (pull_request) Successful in 13m3s
0afb73a506
Drop-in переключение ~28 call-sites в 15 сервисах с
NOVU_WORKFLOW_PORT.triggerWorkflow на NOTIFICATION_PORT.notify (Центр
уведомлений DC v3). Listener'ы/события не тронуты — меняется только тело
вызова: name→workflowId, coopname обязателен (action.coopname/config.coopname),
recipient дополнен username (адресация in-app/web-push).

NotificationCenterModule помечен @Global — порт cross-cutting, консьюмеры
инжектят без импорта модуля (как NOTIFICATION_SUBSCRIPTION_PORT).
Cross-plugin InterCoopCalendarEventNotificationPort (chatcoop) сохранён,
мигрировано лишь тело. Из application NotificationSenderService убраны мёртвые
Novu-измы (broadcast/cancel/getStatus) — не использовались.

tsc: 0 новых ошибок (17 = baseline). NOVU_WORKFLOW_PORT-биндинг остаётся
мёртвым до удаления в эпике 5.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
claude added 1 commit 2026-06-02 13:40:20 +00:00
[C28-26][@ant] refactor: полное удаление Novu из контроллера
Typecheck / desktop (pull_request) Successful in 13m42s
Typecheck / controller (pull_request) Successful in 13m4s
4bb9a317ca
Контроллер больше не зависит от внешнего Novu (ADR-001, DEC-025):
- Удалены infrastructure/novu/* (адаптеры + модуль), порты NovuWorkflow/
  NovuCredentials + старый notification.port (NotificationPort→NovuAdapter),
  NotificationDomainService, DeviceTokenService, webhook-приёмник Novu, novu.utils.
- subscriber_hash переведён на HMAC по SERVER_SECRET (utils/subscriber-hash.util),
  identity своя; subscriber_id (адресация Центра) сохранён — backfill cron оставлен
  без Novu-синхронизации.
- Развязаны AccountDomainService/account+participant-interactor/subscription-service
  от Novu-вызовов; NovuModule/NotificationDomainModule убраны из 7+5 модулей.
- config.novu + NOVU_* env + @novu/api удалены; lockfile обновлён (desktop @novu/js
  снимется синхронно с эпиком 6).
- @coopenomics/notifications: удалён sync/ (внешняя синхронизация), bin/scripts/deps;
  остался каталог типов + шаблоны; пакет пересобран.
- grep -i novu по controller/src пуст; tsc = baseline (0 новых ошибок).

Boot/grep-ERROR верификация — за оператором (поднять контроллер нельзя из фоновой сессии).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
claude added 1 commit 2026-06-02 13:50:05 +00:00
[C28-27][@ant] feat: бэкенд журнала уведомлений — getNotifications/getNotification/resend
Typecheck / desktop (pull_request) Successful in 13m25s
Typecheck / controller (pull_request) Successful in 13m0s
96a8ecdf54
Read-side стола председателя (эпик 6, backend):
- getNotifications: журнал очереди notification_outbox с фильтрами
  (coopname/workflowId/channel/status/recipient) + пагинация (канон createPaginationResult).
- getNotification: детализация уведомления + история попыток из notification_deliveries.
- resendNotification: force-переотправка — новая строка очереди со свежим
  idempotencyKey (обходит дедуп), worker подхватывает.
- Статусы каналов/доставки — GraphQL enum через registerEnumType (канон).
- Гейтинг ролями: журнал/детализация — chairman+member, resend — chairman
  (RolesGuard+AuthRoles; платформенный CASL — Phase 2).

FE (инбокс/журнал) + codegen Zeus/SDK — за оператором (generate-schema/client
локально не запускать); live-подписка in-app — следующая история. tsc: 0 новых ошибок.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
claude added 1 commit 2026-06-02 13:59:41 +00:00
[C28-27][@ant] feat: read-side инбокса пайщика — лента/unreadCount/markRead
Typecheck / desktop (pull_request) Successful in 13m25s
Typecheck / controller (pull_request) Successful in 12m58s
f9543991e3
Эпик 6.6 (backend-часть): личный инбокс канала In-app поверх notification_inbox.
getInboxNotifications (лента по сессии, пагинация канон), getUnreadNotificationsCount
(бейдж колокола), markNotificationRead/markAllNotificationsRead. Скоуп строго по
subscriber_id из JWT — пайщик видит только свой инбокс (ownership на markRead).
Live-подписка onNotification отложена: graphql-ws/PubSub в репо нет — фронт poll'ит.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
claude added 1 commit 2026-06-02 14:02:56 +00:00
[C28-27][@ant] feat: SDK-операции уведомлений — журнал, инбокс, переотправка
Typecheck / desktop (pull_request) Failing after 6m26s
Typecheck / controller (pull_request) Failing after 6m30s
813cc1aeff
Zeus-селекторы (Notification/NotificationDetail/NotificationAttempt/InboxNotification/
UnreadNotificationsCount + пагинации) + queries (getNotifications/getNotification/
getInboxNotifications/getUnreadNotificationsCount) + mutations (resendNotification/
markNotificationRead/markAllNotificationsRead). Enabling-слой для FE 6.4/6.5.
Компилируются после generate-schema/generate-client оператора (zeus regen).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
claude added 1 commit 2026-06-02 14:09:38 +00:00
[C28-27][@ant] feat: личный инбокс пайщика — колокол на нативном Центре
Typecheck / desktop (pull_request) Failing after 4m50s
Typecheck / controller (pull_request) Failing after 4m48s
b500789dd6
Эпик 6.4: виджет NotificationCenter переписан с Novu UI на доменный канон-компонент
shared/ui/domain/NotificationCenter (колокол .icon-btn + BaseBadge-счётчик + q-menu).
Pinia-стор: лента инбокса/unreadCount/markRead/markAllRead через SDK, оптимистичные
отметки, поллинг непрочитанных 30с (live-подписки нет — опрос), тост на новое.
Категория группировки выводится из workflowId. Удалён @novu/js и NOVU_* из env.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
claude added 1 commit 2026-06-02 14:15:11 +00:00
[C28-27][@ant] feat: стол председателя — журнал уведомлений + переотправка
Typecheck / desktop (pull_request) Failing after 4m47s
Typecheck / controller (pull_request) Failing after 4m49s
53376971b7
Эпик 6.5: страница «Журнал уведомлений» (Chairman desktop, /notifications-journal).
Канон-таблица .table-wrap (дата/получатель/тип/канал/статус/попытки), FilterBar по
каналу и статусу, BaseBadge-статусы, кнопка «Переотправить» на строке (resend →
новая строка в очереди, перечитываем). TableSkeleton/EmptyState/«Загрузить ещё».
Имя типа уведомления — из каталога Workflows. Только канон-компоненты + токены.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
claude added 1 commit 2026-06-02 14:17:18 +00:00
[C28-26][@ant] chore: убрать упоминание Novu из комментария init-wallet
Typecheck / desktop (pull_request) Failing after 4m47s
Typecheck / controller (pull_request) Failing after 4m46s
2420d3173f
Бэкенд больше не использует Novu — комментарий устарел.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
claude added 1 commit 2026-06-02 17:11:32 +00:00
[C28-27][@ant] fix: тень панели колокола — канон-токен --p-shadow-pop вместо несуществующего --p-elev-2
Typecheck / desktop (pull_request) Failing after 4m50s
Typecheck / controller (pull_request) Failing after 4m48s
3fd81215cf
Code-review нашёл: --p-elev-2 нет в tokens.css → box-shadow не отрисовывался.
Канон-тень overlay (поповер) — --p-shadow-pop.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
claude added 2 commits 2026-06-02 17:26:59 +00:00
getNotifications/getNotification/resendNotification сверяют coopname
(фильтра и найденной строки) с config.coopname → ForbiddenException на чужой.
Канон-паттерн контроллера (free-decision/branch interactor). Federation-hardening.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
[C28-27][@ant] chore: кодоген схемы и Zeus-клиента для операций Центра уведомлений
Typecheck / desktop (pull_request) Failing after 4m48s
Typecheck / controller (pull_request) Failing after 4m46s
a22abfc45f
generate-schema + generate-client: getNotifications/getInboxNotifications/
getUnreadNotificationsCount/markNotificationRead/markAllNotificationsRead/
resendNotification + enum NotificationChannel/NotificationOutboxStatus.
SDK-операции теперь компилируются (tsc зелёный).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
claude added 1 commit 2026-06-02 17:30:00 +00:00
[C28-27][@ant] chore: убрать schema.gql из трекинга (gitignored-артефакт)
Typecheck / desktop (pull_request) Failing after 4m47s
Typecheck / controller (pull_request) Failing after 4m47s
c3cf1201c8
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
claude added 1 commit 2026-06-07 12:25:04 +00:00
Merge remote-tracking branch 'origin/dev' into feat/notification-center-dc-v3
Typecheck / controller (pull_request) Has been cancelled
Typecheck / desktop (pull_request) Has been cancelled
b653a3cbd5
# Conflicts:
#	components/notifications/package.json
claude changed title from feat: Центр уведомлений DC v3 (замена Novu) [C28-22…27] to feat: Центр уведомлений DC v3 (замена Novu) [C28-22…27] — код-комплит, 6/6 эпиков 2026-06-07 12:26:10 +00:00
claude added 1 commit 2026-06-07 12:53:16 +00:00
[C28-27][@ant] fix: журнал в меню председателя + колокол не прыгает (скелетон/фикс-высота)
Typecheck / desktop (pull_request) Failing after 4m49s
Typecheck / controller (pull_request) Failing after 4m46s
f997c77315
- Журнал уведомлений зарегистрирован в реальном меню стола председателя
  (extensions/chairman/install routes[0].children) — был воткнут в мёртвый
  src/desktops/Chairman/model (не в реестре расширений), потому страница
  была недостижима. Дубль из мёртвого манифеста убран.
- Колокол: спиннер заменён канон-скелетоном (.skel), тело фиксированной
  высоты (~4 уведомления, max 60vh) — панель больше не «прыгает» при
  скелетон → меньше элементов → пусто.
- Футер: вместо неясной пагинации «Все загружены/Показать ещё» —
  навигация «Показать все →» в журнал (только для председателя; q-menu
  закрывается при переходе).
- Бейдж счётчика смещён в угол колокола (-4/-4) — убран «наезд» на иконку.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
claude added 1 commit 2026-06-07 13:08:46 +00:00
[C28-27][@ant] fix: убрать «Показать все» из колокола + бейдж не наезжает
Typecheck / desktop (pull_request) Failing after 4m48s
Typecheck / controller (pull_request) Has been cancelled
423c1d0984
- Футер «Показать все» удалён из личного колокола: журнал — админский
  (стол председателя), пайщику туда не нужно; председатель открывает
  журнал через пункт меню. Колокол = свежие из инбокса + «Прочитать все».
- Бейдж счётчика вытолкнут за угол кнопки через transform translate(45%,-45%)
  — больше не «наезжает» на глиф колокола.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
claude added 1 commit 2026-06-07 13:15:21 +00:00
[C28-27][@ant] feat: журнал — получатель ФИО + видимость ошибок доставки
Typecheck / desktop (pull_request) Failing after 4m49s
Typecheck / controller (pull_request) Failing after 4m46s
a2d5750449
Получатель в журнале:
- две строки — ФИО (резолв username→account через getName на фронте, кэш
  на компонент) + AccountBadge с username и копированием. DTO журнала несёт
  только username/subscriber_id, поэтому ФИО тянем getAccount'ом. Нет ФИО
  (приватные данные недоступны) → одна строка с username.

Видимость ошибки доставки:
- worker раньше логировал только исключение тика; провалы отправки шли лишь
  в БД → при падающем канале в логах пусто и не видно, что worker жив.
  Добавлено: лог старта (onModuleInit), debug-heartbeat тика с числом строк,
  warn на ретрай и error на терминальный провал — с контекстом
  (workflow/канал/получатель/попытка) и текстом ошибки.
- в журнале на ячейке статуса «Ошибка» — q-tooltip с lastError (поле уже
  отдаётся DTO/селектором), курсор help.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
claude added 1 commit 2026-06-07 13:26:28 +00:00
[C28-27][@ant] fix: web-push без подписки — мягкий пропуск, не ошибка
Typecheck / desktop (pull_request) Failing after 4m49s
Typecheck / controller (pull_request) Failing after 4m47s
4f67231b3f
Получатель без активной push-подписки (не дал разрешения / нет PWA / 410)
давал {delivered:false,error} → worker ретраил 5× → терминальный FAILED:
ложные «Ошибка» в журнале и warn в логах для всех, у кого push не настроен
(в dev SW не регистрируется без ENABLE_PWA_DEV → у всех).

Ввёл исход skipped в ChannelDeliveryResult: канал неприменим к получателю.
Адаптер web-push возвращает skipped при 0 подписок / отсутствии username.
Worker гасит такую строку в canceled — без ретраев, без записи попытки в
журнал, без error-лога (debug); причину кладёт в lastError → тултип
«Отменено» на столе председателя. Гейт на слое отправки (состояние подписки
авторитетно в момент доставки), роутер outbox остаётся чистым.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ant approved these changes 2026-06-07 14:34:49 +00:00
Dismissed
ant approved these changes 2026-06-07 14:34:59 +00:00
claude added 1 commit 2026-06-07 14:45:08 +00:00
[C28-27][@ant] fix(lockfile): пересборка после удаления @novu/js — CI frozen-lockfile
Typecheck / desktop (pull_request) Failing after 8m22s
Typecheck / controller (pull_request) Has been cancelled
e199eb4913
Удаление @novu/js из desktop/package.json (b500789dd6) не сопровождалось
пересборкой pnpm-lock.yaml → CI install --frozen-lockfile падал
ERR_PNPM_OUTDATED_LOCKFILE до шага typecheck. Пересобрал lockfile-only.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
claude added 1 commit 2026-06-07 14:58:29 +00:00
[C28-27][@ant] fix(desktop): 4 ошибки типов после де-Novu — тайпчек зелёный
Typecheck / desktop (pull_request) Successful in 13m26s
Typecheck / controller (pull_request) Successful in 13m11s
5b7e1015cd
CI vue-tsc (PR #60) после фикса lockfile дошёл до тайпчека и поймал хвосты:
- src-ssr/middlewares/{injectEnv,generateConfig}.ts: NOVU_APP_ID/BACKEND_URL/
  SOCKET_URL слались в объект EnvVars, откуда поля удалены де-Novu → TS2353.
  Убрал мёртвые строки.
- NotificationJournalWidget.vue: getName() возвращает string|undefined
  (organization_data?.short_name) → .replace на possibly-undefined (TS2532).
  Обернул в (getName(account) ?? '').
- NotificationCenter/model/index.ts: n.createdAt — DateTime-скаляр SDK типа
  unknown, NotificationItem.date ждёт string|Date (TS2322). Каст as string.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ant merged commit 8ffe4bea94 into dev 2026-06-08 08:37:31 +00:00
Sign in to join this conversation.
No Reviewers
No Label
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: C9S/mono#60