aleksey
0565f09a72
test(e2e): ретраи WS-коннекта — транзиентный таймаут после deploy-stage
...
CI / test (push) Successful in 7m12s
CI / deploy-ift (push) Successful in 3m49s
CI / e2e-ift (push) Successful in 1m21s
CI / deploy-stage (push) Successful in 2m58s
CI / e2e-stage (push) Successful in 1m13s
В run 621 после фикса OOM остался один упавший кейс:
admin_websocket_tests падал на gun:await_up(5000) — одиночный
TCP/TLS-таймаут в окно стабилизации traefik/бэкенда сразу после
рестарта контейнера. ws_connect_up/4 теперь ретраит коннект
3 раза с паузой 2s; одинаково в admins- и users-хелперах.
2026-08-24 00:05:25 +03:00
aleksey
a061674408
ci(e2e): readiness-wait перед API-сьюитами на стенде
...
CI / test (push) Successful in 7m38s
CI / deploy-ift (push) Successful in 3m37s
CI / e2e-ift (push) Successful in 1m19s
CI / deploy-stage (push) Successful in 2m47s
CI / e2e-stage (push) Failing after 1m15s
После deploy-stage контейнер ещё стартует, traefik отвечает 502/503,
и первые сьюиты падали с {badmatch,{ok,502}}. run-stand-api-tests.sh
теперь до 150s ждёт, пока HTTP-фронт API_HOST/ADMIN_API_HOST начнёт
отвечать (любой код, кроме 5xx), прежде чем запускать CT.
2026-08-23 23:17:32 +03:00
aleksey
9348c36e52
ci: rerun e2e-stage (в run 617 e2e стартовал в момент рестарта контейнеров после deploy-stage; все 5 падений — транзиентные 502 админ-API)
CI / test (push) Successful in 7m14s
CI / deploy-ift (push) Successful in 3m41s
CI / e2e-ift (push) Successful in 1m16s
CI / deploy-stage (push) Successful in 2m59s
CI / e2e-stage (push) Failing after 59s
2026-08-23 22:20:30 +03:00
aleksey
e0e3455281
fix(ai): STT — имя файла по content-type, webm/mp4 уходили как voice.dat
...
CI / test (push) Successful in 7m46s
CI / deploy-ift (push) Successful in 3m56s
CI / e2e-ift (push) Successful in 1m16s
CI / deploy-stage (push) Successful in 2m54s
CI / e2e-stage (push) Failing after 1m8s
xAI определяет формат аудио по расширению имени файла в
multipart-части, а не по её content-type. Back слал нейтральное
filename="voice.dat": wav проходил случайно (узнаваемый заголовок),
а webm/opus и mp4 из браузерного MediaRecorder получали 400
«Could not detect audio format» → 502 upstream_error → тост
«Распознавание речи временно недоступно».
Расширение выводится из mime (параметры codecs отбрасываются);
неизвестный тип — .wav. Тесты на маппинг и подстановку в multipart.
2026-08-23 21:51:19 +03:00
aleksey
61f7a5f719
revert: STT ipfamily — причина была в истёкшей vless-подписке
...
CI / test (push) Successful in 7m8s
CI / deploy-ift (push) Successful in 4m5s
CI / e2e-ift (push) Successful in 1m15s
CI / deploy-stage (push) Successful in 2m14s
CI / e2e-stage (push) Successful in 1m13s
Коммит b122cf3 винил IPv6/inet6fb4 в таймаутах STT на stage.
Диагностика показала иное: api.x.ai «вешает» запросы с валидным
ключом с российских IP (dummy-ключ отвечает 400 мгновенно),
а vless-подписка истекла — egress-узлов нет. Возврат к прежнему
поведению httpc-профиля.
2026-08-23 00:15:22 +03:00
aleksey
b122cf3339
fix(ai): STT httpc только IPv4 — таймаут на хостах без IPv6
...
CI / test (push) Successful in 7m10s
CI / deploy-ift (push) Successful in 3m19s
CI / e2e-ift (push) Successful in 1m17s
CI / deploy-stage (push) Successful in 2m16s
CI / e2e-stage (push) Successful in 1m15s
На stage прямой режим к api.x.ai падал upstream_error ровно через
STT_TIMEOUT_MS: DNS Erlang отдаёт AAAA первым, IPv6-маршрута на хосте
нет, httpc с дефолтным inet6fb4 висит на connect до таймаута
(curl/wget с happy-eyeballs проходили). ipfamily=inet на профиле
eventhub_stt; IFT (прямой) и прокси-режим не затрагиваются.
2026-08-22 23:41:29 +03:00
aleksey
4486dc87db
Поиск: case-insensitive сравнение в logic_search
...
CI / test (push) Successful in 8m4s
CI / deploy-ift (push) Successful in 3m53s
CI / e2e-ift (push) Successful in 1m16s
CI / deploy-stage (push) Successful in 2m14s
CI / e2e-stage (push) Successful in 1m13s
- сопоставление имени/описания календарей приведено к lowercase
- юнит-тесты: 20/20
2026-08-22 21:08:04 +03:00
aleksey
d46eb6e528
fix(stt): xAI 400 при format=true без language — убираем поле format
...
CI / test (push) Successful in 7m50s
CI / deploy-ift (push) Successful in 3m56s
CI / e2e-ift (push) Successful in 1m18s
CI / deploy-stage (push) Successful in 2m16s
CI / e2e-stage (push) Successful in 1m11s
Smoke на IFT: xAI отвечает 400 "Field 'language' is required when
'format' is true" → бэк маппит в 502 upstream_error. Проверено curl:
без поля format эндпоинт /v1/stt работает (200), язык определяется
автоматически. Убрано поле format из multipart xAI-адаптера, eunit
обновлён (полный прогон: 1211 тестов зелёные).
Refs EventHub/EventHubBack#79
2026-08-21 21:32:13 +03:00
aleksey
1ef9020c40
Голосовой центр L2: прокси транскрибации POST /v1/ai/transcribe
...
CI / test (push) Successful in 7m45s
CI / deploy-ift (push) Successful in 2m53s
CI / e2e-ift (push) Successful in 1m23s
CI / deploy-stage (push) Successful in 2m9s
CI / e2e-stage (push) Successful in 1m50s
Back-прокси STT для press-to-talk (канон PRODUCT-AI, голос и тап —
два канала одного движка действий):
- handler_ai_transcribe + logic_ai_transcribe (паттерн logic_geo):
адаптеры провайдеров xai и openai-совместимый; env STT_PROVIDER,
STT_URL, STT_API_KEY, STT_MODEL, STT_TIMEOUT_MS, STT_PROXY_URL
- multipart read_file_part в handler_upload_utils (лимит 10 MB)
- ETS rate-limit: 10 req/min на пользователя + глобальный троттлинг
апстрима; без ключа — 503 unavailable, ошибки апстрима — 502/503
- аудио и транскрипт не персистятся (приват-инвариант канона)
- отдельный httpc-профиль: STT_PROXY_URL применяется только к STT
(эгресс stt-egress для гео-блокированных провайдеров)
- trails в eventhub_trails.erl, маршрут в eventhub_app.erl,
docker/.env.example дополнен STT_*
Проверка: eunit модуля 8/8; полный прогон eunit зелёный (1211 тестов),
compile без новых warning'ов.
Refs EventHub/EventHubBack#79
Refs EventHub/EventHubFront#76
2026-08-20 23:56:35 +03:00
aleksey
f4d89a262c
feat(ai): пилотные метрики ИИ-подсказок — POST /v1/ai/hint-metrics + админ-сводка
...
CI / test (push) Successful in 7m53s
CI / deploy-ift (push) Successful in 2m41s
CI / e2e-ift (push) Successful in 1m20s
CI / deploy-stage (push) Successful in 2m16s
CI / e2e-stage (push) Successful in 1m13s
Канон PRODUCT-AI (Spec#22): пилотные метрики L1-подсказок для расчёта
голосового гейта L2 (>=20% активных пользователей с >=2 подтверждённых
ghost-бронирований в месяц). Локальные счётчики в Mnesia, без LLM.
- таблица ai_hint_metric (миграция 20260820180000): счётчики
{user, день UTC, pattern, kind}; ленивый prune при >500 строк на
пользователя, retention 92 дня
- POST /v1/ai/hint-metrics (Bearer): батч <=20 событий shown/confirm/dismiss,
202 {accepted:N}; ETS rate limit 30 req/min на пользователя
- GET /v1/admin/ai-metrics/stats (админ-порт): by_kind, active_users_30d,
users_with_confirms_ge2_30d, gate_ratio_30d, top_patterns_by_confirm
- eunit logic_ai_metrics_tests (5/5); синхронизация списка миграций
в migration_engine_tests
Refs EventHub/EventHubBack#78
2026-08-20 18:35:29 +03:00
aleksey
cc08b3b18a
feat(search): default_location календарей в ответе всегда (для вида карты)
...
CI / test (push) Successful in 7m56s
CI / deploy-ift (push) Successful in 3m31s
CI / e2e-ift (push) Successful in 1m18s
CI / deploy-stage (push) Successful in 2m15s
CI / e2e-stage (push) Successful in 1m48s
format_calendar больше не требует geo_origin: default_location (address/
lat/lon либо null) включается в ответ GET /v1/search всегда, если точка
задана, — вид карты на странице поиска строится и без lat/lon в запросе.
distance_km по-прежнему возвращается только при гео-запросе.
Refs EventHub/EventHubBack#77
2026-08-19 22:01:07 +03:00
aleksey
73d43e5fdc
feat(geo): switch geocoder from self-host Photon to OpenCage API
...
CI / test (push) Successful in 7m44s
CI / deploy-ift (push) Successful in 3m57s
CI / e2e-ift (push) Successful in 1m16s
CI / deploy-stage (push) Successful in 2m36s
CI / e2e-stage (push) Successful in 4m12s
logic_geo проксирует OpenCage (suggest/geocode/reverse): env OPENCAGE_API_KEY,
OPENCAGE_URL, OPENCAGE_TIMEOUT_MS, OPENCAGE_COUNTRYCODE, OPENCAGE_THROTTLE_RPS
(1 rps под free-тариф, 0 - выключить). HTTP 402/403/429 апстрима ->
503 geo_unavailable; source=opencage. Сервис photon и volume photon-data
удалены из swarm compose; eunit-фикстуры под формат OpenCage.
Refs EventHub/EventHubBack#77
2026-08-18 23:30:15 +03:00
aleksey
6b70e1336d
feat(geo): Photon proxy, address-only location, nearby search. Refs EventHub/EventHubBack#77
CI / test (push) Successful in 7m38s
CI / deploy-ift (push) Successful in 3m27s
CI / e2e-ift (push) Successful in 1m16s
CI / deploy-stage (push) Successful in 2m18s
CI / e2e-stage (push) Successful in 4m11s
2026-08-17 23:23:10 +03:00
aleksey
501334e241
fix(test): archived month returns 409 before event_in_past.
...
CI / test (push) Successful in 36m6s
CI / deploy-ift (push) Successful in 2m44s
CI / e2e-ift (push) Successful in 1m19s
CI / deploy-stage (push) Successful in 2m34s
CI / e2e-stage (push) Successful in 4m14s
Refs EventHub/EventHubBack#76
Co-authored-by: Cursor <cursoragent@cursor.com >
2026-08-17 16:25:51 +03:00
aleksey
e3f5a7ca13
feat(archive): month snapshots on the same node, drop extra-node and HTML /view.
...
CI / test (push) Failing after 41m15s
CI / deploy-ift (push) Has been skipped
CI / e2e-ift (push) Has been skipped
CI / deploy-stage (push) Has been skipped
CI / e2e-stage (push) Has been skipped
Fixes EventHub/EventHubBack#76
Co-authored-by: Cursor <cursoragent@cursor.com >
2026-08-17 14:59:30 +03:00
aleksey
ef31e7fb40
fix(test): sync migration_engine_tests versions with auth_session_device. Refs EventHub/EventHubBack#74
CI / test (push) Successful in 7m23s
CI / deploy-ift (push) Successful in 3m59s
CI / e2e-ift (push) Successful in 1m29s
CI / deploy-stage (push) Successful in 2m15s
CI / e2e-stage (push) Successful in 4m26s
2026-08-16 18:58:54 +03:00
aleksey
0690020e81
feat(auth): list/revoke sessions with device_name and client_type. Fixes EventHub/EventHubBack#74
CI / test (push) Failing after 10m52s
CI / deploy-ift (push) Has been skipped
CI / e2e-ift (push) Has been skipped
CI / deploy-stage (push) Has been skipped
CI / e2e-stage (push) Has been skipped
2026-08-16 18:30:05 +03:00
aleksey
1c701a5254
fix(share): register calendar_share migration and rebuild old PK schema. Refs EventHub/EventHubBack#73
CI / test (push) Successful in 9m32s
CI / deploy-ift (push) Successful in 4m35s
CI / e2e-ift (push) Successful in 1m40s
CI / deploy-stage (push) Successful in 2m27s
CI / e2e-stage (push) Successful in 4m23s
2026-08-16 10:04:16 +03:00
aleksey
fb927b7f71
fix(ci): e2e-stage defaults to *.stage.calentiq.com when secrets empty.
...
CI / test (push) Successful in 7m31s
CI / deploy-ift (push) Successful in 2m35s
CI / e2e-ift (push) Successful in 2m46s
CI / deploy-stage (push) Successful in 2m33s
CI / e2e-stage (push) Successful in 4m22s
Empty E2E_STAGE_* overrode :-defaults with blank hosts. Refs EventHub/EventHubBack#73
2026-08-16 00:25:12 +03:00
aleksey
c1b4897a8b
fix(test): use commercial fixtures in logic_search_tests.
...
CI / test (push) Successful in 7m27s
CI / deploy-ift (push) Successful in 3m30s
CI / e2e-ift (push) Successful in 1m30s
CI / deploy-stage (push) Successful in 2m5s
CI / e2e-stage (push) Failing after 3m22s
Personal is never discoverable; assert exclusion explicitly. Refs EventHub/EventHubBack#73
2026-08-16 00:04:26 +03:00
aleksey
fcb737eb47
fix(test): ensure calendar_share table in ACL-related eunit suites.
...
CI / test (push) Failing after 6m13s
CI / deploy-ift (push) Has been skipped
CI / e2e-ift (push) Has been skipped
CI / deploy-stage (push) Has been skipped
CI / e2e-stage (push) Has been skipped
Harden core_calendar_share get/list when table missing. Refs EventHub/EventHubBack#73
2026-08-15 23:48:12 +03:00
aleksey
331000a464
feat(share): calendar_share invite/ACL for personal and commercial.
...
CI / test (push) Failing after 6m37s
CI / deploy-ift (push) Has been skipped
CI / e2e-ift (push) Has been skipped
CI / deploy-stage (push) Has been skipped
CI / e2e-stage (push) Has been skipped
Co-editor and deputy grants with mirror_to_default; personal out of search; list owned+shared. Refs EventHub/EventHubBack#73
2026-08-15 23:20:11 +03:00
aleksey
00bf72e4d1
test(notify): prefs gate email/push on reminder and waitlist.
...
CI / test (push) Successful in 7m47s
CI / deploy-ift (push) Successful in 3m5s
CI / e2e-ift (push) Successful in 1m26s
CI / deploy-stage (push) Successful in 2m13s
CI / e2e-stage (push) Failing after 3m23s
Assert email=false skips SMTP; push=true hits web_push_deliver.
Refs EventHub/EventHubBack#75
2026-08-15 21:50:08 +03:00
aleksey
12274f9ed0
feat(notify): Web Push subscriptions and email/push prefs.
...
CI / test (push) Successful in 7m43s
CI / deploy-ift (push) Successful in 3m33s
CI / e2e-ift (push) Successful in 1m28s
CI / deploy-stage (push) Failing after 3m40s
CI / e2e-stage (push) Has been skipped
Prefs in user.preferences; push_subscription table; VAPID send on
reminder/waitlist. Refs EventHub/EventHubBack#75
2026-08-15 20:19:44 +03:00
aleksey
e86d603f30
fix(email): encode Cyrillic binaries with /utf8.
...
CI / test (push) Successful in 7m11s
CI / deploy-ift (push) Successful in 3m12s
CI / e2e-ift (push) Successful in 1m26s
CI / deploy-stage (push) Failing after 3m33s
CI / e2e-stage (push) Has been skipped
OTP truncates non-ASCII in <<"...">> to 8-bit bytes, garbling Russian
in mail and in-app notification copy.
2026-08-15 00:32:33 +03:00
aleksey
0a1e55e036
feat(email): HTTP API transport via Resend (fallback SMTP).
...
CI / test (push) Successful in 7m40s
CI / deploy-ift (push) Successful in 3m29s
CI / e2e-ift (push) Successful in 1m27s
CI / deploy-stage (push) Failing after 3m45s
CI / e2e-stage (push) Has been skipped
Outbound SMTP ports are often blocked on VPS; EMAIL_TRANSPORT=http_api
sends via Resend HTTPS (Brevo optional). OpenSMTPd/SMTP remain available.
2026-08-14 23:54:10 +03:00
aleksey
fb8869fa86
feat(waitlist): optional event waitlist with FIFO promote.
...
CI / test (push) Successful in 7m12s
CI / deploy-ift (push) Successful in 2m44s
CI / e2e-ift (push) Successful in 1m28s
CI / deploy-stage (push) Successful in 2m3s
CI / e2e-stage (push) Successful in 1m24s
Commercial calendars opt in via settings.waitlist_enabled; join when full;
auto-promote on cancel/decline/expire with email and in-app notify.
Refs EventHub/EventHubBack#72
2026-08-14 21:44:42 +03:00
aleksey
29111cd514
feat(upload): avatar and calendar cover via local disk storage.
...
POST /v1/user/me/avatar and /v1/calendars/:id/cover; GET /v1/media;
UPLOAD_DIR on eventhub-data volume; MIME/size limits.
Refs EventHub/EventHubBack#71
Refs EventHub/EventHubSpec#8
2026-08-14 21:27:34 +03:00
aleksey
15ae8d4426
feat(email): send booking reminders before event start.
...
Job process_reminders in subscription_worker; REMINDER_LEAD_HOURS window;
CalenTIQ email + in-app event_reminder; idempotent reminder_sent.
Refs EventHub/EventHubBack#70
2026-08-14 20:59:38 +03:00
aleksey
347a645933
feat(auth): POST /v1/logout and admin logout revoke refresh session. Refs EventHub/EventHubBack#69
CI / test (push) Successful in 7m32s
CI / deploy-ift (push) Successful in 2m29s
CI / e2e-ift (push) Successful in 1m29s
CI / deploy-stage (push) Successful in 2m3s
CI / e2e-stage (push) Successful in 2m1s
2026-08-14 20:23:16 +03:00
aleksey
302e1e5e9e
chore: ignore local Traefik certs; keep make-traefik-certs.sh. [skip ci]
2026-08-14 19:46:18 +03:00
aleksey
69ee037d45
feat(email): SMTP via gen_smtp and CalenTIQ HTML templates. Refs EventHub/EventHubBack#68
2026-08-14 18:51:09 +03:00
aleksey
3d7ae0d372
fix(api): resolve public calendar GET by unique short_name.
CI / test (push) Successful in 7m28s
CI / deploy-ift (push) Successful in 2m46s
CI / e2e-ift (push) Successful in 1m26s
CI / deploy-stage (push) Successful in 2m4s
CI / e2e-stage (push) Successful in 2m4s
2026-08-14 15:00:41 +03:00
aleksey
658cbda4fe
chore(rules): ignore .tmp scratch; IFT e2e needs SMOKE_USER. [skip ci]
2026-08-14 12:52:20 +03:00
aleksey
2567585333
feat(booking): studio journal and instance inbox. Refs EventHub/EventHubFront#60
CI / test (push) Successful in 7m31s
CI / deploy-ift (push) Successful in 2m38s
CI / e2e-ift (push) Successful in 1m26s
CI / deploy-stage (push) Successful in 2m3s
CI / e2e-stage (push) Successful in 1m21s
2026-08-14 11:13:01 +03:00
aleksey
520af23cc6
feat(api): optional auth for public search and studio week. Refs EventHub/EventHubFront#58
2026-08-14 11:02:58 +03:00
aleksey
8a5f254c2a
feat(booking): materialize occurrence on POST bookings. Refs EventHub/EventHubBack#67
2026-08-14 10:52:24 +03:00
aleksey
e3403f6ed6
fix(ift): ObserverWeb mount at "" so CSS/JS paths are not //css-*
...
CI / test (push) Successful in 7m23s
CI / deploy-ift (push) Successful in 5m31s
CI / e2e-ift (push) Successful in 1m31s
CI / deploy-stage (push) Successful in 3m16s
CI / e2e-stage (push) Successful in 1m22s
observer_dashboard("/") makes asset href protocol-relative (//css-HASH). Empty path keeps assets at /css-HASH.
2026-08-13 11:27:41 +03:00
aleksey
37011f66a1
fix(ift): mount ObserverWeb dashboard at / on dedicated host
...
CI / test (push) Successful in 7m42s
CI / deploy-ift (push) Successful in 5m40s
CI / e2e-ift (push) Successful in 1m28s
CI / deploy-stage (push) Successful in 2m12s
CI / e2e-stage (push) Successful in 1m20s
observer.ift.calentiq.com already has its own subdomain; /observer path prefix is redundant.
2026-08-13 10:54:19 +03:00
aleksey
c89d08968d
fix(test): drop redundant user.email index setup
...
CI / test (push) Successful in 7m8s
CI / deploy-ift (push) Successful in 2m53s
CI / e2e-ift (push) Successful in 12m1s
CI / deploy-stage (push) Successful in 2m18s
CI / e2e-stage (push) Successful in 1m18s
ensure_tables already adds the email index; a second add_table_index
aborted with {already_exists,user,3} and failed the suite setup.
2026-08-04 10:46:32 +03:00
aleksey
72bc440049
fix(test): add mnesia secondary indexes in eh_test_support
...
CI / test (push) Failing after 9m25s
CI / deploy-ift (push) Has been skipped
CI / e2e-ift (push) Has been skipped
CI / deploy-stage (push) Has been skipped
CI / e2e-stage (push) Has been skipped
EUnit tables must mirror infra_mnesia:create_indices so dirty_index_read
paths in booking/event/search/user do not abort with badarg.
2026-08-04 10:27:58 +03:00
aleksey
6c0775ee5e
feat(infra): add TTL cleanup worker for append-only tables
...
CI / test (push) Failing after 5m58s
CI / deploy-ift (push) Has been skipped
CI / e2e-ift (push) Has been skipped
CI / deploy-stage (push) Has been skipped
CI / e2e-stage (push) Has been skipped
Periodically prune old read notifications, revoked/expired auth sessions,
and admin_audit in chunked transactions; wire under eventhub config.
2026-08-03 22:57:41 +03:00
aleksey
774f58f9ed
fix(user): transactional status updates and paginated list_users
...
Protect status/login/bot writes with transactions, fix abort Reason
shadowing, keep create_bot email_exists, and add list_users/2 pagination.
2026-08-03 22:56:16 +03:00
aleksey
d88689f1a3
fix(auth): make auth_session mutations transactional
...
Wrap create/rotate/revoke paths in mnesia transactions so concurrent
refresh and family revoke stay atomic; align revoke_* specs with errors.
2026-08-03 22:55:52 +03:00
aleksey
e3467e7412
perf(mnesia): use secondary indexes in booking, event, search, review
...
Replace hot-path dirty_match_object scans with dirty_index_read and batch
calendar lookup in search to cut N+1 enrichment cost.
2026-08-03 22:55:50 +03:00
aleksey
104892bdde
fix(ci): remove orphan containers before mnesia volume wipe [skip ci]
...
After Swarm scale=0, shutdown containers can still hold eventhub-data-N;
force-rm holders and retry volume rm so wipe-mnesia-ift does not fail.
2026-08-01 18:50:07 +03:00
aleksey
2b8fc19822
ci: extract wipe-mnesia into standalone workflows [skip ci]
...
Move IFT/stage Mnesia wipe out of CI pipeline choice so Gitea Actions shows them as separate dispatch workflows.
2026-08-01 18:43:46 +03:00
aleksey
4eb1f326ec
feat(ci): add manual wipe-mnesia jobs for IFT/stage [skip ci]
...
workflow_dispatch only: wipe-mnesia-ift / wipe-mnesia-stage and scripts/ci-full-mnesia-wipe.sh.
Co-authored-by: Cursor <cursoragent@cursor.com >
2026-08-01 18:30:11 +03:00
aleksey
0a5e86c4a0
fix(recurrence): honor BYDAY for weekly RRULE expansion
...
CI / test (push) Successful in 8m33s
CI / deploy-ift (push) Successful in 3m26s
CI / e2e-ift (push) Successful in 3m44s
CI / deploy-stage (push) Successful in 2m14s
CI / e2e-stage (push) Successful in 1m21s
Expand weekdays in the DTSTART week and advance by Interval weeks from Monday;
add unit coverage for MO/WE/FR and mid-week starts.
2026-08-01 18:17:23 +03:00
aleksey
0e4e886cd4
feat(specialists): owner is non-deletable specialist on commercial create
...
CI / test (push) Successful in 8m35s
CI / deploy-ift (push) Successful in 21m2s
CI / e2e-ift (push) Successful in 2m3s
CI / deploy-stage (push) Successful in 2m9s
CI / e2e-stage (push) Successful in 1m16s
Bootstrap calendar_specialist for owner (active); DELETE owner → 403;
toggle via status. Fixes solo assign-to-slot. Refs EventHub/EventHubBack#66
2026-08-01 12:18:36 +03:00