> 🌐 **Жива версія брифа** — завантажено з `formulas.freyal.renderhorizon.org`. **Оновлено:** `2026-09-25 23:30:01 IST` · rev `9615e19` (ребілд кожні 5 хв). Це завжди остання редакція.

# 🆕 Інтерактивний бриф — СТВОРЕННЯ нового звіту

> 🚨 **АКТИВАЦІЯ — асистент читає ПЕРШИМ, до будь-якої дії.**
> Якщо цей файл у контексті чату (користувач вклав/вставив його) — це **ОБОВʼЯЗКОВИЙ протокол** на задачу,
> а не довідка. **Заборонено** одразу будувати звіт/ТЗ ad-hoc. Першим повідомленням: підтверди «**Веду за брифом new-report**»,
> і йди **строго по кроках** — інтерв'ю (питання через **AskUserQuestion**), крос-звірка метрик з живими джерелами,
> self-check, закриття **всіх** питань — і лише тоді чисте ТЗ. **Ознака порушення:** видав результат без інтерв'ю/звірок — почни спочатку.

> **Що це.** Живий сценарій інтерв'ю із замовником. Асистент (Claude) веде замовника по кроках,
> ставить питання, приймає його власний варіант **або** пропонує готове з каталогу, звіряє все
> скілом `report-skill-from-spec`, крос-чекає метрики з [`METRICS_CATALOG.md`](./METRICS_CATALOG.md)
> і **‹зовнішня панель›/settings-provider**, а в кінці видає **чисте ТЗ без питань**, готове до
> `build-and-deploy-report`.
>
> **Вхід:** побажання замовника (усно/текст/скрін/лінк на PBI). **Вихід:** файл `ТЗ_<slug>.md`.
>
> 🚫 **Межі цієї версії брифа:** він **тільки збирає ТЗ**. Не деплоїть, не пише код, не ходить у наші бази/панелі
> й не має доступів. Готовий файл ТЗ ти **надсилаєш власнику** — деплоїть він.

---

## 🤖 Протокол ведення (для асистента — читати перед стартом)

> 🔒 **Що НЕ треба вписувати в ТЗ.**
> ТЗ поїде далі — його читатимуть люди й інструменти на боці виконавця, і він
> зберігається. Тому **не копіюй** у нього паролі й доступи, реальні імена
> клієнтів/мерчантів, телеграм-хендли й персональні дані. Якщо приклад потрібен —
> назви його умовно («мерчант A»), а справжню відповідність передаси окремо.
> Скріни й файли додавай **посиланням через форму завантаження** (§ вкладення),
> а не вставкою в текст — так вони живуть 48 годин і не залишаються назавжди.


1. **Не вигадуй за замовника.** Порожнє поле = відкрите питання, а не здогад.
2. Іди по секціях **1 → 2 → 3 → 4 → 5 → 5-bis → 6 → 7** по черзі. На кожному полі:
   ⚠️ **`5-bis` (МАКЕТ) — повноцінний крок, не примітка.** Раніше маршрут писався як «1→7»,
   і `5-bis` фізично випадав зі списку: секція існувала, а в жодному з 14 ТЗ немає слова «макет».
   Обов'язковий крок, який не потрапив у перелік кроків, не виконується.
   - якщо у замовника **є свій варіант** — зафіксуй його дослівно;
   - якщо **немає / «а що можна?»** — запропонуй варіанти **з каталогу** (розділи 1–5 каталогу) і поясни.
3. **Свіжість формул — ПЕРШИМ ділом.** Підтягни останній каталог із **live-URL**
   `https://formulas.freyal.renderhorizon.org/metrics-catalog.md` (WebFetch) — він оновлюється кожні 5 хв, тож
   свіжий навіть у зовнішнього замовника. На нашому сервері ще й перезвір формулу з canonical
   (**внутрішній довідник фінформул** (у власника), `LEGEND*`, код звіту). **Жива версія перемагає знімок у файлі.**
   Потім крос-чек із каталогом (§2/§3): «це наша `CR`/`vd`/`Vol` чи нове?». У ТЗ іде **жива** формула, не заморожена.
4. Якщо замовник тицяє на статус/баланс/ліміт провайдера — відкрий **розділ 5 каталогу** (supportzones-поля)
   і уточни, яке саме поле панелі `settings-provider` він має на увазі.
**🔴 Ознака порушення:** ти пішов **реалізовувати / деплоїти**, а питання «закриваємо чи є ще
правки?» не ставив. Перехід до реалізації сесію брифа **не закриває** — повернись і постав його.
Так само перевір, чи запускав `brief-gap-check` після кожної секції: цей крок губиться першим,
бо його не видно в результаті.

5. Коли всі секції зібрані — прогони **крок 8 (self-check скілом)**, закрий залишкові питання (крок 9),
   згенеруй **крок 10 (чисте ТЗ)** — і **обов'язково зроби крок 11: спитай, закриваємо сесію чи є ще правки**.
   Не завершуй роботу мовчки: замовник часто має 2-3 правки в голові й не знає, що процес завершено.
6. Зашиті домовленості (IST, INR, статуси, ISO-тиждень) **не перепитуй** — вони вже в каталозі,
   питай лише відхилення від них.
7. **Крос-звірка — з каталогом формул (доступів до наших систем не треба).**
   ✅ **Немає інтернету / не відкривається URL — це НОРМАЛЬНО, не помилка і не блокер.**
   Повний каталог метрик **уже вшитий у цей файл нижче** — просто працюй із ним і йди далі.
   У ТЗ додай один рядок: `Формули звірені з каталогом, вшитим у бриф (версію див. у шапці каталогу)`.
   Якщо інтернет є — можеш додатково звірити зі свіжим каталогом за URL нижче.
   Кожну метрику звір із живим каталогом і пікером:
   - `https://formulas.freyal.renderhorizon.org/metrics-catalog.md` — формули й інваріанти;
   - `https://formulas.freyal.renderhorizon.org/metrics-picker.md` — усі метрики по джерелах, замовник обирає джерело.
   Якщо метрики там немає або формула відрізняється — **це не привід вигадувати**: зафіксуй у ТЗ як
   «потребує підтвердження власником» і сформулюй питання. Звірку з живими даними (ClickHouse, прод-звіти,
   борди) робить **власник** на своєму сервері — тобі туди не треба й не можна.


> Легенда позначок: `☐` — вибір · `→ каталог §X` — де взяти готове · `⚠` — потребує звірки · `❓` — питання замовнику.

---

## 1. Загальне (навіщо і для кого)

> 📎 **НАДІСЛАВ ФАЙЛ — ЗАВАНТАЖ ЙОГО І ПОСТАВ ПОСИЛАННЯ В ТЗ.**
> Скрін, експорт, фото, сторінка — усе, що замовник показує під час розмови.
>
> **Куди:** `https://admin.formulas.freyal.renderhorizon.org/upload`
> Перетягнув файл → одразу отримав посилання → встав його в ТЗ рядком
> `(див. вкладення 01: <посилання>)` і додай у таблицю «📎 Вкладення» наприкінці ТЗ.
>
> 🔴 **Замовник НІЧОГО не прикріплює окремо.** Раніше бриф казав «збережи файл і не забудь
> надіслати разом із ТЗ» — половина забувала, і домовленість «має виглядати ось так» гинула.
> Тепер у ТЗ їде посилання, і файл їде разом із ним сам.
>
> ⏳ Посилання живе **48 годин** — цього досить на узгодження ТЗ. Тому **не тягни**:
> надішли ТЗ власнику, поки вкладення живі.
> ⚠️ Не завантажуй паролі, персональні дані клієнтів і банківські виписки —
> для узгодження вигляду достатньо скріна з інтерфейсу.

> ⏱️ **СКАЖИ ЗАМОВНИКУ ЦЕ ПЕРШИМ — до першого питання.**
>
> «Я поставлю кілька запитань і на виході дам готове ТЗ. Орієнтовно:
> **простий звіт з 1–2 показниками — 30–40 хвилин**, типовий на 8–12 показників — **2–4 години**,
> зазвичай у кілька заходів. **Перерватись можна будь-коли** — те, що зібрали, не пропаде.
> Мінімум, з якого вже є сенс почати: **навіщо звіт + один показник**; решту доповнимо потім.
> Якщо чогось не знаєте — кажіть “не знаю”, я запропоную варіанти з готових звітів.»
>
> **Навіщо.** Замовник відкриває файл і бачить сотню полів — без цієї рамки він або кидає,
> або відповідає навмання, аби швидше. Чесна оцінка на початку дешевша за покинутий бриф.
>
> 🚦 **Якщо замовник не знає, чого хоче** — не веди його по секціях. Спитай **одним питанням**:
> «Яке рішення ви хочете приймати по цьому звіту?» — і запропонуй **готовий звіт** із каталогу §1:
>
> | Він каже | Покажи спершу |
> |---|---|
> | «хто з трейдерів працює погано» | `Scoreboard` — скоринг і тири |
> | «скільки заводимо і виводимо по мерчантах» | `Merchant Out/In` |
> | «що зараз відбувається, оперативно» | `Trader Operational` |
> | «як просів обсяг цього тижня» | `Weekly KPI` |
> | «хто з трейдерів відвалився» | `Retention & Churn` |
> | «як працюють афіліати» | `Affiliate Dashboard` |
>
> Дуже часто виявляється, що потрібного **не треба будувати** — достатньо доопрацювати наявне.
> Це і швидше, і дешевше: тоді переходь на бриф доопрацювання.

- **Робоча назва звіту:** `____`

> 🖼️ **НАЗВА Є → ПОКАЖИ МАКЕТ НАЙБЛИЖЧОГО НАЯВНОГО ЗВІТУ.** Навіть для нового звіту: у 8 випадках із 10 є схожий, і від нього легше відштовхнутись.
>
> `https://formulas.freyal.renderhorizon.org/mockups/<id-звіту>.html`
> Список усіх: `https://formulas.freyal.renderhorizon.org/mockups/`
>
> Це **оригінальний вигляд** звіту — ті самі вкладки, колонки, порядок блоків.
> **Числа в ньому вигадані** (плашка «МАКЕТ» угорі), даних не завантажує, логіну не треба.
>
> **Як вести розмову далі:** відкрий макет і проси замовника показувати **на ньому**:
> — «схоже на це? що лишаємо, що прибираємо?»
> — «яких колонок бракує, які зайві?»
> — «цей блок лишається як є?»
> Потім у ТЗ пиши прив'язку до видимого: *«як у Scoreboard, але замість тирів — колонка X»*,
> а не «десь у погодинному розрізі».
>
>
>
> ✏️ **МАЛЮЙ ПРАВКУ ПРЯМО В МАКЕТІ — не описуй словами.**
> Макет розуміє параметри в URL, тож правку видно **до розробки**:
>
> | Треба показати | Додай до посилання |
> |---|---|
> | цю колонку/картку **міняємо** | `?hl=CR,Score` |
> | **додаємо нову** колонку | `?add=Speed p95` |
> | цю **прибираємо** | `?del=Zone` |
> | нова **KPI-картка** | `?card=Активні трейдери` |
> | новий **графік** | `?chart=CR по днях` |
> | підпис до правки | `?note=правка №3` |
>
> Кілька — через кому, кілька параметрів — через `&`. Приклад готового посилання:
> `…/mockups/trader-scoreboard.html?hl=CR&add=Speed p95&del=Zone&note=правка №3`
>
> Замовник бачить **свою таблицю**, у ній жовтим підсвічено те, що змінюємо,
> зеленим пунктиром домальовано нове, червоним перекреслено те, що прибираємо,
> і внизу — легенда. **Оновлюй посилання по ходу розмови**: домовились про ще одну
> колонку — додав `&add=…` і показав знову. Це і є «як воно виглядатиме».
> 🧭 **І ВІДКРИЙ КАРТУ КОЛОНОК ЦЬОГО ЗВІТУ:**
> `https://formulas.freyal.renderhorizon.org/reports-columns.md`
>
> Там для кожного звіту — **усі колонки з визначенням прямо з коду**: що рахує, які пороги,
> який тип значення. Це дає три речі, яких немає в розмові словами:
> — одразу видно, **чи потрібна метрика вже є** (щоб не додавати дубль під іншою назвою);
> — можна сказати **«після колонки CR»**, а не «десь у таблиці»;
> — видно **тип** (%, хвилини, ₹) — тобто одразу зрозуміло, чи можна її додавати в цю таблицю.
>
> ⚠️ Карта збирається з коду **щодня**, тож відповідає тому, що зараз на сервері.
> Якщо колонки в карті немає — її немає і в звіті, це **нова розробка**, а не «просто показати».
> *Навіщо: без картинки замовник уявляє одне, ти інше, і розходження виявляється
> вже на готовому звіті. Показати наявний звіт швидше, ніж описувати новий словами.*
- **Хто замовник / для кого (роль):** `____` → каталог §UI-ролі (менеджер / афіліат / аналітик / фінанси / CEO)
- ❓ **На яке питання має відповідати звіт? Яке рішення приймають по ньому?** `____`
  *(без цього неможливо обрати метрики — питай обов'язково)*
- ❓ **Це справді новий звіт, чи розширення наявного?** → покажи **каталог §1** (список звітів на halleg).
  Якщо потрібне вже є як вкладка/слайсер — запропонуй доопрацювання (`BRIEF_edit_report.md`) замість нового.
- ❓ **Замовник дав скрін / лінк на інший інструмент?** Це **референс-джерело**: спершу **розпарс його**
  (доступ до чужої панелі є лише у власника), і лише потім став питання. Кейс: замість вибору варіанта
  замовник дав OPS-дашборд, а там була колонка `Topup INR`, яка й відповіла на питання про Exchange-мерчантів.
- **Пріоритет / дедлайн:** `____`


> 🔍 **ПІСЛЯ КОЖНОЇ СЕКЦІЇ — перевір себе за сімома питаннями:**
> 1) чи показував **макет**, а не описував словами? 2) чи «як перевірити» називає **артефакт**,
> який можна відкрити? 3) чи заповнений **паспорт** кожної метрики? 4) чи не взяв дефолт **мовчки**?
> 5) якщо замовник каже «як у тому звіті» — чи звірив **колонки** по карті? 6) чи прохання взагалі
> **здійсненне**? 7) чи не робимо те, що **вже є**?
> Щілина, знайдена зараз, коштує одне питання. Знайдена після збірки — переробку.
> **Роби видимий слід:** після кожної секції — один рядок у чат, навіть коли все чисто
> (`✅ перевірив §3: паспорт повний, критерії прив'язані`). Цей крок губиться першим саме тому,
> що його **не видно в результаті**: замовник бачить ТЗ, а перевірку — ні.

## 2. Джерело даних і період

> 🗣️ **СЛОВНИК «СПИТАЙ ТАК» — читай перед §2 і §3.**
> Замовник переважно **не технічний**: менеджер, фінансист, власник. Терміни нижче він не знає,
> і на питання «який грейн?» просто зупиниться. Ліва колонка — як написано в брифі,
> права — **як це вимовити вголос**. Питай правою, записуй лівою.
>
> | У брифі | Як спитати замовника |
> |---|---|
> | Грейн | «**Один рядок звіту — це що?** година · день · тиждень · місяць» |
> | ISO-тиждень | «тиждень з понеділка по неділю, як у календарі» |
> | as-of дата | «“станом на дату” — рахуємо все, що сталося **до** неї включно» |
> | Закриті періоди | «тільки повністю завершені — вчорашній день, минулий місяць» |
> | SNAPSHOT / FLOW | «**Це “скільки зараз” чи “скільки набігло за період”?** Залишок на рахунку — зараз. Обіг за місяць — за період» |
> | Чи агрегується | «**Чи можна це просто скласти по групах?** Суми й кількості — так. Відсотки й середні — ні, їх треба рахувати заново» |
> | Знаменник | «**Ділимо на що?** На всі дні періоду — чи лише на ті дні, коли була робота?» |
> | Зважене vs просте середнє | «Середній відсоток по 10 трейдерах — це відсоток від їхніх **спільних** сум, чи середнє з їхніх 10 відсотків? Це різні числа» |
> | Перцентиль / P90 | «**У 9 випадках із 10 вкладаємось у скільки хвилин?** Це надійніше за середнє: одна аномалія не псує картину» |
> | Hour-to-Hour | «порівнюємо з тим самим відрізком: сьогодні до 14:00 проти вчора до 14:00» |
> | RLS | «**Хто які дані бачить?** Усі бачать усе — чи кожен менеджер лише своїх?» |
> | Порожній набір | «Якщо за період даних немає — показати прочерк “—” чи нуль? Це різні речі: “—” = не було, “0” = було нуль» |
> | Drill-down | «клікнути на рядок і провалитись у деталі» |
>
> ⚠️ **Не читай замовнику ліву колонку.** Якщо він сам оперує термінами — добре, переходь на них.

- **Джерело:** ☐ ClickHouse `hermes` (за замовч.) ☐ Postgres prod ☐ Google Sheets ☐ інше: `____`
- **Таблиці:** `____` → каталог §7 (`transactions/payments/expenses/bank_accounts/providers/…`)
- **Період даних:** ☐ MTD ☐ ковзне вікно N днів = `__` ☐ фіксований ISO-тиждень ☐ довільний From–To ☐ Hourly
- **Грейн (вкладки):** ☐ Hourly ☐ Daily ☐ Weekly (ISO Пн–Нд) ☐ Monthly — може бути кілька вкладок: `____`
- **Як задається вікно:** ☐ **as-of дата (включно)** + пресети ☐ закриті періоди ☐ довільний From–To — обери: `____`
  - чи входить **поточний неповний день**? (за замовч. — так, якщо as-of = сьогодні; підписати це в UI)
  - ⚠ додай **найдрібніший грейн, що є в джерела-еталона** (зазвичай **день**) — інакше неможливо звірятись день-у-день
- **Таймзона:** IST за замовчуванням; інша → `____`
- **Кого вважаємо «трейдером/клієнтом»:** `____` *(за замовч. — provider p2p; статуси active/…)*

> ❓ **Що означає ПОРОЖНІЙ фільтр — «усі» чи «нічого»?** Для кожного слайсера, який відкриває дані
> (афіліат, мерчант, трейдер), спитай явно: порожній вибір показує все — чи нічого, доки не обрано.
> Змінює перший екран і навантаження на базу; дізнатись про це після релізу дорого.

> 🔄 **Якщо джерело ЗОВНІШНЄ (Google-таблиця, чужий API, файл-мапінг)** — одразу зафіксуй режим оновлення:
> ☐ крон (TTL/розклад) ☐ вручну на запит (команда + хто запускає) ☐ разовий імпорт; і де лежить знімок.
> Розвилка стосується будь-якого мапінгу, не лише контент-розділів.

> 🧭 **ЧУЖИЙ БОРД / КАБІНЕТ / ПАНЕЛЬ як джерело або еталон.**
> Якщо замовник каже, що дані треба брати з іншої панелі, або що «має збігатися з тим кабінетом» —
> **не намагайся її парсити**. Просто зафіксуй у ТЗ: назву панелі, конкретний екран/розділ,
> і **скріншот або експорт** від замовника з прикладом потрібних цифр.
> Розбір самої панелі зробить власник звітів — у нього для цього є доступи й інструмент.

## 3. Метрики (серце ТЗ) — по рядку на метрику

Для кожної: питай назву → **крос-чек з каталогом §2/§3** → якщо є, підстав готову формулу; якщо ні — фіксуй нову.

| # | Метрика (як каже замовник) | Це наша? (з каталогу) | **Джерело формули** (обирає замовник) | Формула (фінальна) | Одиниця |
|---|---|---|---|---|---|
| 1 | `____` | ☐ так: `____` ☐ нова ⚠ | ☐ 🥇 halleg ☐ lab/Фінанси ☐ BAM ☐ dsfrogboard ☐ dsfrogboard-aff ☐ Uspacy CRM ☐ supportzones ☐ eye-of-god | `____` | `____` |
| 2 | `____` | ☐ так ☐ нова ⚠ | ☐ 🥇 halleg ☐ lab ☐ BAM ☐ dsfrogboard ☐ dsfrogboard-aff ☐ Uspacy CRM ☐ supportzones ☐ eye-of-god | `____` | `____` |

### 🪪 ПАСПОРТ МЕТРИКИ — заповнити для КОЖНОЇ, до підписання ТЗ

Без цих полів метрика не вважається описаною. Кожен рядок ловив реальний інцидент.
**Це чекліст-індекс**; розгорнуті пояснення й кейси до кожного поля — у буллетах нижче в цій же секції.

| Поле | Варіанти / формат | Що ловить |
|---|---|---|
| **Джерело формули** | `каталог §X (vNN)` **або** `НОВА → затвердив <хто, дата>` | дрейф: ТЗ спиралось на знімок каталогу, що відстав на 29 версій |
| **Поточний стан** | `НОВЕ` · `Є НА БЕКЕНДІ, НЕ ПОКАЗАНО` · **`Є, АЛЕ РАХУЄ ІНАКШЕ`** · `Є І ПРАВИЛЬНЕ` | 3 пункти «нове» вже були в коді; і навпаки — картка показувала 7 днів замість вікна фільтра |
| **Тип значення** | `SNAPSHOT` (стан «зараз») · `FLOW` (за період) + **чи є історія** | Δ для snapshot-метрик **неможливий заднім числом** |
| **Чи агрегується** | `адитивна` · `відношення` · `середнє` · `перцентиль` | «AVG по групі» зі середніх — математично неможливо |
| **Знаменник** | словами + **по чому усереднюємо**: `дні З АКТИВНІСТЮ` · `усі календарні дні` | різниця до **14 разів** на неактивних афіліатах |
| **Δ** | `різниця (₹)` · `відсоток (%)` + валюта розрахунку `INR`/`USDT` | ТЗ казало «різниця», борд історично показував % |
| **Порожній набір** | `«—» + пояснення` · `0` | «—» = даних не було · «0» = було рівно нуль. Це різні відповіді, і плутати їх не можна |
| **Слайсери** | які саме діють на цей блок | нова вкладка слухала лише період, решту ігнорувала |
| **Назва** | чи є вже така сутність на сусідньому борді — під якою назвою | «Other» на одному борді = «— Без менеджера» на іншому |
| **Як перевірити** | спостережуваний артефакт | див. правило нижче |

> 🔴 **ПРАВИЛО ШАПКИ: якщо текст формули суперечить критерію «Як перевірити» — виграє КРИТЕРІЙ.**
> Критерій привʼязаний до спостережуваного артефакту й не «протухає»; формула, переписана руками, — може.
> *Кейс: формула казала `SUM(cum_dep)` з виключеннями, критерій — «= Deposit у сусідньому звіті 1:1».
> Це виявились **різні метрики**, і правдою був критерій.*

> 🔴 **Джерело формули фіксується для КОЖНОЇ метрики — навіть коли обрано halleg.** Порожня клітинка
> «джерело» = дефолт узяли мовчки, і в ТЗ не видно, чи вибір узагалі був. Аудит 13 наших ТЗ показав:
> у жодному не зафіксовано альтернативного джерела, хоча пікер має **239 метрик із 6 джерел** —
> тобто меню формул де-факто не працювало.
>
> **Коли до якого джерела йти (тригери):**
> | Ситуація | Джерело |
> |---|---|
> | CR / конверсія не сходиться, треба незалежне підтвердження | **eye-of-god** (`success/(success+reject)`, перевірено 12/12) |
> | Статуси, ліміти, пороги, налив, **комісії/фі** провайдера | **supportzones** (першоджерело; комісій немає ні в звітах, ні в CH) |
> | Метрика про **співробітників**: KPI, бонуси, дедуп, індекс, settle-премія | **BAM** |
> | Ліквідність, сеттли, курси, USDT-баланси, диспути, прогноз | **lab.dsfrogboard** |
> | **Комісія аффа/партнера**, частка з трейдера чи мерчанта, «круг», хто на кому висить | **dsfrogboard `/affiliates`** |
> | **Хто веде афіліата** (сервіс-менеджер), tier, статус у воронці, онбординг трейдера (KYC/депозит) | **Uspacy CRM** |
> | Фінансова формула (CR, settlement, deposit, маржинальність, скоринг) | **canonical** `knowledge/financial-formulas/` |
> | Усе інше | 🥇 **halleg** (default) |

> 🔗 **ЗВІТ ТОРКАЄТЬСЯ АФІЛІАТІВ ЧИ АФФ-МЕНЕДЖЕРІВ? Спитай про джерело мапінгу.**
> Прив'язка «афіліат → менеджер» у наших звітах приходить **не з бази платежів**, а із
> **зовнішніх Google-таблиць** — і не з однієї: окремо мапінг афіліат→менеджер (оновлюється
> автоматично щодня), окремо менеджер→афіліати (оновлюється **вручну на запит власника**,
> крона свідомо немає), окремо реєстр самих імен афіліатів. Тому:
> - у звітах **Affiliate Dashboard**, **Affiliate trader**, **Affiliate Weekly** колонка
>   «Менеджер» і слайсер «Менеджер» **залежать від таблиці**, а не від `provider_groups`;
> - рядок «Без менеджера» = афіліата немає **в таблиці**, а не «немає в системі»;
> - різні звіти можуть узяти мапінг **із різних таблиць і різної свіжості** — якщо цифри
>   по менеджерах не сходяться між двома звітами, перевіряй це **першим**.
>
> 🔴 **РІШЕННЯ ВЖЕ УХВАЛЕНО (26.08.2026): переходимо на CRM.**
> CRM стає **єдиним джерелом** прив'язки «афіліат → менеджер», Google-таблиці
> **вимикаються** (не резерв, не звірка — вимикаються).
> **CRM — Uspacy (`fleximinds.uspacy.ua`)** (каталог §9.1 H, довідник `uspacy-crm-affiliates.md`).
> Дані є: **256 афіліатів, 6727 трейдерів** (звірено 2026-09-09). tier (a/b/c/d), % комісії,
> статус-воронка, ланцюжок COP (`parent_affiliate`).
> 🔴 **Мапінг «афіліат → менеджер» = поле `owner`, НЕ `servis_menedzher`** (останнє порожнє в
> усіх 256 картках). `owner` розкривається в email через `/company/v1/users`.
> 🔴 **Доступ вирішує все:** роль `Employee` (право `.view.mine`) бачить лише власні картки —
> звідти й береться «0 даних». Читати CRM треба роллю з `.view.allowed`/`.all` (Head); сервісному
> акаунту для звітів теж потрібне `.allowed`, інакше звіт по менеджерах побачить нуль.
>
> ❓ **Що питати, якщо правка стосується менеджерів:**
> 1. «Цей звіт уже читає CRM чи ще таблицю?» — під час переходу різні звіти
>    можуть бути на різних джерелах, і цифри по менеджерах між ними розійдуться.
> 2. «Що показувати для афіліата, якого **в CRM немає**?» — це головний ризик
>    вимкнення таблиці: такий афіліат мовчки падає в «Без менеджера», і це
>    виглядає як «менеджер не працює», а не як «його не завели в CRM».
>    Домовляйся про **видимий сигнал** (окремий статус/бейдж/лічильник), а не
>    про тихе злиття з рештою.
> 3. «З якої дати CRM — джерело правди?» — історичні тижні до цієї дати
>    рахувалися за таблицею; якщо не зафіксувати межу, порівняння тижнів
>    покаже стрибок, якого насправді не було.
>
> Відповіді **фіксуй у ТЗ**. Поки параметрів CRM немає — це не привід пропустити
> питання: рішення вже ухвалене, і звіт, зроблений «як було», доведеться переробляти.

> 🕒 **ПОЯС ДАНИХ І ПОЯС «СЬОГОДНІ» — ЦЕ РІЗНІ РЕЧІ. Звір їх явно.**
> Наші звіти ріжуть добу в **IST** (`toTimezone(..., 'Asia/Kolkata')`), а сервер живе в **UTC**.
> Якщо «сьогодні»/«зараз» береться функцією на кшталт `date.today()` — це дата **сервера**,
> і вона розходиться з даними **5.5 год щодоби** (з 00:00 до 05:29 IST звіт вважає «сьогодні»
> вчорашнім днем). Дефект тихий: цифри правильні, а підпис «станом на» — ні.
>
> **Перевіряй завжди, коли правка торкається дат, «сьогодні», «зараз», свіжості чи розкладу:**
> 1. у якому поясі **межі даних** (де `toTimezone`)?
> 2. у якому поясі **«сьогодні»** (`date.today()` / `now()` без пояса)?
> 3. у якому поясі **сесія БД** — `SELECT timezone()` у ClickHouse, `SHOW timezone` у Postgres?
>    Наше CH-дзеркало живе в `Asia/Kolkata`: `now()` віддає IST, і **датовий літерал у SQL
>    читається як IST**, а не UTC. Конвертувати межі «в UTC для бази» — і є помилка.
> 4. якщо розходяться — це дефект, навіть якщо замовник про нього не питав.
>
> **Перевір результатом, а не міркуванням.** Будь-яке відновлення стану на минулий момент
> звіряй із прямим знімком на «зараз»: збігається — метод правильний, розходиться — шукай пояс.
> Саме так зсув 5:30 проявився як «черга 568 заявок замість 264».
>
> **Час на екрані — це момент РОЗРАХУНКУ, а не перегляду.** Якщо відповідь кешується,
> годинник у браузері розійдеться з цифрами. Показуй мітку, яку віддав бекенд.
> *Кейс merchant-out-in (18.09): просили «показати час як у бота» — крос-звірка знайшла, що
> «сьогодні» бралось за UTC при IST-даних. Виправили разом із самим показом часу.*

🎛️ **ПОКАЖИ ЗАМОВНИКУ ПІКЕР МЕТРИК** — `https://formulas.freyal.renderhorizon.org/metrics-picker.md`
(усі метрики всіх джерел, згруповані за темами — точну кількість пікер друкує у своїй шапці сам; у кожній темі 🥇 halleg перший). Хай обере, що потрібно і з якого джерела.
Різниці формул-двійників — **каталог §2.6 A**. Якщо метрика є в кількох джерелах з різними формулами —
перелічи варіанти й **хай обере**: «X є у нас (halleg, стандарт) як `…`; у BAM як `…` (різниця — …); у lab як `…`».
**Пріоритет: halleg — за замовчуванням**, решта джерел (lab/Фінанси, BAM, dsfrogboard, dsfrogboard `/affiliates`, supportzones) — **додаткові**.
Не обрав явно → пиши halleg. Обране **фіксуй у таблиці вище** (колонка «Джерело формули») і в чистому ТЗ.
Найчастіші розвилки (§2.6 A): **швидкість** (наш P90 ≠ простий mean ≠ інші відсікання), **CR** (загальний vs по методах vs дедуп),
**Settlement** (3 різні сутності), **First/Second sender** (наше наближення vs канон BAM), **Volume** (з sanity-cap чи без).

**Якщо метрика ПОХІДНА (%, ratio, ARPU, медіана, avg) — зафіксуй порядок дій і базу:**
- `median/avg ЧОГО?` — медіана денних коефіцієнтів ≠ коефіцієнт від медіанних сум (спершу по днях, потім агрегат — чи навпаки);
- **зважено чи середнє по рядках** (`Σчисельник/Σзнаменник` vs `mean(per-row)`);
- що робити, коли **знаменник = 0** (пропустити / BLANK / 0);
- для вікна-порівняння: чи входить у базу сам обраний період (`D-7…D-1` vs `D-6…D`), і що база = **Hour-to-Hour** (каталог §2.4).
- 📆 **«AVG по днях» — по ЯКИХ днях?** ☐ лише дні З АКТИВНІСТЮ ☐ усі календарні дні періоду.
  *Кейс trader-affiliate: для 108 афіліатів зі 167 різниці немає, для решти 59 — **до 14 разів**
  (`Nobiyo` торгував 1 день з 14: ₹10 800 по активних днях проти ₹771 по календарних).*
- 🧮 **Про агрегованість** — відповідь пиши в **паспорт метрики вище**, тут лише пояснення, навіщо питають:
  якщо ту саму метрику просять і в розрізі, і в підсумку «Всього», то для **середніх і перцентилів**
  підсумок не можна скласти з рядків — потрібна окрема передача даних на дрібнішому рівні,
  а не «ще одна колонка». Це **архітектурне** рішення, тому питається ДО оцінки строків.
  *Кейс: «AVG Payin/Trader Daily у розрізі менеджера» — скласти per-affiliate середні й отримати
  середнє менеджера математично неможливо; довелось віддавати на фронт денний грейн (~5 тис. рядків).*
> 🗣️ **ЯК СКАЗАТИ «ЦЬОГО НЕ МОЖНА» — готові формулювання.**
> Технічна причина замовнику нічого не пояснює: «показник без збереженої історії»
> він не прочитає. Кажи **що можна замість**, а не «неможливо».
>
> **Δ для показника без історії:**
> «Ми зберігаємо тільки поточне значення — яким воно було місяць тому, ніде не записано.
> Тому порівняти з минулим місяцем **зараз** не вийде. Можемо почати зберігати щоденні зрізи
> з сьогодні — тоді перше чесне порівняння буде через місяць. Що можна зараз: показати поточне
> значення й динаміку тих показників, де історія вже є.»
>
> **Середнє/перцентиль у розрізі групи:**
> «Середні й відсотки не можна просто скласти: середнє з середніх дає інше число, ніж правда.
> Порахуємо заново з вихідних даних — це трохи довша розробка, зате цифра буде правильна.»
>
> **Метрики немає в даних:**
> «Такого показника ми зараз не збираємо — його немає в жодному джерелі. Варіанти: (а) взяти
> близький за змістом — ось такий; (б) домовитись, щоб його почали писати, і повернутись пізніше.»
>
> **«Хочу 1:1 як у тому кабінеті»:**
> «Зійтись рівно можна лише на **закритому** періоді — вчора й раніше. На сьогоднішньому дні
> дані ще доїжджають, і розбіжність буде плавати з обох боків. Тому звіряємось на закритому,
> а поточний день дивимось як оперативний.»

- 🕰️ **Якщо просять Δ / порівняння з минулим періодом — чи є в метрики ІСТОРІЯ?**
  ☐ FLOW (є за визначенням) ☐ **SNAPSHOT** → чи зберігаються зрізи? ☐ так ☐ **ні → Δ заднім числом неможливий**,
  це окрема задача «почати щоденні зрізи», перший Δ — через період порівняння.
  *Кейс: Deposit (поточний баланс — зберігається лише «зараз») і Score (ковзне вікно 30 днів, яке
  щоразу перезаписується) стояли в ТЗ поруч із Payin, ніби це однорідні показники. А Payin —
  накопичувальний, у нього історія є; у тих двох її немає взагалі.*
- 📐 **Якщо Δ потрібен — У ЧОМУ його показуємо?** ☐ різниця в одиницях метрики (₹) ☐ відсоток (%).
  Для валютних метрик окремо: рахуємо на ☐ **INR** ☐ **USDT**.
  *Кейс: ТЗ казало `Δ = MTD_cur − MTD_prev` (тобто ₹), а борд історично показував % — розійшлись мовчки.
  Окремо Deposit: баланс зберігається в USDT, тож перерахунок на INR показував би **рух курсу замість руху
  коштів**. Такі рішення фіксуй у ТЗ, а не в коментарі коду.*
- 🕳️ **Що показуємо на порожньому/недостатньому наборі:** ☐ `—` з поясненням ☐ `0`.
  *Якщо за обраний період даних немає — сторінка може **впасти з помилкою**, а не показати порожньо.
  Тому домовитись треба заздалегідь: «—» означає «даних не було», «0» означає «було рівно нуль».
  Для замовника це різні відповіді, і плутати їх не можна.*

**Чи може сутність належати КІЛЬКОМ значенням виміру** (трейдер — двом групам, афіліат — двом менеджерам)?
Якщо так — вимір зберігається **списком**, при мультивиборі рядки дедуплікуються, а «сума по значеннях»
законно більша за загальну (підписати в Легенді).

**Підказки для крос-чеку (найчастіші збіги):**
- «конверсія / success / CR» → **метод фінансів** (каталог §2.2): `CR = success / final`, знаменник — лише **фінальні** статуси
  (success+reject+cancel+expire+fail), **без** `pending`/`processing`; success = `completed`+`successed_by_partner` в усіх звітах. **НЕ** `COUNT(*)`.
- «обсяг» → `PayIn Volume` (§2.1) — **без** шумового порога, рахуємо по ВСІХ провайдерах. «ефективність трейдера» → уточни: `Vol/Dep`, `CR` чи `Total score`?
- «депозит», «settlement», «баланс/ліміт», «scoring/тир» → фінансові/скорингові → **звірити з knowledge + каталог §3/§5**.
- «швидкість payin/payout» → `PayIn/PayOut speed` (§2.2), **P90** (`quantileExactInclusive(0.9)` — канон §2.5-bis),
  фільтр >3 трз (поріг >5k прибрано). *(Тут роками стояв «95-й перцентиль», хоча канон каталогу — P90.
  Розбіжність поїхала б у ТЗ як факт: на живих даних P90 vs P95 давали 5.0 проти 7.9 хв, +58%.)*

**Пастки метрик (з реальних кейсів — перевіряй завжди):**
- 🔴 **Подія проти стану.** Якщо метрика про **зміну** (перехід статусу, апеляція, повернення),
  то вона рахується з журналу подій, а не з поточного зрізу. Спитай замовника прямо:
  «нас цікавить, **скільки зараз** таких, чи **скільки разів це сталося** за період?» —
  це різні числа й різна складність. Наявність такого журналу перевірить власник.
- 🔴 **Фраза без МЕТОДУ — це не вимога, а джерело нескінченних розбіжностей.** «Відкид викидів», «очищені дані»,
  «без аномалій» — якщо не названо **як саме** (поріг? σ? зріз відсотка?), кожен реалізує по-своєму й еталон
  невідтворюваний у принципі. Кейс: у каталозі роками стояв «p95 з відкидом викидів» — а в коді відкиду **не було**.
  ⚠️ Плюс подвійне відсікання: перцентиль **уже** ріже хвіст; відкид зверху ріже його вдруге.
- 🔴 **Замало даних → «—» і БЕЗ балів, а не нуль.** Нуль карає за відсутність історії й тягне сутність униз рейтингу;
  «немає даних» — чесно. Кейс: трейдер з одним платежем за 20 секунд ставав «найшвидшим у портфелі» —
  і отримував за це реальні бали (швидкість важить 20+18 у скорингу).
- 🧰 **Звірку цифр із живими даними робить власник звітів** — у нього доступи до джерел.
  Твоя частина: зафіксувати в ТЗ **як саме перевіряти** («має збігтися з таким-то екраном/звітом»),
  щоб було з чим звіряти. Самих цифр тут рахувати не треба.
- 🔴 **Медіана/перцентиль: перевір СЕМАНТИКУ ДВИГУНА емпірично, а не за назвою.**
  `quantileExact` ≠ `quantileExactInclusive` ≠ `statistics.median` ≠ інтерполяційний перцентиль —
  на малих вибірках розходяться помітно. Канон (див. каталог §2.5-bis): `v[min(floor(q*n), n-1)]`.
  Кейс: `[10,20,30,40]` → CH **30**, `statistics.median` **25**; на живих даних це дало б 85.9 замість 87.4.
  Перед ТЗ прогони обидві формули на 2 вибірках (парна + непарна) і зафіксуй у DoD очікувані числа.
- 🔴 **База порівняння: 3 питання, які змінюють число.** (1) Чи входить у базу сам обраний період
  *(не має входити)*; (2) що робити з періодами, де знаменник = 0 *(викидати з бази)*;
  (3) скільки мінімум зразків, щоб узагалі показувати *(<2 → «—», не рахувати Δ)*.
- 🔴 **Метрика з тижневим профілем → база по ДНЮ ТИЖНЯ, а не N днів поспіль.**
  Інакше вихідні змішуються з буднями й Δ бреше. Кейс `capacity`: база ще й по годині/півгодині.
- 🔴 **«Вхід»/«вихід» можуть мати РІЗНЕ джерело для різних типів сутності — перевіряй по кожному типу окремо.**
  Кейс: у `[EXC]`-мерчантів `payin = 0` (8 з 8), їхній вхід — це **topup** (`transactions.operation='expense'`).
  Наївна спільна формула `out/in` дала б ділення на нуль. Спершу зроби зріз «метрика × тип сутності», потім формулу.
- 🔴 **Name-collision:** якщо назва метрики збігається з каталогом, але **приклад замовника** (вигрузка/скрін) натякає
  на іншу формулу — **виграє приклад/замовник**, каталожну познач як «не застосовна тут». Класика: `Settlement`
  (у нас `SUM(expenses.cash_out)`, а по зовн. шлюзах — розрахункова `−(AmountIN−fee)`); `speed` (у нас median/p95, а
  замовник хоче простий mean без відсікань).
- 🟠 **Метрика-наближення:** якщо точного джерела в наших даних **немає** (метрика живе в Looker/іншій системі) —
  не обіцяй 1:1: узгодь **наближення**, зафіксуй gap і критерій «best-effort», а не точний збіг.
- 🟠 **Per-day / rate метрики:** ділити на **календарні** дні періоду чи на **активні** (і який поріг активності,
  напр. ≥20 успішних/день)? — питай завжди для будь-якої «/день».
- 🎲 **Показник, зібраний з історії подій, треба прогнати двічі й порівняти.**
  Якщо за один день сталося кілька змін поспіль, машина може щоразу брати різну з них —
  і те саме число «стрибатиме» між перерахунками. Звірка з еталоном це НЕ покаже, бо еталон
  рахується так само. *Кейс: «дожив до 14 дня» давав 151 → 165 → 175 на тих самих даних;
  правильна цифра виявилась **82.6%, а не ~34%** — тобто помилка була вдвічі.*
- 🔴 **Метрики-СТАНУ vs метрики-ПЕРІОДУ — не змішувати в одній таблиці.** «Зараз»: pending / review / черга /
  balance / наливи. «За вікно»: обсяг / CR / кількість. Стан у історичному вікні буде **структурно порожнім**
  (кейс: Pending/Review завжди «—», бо вікно закінчується вчора), а balance не сходитиметься арифметично.
  Якщо треба разом — підписати «(зараз)» у заголовку і пояснити в Legend.
- 🟠 **Власник формули:** для кожної спірної метрики — **хто її визначив** (напр. фінансист/аналітик), з ким підтвердити.
- 🏷️ **Чи існує вже назва для цього поняття на сусідньому борді?** Перш ніж вигадувати підпис —
  перевір, як ту саму сутність називають у наявних звітах/панелях, і **бери наявну назву**.
  Якщо свідомо називаєш інакше — запиши в ТЗ, чому.
  *Кейс: дашборд називав групу без менеджера `Other`, а Weekly — `— Без менеджера`. Той самий набір
  із 10 афіліатів і ₹369M обороту, але `Other` читалось як менеджер на ім'я Other — тобто **найбільша
  діра в покритті виглядала як звичайна група**.*

- 🧩 **Дві вкладки показують ту саму сутність → одна функція групування на обидві.**
  Якщо у звіті вже є канонічний ключ (наприклад `merchant_key()`), нова вкладка бере ЙОГО, а не пише
  своє групування в SQL.
  *Кейс merchant-out-in: нова вкладка різала назву по `|` у запиті, і `‹мерчант›` + `‹мерчант›`
  стали двома мерчантами тут, лишаючись одним у вкладці «Мерчанти» — 20 рядків проти 19. Обидві цифри
  виглядають правдоподібно, поки не покласти поруч; помітив замовник, а не звірка.*

> ⚠ Кожну **нову** або змінену формулу познач і винеси в крок 9 як питання «підтвердь точну формулу».

## 4. Розрізи / фільтри / права

- **Слайсери (dropdown):** `____` → каталог §4 (Affiliate / Manager / Status / Group / Trader / Tier / From–To)
- **Групування (рядки таблиці / матриці):** `____`
- **Порівняння:** ☐ Δ до попереднього періоду (семафор) ☐ Hour-to-Hour ★ ☐ TOP-N ☐ тренд/Change — деталі: `____`
- **RLS (хто що бачить):** ☐ admin — все ☐ user — лише свої менеджери/афіліати ☐ інше: `____`

🔲 **Матриця «слайсер × блок» — заповнити явно, не словами «слайсери працюють».**
Рядки — кожен слайсер, стовпці — кожен блок/вкладка звіту, у клітинці ✅/✗:

| Слайсер \ Блок | KPI-картки | Таблиця | Графік | `____` |
|---|---|---|---|---|
| Період | ✅ | ✅ | ✅ | |
| `____` | | | | |
| `____` | | | | |

*Навіщо: формулювання «слайсери без змін» читається як «наявні не ламаємо», а не «нові блоки їх слухають».
Кейс: нова вкладка «Воронка» реагувала лише на період, а Менеджер / Афіліат / Статус / Tier ігнорувала —
у ТЗ про це не було ні слова. Побічний ефект матриці: одразу видно слайсери, яких на борді **не існує**
(у тому ж ТЗ значились Group і Trader, яких на дашборді немає).*

## 5. Візуалізація / layout

🎨 **ПОКАЖИ ЗАМОВНИКУ ПІКЕР ВІЗУАЛІЗАЦІЙ** — `https://formulas.freyal.renderhorizon.org/visual-picker.md`
🖼️ **І ГАЛЕРЕЮ** — `https://formulas.freyal.renderhorizon.org/visual-gallery.html` — це ті самі патерни,
але **намальовані**. Замовнику показуй галерею (він бачить вигляд), пікер лишай собі для назв патернів.
*(Галерея публікувалась, але в брифах не згадувалась жодного разу — тому й не показувалась нікому.)*
(графіки · матриці · теплокарти · таблиці · воронки · сигнали — усе з наших живих звітів + eye-of-god/BAM/lab).

> 🔴 **Вигляд обирається так само, як формула — по блоках і ЗАВЖДИ фіксується в ТЗ.**
> Формат рядка: `блок · патерн · як у <звіт> · що на осях/у колонках`.
> Не питай «як має виглядати?» абстрактно — **запропонуй 2–3 конкретні варіанти** з пікера й дай обрати.
>
> **Пропонуй по ходу розмови, від питання замовника:**
> | Він каже | Запропонуй |
> |---|---|
> | «скільки всього» | KPI-картки (+Δ до попереднього) |
> | «як змінюється» | лінія по днях · стовпчики · **+baseline-коридор**, якщо треба «нормально це чи ні» |
> | «з чого складається» | стек-стовпчики (структура) або кільце (3–6 часток) |
> | «коли саме / о котрій» | погодинний профіль або **матриця день × година** (як `operational`) |
> | «хто відхиляється» | таблиця + коридор `P25–P75` + прапорець `⚠️` + банер угорі (як `merchant-out-in`) |
> | «де ми губимо» | воронка втрат (як eye-of-god) або стек за категоріями |
> | «хто кращий» | зони/тири (як `scoreboard`) або сортована таблиця з пігулками |
> | «треба все й одразу» | широкий борд зі sticky-шапкою (як BAM) — попередь про горизонтальний скрол |
>

> 🤔 **ЗАМОВНИК ПРОСИТЬ ПОРАДИТИ («а як краще, щоб було видно цей показник?»)**
> Не відповідай «залежить». Спитай **три речі** — вони й визначають вибір:
>
> | Питання | Що змінює |
> |---|---|
> | **Скільки сутностей** показуємо? | 1 → картка · 5–20 → таблиця · 100+ → топ-N + пошук (усе одразу не читається) |
> | Треба бачити **як було раніше**? | так → лінія/стовпчики в часі · ні → зріз «зараз» |
> | Є **норма**, з якою порівнюємо? | так → коридор `P25–P75` або поріг зі світлофором · ні → просто значення |
>
> **Далі — від ТИПУ показника** (тип видно в карті колонок `reports-columns.md`):
>
> | Тип показника | Як показують у наших звітах | Чому саме так |
> |---|---|---|
> | **Відсоток** (CR, Settle, Auto) | KPI-картка з Δ · лінія по днях · **коридор P25–P75** | % сам по собі нічого не каже: 58% це добре чи погано? Потрібна база порівняння |
> | **Гроші** (обсяг, payin/payout) | стовпчики по днях · KPI + Δ · стек за категоріями | сума добре читається стовпчиками; стек показує, з чого склалась |
> | **Швидкість / перцентиль** (P90) | таблиця з коридором · розподіл | **середнє бреше**: одна аномалія зсуває його. Тому перцентиль + видимий розкид |
> | **Лічильник** (к-ть трейдерів, днів) | KPI-картка · лінія | ціле число, порівнюється просто |
> | **Зона / тир** | пігулки кольором · сортована таблиця | категорія читається кольором швидше за текст |
> | **Час доби / день тижня** | **матриця день × година** | двовимірний патерн видно лише матрицею, у лінії він губиться |
>
> **Як подати пораду:** назви **один рекомендований** варіант і **скажи чому одним реченням**
> («для відсотка потрібна база порівняння, тому лінія з коридором — інакше не видно, 58% це добре чи ні»),
> покажи його **на макеті** (`?add=`/`?chart=`), і лише потім згадай альтернативу.
> ⚠️ **Не вивалюй список із шести варіантів** — замовник, який просить поради, не хоче ще одного вибору.
> Йому потрібна рекомендація, від якої можна відштовхнутись.
> 🖼️ **Показуй приклад, а не назву патерна.** Коли пропонуєш варіант — дай **міні-превʼю блоку**
> (кілька рядків HTML із вигаданими даними або хоча б схему в тексті), щоб замовник побачив, про що мова.
> «Таблиця з коридором норми» і **побачена** таблиця з коридором — це різний рівень розуміння.
> Найкращий варіант познач як **рекомендований** і скажи **чому** («для цього питання в наших звітах
> прижився саме такий — див. `capacity`»), але лиши вибір за замовником.
>
> ⚠️ **Матриця/теплокарта потребує двох осей і однієї метрики** — уточни, що на рядках, що на колонках,
> і **чим фарбуємо**. Без цього «зроби теплокарту» — не вимога, а побажання.


- 🧱 **Обовʼязковий скелет (не питання — правило):**
  - **Тултип має пояснювати КОНКРЕТНЕ число, а не лише формулу.** У підказку клітинки підставляй
    значення цього рядка: `Виведено/Заведено = OUT ₹2.1M / IN ₹2.4M × 100 = 87.4%`, для Δ — усе рівняння
    з базою й коридором. Формула без чисел не відповідає на питання «звідки це взялось».
    **Порожній стан теж пояснює себе:** «—» має тултип із причиною (замало зразків / знаменник = 0).
    Покриття тултипами — **пункт DoD**: усі колонки всіх таблиць + усі KPI-картки, без винятків.
  - **Новий контроль не має дублювати наявний.** Додаєш фільтр/період/перемикач — перевір, чи він
    не перекриває старий, і **прибери старий**. Кейс: у звіті одночасно жили пресети + «Станом на» +
    «з–по» — три механізми дат, які скидали одне одного. Стало: один діапазон, пресети лише виставляють межі.
  - **Посилання й кнопки-завантаження — `href` прямо в HTML, а не через JS.** Якщо скрипт не відпрацював
    (блокувальник, корпоративний проксі, iframe) — елемент без `href` **мертвий, клік нічого не робить**.
    JS лишай лише як доповнення. Для зовнішніх — додай текстовий запасний лінк.
  - **Патерни «зверни увагу» (бери готове, не вигадуй):** банер угорі з рівнями `warning/critical`
    (як `alerts` у `capacity`) · бейдж-причина в рядку (як `⛔ Alert` у `affiliate_tier`) ·
    бейджі `ok/warning/critical` (`hermes-load-monitor`) · кольорові пігулки за порогами.
    Поріг сигналу краще **самонормований** (вихід за коридор P25–P75 цієї ж сутності), ніж єдиний для всіх.
  - **UI-shell:** підключити спільні `/static/sidebar.css` + `/static/sidebar.js` і додати звіт у META сайдбара.
    Власну навігацію не робити. ⚠ Сайдбар ховає `.top-nav` — **не називай так свою шапку**, зникне разом із кнопками.
  - **Стандартні контроли мають house-реалізацію — спершу подивись сусідній звіт і повтори.**
    Валюта = **НЕ фільтр даних**, а конвертація показу (бекенд віддає `rate`, фронт ділить). Так само періоди/експорт/H2H.
  - **Legend — угорі** (одразу під фільтрами), не в кінці сторінки: інакше клік виглядає як «кнопка не працює».
  - **Tooltip на КОЖНІЙ колонці**: формула + як читати.
  - **Drill-down: усі метрики на КОЖНОМУ рівні**, включно з Δ і похідними (кейс: різнонаправлені рухи кабінетів
    гасились у сумі мерчанта — на верхньому рівні цього не видно).
- **Елементи:** ☐ KPI-картки ☐ таблиця/матриця ☐ графік (лінія/бар/pie) ☐ вкладки-періоди ☐ drill-down ☐ Legend-вкладка
- **Ключові колонки і порядок:** `____`
- **Кольори/RAG (норма/ризик/спад):** за замовч. семафор good/neutral/bad — інше: `____`
- **Референс / мокап:** `____` *(лінк на PBI-сторінку, скрін, приклад — дуже пришвидшує)*
- **Експорт:** ☐ не треба ☐ Excel ☐ CSV ☐ Google Sheets — формат: `____`
  - ⚠ **Числовий формат примусово** (Number, N знаків) — інакше Excel/локаль перетворює дроби на дати («5.06»→«05.Чер»). Питай, чи потрібен авто-push у Sheets по API, чи достатньо файлу.

## 5-bis. 🖼️ МАКЕТ — показати, ЯК це виглядатиме (до ТЗ, обовʼязково)

> **Навіщо:** словами «таблиця з коридором і прапорцем» замовник уявляє одне, ти — інше. Макет за 2 хвилини
> знімає цілий раунд правок після збірки. **Це найдешевша точка змін у всьому процесі.**

**Інструмент:** зроби макет **HTML-сторінкою з вигаданими (синтетичними) числами** — так замовник
побачить вигляд, не плутаючи його з реальними даними. Обов'язково постав угорі помітну плашку
**«МАКЕТ — цифри вигадані»**. Палітра наших звітів: `#FFFFFF` фон · `#252423` текст · `#118DFF` акцент.

**Що робиш:**
1. Збери **самодостатній HTML** — один файл, **без бекенду**, з **синтетичними** даними (3–6 рядків досить).
   Стиль — наш: темна тема, ті самі кольори/пігулки/семафор, що в решті звітів (візьми будь-який
   `static/*.html` як зразок, щоб макет не виглядав чужим).
2. **Угорі — помітна плашка:** `🖼️ МАКЕТ · дані вигадані · бекенду немає · це лише вигляд`.
   Плашку **не прибирати** — інакше макет підуть показувати як готовий звіт.
3. Покажи **кожен блок**, про який домовились: KPI-ряд, таблиця з потрібними колонками, графік/матриця,
   легенда, сигнали. Інтерактив імітувати не треба — досить статики.
4. Якщо для блока є **2 доречні патерни** — покажи **обидва** поруч і дай обрати
   (напр. «Δ до попереднього» vs «коридор P25–P75 + прапорець»).

**Як віддати:** віддай HTML **одним блоком коду в чаті** з підписом «збережи як `макет.html` і відкрий у браузері».
Якщо середовище вміє вкладення/артефакти — можеш додатково дати файлом, але блок у чаті обовʼязковий.

> 🔴 **Ворота:** після показу **спитай прямо: «що тут змінити?»** і **дочекайся відповіді**.
> Правки внось у макет і показуй знову — цикл повторюється, поки замовник не скаже «так, годиться».
> **Поки макет не підтверджено — ТЗ не генеруй.**
> У ТЗ потім впиши рядок: `Макет затверджено: <файл/лінк>, версія <N>`.

⚠️ **Чого макет НЕ робить:** не рахує справжніх цифр, не ходить у базу, не є доказом, що метрика зійдеться.
Числа в ньому — **ілюстративні**; звірка формул усе одно окремо (§3 і §8).


## 6. Оновлення, онлайн-звірка і приймання

- **Цикл оновлення:** ☐ live ~5 хв ☐ worker-кеш (30 хв / 4 год) ☐ refresh-кнопка — за важкістю: `____`
- **Онлайн-звірка (real-time) — з чим звіряємо наживо** (каталог §9): ☐ CH `hermes` (raw SQL, еталон формул)
  ☐ публічний каталог формул ☐ пікер метрик — обери, з якого **джерела формули** береться метрика: `____`
  *(звірку з живими даними робить власник; тобі доступи не потрібні)*
  - **Будь-яка розбіжність формули/цифри/назви/фільтра з джерелом → питання замовнику** (протокол §7), а не мовчазне «close enough».
- **Допустима розбіжність:** ⚖️ **1:1 — точний збіг** (гроші до копійки/₹, скори/проценти до пункту, лічильники до одиниці).
  Будь-яка ненульова різниця = дефект, шукаємо корінь. Виняток лише — CDC-лаг на межі вікна (дочекатись синку й перезвірити). Каталог §9.2.
- **Хто приймає результат:** `____`

## 📎 Вкладення від замовника
| № | Файл | Що показує | До якого пункту |
|---|---|---|---|
| 01 | `<ім'я файлу>` | `<напр. вкладка Hourly, як має виглядати>` | `<§5 / пункт 2>` |
| 02 | `<…>` | `<…>` | `<…>` |
*(немає вкладень — так і напиши: «вкладень немає». Порожній рядок читається як «забули».)*

## 7. Передача ТЗ (деплой — НЕ твоя частина)

> 🚫 **Цей бриф нічого не деплоїть і не пише код.** Його єдиний результат — **файл ТЗ**.
- **Що робиш:** зберігаєш готовий `ТЗ_<slug>.md` (крок 10) і **надсилаєш власнику звітів**. Він сам збирає й викочує.
- **Бажаний термін / пріоритет:** `____` (щоб власник спланував чергу)
- **Хто приймає результат:** `____`
- ❗️ **Не роби сам:** не створюй сторінок/модулів, не чіпай сервери, не «допомагай зібрати звіт»,
  не проси доступів до баз чи панелей. Якщо тебе просять зробити сам звіт — скажи, що це робить власник за цим ТЗ.

---

## 8. ✅ Self-check скілом `report-skill-from-spec` (Capture Intent)

Асистент заповнює цю таблицю **зі зібраного вище** і показує замовнику для підтвердження.
Порожня клітинка = недопрацьоване питання → повертайся до відповідної секції.

| Параметр | Значення (зі зібраного) | Джерело в брифі |
|---|---|---|
| Призначення | `____` | §1 |
| Аудиторія | `____` | §1 |
| Грейн | `____` | §2 |
| Метрики (+формули) | `____` | §3 |
| Розмірності | `____` | §4 |
| Порівняння | `____` | §4 |
| Джерело | `____` | §2/§7-каталог |
| Оновлення | `____` | §6 |
| Доступ/RLS | `____` | §4 |

**Крос-чек метрик** (обов'язково перед ТЗ):
- [ ] **⚖️ Кожен % / avg — зважений чи середнє по рядках?** Для КОЖНОЇ частки й середнього зафіксовано базу:
      `Σчисельник/Σзнаменник` (зважено) vs `mean(per-row)` (незважено). Незважене середнє припустиме лише
      якщо саме так підписано на картці. *(Кейс: Hourly «Total CR» рахував середнє з 24 годинних CR — 54.1 %
      замість 52.4 %; через рік той самий клас виплив в `avgSettle` — 14.6 % замість 15.5 %.)*
- [ ] **🔢 Популяція (знаменник) кожної вкладки/картки зафіксована явно** — на якій множині сутностей
      рахується кожен блок і чи вона **та сама** для списку / KPI / графіка / деталізації; чи потрапляють
      сутності, **створені всередині періоду**. Однакова назва ≠ однакова база.
- [ ] Кожна метрика зіставлена з каталогом §2/§3 — позначено «наша» або «нова».
- [ ] Усі нові/змінені формули звірені з **внутрішній довідник фінформул** (у власника) (де застосовно).
- [ ] Поля статусу/балансу/ліміту звірені з каталогом §5 (supportzones `settings-provider`).
- [ ] Зашиті інваріанти (§6 каталогу) враховані або свідомо перевизначені.

**Перевірка перед фіналізацією ТЗ** (звірку з живими даними робить власник — доступи тобі не потрібні):
- [ ] У кожної метрики вказано **джерело формули** й **як перевірити** результат
      («має збігтися з таким-то звітом/екраном») — щоб власнику було з чим звіряти
- [ ] Кожна метрика має **джерело + формулу** з каталогу (або позначена «потребує підтвердження власником»)
- [ ] Розбіжності залоговані (`метрика · джерело · час IST · очікуване/факт · Δ · причина`) — без «підгонки».
- [ ] **Звірку виконано по ОБОХ вікнах — поточному І попередньому**, а не лише по поточному.
      *(Кейс: денний грейн клав у «попереднє» цілий місяць замість обрізаного вікна. Колонка таблиці була
      правильна, а KPI-картка й матриця — ні, розбіжність ~21%. Не спіймалось, бо перша звірка дивилась
      лише поточне вікно.)*
- [ ] **Звірку зроблено на ЗАКРИТОМУ періоді** (минулий місяць), а не на поточному.
      *(На живому періоді дані доїжджають: напрям розбіжності перевертається між прогонами, і це хибний
      сигнал. На закритому — детерміновано: у нас так вийшло 0 розбіжностей на 12 метриках × 184 афіліати.)*
- [ ] **RLS перевірено під РЕАЛЬНИМ користувачем ролі `user`** — включно з вигрузкою (XLSX/CSV),
      а не лише симуляцією на бекенді. Експорт — найчастіша дірка: він ходить іншим шляхом, ніж таблиця.

> 🔴 **Ворота «джерела»:** перш ніж іти далі, дай відповідь на два питання:
> 1. **Чи для кожної метрики заповнено «джерело формули»?** Порожньо — повернись у §3.
> 2. **Чи хоч для однієї метрики звірився з альтернативним джерелом — і що показала звірка?**
>    Якщо жодного разу — це **сигнал**, а не норма: пройди **таблицю тригерів (§3) рядок за рядком**
>    і для кожного скажи вголос «ні, бо…». Перелік джерел тут навмисно **не дублюється**: він
>    змінюється (реєстр — каталог §9.1), а копія в цьому абзаці одного разу вже відстала —
>    і джерела, доданого пізніше, ворота не бачили.
>    Результат звірки пиши в ТЗ рядком: `звірено з <джерело>: <збіг N/N | розбіжність + причина>`.

## 9. ❓ Фінальні питання замовнику (закрити ДО генерації ТЗ)

Асистент виписує сюди **тільки реальні пробіли** після self-check і задає їх одним раундом.

> 🧾 **Два типи питань — не плутати, кому яке:**
> | Тип | Приклади | Кому |
> |---|---|---|
> | **Бізнесові** (доступів НЕ потребують) | модель зберігання, хто має право редагувати, чи потрібна історія змін, пороги, терміновість, хто приймає | **замовнику, тут і зараз** (AskUserQuestion) |
> | **Технічні** (потрібні доступи/код/дані) | чи існує таке поле в CH, як реалізовано в коді, скільки рядків у популяції | **власнику** (у клієнтській версії — «потребує підтвердження власником») |
>
> 🔴 Не спихай власнику бізнесові питання — замовник відповість на них за 30 секунд, а інакше ТЗ приїде
> непридатним до роботи. *(Кейс 2026-08-13: 8 «❓ потребує підтвердження власником» у клієнтському ТЗ,
> з них 5 — суто бізнесові.)*


1. `____` *(типово: точна формула нової метрики / поріг «успіху»)*
2. `____` *(типово: чи потрібен Hour-to-Hour; який цикл оновлення)*
3. `____` *(типово: RLS чи All data; з чим звіряти цифри)*

> Коли всі відповіді отримані і крос-чек зелений — переходь до кроку 10. Питань у ТЗ бути **не повинно**.

---

## 10. 📄 Чисте ТЗ — ВИДАЙ У ЧАТ ОДНИМ БЛОКОМ

> Асистент генерує цей блок повністю заповненим, без `____` і без `❓`.
>
> **🔴 Як саме віддати (обов'язково):**
> 1. Виведи ТЗ **одним суцільним markdown-блоком** прямо в чат — щоб замовник міг скопіювати його цілком
>    однією кнопкою. **Не розбивай** на кілька повідомлень і не перемежовуй коментарями.
> 2. **Перед блоком** напиши один рядок: `Готове ТЗ — скопіюй і надішли власнику звітів. Ім'я файлу: ТЗ_<slug>.md`
> 3. **Після блоку** — короткий підсумок 2–3 рядки (що замовили, які метрики, який період), щоб замовнику було
>    що написати в супровідному повідомленні.
> 4. Якщо в чаті доступне вкладення/артефакт — можеш **додатково** дати файл `ТЗ_<slug>.md`, але
>    **блок у чаті обов'язковий у будь-якому разі** (він працює скрізь).
>
> 🚫 Не намагайся зберегти файл на сервер, надіслати його кудись сам або «передати власнику» —
> **пересилає замовник власноруч**. Твоя робота закінчується виданим блоком.

```markdown
# ТЗ: <Назва звіту>  ·  id: trader-<slug>  ·  дата: <YYYY-MM-DD>

## 1. Призначення та аудиторія
<на яке питання відповідає, хто користувач, яке рішення приймають>

## 2. Джерело даних і період
- Джерело: ClickHouse hermes · таблиці: <...>
- Період / грейн: <вкладки Daily/Weekly/…> · TZ: Asia/Kolkata · дані з 2026-01-01
- Трейдер = <означення>; інваріанти: Operation provider виключено, payout ABS (шумовий поріг vol_filter НЕ застосовується)

## 3. Метрики (кожна з фінальною формулою)
| Метрика | Формула | **Джерело формули** (halleg / lab / BAM / dsfrogboard / dsfrogboard-aff / supportzones / eye-of-god) | Примітка |
|---|---|---|---|
| <...> | <...> | <...> ← заповнювати ЗАВЖДИ, навіть якщо halleg | наша / нова-звірена / звірена з <джерело>: <результат> |

## 4. Розрізи, фільтри, порівняння
- Слайсери: <...> · Групування: <...>
- Порівняння: Δ-семафор / H2H / TOP-N: <...>
- RLS: <admin/user; що бачить user>

## 5. Layout і стиль
- Елементи: KPI-картки + таблиця + Δ-семафор + Legend-вкладка (наш шаблон)
- Ключові колонки/порядок: <...> · Референс: <лінк/мокап>
- Мова UI укр., ₹/IST, палітра #FFFFFF/#252423/#118DFF, семафор good/neutral/bad
- **Макет затверджено:** `<файл/лінк>` · версія `<N>` ⚠️ макет = вигляд, не цифри
- **Патерни блоків** (з `visual-picker.md` — заповнювати по кожному блоку):

| Блок | Патерн | Як у звіті | Осі / колонки / чим фарбуємо |
|---|---|---|---|
| <...> | <лінія + baseline-коридор> | <capacity> | X: день · Y: ₹ · коридор P25–P75 |
| <...> | <матриця день × година> | <operational> | рядки: банк · колонки: година IST · колір: к-сть |

## 6. Оновлення і приймання
- Цикл: <live 5хв / worker 30хв / 4год> · кнопка Update
- Ground-truth звірки: <CH hermes + halleg (прод)> · точність: **1:1** (до копійки/₹, до пункту; нульова розбіжність)
- Приймає: <...>

## 7. Деплой
- Виконує **власник звітів** за цим ТЗ. Замовник ТЗ сервери не чіпає.


## 8. Definition of Done (перевіряє власник при збірці)
- [ ] **Сесію брифа закрито явно** — питання «закриваємо чи є ще правки?» поставлено і отримано відповідь. Перехід до реалізації/деплою сесію НЕ закриває
- [ ] **Вкладення замовника збережені** (скріни/експорти/лінки) і перелічені в ТЗ з прив'язкою до пункту — або явно вказано «вкладень немає»
- [ ] Кожна метрика має **джерело + формулу** (або позначку «потребує підтвердження власником»)
- [ ] **Джерело формули зафіксовано ЯВНО для кожної метрики** (`halleg` / `lab` / `BAM` / `dsfrogboard` / `dsfrogboard-aff` /
      `supportzones` / `eye-of-god`) — порожня клітинка = не заповнено, а не «за замовчуванням»
- [ ] **Паспорт метрики заповнений** для кожної: тип значення · чи агрегується · знаменник ·
      Δ (₹/%) · порожній набір · назва на сусідньому борді
- [ ] **Макет затверджено замовником** — лінк/файл: `____` (без цього ТЗ не приймається)
- [ ] **Матриця «слайсер × блок»** заповнена — які слайсери діють на кожен блок
- [ ] Цифри звірені з ground-truth **1:1** — до копійки/₹ і до пункту
- [ ] RLS: користувач бачить лише своє
```


---


## 11. 🚦 ТЗ готове — закриваємо сесію чи ще працюємо?

**Не завершуй роботу мовчки.** Видав ТЗ — постав замовнику питання через **AskUserQuestion**
(один короткий вибір, не абзац тексту):

> «ТЗ готове. Що далі?»
>
> | Варіант | Що робимо |
> |---|---|
> | ✅ **Все, закриваємо** | фіксую ТЗ як фінальне, роблю підсумок і завершую |
> | ✏️ **Є правки в ТЗ** | що саме — виправляю і видаю оновлену версію |
> | ➕ **Є ще питання/побажання** | повертаємось у потрібну секцію брифа й доповнюємо |
> | ⏸ **Перерва, продовжимо пізніше** | зберігаю поточний стан, кажу, на чому спинились |


> 🔴 **НАЙЧАСТІШИЙ ОБХІД ЦЬОГО КРОКУ — і як його не зробити.**
>
> Ти видав ТЗ, замовник каже «добре, вноси на freyal» — і ти йдеш реалізовувати.
> Питання так і не пролунало, сесія брифа лишається відкритою **назавжди**.
> Крок не забули — його **обійшов сам потік роботи**.
>
> **Правило:** перехід до реалізації **НЕ закриває** сесію брифа. Це різні речі:
> ТЗ приймає замовник, реалізацію робить власник. Тому:
>
> | Ситуація | Що зробити |
> |---|---|
> | Просять реалізувати **одразу після ТЗ** | спочатку **постав питання цього кроку**, потім іди робити |
> | Ти **вже пішов** реалізовувати | повернись і постав його — навіть якщо звіт уже зібраний |
> | Звіт **уже в проді** | питання лишається чинним, просто інші варіанти ↓ |
>
> **Варіанти, коли реалізація вже відбулася:**
>
> | Варіант | Що робимо |
> |---|---|
> | ✅ **Усе так, закриваємо** | фіксую ТЗ фінальним, підсумок, звіт про покращення брифа |
> | ✏️ **Правка в ТЗ + закрити** | ТЗ має відповідати тому, що **реально зібрано** — інакше ТЗ бреше |
> | 🔧 **Є правки у звіті** | це вже нова ітерація → бриф **доопрацювання**, не продовження цього |
>
> ⚠️ **ТЗ і прод мають збігатися.** Якщо під час реалізації щось зробили інакше
> (інша колонка, інший поріг, інший вигляд) — **онови ТЗ**, поки памʼятаєш.
> Розбіжність «у ТЗ одне, у проді інше» спливе на першій же звірці й коштуватиме розслідування.

**Навіщо це.** Замовник часто не знає, що процес завершено, і мовчить — а насправді має
дві-три правки в голові. Або навпаки: ти чекаєш підтвердження, а він вважає, що вже все.
Явне питання знімає обидві ситуації й коштує один хід.

**Якщо обрано «закриваємо»** — дай короткий підсумок **у трьох рядках**:
- що замовили (робоча назва + одне речення суті);
- **чого бракує для старту** — якщо є незакриті залежності (немає даних, потрібне рішення власника);
- **що далі й від кого** — «файл ТЗ надсилаєш власнику звітів, збірку робить він».

⚠️ **Не закривай сесію, поки в ТЗ лишився хоч один `____`, `<...>` або «уточнимо пізніше».**
Це ті самі ворота «нуль відкритих питань» — просто останній момент, коли їх можна перевірити.

> ♻️ **І ОДРАЗУ ПІСЛЯ ПІДСУМКУ — коротко зафіксуй для себе, що можна покращити.**
> Де замовник спіткнувся · про що спитав, чого бриф не передбачав · що спрацювало добре ·
> що конкретно додати і **в яку саме секцію**. Тільки зі спостережень цієї сесії.
> Це не для замовника — це нотатка, яку передаси власнику разом із ТЗ.


## ♻️ Саморозвиток брифа (обов'язково наприкінці кожного запуску)

Після видачі ТЗ асистент **додає 1–3 конкретні пропозиції**, як покращити **сам цей бриф** — що було незручно,
чого бракувало, яке питання мало б бути стандартним, яка пастка спливла. Записує у **`BRIEF_IMPROVEMENTS_LOG.md`**
(формат — у самому лозі). Це **НЕ змінює бриф автоматично**: рішення «вносити чи ні» приймає власник (freyal).

**Що є хорошою пропозицією:** узагальнюване правило (не разова деталь цього ТЗ), з коротким «чому» і посиланням на
кейс. Приклад: «додати стандартне питання про календарні vs активні дні» ← бо на merchant-SLU це зайняло раунд Q&A.
<!-- INTERNAL:END -->
## ♻️ Саморозвиток брифа (обов'язково наприкінці кожного запуску)

Після видачі ТЗ асистент **додає 1–3 конкретні пропозиції**, як покращити **сам цей бриф** — що було незручно,
чого бракувало, яке питання мало б бути стандартним, яка пастка спливла.

**🔴 Куди їх подіти:** у тебе **немає доступу** до нашого журналу покращень, тому **виведи пропозиції окремим
коротким блоком у чат — одразу після блоку ТЗ**, під заголовком `## 💡 Пропозиції до брифа`. Замовник перешле
їх разом із ТЗ. **Рішення «вносити чи ні» приймає власник звітів** — ти нічого не змінюєш і нікуди не записуєш.

**Формат кожної пропозиції — один рядок:** `пропозиція · чому (що саме було незручно в цій розмові)`.

**Що є хорошою пропозицією:** узагальнюване правило (не разова деталь цього ТЗ), з коротким «чому» і
посиланням на кейс із цієї ж розмови. Приклад: «додати стандартне питання про календарні vs активні дні»
← бо довелось уточнювати окремим раундом.


---

> 🌐 **Публічне дзеркало формул** — `formulas.freyal.renderhorizon.org`. Лише визначення метрик/формул; **живих цифр і доступів тут немає** (вони звіряються на сервері за авторизацією).
> **Згенеровано:** `2026-09-25 23:30:01 IST` · rev `9615e19` · оновлюється автоматично кожні 5 хв із canonical. Це завжди остання версія — використовуй її замість знімка у скачаному брифі.

# 📚 Універсальний каталог метрик і ресурсів — `reports.halleg.renderhorizon.org`

> Єдина база знань для обох брифів (`BRIEF_new_report.md`, `BRIEF_edit_report.md`).
> Тут зібрані **всі** звіти, метрики, формули, розрізи, поля provider-settings (supportzones)
> і доменні інваріанти нашого сервера. Бриф під час інтерв'ю **звіряє** побажання замовника саме
> з цим каталогом — щоб не вигадувати метрику, яка вже є, і не забути обов'язкові домовленості.
>
> Джерела істини: модуль маршрутів (маршрути), внутрішній глосарій,
> `report-skill-from-spec/references/report-patterns.md`, модуль звіту
> (маппінг supportzones), **внутрішній довідник фінформул** (у власника) (canonical фінформули).

---

## 🕐 Актуальність формул (читати ПЕРШИМ)

- **🌐 ЗАВЖДИ СВІЖЕ ДЖЕРЕЛО (для всіх, хто скачав бриф):**
  **`https://formulas.freyal.renderhorizon.org/metrics-catalog.md`**
  Перед кожним ТЗ **підтягни цей URL** (WebFetch/браузер) і бери формули **звідти** — він ребілдиться з canonical
  **кожні 5 хв**, тож формули останні навіть у зовнішнього замовника без доступу до наших репозиторіїв.
  Текст нижче в цьому файлі — лише **офлайн-фолбек** на випадок, якщо URL недоступний.
- **Знімок станом на:** `2026-08-14` · **версія каталогу:** `v37`.
  *(цей рядок при публікації **автоматично** синхронізується з верхнім рядком Changelog — руками не правити)*
- **Правило свіжості (головне).** Цей файл — **знімок**, формули з часом дрейфують (напр. CR перевели на метод
  Фінансів 2026-07-27). Тому **перед тим як вписати будь-яку формулу в ТЗ — візьми її з live-URL вище** (а на
  нашому сервері ще й перезвір із canonical-джерелом наживо, список нижче). Якщо жива версія відрізняється від
  знімка — **жива перемагає**, а знімок тут же онови (рядок формули + Changelog + дата/версія вгорі).
- **Джерело істини по кожному типу формул** (перезвіряти саме тут):

| Тип формули | Canonical (де актуальна версія) |
|---|---|
| Фінансові (CR, settlement, deposit, комісії, маржинальність, scoring) | **внутрішній довідник фінформул** (у власника) + `lab-dashboard-metrics.md` |
| Визначення метрик звітів (легенда) | внутрішній глосарій, `legend_rows.txt` |
| Реально реалізована формула конкретного звіту | код `<внутрішній репозиторій>/*.py` (модуль, що будує звіт) |
| Кінцеві цифри | live-джерела §9 (CH hermes / halleg / supportzones / dsfrogboard) |

- **Освіжити:** відкрий свіжий каталог за посиланням вище — він оновлюється кожні 5 хв; якщо формула різниться зі вшитою копією, бери з посилання.
  додати запис у Changelog, підняти дату/версію. `knowledge/` синкається cron'ом кожні 5 хв (hard-reset на origin) —
  тому це завжди остання версія компанії.


## 0. Як користуватись каталогом (для інтерв'ю)

0. **Освіжи формули ПЕРШИМ ділом** (див. «Актуальність формул» вище): перезвір кожну потрібну формулу з canonical
   наживо; застарілі в знімку — онови. У ТЗ іде **жива** формула, не заморожена.

1. Замовник називає метрику/розріз → знайди її нижче.
2. **Є в каталозі** → бери готову формулу, не переписуй. Уточни лише пороги/період, якщо відрізняються.
3. **Немає** → це нова метрика → зафіксуй точну формулу зі слів замовника + познач `⚠ нова, звірити з knowledge`.
4. **Фінансова** (комісії, settlement, deposit, scoring, конверсії) → **обов'язково** звірити з
   **внутрішній довідник фінформул** (у власника); якщо там інше — не застосовуй мовчки, спитай.
5. **🔴 Крос-звірка з живими джерелами (§9) — під час брифа.** Кожну **формулу/метрику/фільтр/нейминг** зістав із
   CH `hermes` + halleg (+ dsfrogboard для ліквідності/диспутів/прогнозу; supportzones — через CH-репліку). **Спершу
   вирівняй фільтр, потім порівнюй.** Будь-яка розбіжність (формула/цифра/назва/значення фільтра) → **питання замовнику**.

---

## 1. Наявні звіти на сервері (щоб не дублювати)

`REPORT_LABELS` з модуль маршрутів. Перед створенням нового звіту перевір — можливо, потрібне
вже є як вкладка/слайсер існуючого.

| id (URL `/…`) | Назва в UI | Про що |
|---|---|---|
| `trader-kpi` | Trader KPI | Головні KPI по трейдерах (обсяг, CR, активні, депозити) |
| `trader-operational` | Trader Operational | Оперативний зріз: зміни, наливи, on-shift, аномалії |
| `trader-detailed` | Trader Detailed | Деталізація по провайдеру: баланси/ліміти/статуси (тягне supportzones-поля) |
| `trader-hourly-calculator` | Trader Hourly Calculator | Погодинний калькулятор навантаження |
| `trader-scoreboard` | Scoreboard | Скоринг трейдерів, тири, Total score + тренди |
| `trader-weekly` | Weekly KPI | Тижневі KPI (ISO Пн–Нд), Δ тиждень-до-тижня |
| `trader-affiliate` | Affiliate Dashboard | Дашборд по афіліатах (пули трейдерів) |
| `trader-affiliate-weekly` | Affiliate Weekly | Тижневий по афіліатах: portfolio / funnel / risk / subaff-matrix |
| `trader-affiliate-tier` | Affiliate Tier | Тири на рівні афіліатів |
| `churn` | Retention & Churn | Відтік/реактивація трейдерів |
| ~~`churn-v2`~~ | Retention & Churn V2 | 🗑 немає в коді й правах · Оновлена методологія churn |
| `trader-merchant-slu` | Merchant SLU | Зріз по мерчантах / SLU-групі |
| ~~`trader-main-dashboard`~~ | Main Dashboard | 🗑 немає в коді й правах · Головний дашборд (еталон-порт PBI Main_dashboard) |
| ~~`trader-daily-payin`~~ | Daily Payin | 🗑 немає в коді й правах · Денний payin-обсяг |
| `trader-affiliate-native` | Affiliate trader | Дашборд по афіліатах (нативна реалізація «внутрішній модуль»): PayIn/PayOut, трейдери, нові кабінети, запуски — по кожному афіліату |
| `merchant-out-in` | Merchant Out/In | Скільки мерчант виводить відносно того, скільки заводить: IN ₹ · OUT ₹ · Баланс ₹ · Виведено/Заведено % · Δ (у режимі 1 дня — до медіани 7д). Окремий блок Exchange (вхід = topup) |
| ~~`trader-recovery`~~ | Recovery Report | 🗑 немає в коді й правах · прибрано власником 14.08.2026 (свідомо, не втрата) · Повернення трейдерів із churn: динаміка й обсяги повернених. Його визначення «churn = пауза ≥30 днів» **перенесено в §2.4-bis** — воно НЕ збігається з канонічним тижневим churn |
| ~~`traders-active`~~ | Active Traders | ⏸ вимкнено в коді · Активні трейдери (окремий зріз; уточнити деталі по коду перед використанням у ТЗ) |
| ~~`trader-capacity`~~ | Trader Capacity | ⏸ приховано (owner перебудовує на staging) |
| ~~`trader-shifts`~~ | Trader Shifts | ⏸ приховано |
| `threshold-analysis` | Threshold Analysis | Пороги скорингу трейдерів (Trader Scoring 2026) — підбір/аналіз брекетів. Сторінка під логіном, у `REPORT_LABELS` **немає**, тому в навігації не з'являється |
| `affiliate-report` | Affiliate Dashboard — єдиний звіт | Зведена сторінка по афіліатах: status + coverage-матриця + open questions + архітектура. Legacy-URL `/affiliate-status`, `/affiliate-coverage`, `/affiliate-open-questions` → **301 сюди** |
| `metrics-legend` | Легенда метрик | Довідка по метриках Trader Reporting (обсяги, конверсія й автоматизація, трейдери й рахунки) — не звіт, а супровідна сторінка |
| `affiliate-churn-methodology` | Churn & Reactivation — методологія | Опис методології churn по афіліатах: шкала когорт 7d/14d, три булеві ознаки на трейдера, класифікація статусу, Churn Rate |
| `trader-hourly-backtest` | Hourly Backtest | ⚠️ без маршруту · Бектест погодинної моделі; права є (4 юзери), сторінки в «внутрішній модуль» немає — legacy або окремий сервіс, звіряти по коду перед ТЗ |
| `trader-teamlead` | Teamlead Trader | ⚠️ **AUTO — опис не заповнено** |

> ⚠️ **Чого в цьому списку НЕМАЄ — того й немає в наших звітах.** Зокрема **кабінет партнера**
> (mithras Affiliate Cabinet, `/cabinet/*`) — це **окремий продукт іншої команди**, не наш звіт.
> Його розділи (**Payouts**, **Structure/Commission**, **картка трейдера**, «My traders») до `trader-*`
> не належать. Якщо замовник оперує цими назвами — спершу зʼясуй продукт: правка чужого кабінету йде
> до їхніх розробників, а «те саме, але в нашому звіті» — це **нова розробка**, не правка відображення.

Каталог у застосунку віддається endpoint-ом `GET /api/reports-list`.

<!-- згенеровано import_reports_list.py @ 2026-09-25 10:15 IST · джерела: app.py · БД доступів -->
**Авто-реєстр звітів (22) — знімок із коду, оновлено `2026-09-25 10:15 IST`:**

| id | Назва в UI | Сторінка | Юзерів з доступом |
|---|---|---|---|
| `affiliate-churn-methodology` | — | ✅ | 0 |
| `affiliate-report` | — | ✅ | 0 |
| `churn` | Retention & Churn | ✅ | 15 |
| `merchant-out-in` | Merchant Out/In | ✅ | 4 |
| `metrics-legend` | — | ✅ | 0 |
| `threshold-analysis` | — | ✅ | 0 |
| `trader-affiliate` | Affiliate Dashboard | ✅ | 13 |
| `trader-affiliate-native` | Affiliate trader | ✅ | 8 |
| `trader-affiliate-tier` | Affiliate Tier | ✅ | 10 |
| `trader-affiliate-weekly` | Affiliate Weekly | ✅ | 11 |
| `trader-capacity` | Trader Capacity | ✅ | 7 |
| `trader-detailed` | Trader Detailed | ✅ | 102 |
| `trader-hourly-backtest` | — | ⚠️ немає маршруту | 4 |
| `trader-hourly-calculator` | Trader Hourly Calculator | ✅ | 19 |
| `trader-kpi` | Trader KPI | ✅ | 12 |
| `trader-merchant-slu` | Merchant SLU | ✅ | 8 |
| `trader-operational` | Trader Operational | ✅ | 104 |
| `trader-scoreboard` | Scoreboard | ✅ | 110 |
| `trader-shifts` | Trader Shifts | 🚫 вимкнено (закоментовано) | 0 |
| `trader-teamlead` | Teamlead Trader | ✅ | 96 |
| `trader-weekly` | Weekly KPI | ✅ | 99 |
| `traders-active` | Active Traders | 🚫 вимкнено (закоментовано) | 4 |

> ✅ **Дрейфу немає** — усі звіти з коду описані в §1.
>
> 🚫 **Вимкнені в коді** (маршрут/лейбл закоментовано — сторінка НЕ відкривається, хоч права могли лишитись): `trader-shifts` · `traders-active`.
>
> ⚠️ **Без маршруту в «внутрішній модуль»** (legacy / окремий сервіс — звіряти по коду перед ТЗ): `trader-hourly-backtest`.
>
> 👻 **ОПИСАНО В §1, але в коді/правах уже НЕМАЄ** — не пропонуй ці звіти замовнику без перевірки: `churn-v2` · `trader-daily-payin` · `trader-main-dashboard` · `trader-recovery`.
>
> ↪️ **Legacy-URL (301-редіректи)** — якщо замовник дасть таке посилання, це той самий звіт: `/affiliate-coverage` → `/affiliate-report` · `/affiliate-open-questions` → `/affiliate-report` · `/affiliate-status` → `/affiliate-report`.
>
> 🧪 **Не звіти — демо/службові сторінки без авторизації** (у реєстр не входять): `/kpi-compact-demo` · `/nav-rich` · `/nav-sidebar-demo` · `/nav-v1-simple` · `/nav-variants` · `/sidebar`.
*(авто-реєстр із коду підставляється генератором зі знімка `reports-list.snapshot.md`)*

> ⚠️ **Перевіряй дрейф:** авторитетний перелік — `REPORT_LABELS` + маршрути `@app.get` у модуль маршрутів
> і таблиця `permissions` в БД доступів. **Таблицю вище тепер доглядає `tools/import_reports_list.py`**
> (щодня о 04:45 UTC): нові живі звіти дописуються, зниклі позначаються `~~закреслено~~`, а курований
> опис «Про що» **ніколи не затирається**. Ручне «⏸ приховано» скрипт теж не скасовує.
>
> Читай статуси так: `~~id~~ 🗑` — у коді й правах уже немає (не пропонуй замовнику без перевірки);
> `~~id~~ ⏸` — свідомо приховано або закоментовано в коді; `⚠️ без маршруту` — права є, сторінки немає
> (legacy/окремий сервіс, звіряти по коду перед ТЗ). Свіжий зріз із лічильниками — в авто-реєстрі нижче.

---

## 2. Бібліотека метрик (переписуй формулу, не вигадуй)

### 2.1 Обсяг і активність
| Метрика | Код/алиас | Формула | Одиниця |
|---|---|---|---|
| PayIn Volume | `vol` | `SUM(amount) WHERE operation='payin'` (по ВСІХ провайдерах — без vol_filter) | ₹ |
| PayOut Volume | — | `SUM(ABS(amount)) WHERE operation='payout'` (в Hermes payout з мінусом → `ABS`) | ₹ |
| Volume, L (scaled) | `Volume,L` | `vol / 100 000` | — |
| Active Traders | `tru` | `COUNT(DISTINCT provider)` з `payin>0` | шт |
| AVG Active Traders | `tr` | `AVG(daily_active_count)`; завжди `tr ≤ tru` | шт |
| Active days | — | к-сть різних днів з ≥1 транзакцією у періоді | дн |
| Avg PayIn/Trader | `avg` | `vol / SUM(daily_active_count)` (знаменник — сума, не середнє) | ₹ |
| Active accounts | — | `COUNT(DISTINCT bank_account)` з ≥1 успішною трз | шт |
| New Traders | `nt` | перша payin-транзакція припадає на період | шт |

### 2.2 Якість / конверсія / швидкість
| Метрика | Формула | Примітка |
|---|---|---|
| **CR (Success Rate), %** — ⚖️ **метод фінансів (стандарт Андрія)** | `success / final` (див. callout нижче) — **НЕ** `success/COUNT(*)` | BLANK при final=0 |
| Change CR / Trend CR | абсолютна різниця CR (п.п.) поточний − попередній | лише «зрілі» трейдери (30D≥2.0міс / 14D≥0.9 / 7D≥0.5) |
| % Auto (Auto success) | `COUNT(success AND edited_by_support=false) / COUNT(success)` | payout-auto: `checking_by = AUTO_PAYOUT_CHECKER` |
| **PayIn speed** | **P90** `quantileExactInclusive(0.9)` від `payments.source_created_at` до **`transactions.source_created_at`** (момент приходу транзакції), у хвилинах. **Винятки — частина визначення:** без апеляцій (`appeals.payment_pid`), без `is_cancel_transaction`, без `is_aborted`, лише `completed`/`successed_by_partner`, `dateDiff ≥ 0`. ⚠️ Додаткового «відкиду викидів» **НЕМАЄ** — перцентиль ріже хвіст сам. **Guard:** при `n < 4` показувати `—` і **не давати балів** (не нуль — нуль карає). Джерело істини: «внутрішній модуль» |
| AVG speed / Total Time (простий) | `AVG(source_updated_at − source_created_at)` хв по success payin, **без** відсікань/перцентилів — **інша** метрика, ніж «PayIn speed» вище. SLU=чистий (без апілів), merchant=з апілами. Методику визначає власник (напр. la_grange_FC) | не плутати з p95-варіантом |
| **PayOut speed** | **P90** `quantileExactInclusive(0.9)` від `assigned_at` до `source_updated_at`, у хвилинах. Лише `completed`/`successed_by_partner`, `dateDiff ≥ 0`, guard `toYear(assigned_at) ≥ 2000` (epoch-zero). Той самий guard `n < 4` → `—`. Джерело істини: «внутрішній модуль» |
| PI/PO median time | `MEDIAN(approved_at − source_created_at)` по completed | джерело — `payment_status_history` |
| %Payin < 5min | частка payin, оброблених <5 хв | quality-of-service |
| %Payout < 30min | частка payout, оброблених <30 хв | |

> ⚖️ **CR — метод фінансів (обов'язковий стандарт: усі звіти зводимо до того, як рахують Фінанси/lab).**
> `CR = success / final`, де:
> - **success (чисельник)** = `status IN ('completed','successed_by_partner')` — **однаково в усіх звітах**.
>   Прибрати внутрішню розбіжність: `Operational` рахував success тільки як `successed_by_partner`,
>   `Merchant-SLU` — тільки як `completed`; звести до **обох** статусів скрізь.
> - **final (знаменник)** = лише заявки, що дійшли **фінального** статусу: `success + reject + cancel + expire + fail`.
>   **Виключити `pending`/`processing`** (ще не фінальні). **НЕ `COUNT(*)`** — саме тут головне розходження.
> - **Навіщо:** щоб CR збігався з lab (Фінанси) та `eye-of-god` **1:1**. Наш старий CR (з `COUNT(*)`) систематично
>   **занижений** — незавершені заявки висять у знаменнику. Різниця максимальна протягом дня (багато `pending`).
> - **Коди статусів у hermes:** success = `completed`/`successed_by_partner`; reject = `rejected`/`rejected_by_partner`.
>   `cancel`/`expire`/`fail` у нашому коді **не використовуються** — точний перелік фінальних кодів **уточнити у Фінансів**.
> - ⚠ **Canonical:** цієї формули ще НЕ в **внутрішній довідник фінформул** (у власника) (lab-файл не має метрики конверсії).
>   Після підтвердження списку фінальних статусів — **зафіксувати через MR у `renderhorizon/knowledge`**, і лише тоді вважати остаточною.

### 2.3 Гроші / депозити / ефективність
| Метрика | Формула | Примітка |
|---|---|---|
| Deposit | `SUM(cum_dep)` на LAST_DAY періоду (snapshot; виключити archive і рахунки «Payable») | у Scoreboard — bank accounts з тегом "deposit" |
| Settlement | **дві форми:** (1) внутр. трейдери — `SUM(expenses.cash_out) WHERE category='settlement'`; (2) **зовн. шлюзи/SLU** — розрахункова `−SUM(amount−fee) WHERE completed` (проводок у БД немає) | fin → knowledge; яка форма — за типом сутності |
| Settle/PayIn %, `stl%` | `SUM(settlement) / vol` | |
| Settle | сума розрахованих коштів за період; в UI інвертована (`×-1`) як додатне | |
| **Vol/Dep Ratio** `vd` | `(vol/active_days) / (dep_active × 93)`. `93` — константа з PBI, **не міняти** | >2.0 добре · 1.5–2.0 норма · <1.5 низько |
| Balance2dep, % | мінімальний поріг balance/deposit, щоб трейдер лишався активним | risk-mgmt |
| Statement % | частка bank_accounts з підвантаженою випискою (`bank_statement_reports`) | |

### 2.4 Порівняння / динаміка (є майже в кожному звіті)
- **Δ до попереднього періоду** (семафор good/neutral/bad) — обов'язковий патерн.
- **Change / Trend** *(volume, CR, active days, total score)* — з фільтром «зрілості» (виключає новачків <2 міс).
- **Traders change / growth %** — `count − count_prev` та `(count−count_prev)/count_prev`.
- **Hour-to-Hour ★** — «чесне порівняння»: обрізає кожен історичний період до поточної позиції всередині
  періоду (день → до поточної години IST; тиждень → до дня+години; місяць → до дня місяця).
  🔴 **Це стандарт для БУДЬ-ЯКОГО `previousValue` / Δ / «vs попередній період» — у KPI-картках так само,
  як у графіках і таблицях.** «Попередній період» = **той самий відрізок попереднього періоду**
  (місяць → 1..той самий день місяця), а **НЕ** «попередні N днів впритул».
  *(Кейс кабінету mithras: prev для month рахували як 19–31.07 замість 01–13.07 → картка показувала
  зростання **+29.3 %** замість чесних **+11.1 %**; формально жодного речення каталогу не порушено,
  бо правило читалось як опція для порівняльних блоків. Тепер — обовʼязкове для всіх Δ.)*
  Якщо свідомо потрібне інше вікно порівняння — **підписати його на картці явно**.
- **TOP-N Best/Worst** — топ-7 за приростом/падінням Total score.
- **Weekly comparison** — ключові метрики поточний тиждень vs попередній.

### 2.4-bis Churn / Reactivation — КАНОН і дві розбіжності (зафіксовано 2026-08-14)

> Записано, бо визначення жило **лише в описі звіту `trader-recovery`**, а той звіт видалено 14.08.2026.
> Одна назва «churn» використовується у трьох місцях із **різними** вікнами — питай, яке саме мають на увазі.

**🥇 КАНОН (звіти `churn`, Trader KPI, афіліатські):** логіка Power BI `Trader_Weekly`, 1:1.
Джерело істини — `<внутрішній репозиторій>/churn.py`:
- **`active(W)`** = **≥1 `ClientPayin`** (`payments.type='payin'`) у вікні W, **БУДЬ-ЯКОГО статусу** —
  тобто **факт спроби**, а не успіх;
- грейн трейдера = `AccountProvider.id` через `BankAccount.provider_pid`;
- TZ **IST**, вікна **напіввідкриті** `[start, end)`, `today` = дата «зараз» за IST;
- **Churned This Week** = `active(Last) AND NOT active(This)`;
  **Churned Last Week** = `active(PrevPrev) AND NOT active(Last)` (зсув WoW = 7 днів);
- виключення: `name='Operation provider'`, `%QA_Test%`, `lower(name) LIKE '%test%'`.

> 🔴 **Розбіжність №1 з інваріантом §6.** Глобально «успіх = `completed`/`successed_by_partner`», а тут
> `active` рахує **будь-який** payin. Це **навмисно** (дзеркалимо PBI), але саме тому churn НЕ можна
> звіряти з активністю з інших звітів «в лоб» — знаменники різні.

> 🔴 **Розбіжність №2 — «пауза ≥30 днів».** Видалений `trader-recovery` визначав churn як
> **паузу ≥30 днів** (gap між активними payin-днями ≥31) — це **інше** поняття (довгостроковий відтік
> для аналізу повернень), не тижневий канон вище. Якщо замовник каже «churn 30 днів» — він має на увазі
> саме цю логіку, і її **немає в жодному живому звіті**.

**Супровідні матеріали:** `/affiliate-churn-methodology` (жива сторінка) описує ту саму PBI-логіку
через когорти: `ever_before` (до `today−14`), `active_last_week` `[today−14, today−7)`,
`active_this_week` `[today−7, today]`. Звіт `churn` має грейн «когорта × день», вікно 14 днів;
його `_pct()` — **інтерполяція, не канон** (див. §2.5-bis про перцентилі).

> ⚠️ «внутрішній модуль» посилається на `CHURN_LOGIC.md` — **такого файлу в репозиторії немає**.
> Єдине живе джерело методології — докстрінг самого «внутрішній модуль»; при правках churn читати його.

### 2.5 Реєстр метрик по джерелах (provenance — звідки яка; без дублювання)

> **Легенда джерел:** `[halleg]` — рахується в коді звіту (`<внутрішній репозиторій>/<module>.py`); `[knowledge-lab]` — canonical у
> **внутрішній довідник фінформул** (у власника) (**повна формула ТАМ, тут не дублюємо**);
> `[dsfrogboard]` — борд ліквідності/диспутів/прогнозу; `[supportzones→CH]` — поля provider-settings (§5).
> Метрики **§2.1–§2.4 вище** = `[halleg]` + `report-patterns` (базовий набір trader-*). Нижче — тільки те, чого там НЕ було.

**a) Звіт-специфічні halleg, яких не було в §2.1–§2.4:**
| Метрика | Формула (стисло) | Модуль `[halleg]` |
|---|---|---|
| % First / Second+ Sender | `uniqExactIf(рахунок платив цьому мерчанту ≥3)/uniqExact` — **наближення** (точне в Looker) | «внутрішній модуль» |
| AVG Total Time (merchant) / AVG speed (SLU) | `AVG(source_updated_at−source_created_at)` хв; SLU=без апілів, merchant=з апілами | «внутрішній модуль» |
| Оборот G-I | `AmountIN + |Settlement| + PayOut` | «внутрішній модуль» |
| Days 20+ Success · Avg/day (active) | к-сть днів з ≥20 completed · `AmountIN / активні дні` | «внутрішній модуль» |
| Merchant ARPU · Payout ARPU | `TotalPayin/Success` · `Payout/success_payout` | «внутрішній модуль» |
| CRBA (CR by bank account) | `crba_num/crba_den` | «внутрішній модуль» |
| % Success-with-UTR (isUTR) · % isBankAccount | `success/utr_cnt` · `1−nobank/den` | «внутрішній модуль» |
| Appeal % · Settle % | `appeal_cnt/total` · `−settle/payin` | «внутрішній модуль» |
| Unresolved Payin/Payout appeals | `COUNT appeals status NOT LIKE 'resolved%'` (payin через bank_account, payout через provider_pid) | «внутрішній модуль» (§7) |

**b) Фінансові/USDT метрики — `[knowledge-lab]` (105 шт; формули в canonical, НЕ дублюємо):**
Повний реєстр з формулами — **внутрішній довідник фінформул** (у власника). Групи (сегменти p2p/intent/transit + USDT):
- **payin / payout**: `payin_total/p2p/intent/transit(+_usdt)`, `payout_total/p2p/intent/transit`, `payout_imps`, `payout_upi(+_transit)`
- **payin_gateway** (шлюзові payin p2p/intent) · **volume_intent** (`payin_intent_quasi/real`) · **transactions** (`tx_total/p2p/intent/transit` + quasi/real, `tx_all_total`)
- **merchant_aff · merchant_fee_payin · merchant_fee_payout** (мерчант-affiliate дохід + комісії)
- **provider_aff · provider_fee_tx · provider_fee_expense** (провайдер-affiliate + комісії, `prov_fee_purpose`)
- **cost_analysis** (`payin_usdt_ap_*` по класах трейдерів, `settle_fee_*`, `prov_fee_tx_*`)
- **revenue · fx_revenue · chargeback · settlement** (`settle_fee_usdt`) · **transit_errors / transit_presents**
- **withdrawal · withdrawal_rev · bonus · sanction**
- **Marginality** (derived, вкладки «Партнёри»/«Провайдери»)
> Усі — база той самий CH `hermes`, 4 правила CH (FINAL · `_peerdb_is_deleted=0` · `toTimezone(...,IST)` · статус `IN('completed','successed_by_partner')`). Валюта більшості — **USDT**.

*(повні формули lab — у `metrics-catalog.md` / `lab-metrics-full.md` на цьому ж хості, §2.5b; авто-імпорт, синк 5 хв)*
*(Повні формули lab підставляються сюди генератором при кожному ребілді — авто-імпорт з `lab-dashboard-metrics.md`, синк 5 хв. У цьому вихідному файлі — лише індекс вище; у публічній версії тут будуть усі 105 формул.)*

**c) dsfrogboard — `[dsfrogboard]` (інший домен, §9.1 D):**
| Домен | Метрики/поля | Джерело |
|---|---|---|
| Диспути (complaints) | `total · fin_open · draft · in_progress · need_more_proofs · sent_to_merchant · forwarded_to_fin · closed_approved · closed_rejected` | API «внутрішній API джерела» |
| Прогноз (forecast) | `payin_actual/fc · payout_actual/fc · payin_p2p_actual/fc · payin_intent_actual/fc · MAPE(all/p2p/intent)` (модель 7д×сезон дня тижня, INR) | API «внутрішній API джерела» |
| Ліквідність | вкладки `main/rates/logistics/journal/history/cap_history/settle/forecast`; таблиці `l0–l9`; USDT-баланси/курси/сеттли/капітал | inline HTML (server-render) |
> Payin/payout dsfrogboard = CH-based, але з власним фільтром (виключає неатрибутовані payout) — див. §9.4.

**c2) dsfrogboard BAM — `[dsfrogboard-bam]` (KPI/bonus-дашборд співробітників, §9.1 E):**
KPI-колонки живого дашборда: CR по бакетах сум (`<1k / 1-5k / 5-10k / ≥10k`), CR дедуп/сегмент, Avg trader/total time,
`t успеха/UTR p50/p90`, cust.rej→успех/реджект, No-UTR/No-delivery, Overturn proxy, Force-majeure, Апеляцій, Score(0-100),
маржинальний індекс, Settlement-премія. Вкладки: dashboard/bonuses/audit/admin/guide.

**c3) dsfrogboard `/affiliates.html` — `[dsfrogboard-aff]` (реєстр комісій аффів, §9.1 G):**
| Домен | Метрики/поля | Джерело |
|---|---|---|
| Конфиг трейдер↔аффы | `pct_in · pct_out` трейдера; звʼязки `{aff_name, role, pct_in, effective_from/to, tiers}`; ролі `main/aff/manager/team_lead/lead(COP)/self/aff_manager`; 11 статусів трейдера | «внутрішній API джерела» |
| **Общий круг** | `pct_in + pct_out + Σ pct_in аффів` (крім `role='lead'` і `is_global`), стеля `monopoly_round` = **5%** | обчислюється на фронті + «внутрішній API джерела» |
| Реестр аффів | `type(provider_aff/manager/main/team_lead/lead) · default_pct · wallet_chain · wallet_monthly · payout_group_id · solo · closed_group · eff_from/to` | «внутрішній API джерела», «внутрішній API джерела» |
| Проверка системы | `in_doc · matched · our_affs[] ⇄ sys_affs[] · diffs[kind: pct/only_doc/only_sys] · name_collisions` | «внутрішній API джерела» |
| Мерчанты | `merchant · mode(payin/payout) · volume_inr · is_exc · is_scam · has_aff · sys_match` + аффи мерчанта | «внутрішній API джерела» |
| Дублікати / спільні | 43 групи схожих імен аффів; афф на кількох «главных» (`pinned_main_id`) | «внутрішній API джерела», «внутрішній API джерела» |
> ⚠️ **Дві одиниці відсотка на одному борді:** `by-trader.pct_in` — **дріб** (0.004), `system-check.our_affs.pct` — **в.п.** (0.4).
> Це одне й те саме; порівняння «в лоб» дає розходження рівно в 100×. Повний довідник + пастки: `references/dsfrogboard-affiliates-metrics.md`.
📖 **Повний BAM-довідник для звірок — `references/bam-metrics-and-logic.md`** (live: `formulas.freyal…/bam-metrics-and-logic.md`):
формула + **джерело (PG/CH) по КОЖНІЙ метриці** + усі алерти/пороги + логіка бонусів (блоки 1–4, force-majeure) + фільтри.
**Джерело даних — per-метрика (з довідника):** гроші/індекси/timeseries — **ClickHouse** (`/kpi`, `/merchant-kpi`, `/hourly`,
`/cr-by-merchant`, `/hourly-success`, `/payin-payout-ratio`, `/merchant-index`); деталізація/списки/downtime/черга/партнер-аналітика/
antifraud — **Postgres** (`/merchant-list`, `/downtime-alerts`, `/payout-queue`, усі `/partner/*`, `/antifraud` з `History`);
орг-структура/бонуси/baseline/audit — **SQLite** `db-bam`; downtime-timeline — **JSONL**. Success=`completed,successed_by_partner`,
sanity-cap `amount<100M`, TZ IST. Auth — окрема BAM-сесія (cookie `bam_sid`), НЕ Lab.

<!-- згенеровано import_bam_metrics.py @ 2026-09-02 20:47:01 IST · джерело: dsfrogboard /bam/ (роль head) · авто-глосарій із бандла -->
**Вкладки BAM:** admin, audit, bam-motivation, bonuses, dashboard, guide, portfolio, sales-index. **Дані:** payment-метрики — ClickHouse (hermes, senderAccount/History); org/бонуси/скоуп — Postgres BAM.

**Визначення метрик BAM (43) — `[dsfrogboard-bam]`** (авто-імпорт із бандла `bam-app.js`):

| Ключ | Метрика | Визначення (як рахується) |
|---|---|---|
| `kpi_amount_in` | Amount In | Сумма успешных пополнений (PayIn) в INR за окно фильтра по выбранным мерчантам. |
| `kpi_amount_out` | Amount Out | Сумма успешных выплат (PayOut) в INR за окно фильтра. |
| `kpi_requests` | Requests | Количество созданных PayIn-заявок за окно (все статусы, включая отклонённые). |
| `kpi_arpu` | ARPU | Средний чек проводки payin. |
| `kpi_cr_p2p` | CR P2P | Конверсия P2P-пейинов (метод upi): доля успешных среди завершившихся заявок (pending не считаются). |
| `kpi_cr_intent` | CR Intent | Конверсия intent-пейинов (метод upi_intent), pending не считаются. |
| `kpi_cr_button` | CR Button | Конверсия button-пейинов (кнопки Paytm/PhonePe), pending не считаются. |
| `kpi_payin_p90` | PayIn P90 | 90-й перцентиль времени от создания PayIn-заявки до её успеха: 90% успешных пейинов проходят быстрее этого времени. |
| `kpi_payout_p90` | PayOut P90 | 90-й перцентиль времени прохождения успешной выплаты (создание → успех). |
| `p_downtime` | Downtime Alerts | Мерчанты, у которых прямо сейчас трафик остановлен или деградировал (по снимкам PSP-включений). Красный = выключен, оранжевый = предупреждение. |
| `p_dtl` | Downtime Timeline | Визуальная лента включений/выключений мерчантов в PSP за выбранное окно: видно, кто когда падал и сколько простоял. Показываются только мерчанты, у которых были отключения в окне. |
| `p_payin_ts` | Объём PayIn | Суммы успешных пополнений по бакетам времени (час/день/неделя/месяц). Учитывает фильтры мерчантов/виджета/периода. Время — IST. |
| `p_payout_ts` | Объём PayOut | Суммы выплат по бакетам времени, с фильтром метода All/UPI/IMPS. |
| `p_merchlist` | Merchant List | Сводка по каждому мерчанту за окно фильтра: объёмы, CR, чеки, скорости. Сортировка по клику на заголовок. |
| `p_index` | Индекс мерчанта | Композитный скор здоровья мерчанта за rolling 30 дней (объём, CR, стабильность, динамика). Подробная формула — во вкладке «Справка» → «Индекс мерчанта». |
| `p_crmerch` | CR by Merchant (payouts) | Выплаты по каждому мерчанту: количество/суммы всех, успешных и отклонённых, конверсия и средний чек. Переключатель All/UPI/IMPS — метод выплаты. |
| `p_hourly` | Hourly Activity | Пейины по часам за сегодня (IST) в сравнении со вчера: сколько заявок, сколько успешных, CR, а также заявки без UTR и «не выдача» (система не смогла выдать реквизиты). |
| `p_queue` | Payout Queue | Текущая очередь выплат (pending/processing): сколько заявок и денег ждут выплаты прямо сейчас. |
| `p_hsuccess` | Hourly Payout Success | Почасовая динамика выплат: заявки, успехи, CR по часам. |
| `p_ratio` | PayIn / PayOut Ratio | Баланс входа и выхода per-мерчант: сколько денег заходит против выплат. Перекос показывает, кому не хватает входа или выхода. |
| `p_days` | Объём, CR, чеки по дням | Разбивка окна фильтра по дням: объём, количество, CR, средний чек — динамика внутри периода. |
| `p_utr` | UTR и скорость по дням | Доля пейинов без UTR (клиент не приложил реквизит платежа) и скорость прохождения (P90) по дням. |
| `p_senders` | Сендеры, первинка/вторинка, дедуп | Три блока по дням: уникальные отправители, доля новых/повторных, и метрики с дедупликацией повторных попыток (см. «?» у колонок). |
| `p_g1h1` | customerRejected и апелляции | Пейины, где клиент сам отметил «не оплатил/отмена» (customerRejected), их дальнейшая судьба, и доля заявок с апелляциями. |
| `p_j1` | Распределение по кнопкам виджета | Какие кнопки нажимают клиенты в платёжном виджете (PhonePe/Paytm/QR/GPay…) и какая конверсия у каждой. Пейины без клика по кнопке (прямой P2P/интент-флоу) исключены. |
| `p_e2` | Сегменты сендеров по CR | Отправители сгруппированы по личной конверсии: High = CR ≥ 50%, Mid = 20–50%, Low < 20%. Показывает качество базы плательщиков. |
| `p_feedback` | Причины отмены и реджекта | Причины, которые клиенты указали в фидбеке при отмене/неуспехе платежа, агрегат за период. |
| `p_antifraud` | Антифрод — нестворенные пейины | Заявки, которые антифрод не дал создать. Тяжёлый запрос по History — грузится по кнопке. |
| `p_baseline` | Baseline индекса | Опорные значения для шкалы A1 индекса мерчанта. |
| `c_senders` | Сендеров | Уникальные отправители (senderAccount) с пейинами за день. |
| `c_avg_ps` | Avg payin/sender | Сколько пейинов в среднем делает один отправитель за день. |
| `c_first` | % первинки | Доля пейинов от отправителей, которых НИКОГДА раньше не было в системе (первый платёж этого senderAccount за всю историю). |
| `c_repeat` | % вторинки | Доля пейинов от повторных отправителей (уже платили раньше). |
| `c_intents` | Намерений (D3) | Уникальные «намерения оплатить»: повторные попытки одного клиента схлопнуты по связке день + мерчант + отправитель + сумма. |
| `c_attempts` | Спроб | Сколько всего пейинов (попыток) создали эти намерения — ретраи считаются каждым разом. |
| `c_dup` | % дублей | Доля повторных попыток: сколько пейинов — это ретраи того же намерения. |
| `c_crdedup` | CR дедуп % | Честная конверсия: доля намерений, где ХОТЯ БЫ ОДНА попытка закончилась успехом. Не штрафует за ретраи, в отличие от обычного CR. |
| `c_custrej` | cust.rejected | Пейины, где клиент сам нажал «не оплатил / отмена» в виджете. |
| `c_cr_succ` | cust.rej → успех | Из них всё же закончились успехом (деньги пришли, несмотря на отметку клиента). |
| `c_cr_rej` | cust.rej → реджект | Из них закончились реджектом/отменой. |
| `c_appeals` | Апелляций | Пейины дня, по которым создана апелляция. |
| `c_pct_app` | % апелл. | Доля пейинов с апелляцией от всех пейинов дня. |
| `c_overturn` | Overturn proxy % | Доля апелляций, где пейин в итоге успешен — прокси «апелляция обёрнута в успех». |

**KPI-колонки дашборда (151):** `Merchant` · `Amount In` · `Δ In` · `Ср ДН(3н)` · `Фкст ДН` · `Δ темп ДН` · `Amount Out` · `Δ Out` · `CR` · `Δ CR` · `PayIn P90` · `Δ P90 In` · `PayOut P90` · `Δ P90 Out` · `Мерчант` · `PayIn 30d ₹` · `Score (0-100)` · `Дата` · `% UTR` · `UTR→успех %` · `UTR→реджект %` · `t успеха p50 мин` · `t успеха p90 мин` · `t UTR p50 мин` · `t UTR p90 мин` · `Сендеров` · `Avg payin/sender` · `% первинки` · `% вторинки` · `Намерений (D3)` · `Спроб` · `% дублей` · `CR дедуп %` · `cust.rejected` · `cust.rej → успех` · `cust.rej → реджект` · `Апелляций` · `% апелл.` · `Overturn proxy %` · `Кнопка` · `Пейинов` · `% от всех` · `CR %` · `Сегмент` · `CR сегмента %` · `% от сендеров` · `% от пейинов` · `Тип` · `Причина` · `Метод` · `IN min` · `IN max` · `OUT min` · `OUT max` · `Deposit` · `Overdraft` · `Вкл` · `Total cnt` · `Total ₹` · `Success cnt` · `Success ₹` · `Reject cnt` · `ARPU` · `Avg time` · `Total` · `Success` · `Sum` · `vs yest CR` · `No-UTR` · `No-delivery` · `Success sum` · `Requests` · `Assigned` · `Avg total time` · `Avg trader time` · `PayIn ₹` · `PayOut ₹` · `Surplus` · `Balance ₹` · `PayOut/PayIn` · `Приоритет` · `Создано` · `Успех` · `Оборот ₹` · `Avg чек` · `Медиана` · `CR <1k` · `CR 1-5k` · `CR 5-10k` · `CR ≥10k` · `% скопир.` · `Имя` · `Роль` · `Тим-лид` · `Мерчей` · `Флаги` · `Статус` · `Действия` · `Сотрудник` · `Партнёр` · `С` · `По` · `Время` · `Кто` · `Действие` · `Сущность` · `Детали` · `Ставка $` · `Блок 1 · рост` · `Блок 2 · команда` · `Блок 3 · индекс` · `Блок 4 · settle` · `Итого Бонусы $` · `Force-majeure` · `Рост портфеля` · `Бонус` · `Рост` · `Δ индекса м/м` · `Рост доли settlement` · `Премия` · `current index` · `Δ от baseline` · `score` · `#` · `Компонент` · `Вес` · `Формула` · `Норма (=50)` · `Режим` · `Пул` · `Порог $75K` · `Кому` · `P2P круг` · `пул` · `Intent круг` · `Button круг` · `Индекс месяца` · `Доля Sales` · `Грейд` · `Оклад / мес` · `Переход (3 месяца подряд)` · `База (% оклада)` · `Junior` · `Middle` · `Senior` · `Индекс маржи` · `Множитель` · `Senior, рост 20% ($900 базы)` · `Рост приоритетного метода к своей базе` · `Доля мерча` · `Бонус (% оклада)`

> 📖 **Повний BAM-довідник** (формула + джерело **PG/CH** по КОЖНІЙ метриці + алерти/пороги/логіка бонусів/фільтри): **`bam-metrics-and-logic.md`** на цьому хості — використовуй його для звірок BAM-метрик.
*(повний глосарій підставляється генератором зі снапшоту `bam-metrics.snapshot.md` — авто-оновлення: «внутрішній модуль» логіниться в BAM і парсить бандл щогодини, генератор підставляє тут кожні 5 хв.)*

**c3) eye-of-god — `[eye-of-god]` (§9.1 F):**
KPI (`req_in`,`amount_in`,`arpu`,`pct_autopayin`,`speed_avg/p90`,`payout_cr`,`speed_out_avg/p90`,`‹мерчант›/real_‹мерчант›/present/antigift`) ·
по партнерах (`total_id`,`success`,`reject`,`cr`,`pct_appeal`) · воронка втрат (`cc_true/false`,`r_auto`,`r_auto_client`) ·
live-стан (`review/pending/processing/stuck`, `saved_sum_month`) · апеляції у **% від трафіку** · якість саппорту (`p90_min`) ·
швидкість-гістограма (0-10м…>12год) · transit-KPI. Зрізи: Все/UPI/INTENT/BUTTON/TRANSIT + **UI vs NOUI**.
⭐ **CR тут = `success/(success+reject)`** — перевірено на живих даних (12/12 рядків), тобто **той самий канон Фінансів**.
Повний перелік: `references/eye-of-god-metrics.md`.

> **➕ 2026-08-10 (запит 77 «розпарсь всі вкладки»):** розпарсено **всі 36 вкладок** сайдбару
> (внутрішній API→`pages[]`), 4 секції — раніше покривали лише 4 сторінки. **Нові родини метрик** (усі в пікері+довіднику):
> - **ТРАФІК:** `Свод` (traffic vs auto vs appeals, `appeal_pct/auto_pct`) · `Капасити` (попит vs ємність, `cnt/amt_used/limit`, беклог, маржа=`fee_sum`) ·
>   **`SMS/Push`** (`inc_cr/wd_cr` + окремо `sms_/push_`, `auto_payin_cr`~99%) · `Автоматика` (`auto_pct` по методу/трейдеру/банку + heat) ·
>   `Активні рахунки` (heatmap банк×година, `avg/max` рахунків) · `Апеляції-board` (`won/total`, `% від success-трафіку`, `avg_time_min`) ·
>   `Квази интент` (`split_cr`, `pct_quazi`, `split_losses`, `quasi-pulse`) · `UPI Intent` (`quasi_cr` vs `gw_cr`) · `UPI Трафік` (методи закриття `m_push/sms/receipt/support/appeal`) ·
>   `Button` (`cr`,`auto_pct`,`target_buttons`,`sender_banks`,`suffixes`) · `Обзор пейаутів` (`vol_paid/total`,`stuck`,`partially`,бакети).
> - **основні:** `Обзор методов` (усі методи в один екран + `fraud_rejection_pct` + `utrEntry` paste/typing/mix) · **`Потери транзита`** (7 категорій `c1..c7`, тригер №5 «списання») · `Heatmap` P90 виплат (сума×година).
> - **САППОРТ:** `Саппорт` (час обробки + розбір апеляцій won/lost) · **`Скоринг сендерів`** (бакети blacklist<20/risky/normal/trusted≥80, `avg_score/avg_cr`) ·
>   `Daily PayIN/PayOut/NetBalance` (delta, `net=payin−payout`, `avg_total` vs `avg_trader`) · `Bank Accounts / By Bank` (CR/ARPU по банку) · `PayIn process` (auto/manual/review, appeal з/без UTR) · `Provider Balance` (леджер `net=deposit+payin−payout−expense−fee−settle`).
> - **OPERATIONS:** `CR Bank / CR Bank-account` (канон `success/(success+reject)` напряму) · `Turnover / Settlement` (payin/payout/settle, INR/USDT) · `Providers / CR Auto Providers` (`effective`,`sms_pct`) · **`IMPS Payouts`** (`throwback_pct` відкати ≥50хв, `m3_pct` ≥3 спроби `assigningCount`).
>
> **✅ Звірено 1:1 з CH `hermes` (2026-08-10):** метод-зріз=`payments.payment_method`(upi_intent/upi/button); CR=`success/(success+reject)` — **button IST-день 08-08: CH 30811/56.6% = eye-of-god 30813/56.6%**; `total`=final; доба=**IST(UTC+5:30)**; SMS/Push=`messages.type∈{push,sms}`×`msg_type∈{income,withdraw}`; UTR=`messages.utr_extracted`; UI/NOUI=`source_payment_widget_id`. ⚠ `auto_pct` знаменник = `inc_ok`(payin з income-сигналом), не «всі payin» — пінити на етапі ТЗ. Скоуп IN → рації 1:1, абсолюти лише в тому ж скоупі. Деталі: `references/eye-of-god-metrics.md` §«Верифікація 1:1».

**d) supportzones — `[supportzones]` (§9.1 C):** поля provider-settings (Status/Balance/Limit/%/restrict/naliv/working-hours) є в **§5** `[supportzones→CH]`.

> **➕ 2026-08-12 — ДОСТУП ВІДКРИТО (було ❌ CF 403), тепер читаємо напряму, а не лише через CH-репліку.**
> Перевірено живими GraphQL-запитами (`accountProvidersCount=6278`). **Що можна звіряти:**
> - **Налаштування провайдера — першоджерело:** `status/statusUpdatedAt` · `isTrusted` · `lockReasons` · `providerBalance` ·
>   `restrictToggleForPayin(+Comment)` · `payoutAccess` · ліміти `min/maxPayin(Payout)AmountLimit` ·
>   `daily/monthlyIncomingTransactionAmount(+Limit)` · пороги переповнення (`customLimitToWarnAboutOverflow`,
>   `warnWhenBalanceToDepositThreshold`, `zeroPriorityWhenBalanceToDepositThreshold`) · `timezone` · `workWithTrafficType`.
> - **💰 Комісії (яких немає ні в наших звітах, ні в CH):** `feePercentPayin(+High)` · `feePercentPayinAcq(+High)` ·
>   `feePercentPayinIBAN(+High)` · **`feePercentPayinUpiIntent`** · `feePercentPayout(+AfterThreshold)` · `payoutFixedFee` ·
>   `effectivePercent(+ACQ)` · `nightFeeBonusPercentPayin` · settlement-% (`custom/overrideSettlementFeePercent`) ·
>   **пер-афіліатні** `aff2…aff10 × Payin/PayinACQ/Payout Fee`. → база для маржинальних метрик.
> - **⭐ CR по годинах:** «CR по годинах» (запит — у закритому довіднику) → `totalPayins · successPayins · failurePayins · conversion · senderConversion`
>   (+`ByMinute` з `pendingPayins`, поріг `payinsConversionByHourThreshold`). Фільтри: мерчант/кампанія/провайдер/банк/
>   `requestType[]`/`trafficType`/`paymentMethodTypes[]`/`excludePayinsWithoutBankAccount`. Час — **IST**.
> - **Ще:** банк-акаунти (`balance`, `transferLimitDay`, `numberTransferLimitDay`), виписки (`bankStatementRow` — під `statement`-субскор
>   Scoreboard), чорний список відправників, девайси, історія змін провайдера, `clientPayin/clientPayout`.
>
> **⚠️ Формула CR тут ІНША за знаменником:** `conversion = successPayins / totalPayins`, де `totalPayins` **включає pending**.
> На **закритих** годинах `pending=0` → збігається з нашим каноном (перевірено: 8383/13245 = 63.29 = `success/(success+reject)`).
> На **поточній неповній** годині CR **занижена** (59.28 замість 63.44). 👉 Звіряти **лише закриті періоди**.
>
> **Звірка з CH (закрита година 19:00–20:00 IST):** `failure` збігся **точно 4862 = 4862**, але **лише з `FINAL`**
> (без FINAL — 4932). `total/success` ще розходяться на ~0.5–0.7% → **дефолтні фільтри панелі не вирівняно**;
> статус звірки — «≈, потребує вирівнювання фільтра», **не 1:1**. Деталі: `references/supportzones-metrics.md`.

---

### 2.5-bis 📐 Медіани й перцентилі — КАНОН (звірено з CH 2026-08-13)

> **Проблема:** у звітах було **5 різних двигунів** медіани — на тих самих даних вони дають РІЗНІ числа.
> Тому канон зафіксовано явно.

**Канон = семантика ClickHouse `quantileExact(level)`:**
```
відсортований масив v довжини n  →  v[ min( floor(level * n), n-1 ) ]
```
Береться **реальний елемент**, без усереднення й інтерполяції. Python-еквівалент — функція `_q()`
(див. «внутрішній модуль»). Перевірено емпірично, 6/6 випадків:

| Вибірка | q=0.25 | q=0.50 | q=0.75 |
|---|---|---|---|
| `[10,20,30,40]` | 20 | **30** | 40 |
| `[10,20,30,40,50,60,70]` | 20 | **40** | 60 |

⚠️ **`statistics.median` на `[10,20,30,40]` дає 25, а CH — 30.** На парних вибірках (а сезонна база = 4 зразки)
розбіжність гарантована. Живий кейс: один із мерчантів за 4 четверги `[70.9, 84.4, 87.4, 90.0]` → канон **87.4**,
`statistics.median` дав би **85.9**.

> 🔀 **Який двигун де застосовувати (уточнено 2026-08-14 за кодом):**
> - **`quantileExact`** — коли рахуємо **базу порівняння** (медіани, коридори P25–P75) і звіряємось із CH
>   вручну. Саме його семантика описана формулою вище.
> - **`quantileExactInclusive`** — коли рахуємо **швидкості** (`PayIn/PayOut speed` у «внутрішній модуль»).
>   Це історично закладено в скоринг; міняти двигун = зсунути бали всім, тому лишаємо як є.
>
> ⚠️ **Два методи співіснують свідомо.** Порівнюючи швидкість із зовнішнім джерелом — уточни, який
> перцентиль воно рахує, інакше отримаєш «розбіжність», якої немає. Ніколи не змішувати їх в одній колонці.

**Правила бази порівняння:**
1. **Поточний період у базу НЕ входить** (інакше метрика порівнюється сама з собою).
2. Для метрик із **тижневим профілем** база береться **по дню тижня** (пн↔пн), а не N днів поспіль —
   інакше вихідні змішуються з буднями. Кейс `capacity`: база ще й по годині/півгодині.
3. Спершу **згорнути грейн** (кабінети→мерчант), потім рахувати похідну метрику, і лише потім медіану.
   Медіана денних коефіцієнтів ≠ коефіцієнт від медіанних сум.
4. Вікна з нульовим знаменником у базу не входять; при **<2 зразках** — показувати `—`, не рахувати Δ.
5. Аномалія = вихід за коридор **P25–P75** тієї самої бази (самонормується під кожну сутність).

**Реєстр — хто який двигун використовує (станом на 2026-08-13):**

| Звіт | Грейн бази | Вікно | Двигун |
|---|---|---|---|
| `merchant-out-in` | мерчант/кабінет × день і × день тижня | `D-7…D-1` + 4 тижні | ✅ канон `_q()` |
| `capacity` | день тижня × година × півгодина | `D-35…D-7` (свіжий тиждень виключено) | ✅ CH `quantileExact` |
| `churn` | когорта × день | 14 днів | ⚠️ `_pct()` — **інтерполяція**, не канон |
| `THC` | день тижня × година | 56 днів | ⚠️ **зважена** медіана (свіжі ×1.5) — свідомо інша |
| `weekly` · `scoreboard` · `daily-payin` | швидкість | період | ⚠️ `quantileExactInclusive` — інший метод |
| `main-dashboard` · `active-traders` | скор / кола | період | ⚠️ `statistics.median` |

👉 При звірці метрики з медіаною **спершу дивись, який двигун** у звіті — інакше «розбіжність» виявиться
різницею методів, а не дефектом.

---

### 2.6 🎛️ МЕНЮ ФОРМУЛ — показати замовнику, хай обере джерело

> **Правило пріоритету:** 🥇 **наші звіти halleg — стандарт за замовчуванням.** Решта джерел (lab/Фінанси, BAM,
> dsfrogboard, dsfrogboard `/affiliates`, supportzones) — **додаткові варіанти**. Якщо замовник не обрав явно — береться **halleg**.
> Обраний варіант **фіксується в ТЗ** рядком: `метрика · джерело · формула`.
>
> 📋 **ПОВНИЙ ПІКЕР — усі метрики всіх джерел, згруповані за темами** (кількість пікер друкує у своїй шапці — вона авто-оновлювана):
> **`https://formulas.freyal.renderhorizon.org/metrics-picker.md`** (авто-оновлення).
> Теми: ⏱️ Швидкість · 👥 Сендери · 🛡️ Ризик/статуси · 💧 Ліквідність/прогноз · 💰 Гроші/комісії/FX · 🎯 Конверсія · 📊 Обсяг.
> Усередині кожної теми **першим іде 🥇 halleg**, далі lab (105) · BAM (43) · dsfrogboard · dsfrogboard `/affiliates` · supportzones.
> **Показуй цей файл замовнику** — хай обере метрики й джерела; таблиця A нижче пояснює різниці двійників.

#### A. Метрики-двійники (є в кількох джерелах з РІЗНИМИ формулами) — тут замовник обирає

| Метрика | 🥇 **halleg (default)** | lab / Фінанси | BAM | Чим відрізняється (на що зважати) |
|---|---|---|---|---|
| **CR** | `success/final` — метод Фінансів; success=`completed`+`successed_by_partner`, знаменник без `pending`. ✅ **eye-of-god рахує так само** (`success/(success+reject)`, перевірено 12/12) — це незалежне підтвердження канону | (окремої CR немає — лише success-counts `tx_*`) | `cr_p2p` / `cr_intent` / `cr_button` = `success/non_pending` **окремо по методу** (upi / upi_intent / button) | Метод той самий. BAM дає **розбивку по платіжному методу**; halleg — загальний. Хочеш по методах → BAM-варіант |
| **CR (дедуп)** | — | — | `cr_dedup` = `Σ(намірів з ≥1 успіхом)/intents` (намір = день+партнер+сендер+сума) | **Не штрафує за ретраї.** Унікальна для BAM; беруть, коли важлива «чесна» конверсія |
| **PayIn Volume** | `SUM(amount)` payin success, **усі провайдери** (без порога) | `payin_total` + сегменти `p2p/intent/transit` (+USDT-версії) | `amount_in` = те саме, але **sanity-cap `amount<100M`** | lab дає **сегментацію**; BAM відрізає аномальні суми (cap). halleg — «як є» |
| **PayOut Volume** | `SUM(ABS(amount))` payout success | `payout_total` + сегменти, `payout_upi/imps` | `amount_out` | lab/BAM мають розбивку по методу виплати (upi/imps) |
| **ARPU / середній чек** | `TotalPayin/Success` (merchant-slu) | — | `arpu` = `amount_in/success_count` (payin); у `/cr-by-merchant` — `success_amount/success_count` (**payout**) | Формула та сама; уточни **payin чи payout** ARPU |
| **Швидкість payin** | `PayIn speed` — **P90** з винятками (апеляції/скасовані/перервані), без окремого відкиду |
| **Час обробки payout** | `PayOut speed` (p95) / `AVG Total Time` (mean) | — | `avg_total_time` (createdAt→completed) і `avg_trader_time` (assignedAt→completed) | BAM розділяє «загальний час» і «час у руках трейдера» |
| **% First / Second sender** | наближення: `counterparty_account`, повторний ≈ `MAX(cc)≥3` | — | **канон:** `eligible_send` = сендери з `MAX(completedCount)≥3` за всю історію; `first` = <3 успіхів у житті | BAM — **точне** визначення (наш halleg-варіант це його наближення) |
| **Settlement** | (1) внутр. трейдери `SUM(expenses.cash_out)`; (2) зовн. шлюзи `−SUM(amount−fee)` | `settle_fee_usdt` (+ по провайдерах intent/p2p), USDT | частка виводу `Σwithdraw_usdt/Σpayin_usdt` (для бонусів) | Три **різні сутності** з одною назвою — обов'язково уточнити, яка потрібна |

#### B. Метрики, що є ТІЛЬКИ в додаткових джерелах (можна взяти, якщо треба)

| Джерело | Що дає унікального |
|---|---|
| **lab / Фінанси** `[knowledge-lab]` | 105 фін-метрик у **USDT**: комісії мерчант/провайдер (`merch_fee_*`, `prov_fee_*`), affiliate-дохід, `fx_revenue`, `chargeback`, `transit_errors/presents`, `withdrawal_rev`, `bonus/sanction`, marginality-derived |
| **BAM** `[dsfrogboard-bam]` | `merchant_index` (композит здоровʼя), сегменти сендерів (High/Mid/Low CR), дедуп-метрики (`intents/attempts/%дублів`), downtime-алерти й timeline, черга виплат, кнопки віджета, feedback-причини, antifraud-нествор. пейини, логіка бонусів/порогів |
| **dsfrogboard (lab)** | ліквідність, сеттли, курси, USDT-баланси, диспути (`complaints`), прогноз payin/payout (+MAPE) |
| **dsfrogboard `/affiliates`** | комісії аффів: частка з трейдера/мерчанта, «загальний круг» (стеля 5%), розходження документ⇄система, реєстр/дублікати аффів, обсяг INR по мерчанту |
| **Uspacy CRM** | картки афіліатів/трейдерів: сервіс-менеджер (заміна Google-таблиці), tier, % комісії, статус-воронка, онбординг, ланцюжок COP |
| **supportzones → CH** | статуси провайдера (Restricted/Suspicious/Locked), Balance/Limit/%, restrict-toggle, наливи, робочі години (§5) |

#### C. Як показувати замовнику (скрипт для брифа)
> «Метрика **X** є у *N* джерелах: **наш стандарт (halleg)** — `формула`; додатково **BAM** — `формула` (відрізняється тим-то);
> **lab/Фінанси** — `формула`. За замовчуванням беремо halleg. Береш стандарт чи інший варіант?»
> Відповідь фіксуємо в ТЗ: `метрика · обране джерело · фінальна формула · чому`.

---

## 3. Скоринг трейдерів (Scoreboard) і тири

**Total score** = зважене середнє 7 субскорів (кожен 0–100):
`Volume 30% · Success 20% · Activity 10% · Tenure 10% · VeloIn 10% · VeloOut 10% · Stability 10%`.
(Альтернативний «чистий» вигляд у report-patterns: сума субскорів − Cost, макс 700 — уточни, яку шкалу хоче замовник.)

| Субскор | Логіка (пороги фіксовані, не квантилі) |
|---|---|
| **Volume score** | ≥20M:100 · 10–20M:80 · 5–10M:60 · 2.5–5M:40 · 1–2.5M:20 · <1M: `(vol/100k)*2` |
| **Success score** | ≥50%:100 · 45–49.9:85 · 40–44.9:70 · 35–39.9:50 · 30–34.9:30 · <30%:0 |
| **Activity score** | active_days<5 → 0; інакше `(active_days/30)*100` |
| **Tenure score** | ≥12міс:100 · 6–11:70 · 3–5:50 · 1–2:30 · <1:10 |
| **VeloIn score** | ≤3хв:100 · 3.1–5:80 · 5.1–10:60 · 10.1–15:40 · >15:20 |
| **VeloOut score** | ≤15хв:100 · 15.1–20:80 · 20.1–25:60 · 25.1–30:40 · >30:20 |
| **Stability score** | від коеф. варіації (std/avg volume): менша варіація → вищий бал (max 100); якщо CV>0.8 або avg=0 → 20 |

**Тир (Tier)** з Total score: **Platinum ≥80 · Gold 65–79 · Silver 45–64 · Bronze 30–44 · Inactive <30**.
Є `Tier prev` (базлайн) і матриця міграцій `Previous Tier → Current Tier`.
**VAS-код** (Volume·Activity·Stability, напр. `1-1-1` найкращі, `3-3-3` кандидати на churn/реактивацію).

> Порог/ваги — узгоджені з PBI-еталоном. Якщо замовник хоче інші — це **зміна формули**, фіксувати окремо
> і звіряти з `knowledge/financial-formulas/` та PBI Main_dashboard.

---

## 4. Розрізи / слайсери / фільтри (стандартний набір)

| Слайсер | Значення |
|---|---|
| **Affiliate** (multi) | визначається за префіксом імені трейдера (`G[P2P] → Griffin`); підтримує Master & Sub-affiliate |
| **Manager** (multi) | provider_group (apg.name) |
| **Status** | active / archive / audit / restricted / suspicious / locked / scammer / lost / 60DaysLost / … |
| **Group** | P2P (default) · SLU · Intent · Transit · Other · ALL |
| **Trader / Name** | конкретний провайдер |
| **Tier** | Platinum / Gold / Silver / Bronze / Inactive |
| **Period / From–To** | Daily / Hourly / Weekly (ISO) / Monthly; або довільний диапазон |
| Active days / Working month / PayIn·PayOut speed | діапазонні фільтри (переважно в scoreboard/analysis) |

Кнопка **Clear all slicers** скидає всі фільтри.

---

## 5. Provider-settings (‹зовнішня панель›) → як лягає у звіти

Панель `‹зовнішня панель›/settings-provider` (Keystone/Hermes «Account Providers») — джерело
статусів і балансів провайдера. Ці поля вже споживає `trader-detailed` (і частково `operational`).
Дані доступні в ClickHouse `hermes` через таблицю `providers` (CDC-реплікa) — окремий логін у панель
для побудови звіту **не потрібен**, панель — це людський UI над тими самими полями.

| Поле в supportzones «Account Providers» | Поле в даних (CH `providers`/`bank_accounts`) | Використання у звіті |
|---|---|---|
| **Status** (бейджі Restricted / Suspicious / Locked) | `ap.status` | статус-бейдж, слайсер Status; лише 3 значення дають бейдж, `active/audit/…` — без бейджа |
| **Balance** | operational `earnings_balance` (canonical, не `provider_balance`) | колонка Balance, big-balance ≥100k |
| **Limit** | `custom_limit_to_warn_about_overflow` (fallback = deposit INR) | колонка Limit; overflow-warn |
| **%** (Balance/Limit) | `balance / limit` | напр. 229k/600k = 38.2% |
| **Restrict toggle (payin)** | `restrictToggleForPayin` / `restrict_toggle_for_payin` | antiscam-ознака (не є статусом!) |
| **Balance-threshold ban** | `is_restricted_by_balance_threshold` (`in_ban`) | risk: balance2dep поріг |
| **Working hours** | `working_hours_from` / `..._to` + `on_shift` | зміна/шифт, аномалії on-shift |
| **Deposit** | `bank_account_state.balance` рахунку-депозиту × exchange rate | deposit_inr |
| **Наливи (naliv)** | `bank_accounts.for_client_payin=1 AND is_paused=0 AND work_state='work'` | к-сть активних UPI |
| **Group / Manager** | `provider_group.name` | розріз Manager |

> ⚠ Для звіту цифри беремо **з ClickHouse `hermes`** (реплікa supportzones), а не скрейпимо панель.
> supportzones-панель у брифі — це спосіб для замовника **показати пальцем**, яке саме поле/статус він має на увазі.

---

## 6. Доменні інваріанти (зашиті домовленості — не перепитувати щоразу)

- Ринок **Індія**. Валюта **INR (₹)**. Час **Asia/Kolkata (IST, UTC+5:30)**.
- `day = toDate(toTimezone(ts,'Asia/Kolkata'))`. Тиждень — **ISO Пн–Нд**. Дані з **2026-01-01**.
- **Шумовий поріг `vol_filter>5k` — ПРИБРАНО (рішення власника 2026-07-30), НЕ застосовується.** Обсяг і «активні трейдери» рахуються по **ВСІХ** провайдерах.
  ⚠ **Наслідок (точно, за кодом halleg):** розбіжність очікувана лише проти конкретних місць, що ще фільтрують:
  **main-dashboard** (New Traders `v>5000`; швидкість `cnt>3 AND sum>5000`), **affiliate** і **provider-analytics** (швидкість),
  та legacy `make_sql`-CTE у trader-kpi (vol/Churn7D/Formal). **trader-kpi (кеш) — вже БЕЗ порога** (`>0` з 2026-06-12).
  **Взагалі не фільтрують:** operational, weekly, scoreboard, merchant-slu, daily-payin, detailed, churn, churn-v2.
  Тож проти цих — розбіжності не буде; проти перелічених вище — буде, і це не дефект. Хочеш поріг у конкретному звіті — задай явно.
- Службовий провайдер **`Operation provider`** завжди виключений.
- **Трейдер = provider (AccountProvider)**; PayOut зберігається з мінусом → в UI `ABS`.
- Статуси платежів: успіх = `completed` / `successed_by_partner`; відмова = `rejected_by_partner` / `Rejected`.
- Термінальні статуси провайдера (churn): `scammer, lost, 60DaysLost, locked, archive, restricted`.
- **BLANK/пусто** у клітинці = «недостатньо даних» (новачок без історії або дільник 0), а не 0.
- Мова UI — **українська**, код — англійська.

## 7. Джерела даних і оновлення

- **ClickHouse `hermes`** (live-реплікa прод-PG Hermes через PeerDB CDC, лаг сек-хв).
  Таблиці: `transactions`, `payments` (payin/payout), `expenses`, `bank_accounts`, `providers`,
  `provider_groups`, `payment_status_history`, `exchange_rates`, `bank_statement_reports`, `appeals`.
  - **`appeals`** (апеляції): `status` — 9 значень; **невирішені** = `status NOT LIKE 'resolved%'` (= `new`+`verifying`),
    решта — `resolved_*`. Тип за `payments.type`; **привʼязка до трейдера різна:** payin через
    `payments.bank_account_pid→bank_accounts.provider_pid`, payout через `payments.provider_pid`
    (payin НЕ має provider_pid!). Невидалені: `_peerdb_is_deleted=0 AND deleted_at < '1971-01-01'`.
  - ⚙️ **CH-пастка:** `FINAL` на version-таблицях лишає дублі → JOIN множить лічильники; дедуплікуй `GROUP BY pid`.
  - ⚠ **§7 — це підмножина ~60 таблиць hermes.** Повний перелік завжди: `SELECT name FROM system.tables WHERE database='hermes'`.
    Якщо метрики немає в §2 і джерела не видно — **перелічи таблиці/колонки наживо**, не парань «джерело невідоме» на збірку.
  - **Історія/події (для метрик «дата переходу», churn-by-status, зміни лімітів/конфігу):**
    `account_provider_events` — **аудит змін полів провайдера** (`field_changed`, `value_old→value_new`, `changed_at`); зокрема
    `field_changed='status'` = **повна історія статусів** (terminal-churn датується точно, best-effort НЕ потрібен);
    `bank_account_events` / `bank_account_state_history` / `bank_account_device_state_history` — історія станів рахунків;
    `payment_status_history` — переходи статусів платежів; `providers.status_updated_at`/`changer_id` — лише ОСТАННЯ зміна статусу.
  - **Інші корисні:** `settlements` (проводки сеттлів), `account_daily_activity`, `provider_work_shifts`, `expense_categories`,
    `partners`, `partner_fees`, `balance_movements`, `accounting_entries`, `operation_payins`/`operation_payouts`, `purchase_rates`,
    `antifraud_*`, `scam_records`, `statement_*`. (Не всі документовані — колонки дивись `system.columns`.)

<!-- згенеровано import_hermes_tables.py @ 2026-09-25 09:53 IST · джерело: system.tables -->
**Повний інвентар hermes (63 таблиць)** — авто-оновлення. Колонки конкретної таблиці: `SELECT name,type FROM system.columns WHERE database='hermes' AND table='<t>'`.

- **Платежі / транзакції:** `client_payin_statement_approvals`, `operation_payins`, `operation_payouts`, `payment_matches`, `payment_status_history`, `payment_widgets`, `payments`, `transactions`
- **Провайдери (трейдери):** `account_daily_activity`, `account_provider_events`, `agg_provider_balances`, `provider_employees`, `provider_groups`, `provider_work_shifts`, `providers`
- **Банк-рахунки:** `agg_bank_accounts`, `antifraud_banks`, `bank_account_device_state_history`, `bank_account_events`, `bank_account_groups`, `bank_account_partners`, `bank_account_state`, `bank_account_state_history`, `bank_account_upi_apps`, `bank_accounts`, `bank_format_configs`, `banks`
- **Виписки / statements:** `bank_statement_reports`, `bank_statements`, `reconciliation_runs`, `statement_account_assignments`, `statement_entries`, `statement_entry_links`, `statement_metrics`, `statement_processing`
- **Фінанси / комісії / курси:** `accounting_entries`, `affiliate_withdrawals`, `balance_movements`, `exchange_rates`, `expense_categories`, `expenses`, `partner_fees`, `purchase_rates`, `settlements`
- **Апеляції / диспути:** `appeal_files`, `appeals`
- **Антифрод / ризик:** `antifraud_action_logs`, `antifraud_comments`, `antifraud_reference_receipts`, `antifraud_reviews`, `devices`, `receipts`, `scam_records`, `sender_blacklist`
- **Партнери / афіліати / кампанії:** `affiliates`, `campaigns`, `partners`, `ref_registry`
- **Довідники / інше:** `accountants`, `comments`, `countries`, `messages`, `users`
*(повний перелік таблиць hermes підставляється генератором зі снапшоту `hermes-tables.snapshot.md` — авто-інвентар: «внутрішній модуль» щодня, генератор кожні 5 хв. Колонки конкретної таблиці — `system.columns` наживо.)*
- **Оновлення/кеш**: легкий live — in-memory ~5 хв (кнопка Update форсить); важкий — фоновий worker
  у SQLite/JSON («внутрішній модуль»→`kpi_cache.db`; «внутрішній модуль»→`mat_cache.db`;
  «внутрішній модуль»→`scoreboard_cache.json`), timer 5 хв / 30 хв / 4 год за важкістю.
- **Грейн розрахунку** (щоб збігалось з еталоном): CR/Auto — на рівні `(trader×slot×hour)` потім `SUM`;
  Volume: Hourly = `SUM(ABS)` на (trader×slot), Daily/Matrix = `ABS(SUM)` на день.

## 8. Стек і стиль (успадковувати завжди)

- Backend **FastAPI** (Python) · Frontend **Vanilla JS + Chart.js 4.x** (без React/Vue) · Nginx + Let's Encrypt.
- UI-шаблон: шапка+логотип → вкладки-періоди (Daily/Hourly/Weekly/Monthly) + вкладка **Legend** →
  слайсери → **KPI-картки** → таблиця/матриця → **Δ-семафор** → картка LastRefresh + кнопка Update.
- Палітра: фон `#FFFFFF`, текст `#252423`, акцент `#118DFF`; семафор good `#1AAB40` / neutral `#D9B300` / bad `#D64554`.
- **Доступ/RLS**: JWT + bcrypt; `admin` (все + аудит), `user` (бачить лише своїх менеджерів/афіліатів).
  Кожен запит логується (user, endpoint, фільтри, timestamp).
- **Деплой:** виконує **власник звітів** (замовник ТЗ сервери не чіпає).