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

# 🔧 Інтерактивний бриф — ДООПРАЦЮВАННЯ звіту

> 🚨 **АКТИВАЦІЯ — асистент читає ПЕРШИМ, до будь-якої дії.**
> Якщо цей файл потрапив у контекст чату (користувач вклав/вставив його) — це **НЕ довідка, не приклад і не контекст**,
> а **ОБОВʼЯЗКОВИЙ протокол** саме на цю задачу. **Заборонено** одразу робити задачу ad-hoc (писати код/ТЗ/правки),
> «наче брифа й не було». Натомість, **першим повідомленням**:
> 1. Підтверди: «**Веду за брифом edit-report**» і назви, який звіт правимо (§1). Якщо звіт не ідентифікований — спитай (§1).
> 2. Далі йди **строго по секціях §1→§10**: інтерв'ю (пари «що зараз → що треба» + тип),
>    self-check (§7), закриття **всіх** питань (§8) — і лише тоді ТЗ (§9). Після ТЗ — **обов'язково §10: спитай, закриваємо чи є ще правки**.
> 3. Питання став через **AskUserQuestion**, не в вільному тексті. Дрібні техдеталі вирішуй сам.
> 4. **🚦 Ворота перед §9: у ТЗ має бути НУЛЬ відкритих питань.** Якщо після §8 щось лишилось
>    невизначеним — це не «TBD у документі», а ще одне питання замовнику. ТЗ із «уточнимо потім»
>    повертається на доопрацювання: власник не може за ним зібрати звіт.
> 4. Якщо задача **не про звіт** — скажи це явно й запропонуй, як діяти; не ігноруй бриф мовчки.
> **🔴 Ознака порушення:** ти видав результат, а інтерв'ю/крос-звірки/self-check не було. Це помилка — почни з кроку 1.
> **🔴 Друга ознака порушення:** ти пішов **реалізовувати / деплоїти**, а питання «закриваємо чи є ще
> правки?» не ставив. Перехід до реалізації сесію брифа **не закриває** — повернись і постав його.
> Так само перевір, чи запускав `brief-gap-check` після кожної секції: цей крок губиться першим,
> бо його не видно в результаті.

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

---

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

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


1. Спершу **ідентифікуй звіт** (§1) — без цього не рухайся.
2. По кожному пункту змін питай пару **«що зараз → що треба»** і **тип** зміни.
3. Зміна формули/метрики → спершу підтягни **live-URL** формул
   `https://formulas.freyal.renderhorizon.org/metrics-catalog.md` (свіжо для всіх, кожні 5 хв); на сервері ще й
   перезвір із canonical (**внутрішній довідник фінформул** (у власника), `LEGEND*`, код звіту) — **жива версія
   перемагає знімок**. Далі крос-чек з §2/§3; фінансову → звірити з knowledge і оновити `## Історія` у файлі формули.
4. Правка статусів/балансів/лімітів → **каталог §5** (поля supportzones `settings-provider`).
5. **Регресія — головне у правках.** Завжди фіксуй §4: що НЕ можна зламати.
6. Після збору — self-check (§7), фінальні питання (§8), потім чисте ТЗ (§9).
7. Зашиті інваріанти (IST/INR/статуси) не перепитуй — питай лише відхилення.

> 🕒 **ПОЯС ДАНИХ І ПОЯС «СЬОГОДНІ» — ЦЕ РІЗНІ РЕЧІ. Звір їх явно.**
> Наші звіти ріжуть добу в **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-даних. Виправили разом із самим показом часу.*

7-bis. 🥇 **«Як перевірити» головніше за текст формули.** Якщо критерій приймання суперечить написаній
   формулі — **виграє критерій**, а розбіжність виноситься питанням замовнику. Причина: критерій привʼязаний
   до спостережуваного артефакту («= Deposit у trader-detailed 1:1»), тому не може протухнути; переписана
   вручну формула — може, і тихо.
   *Кейс Affiliate, двічі поспіль: формула казала Deposit = `SUM(cum_dep)` з виключенням archive/Payable, а
   критерій — «= trader-detailed»; це різні метрики, і правда була за критерієм. Так само Менеджер: формула
   `apg.name`, критерій «збіг зі слайсером», а слайсер бере з Google-доку.*
   → Практичний висновок: **не приймай пункт без «Як перевірити»** — воно знімає більше двозначностей, ніж
   сама формула.
8. **Крос-звірка — з каталогом формул (доступів не треба).**
   ✅ **Немає інтернету / не відкривається URL — це НОРМАЛЬНО, не блокер:** повний каталог **вшитий у цей файл нижче**.
   Працюй із ним, а в ТЗ познач: `Формули звірені з каталогом, вшитим у бриф`. Кожну змінену метрику/формулу звір із
   `https://formulas.freyal.renderhorizon.org/metrics-catalog.md` і `…/metrics-picker.md`. Немає або відрізняється —
   пиши в ТЗ «потребує підтвердження власником» + питання, **не вигадуй**. Звірку з живими даними робить власник.


---

## 1. Який звіт правимо

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

> ⏱️ **СКАЖИ ЗАМОВНИКУ ЦЕ ПЕРШИМ — до першого питання.**
>
> «Я поставлю кілька запитань і на виході дам готове ТЗ на правку. Орієнтовно:
> **дрібна правка (перейменувати колонку, змінити порядок) — 15–20 хвилин**,
> одна змістовна зміна — **40–70 хвилин**, три-чотири пункти — **2–3 години**.
> **Перерватись можна будь-коли.** Якщо правка дрібна — скажіть одразу, підемо коротким шляхом.»
>
> **Навіщо.** Замовник із «просто перейменуйте колонку» бачить анкету на сотню полів і закриває файл.
> Чесна оцінка на початку + класи правок нижче знімають це.

> 🖼️ **ЗВІТ ВИЗНАЧЕНО → ОДРАЗУ ПОКАЖИ ЙОГО МАКЕТ.** Не обговорюй правку словами.
>
> `https://formulas.freyal.renderhorizon.org/mockups/<id-звіту>.html`
> Список усіх: `https://formulas.freyal.renderhorizon.org/mockups/`
>
> Це **оригінальний вигляд** звіту — ті самі вкладки, колонки, порядок блоків.
> **Числа в ньому вигадані** (плашка «МАКЕТ» угорі), даних не завантажує, логіну не треба.
>
> **Як вести розмову далі:** відкрий макет і проси замовника показувати **на ньому**:
> — «на якій саме вкладці це має бути?»
> — «яку колонку міняємо / куди додаємо нову?»
> — «цей блок лишається як є?»
> Потім у ТЗ пиши прив'язку до видимого: *«вкладка Hourly → таблиця → колонка після CR»*,
> а не «десь у погодинному розрізі».
>
>
>
> ✏️ **МАЛЮЙ ПРАВКУ ПРЯМО В МАКЕТІ — не описуй словами.**
> Макет розуміє параметри в 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»**, а не «десь у таблиці»;
> — видно **тип** (%, хвилини, ₹) — тобто одразу зрозуміло, чи можна її додавати в цю таблицю.
>
> ⚠️ Карта збирається з коду **щодня**, тож відповідає тому, що зараз на сервері.
> Якщо колонки в карті немає — її немає і в звіті, це **нова розробка**, а не «просто показати».
> *Навіщо: без картинки замовник уявляє одне, ти інше, і розходження виявляється
> вже на готовому звіті. Макет коштує один клік і знімає цілий раунд правок.*

- **Назва / URL звіту:** `____` *(достатньо посилання або назви зі списку звітів)*
- ❓ **Хто просить зміну і навіщо (яке рішення це покращить):** `____`
- ❓ **Дали скрін/лінк на інший інструмент?** Опиши, що на ньому важливо — прикріпи скрін.
  🚫 **Не питай і не шукай:** назви файлів, модулів, гілок, серверів, доступів до баз — це внутрішня кухня
  власника. Якщо не знаєш id звіту чи де він лежить — **так і пиши «невідомо»**, власник визначить сам.

> 🧭 **Звір НАЗВИ РОЗДІЛІВ, якими оперує замовник, зі списком вкладок звіту — ДО планування.**
> Замовник називає id нашого звіту, але описує «Payouts», «Structure», «картку трейдера»? Перевір, чи такі
> вкладки взагалі є (каталог §1 + сам звіт). Якщо назви немає — це **або інший продукт, або нова розробка**,
> і це різні гроші. *(Кейс 2026-08-13: ТЗ мало шапку `trader-affiliate-native`, а п.3–5 описували кабінет
> партнера — Payouts/Structure/картка трейдера є тільки там; у нашому звіті таких розділів ніколи не було.)*
>
> 🧅 **Спитай себе: у скількох ШАРАХ живе те, що просять змінити.** Одне імʼя/поле часто існує в 3–4 місцях
> одночасно: джерело (CH/довідник) · мапінг у коді · права (`permissions.allowed_*`) · підпис у UI.
> Правка одного шару без решти = або «нічого не змінилось», або тихо зламані доступи.
> *(Кейс: «замінити Display Name менеджерів» — імена жили в `provider_groups`, `allowed_managers`,
> `users.manager` і `display_name`; у трьох шарах перейменування вже було, у четвертому ні.)*

> 🔀 **Обʼєкт — НЕ наш звіт зі списку §1 каталогу?** (кабінет/борда іншої команди, зовнішній продукт,
> «звірте нам оце перед продом»). Тоді це **не правка, а аудит чужого продукту** — гілка інша, скажи це
> замовнику явно й перемкнись:
> - 🔴 **Спершу перевір, чи КАТАЛОГ відповідає нашому КОДУ — і лише потім суди чужий продукт.**
>   Каталог пишеться руками й дрейфує; код — ні. Кейс mithras (14.08): команда кабінету поставила 3 питання
>   про швидкість — і по всіх трьох мала рацію. «внутрішній модуль» роками рахував **P90 з винятками**, а каталог
>   вимагав **p95** із «відкидом викидів», якого в коді немає, і порогом `>3 трз`, якого теж немає.
>   Ми ледь не змусили їх «виправити» коректну реалізацію під помилковий документ.
>   👉 Порядок: **код звіту → каталог → чужий продукт**. Розбіжність код↔каталог = дефект каталогу, не продукту.
> - **Вимоги** беремо не з «ТЗ на звіт» (його може не бути), а з **каталогу метрик** — він і є специфікацією:
>   §2 формули · §3 скоринг · §4 слайсери · §6 доменні інваріанти. Кожне відхилення = пункт аудиту.
> - **Джерела звірки:** каталог (вимоги) · CH `hermes` (ground truth) · наша репліка того ж домену, якщо є
>   (напр. `affiliate-pc` для афіліат-кабінету) · внутрішня узгодженість вкладок між собою.
> - **§6 «Деплой»** не застосовується: правку робить **чужа команда**. Вихід — той самий файл ТЗ,
>   але адресат — їхні розробники; додай розділ «Питання до власника методики» для того, що потребує
>   рішення бізнесу, а не коду (пороги, вибір джерела формули).
> - **Точність 1:1 (каталог §9.2) досяжна лише на ЗАКРИТИХ періодах** — дві живі системи завжди дрейфують
>   на хвилинному лагу; на поточному дні розбіжність 0.0x% це не дефект. Приймання — на закритому періоді.
> - ⚠️ Не давай «GO на прод» лише на підставі збігу цифр: збіг сум ≠ повнота функціоналу й ≠ однакові
>   дефініції під однаковими лейблами (кейс кабінету mithras: усі гроші зійшлися до копійки, а «тиждень»,
>   «попередній період» і «avg settle» означали не те, що в каталозі).


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

## 2. Що міняємо (по пунктах)

> ⚡ **СПЕРШУ ВИЗНАЧ КЛАС ПРАВКИ — від нього залежить, скільки кроків проходити.**
>
> | Клас | Що це | Що заповнюємо |
> |---|---|---|
> | 🟢 **Косметика** | перейменувати колонку чи підпис · змінити порядок колонок · сховати колонку · змінити колір порога · поправити текст | **§1** + рядок «було → стало» + **§4 «які цифри НЕ змінюються»** + одне питання: *«ця назва вживається ще десь — у Legend, експорті, іншому звіті?»*.<br>**Пропускаємо:** §3-bis (макет), §7 (self-check), більшість DoD |
> | 🟡 **Відображення** | додати колонку з наявних даних · новий слайсер · змінити сортування/групування | §1 · §2 · §3 (коротко) · §3-bis (макет «до→після») · §4 регресія |
> | 🔴 **Логіка** | змінити формулу · додати метрику · змінити вікно/період · зачепити скоринг | **усі секції**, без винятків |
>
> **Навіщо це.** Раніше перейменування колонки коштувало 40–70 хвилин і проходило ті самі ворота,
> що й нова метрика: макет із воротами, 19-рядковий self-check, DoD на 18 пунктів.
> Замовник із дрібною правкою бачив тригодинну анкету й закривав файл.
>
> ⚠️ **Косметика ≠ безпечно.** Перейменування буває багатошаровим: та сама назва може жити
> в заголовку, у Legend, у назві колонки експорту й у фільтрі. Тому питання «де ще вживається»
> лишається обов'язковим навіть у 🟢-класі — саме воно й ловить пропущений шар.

> 🔻 **Зміна БАЗОВОЇ метрики — у ДВА КРОКИ, ніколи одномоментно.**
> Спершу нова версія **окремою колонкою поруч** зі старою (нічого не ламається, видно дельту), і лише
> після звірки на живих даних — заміна основної + перекалібрування порогів.
> Кейс: перехід P90→p95 дав би +58% (7.9 проти 5.0 хв) і зсунув би score **усім** трейдерам, а порівняння
> з історією стало б неможливим. Особливо якщо метрика **дає бали** — питай не лише «показувати?», а й
> «нараховувати?».

> 🔁 **Питання-паритет (став ОДРАЗУ, до деталей):** «які ще блоки/таблиці цього звіту показують ту саму
> метрику — і чи міняємо їх теж?» Правка однієї таблиці майже завжди тягне решту: додали медіани в
> «Мерчанти» — замовник одразу попросив те саме в «Exchange Merchants». Дешевше спланувати відразу,
> ніж робити другим заходом.
> ⚠️ **Але знаменник між блоками може відрізнятися** — перевір по кожному окремо. Кейс: у звичайних
> мерчантів вхід = `payin`, у Exchange = `topup` (payin у них не буває). Бази порівняння таких блоків
> **не змішувати** — рахувати окремо, інакше медіана однієї сутності потрапить у базу іншої.


| # | Що зараз | Що треба | Тип зміни |
|---|---|---|---|
| 1 | `____` | `____` | ☐ баг ☐ нова метрика ☐ зміна формули ☐ UI/layout ☐ фільтр/права ☐ експорт ☐ продуктивність ☐ **контент/довідник (зовн. джерело)** |
| 2 | `____` | `____` | ☐ … |


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

## 3. Деталі по кожному пункту

### Пункт 1
- **Опис:** `____`
- 🧭 **ПОТОЧНИЙ СТАН У КОДІ — заповнити ДО підписання, подивившись у код:**
  ☐ `НОВЕ` (у коді нічого немає)
  ☐ `Є НА БЕКЕНДІ, НЕ ПОКАЗАНО` (рахується, треба лише вивести — це не «нова метрика», це UI)
  ☐ `Є, АЛЕ РАХУЄ ІНАКШЕ` ← **найнебезпечніша категорія**
  ☐ `Є І ПРАВИЛЬНЕ` (нічого не робимо, лише записати в регресію §4)
  *Чому окремим полем: «Є, АЛЕ РАХУЄ ІНАКШЕ» візуально виглядає як готове, тому ніхто не перевіряє, і
  розбіжність живе до першої звірки з іншим звітом. Кейс trader-affiliate: ТЗ вимагав KPI «активні
  трейдери = payin>0 за вікно фільтра», картка з такою назвою вже була — але показувала фіксовані 7 днів.
  Правильне число вже лежало в payload, на екран виводилось інше; знайшлось аж на фінальному аудиті.
  Дзеркальна помилка теж коштує: три пункти того ж ТЗ були позначені «нове», а насправді вже працювали
  (Deposit рахувався, XLSX був, KPI-картка була) — завищена оцінка на рівному місці.*
- **Якщо баг** — як відтворити + приклад невірного значення: `____` → очікуване: `____`
- **Якщо зміна формули/метрики** — стара: `____` → нова: `____`; **чому:** `____`
  - крос-чек з каталогом §2/§3: це наша `____` чи нова ⚠
  - 📌 **НЕ копіюй текст формули в ТЗ — постав посилання:** `каталог §__ (v__, дата)` або
    `НЕМАЄ В КАТАЛОЗІ → статус НОВА · затвердив: ____ · дата: ____`.
    *Переписана формула не старіє видимо, посилання з версією — старіє. Кейс Affiliate: ТЗ спиралось на
    знімок каталогу v6, який на момент реалізації відстав на **29 версій** (жива — v35). Наслідок — 6
    дрейфів, зокрема Deposit, де в один рядок склеїли ДВІ різні метрики (flow `cum_dep` і state
    `account_balance`), і «стару» avg процитували формулою, якої в коді ніколи не було.*
  - ⚠️ **Якщо метрики немає в каталозі — це НЕ звичайний пункт.** Позначити «НОВА» і затвердити визначення
    у власника ДО реалізації. *Кейс: воронка активації (time-to-first-payin, % доживших до 14/30 дня) не
    існувала ні в каталозі, ні в knowledge, ні в коді — була винайдена в момент написання ТЗ, але подана
    як звичайний пункт нарівні з рештою.*
  - 🎛️ **джерело формули** (каталог §2.6): ☐ 🥇 halleg (default) ☐ lab/Фінанси ☐ BAM ☐ dsfrogboard ☐ dsfrogboard-aff (комісії аффів/«круг») ☐ Uspacy CRM (картки афіліатів/трейдерів) ☐ supportzones ☐ eye-of-god —
    якщо метрика є в кількох джерелах, покажи варіанти замовнику і зафіксуй обране (halleg — за замовчуванням)
  - фінансова? → звірити/оновити **внутрішній довідник фінформул** (у власника)<name>.md` (секція `## Історія`)
- **Якщо UI/фільтр/права** — що саме на екрані змінюється: `____`
- **Якщо нова метрика/лічильник з нового джерела** — перевір **окремо для кожного під-типу** (payin/payout, in/out…),
  як сутність привʼязується до трейдера: ланка може **відрізнятись** (напр. payin через `bank_account_pid→provider`,
  payout через `provider_pid`). Єдина ланка на всі під-типи = класична причина «половина даних зникла» (0 payin).
- **Якщо контент/довідковий розділ із зовнішнього джерела** (глосарій, довідник помилок, інструкції — не CH-дані):
  - **Джерело:** який файл/URL (Google Sheet / Google Doc / інше)? Доступ — **публічний export** (`?format=csv/txt/html/xlsx`)
    чи треба завантажити/розшарити? *(Google: перевір export напряму — часто «всім за посиланням» вже віддає.)*
  - **Доставка даних — обери з замовником:** ☐ **A) backend-ендпойнт з кешем** (сам фетчить+парсить джерело, TTL 6–12 год,
    **self-updating** коли замовник дописує) ☐ **B) статичний JSON** (простіше, але оновлення — ручний ре-парсинг). Зафіксуй + TTL.
  - **Структура/групування — ПИТАЙ ОКРЕМО ПО КОЖНІЙ ЧАСТИНІ:** одним списком vs **по групах як у джерелі** (вкладки/відділи).
    *(Кейс: глосарій спершу «одним списком», згодом — по 4 відділах; помилки — по 6 вкладках. Різні частини = різне групування.)*
  - **Медіа/картинки:** імпортувати? спосіб — ☐ base64 inline (простіше, але роздуває відповідь; наш кейс ~4.3МБ) ☐ окремі файли/URL.
    **Бюджет розміру** відповіді/сторінки зафіксуй явно.
  - **Мова:** показувати **мовою джерела** (дослівно) чи перекладати? *(частини можуть бути різними мовами — глосарій UA, помилки RU.)*
  - **Розміщення в UI:** окрема вкладка vs секція; **поруч із чим** (напр. біля Legend).
- 🔴 **«Вхід»/«вихід» можуть мати РІЗНЕ джерело для різних типів сутності — перевіряй по кожному типу окремо.**
  Кейс: у `[EXC]`-мерчантів `payin = 0` (8 з 8), їхній вхід — це **topup** (`transactions.operation='expense'`).
  Наївна спільна формула `out/in` дала б ділення на нуль. Спершу зроби зріз «метрика × тип сутності», потім формулу.
- **Якщо метрика ПОХІДНА (%, ratio, ARPU, медіана, avg)** — зафіксуй **порядок дій** і **базу**:
  - `median/avg ЧОГО?` — медіана денних коефіцієнтів ≠ коефіцієнт від медіанних сум; спершу рахуємо метрику
    по днях і потім агрегуємо — чи навпаки? *(Кейс merchant-out-in: «день до медіани за 7 днів» без уточнення
    дало б `median(OUT)/median(IN)` — інше число.)*
  - **зважено чи середнє по рядках** (`Σчисельник/Σзнаменник` vs `mean(per-row)`) — див. self-check §7;
  - що робити з днями/рядками, де **знаменник = 0** (пропустити / показати BLANK / 0).
  - 📆 **«AVG по днях» — по ЯКИХ днях?** ☐ лише дні З АКТИВНІСТЮ ☐ усі календарні дні періоду.
    *Два легальні варіанти, які відповідають на різні питання: «скільки робить трейдер у робочий день» vs
    «скільки приносить афіліат у середньому за день періоду». Кейс trader-affiliate: для 108 афіліатів зі
    167 різниці немає (активні щодня), а для решти 59 різниця **до 14 разів** — `Nobiyo` торгував 1 день з
    14: ₹10 800 по активних днях проти ₹771 по календарних.*
- 🧮 **ЧИ АГРЕГУЄТЬСЯ метрика — визначає архітектуру, а не лише формулу:**
  ☐ адитивна (сума, лічильник) — можна складати по групах
  ☐ відношення — перерахувати з чисельника і знаменника, не складати
  ☐ **середнє** — НЕ агрегується, потрібен вихідний грейн
  ☐ **перцентиль** — НЕ агрегується взагалі
  *Питати ДО оцінки: якщо метрику просять і в розрізі (по менеджеру, по групі), і в summary — для середніх
  і перцентилів це означає окрему передачу грейну, а не «ще одну колонку». Кейс: «AVG Payin/Trader Daily у
  розрізі менеджера» — скласти per-affiliate AVG математично неможливо, довелось віддавати на фронт денний
  грейн (~5 тис. рядків), щоб перерахувати точно для будь-якого групування. А P90 на рівні «Всього» не
  показати взагалі без окремого запиту.*

> 🗣️ **ЯК СКАЗАТИ «ЦЬОГО НЕ МОЖНА» — готові формулювання.**
> Технічна причина замовнику нічого не пояснює. Кажи **що можна замість**.
>
> **Δ для показника без історії:** «Ми зберігаємо тільки поточне значення — яким воно було
> місяць тому, ніде не записано. Порівняти з минулим **зараз** не вийде. Можемо почати
> зберігати щоденні зрізи — перше чесне порівняння буде через місяць.»
>
> **Середнє/перцентиль у розрізі:** «Середні й відсотки не можна просто скласти — середнє
> з середніх дає інше число. Порахуємо заново з вихідних даних: трохи довша розробка, зате правильно.»
>
> **«Хочу 1:1 як у тому кабінеті»:** «Зійтись рівно можна лише на **закритому** періоді — вчора
> й раніше. На сьогоднішньому дні дані ще доїжджають, і різниця плаватиме з обох боків.»
>
> **Зміна зачіпає історію:** «Якщо змінити формулу, старі цифри теж перерахуються — звіти
> за минулі місяці стануть іншими. Тому робимо у два кроки: спершу нова колонка поруч, звіряємо, потім заміна.»

- **Якщо є вікно-ПОРІВНЯННЯ** (день до медіани, тиждень до тижня, MTD до MTD):
  - чи входить у базу **сам обраний період** (`D-7…D-1` vs `D-6…D`)? Обидва варіанти легальні й дають різні числа;
  - база = **Hour-to-Hour** (той самий відрізок попереднього періоду) — це стандарт каталогу §2.4 і для KPI-карток теж.
  - 🕰️ **ЧИ Є ІСТОРІЯ? — перевірити ДО того, як обіцяти Δ.** Для кожної метрики, до якої просять Δ:
    тип значення ☐ FLOW (накопичення за період — історія є за визначенням) ☐ **SNAPSHOT** (стан «зараз»).
    Для SNAPSHOT: чи зберігається історія? ☐ так, де: `____` ☐ **НІ → Δ заднім числом НЕМОЖЛИВИЙ**.
    → Якщо ні, а Δ потрібен — це **окрема задача** «почати щоденні зрізи», і перший реальний Δ буде
    **через період порівняння** (для Δ MTD — через місяць). Не ховати це всередину пункту.
    *Кейс trader-affiliate: ТЗ вимагав Δ MTD для Deposit і Score нарівні з Payin/Payout, ніби вони однорідні.
    Насправді Deposit — поточний баланс рахунку (зберігається лише поточне значення — попередніх версій не лишається), а Score — ковзне вікно 30 днів, що перезаписується кожні
    4 год. Обидва без історії. Довелось будувати підсистему щоденних зрізів + чекати місяць. Одне питання
    в брифі проти підсистеми у складі спринту.*
  - 📐 **Δ показуємо як:** ☐ різницю (в одиницях метрики) ☐ відсоток. **Для валютних — рахуємо на:** ☐ INR ☐ USDT.
    *Кейс: ТЗ казав `Δ = MTD_cur − MTD_prev` (різниця в ₹), борд історично показує % — розійшлись мовчки.
    А для Deposit баланс зберігається в USDT, тож рахунок на INR видавав би рух курсу за рух коштів.*
- **Очікуваний результат (як перевіримо, що ок):** `____`

*(Копіюй блок «Пункт N» під кожен рядок таблиці §2.)*

🎨 **Якщо правка стосується ВИГЛЯДУ (новий блок, графік, матриця, колонка) — покажи пікер візуалізацій:**
`https://formulas.freyal.renderhorizon.org/visual-picker.md`
🖼️ **І ГАЛЕРЕЮ** — `https://formulas.freyal.renderhorizon.org/visual-gallery.html` — ті самі патерни,
але **намальовані**: замовнику показуй галерею, пікер лишай собі для назв.
Не питай «як має виглядати?» абстрактно —
**запропонуй 2–3 готові патерни** («лінія + коридор P25–P75 як у capacity», «матриця день×година як в operational»,
«таблиця з прапорцем аномалії як у merchant-out-in») і зафіксуй вибір рядком:
`блок · патерн · як у <звіт> · що на осях/у колонках`.

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

### ⚙️ Технічні нюанси реалізації

> Цю частину опрацьовує власник звітів при збірці — тобі заповнювати нічого не треба.
> Якщо якийсь із пунктів впливає на **зміст** звіту (наприклад, що показувати, коли даних немає),
> він винесений окремим питанням вище.

## 3-bis. 🖼️ МАКЕТ ЗМІН — показати «до → після» (до ТЗ, обовʼязково)

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

**Інструмент:** зроби макет «до → після» **HTML-сторінкою з вигаданими числами** — щоб замовник
побачив зміну, не плутаючи її з реальними даними. Угорі — помітна плашка **«МАКЕТ — цифри вигадані»**.
Швидший шлях: візьми готовий макет звіту `formulas.freyal.renderhorizon.org/mockups/<id>.html`
і додай параметри `?hl=`/`?add=`/`?del=` — правка намалюється прямо в ньому.

**Що робиш:**
1. Самодостатній **HTML без бекенду** з **синтетичними** даними, у стилі того самого звіту
   (візьми його `static/<звіт>.html` за зразок — макет має виглядати як він, а не як чужа сторінка).
2. **Угорі плашка:** `🖼️ МАКЕТ · дані вигадані · бекенду немає · це лише вигляд`. Не прибирати.
3. Показати **«до → після»**: як блок виглядає зараз і яким стане. Якщо колонок багато — досить фрагмента
   з сусідніми колонками, щоб було видно **порядок і місце** нової.
4. Позначити, що саме змінилось: нові колонки/блоки підсвітити, прибрані — закреслити.

**Як віддати:** HTML **одним блоком коду в чаті** + підпис «збережи як `макет.html` і відкрий у браузері».

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

⚠️ Макет **не перевіряє цифри** — він лише про вигляд і розташування. Формули звіряються окремо (§8).


## 4. 🚧 Регресія — що НЕ можна зламати (обов'язково)

- **Метрики/цифри, які лишаються без змін:** `____`
- **Слайсери/фільтри/RLS, які не чіпаємо:** `____`
- 🎛️ **МАТРИЦЯ «слайсери × НОВІ блоки» — заповнити таблицею, а не фразою:**

  | Слайсер | новий блок A | новий блок B |
  |---|---|---|
  | Менеджер | ☐ діє ☐ ні | ☐ діє ☐ ні |
  | Афіліат / Статус / Tier / Період | ☐ ☐ | ☐ ☐ |

  *«Слайсери без змін» читається як «наявні не ламаємо», а не «нові блоки їх слухають» — і нова вкладка
  виходить глухою до фільтрів. Кейс: вкладка «Воронка» спершу реагувала лише на період, а Менеджер /
  Афіліат / Статус / Tier ігнорувала — відфільтрував на одного менеджера, а воронка показує всіх.*
  ⚠️ Заразом матриця ловить **неіснуючі слайсери**: у тому ж ТЗ серед «незмінних» перелічили Group і
  Trader, яких на дашборді немає взагалі.
  ⚠️ **Якщо блок агрегує (summary, «Всього», розріз по групах) — RLS ріже ДО побудови підсумків**, інакше
  обмежений користувач бачить чужі цифри в total, навіть коли рядки відфільтровані.
- 🧩 **Нова вкладка/блок показує ту саму сутність, що вже є деінде → групувати ЇЇ ЖЕ функцією.**
  Не пиши нове групування в SQL, якщо у звіті вже є канонічне (наприклад `merchant_key()`).
  *Кейс merchant-out-in: нова вкладка різала назву по `|` прямо в запиті, тож `‹мерчант›` і
  `‹мерчант›` стали двома мерчантами тут і лишились одним у вкладці «Мерчанти» — 20 рядків проти 19.
  Помітив замовник, а не звірка: обидві цифри виглядають правдоподібно, поки не покласти поруч.*
- 🏷️ **Чи існує вже НАЗВА для цього поняття на сусідньому борді?** `____`
  *Кейс: дашборд називав групу без менеджера «Other», Weekly — «— Без менеджера». Той самий набір із 10
  афіліатів і ₹369M обороту. На борді «Other» читалось як менеджер на імʼя Other, тобто найбільша діра в
  покритті виглядала як звичайна група. Плюс KPI-картка «без менеджера» шукала рядок з третьою назвою і
  завжди показувала 0.*
- **Інші звіти, що читають ті самі поля** (напр. supportzones-поля використовує і `trader-detailed`, і `operational`): `____`
- **Сверка після правки — з чим:** `____` · **точність: ⚖️ 1:1** (до копійки/₹, до пункту; нульова розбіжність, каталог §9.2)
- 🧩 **Чи може сутність належати КІЛЬКОМ значенням виміру?** (афіліат — двом менеджерам, трейдер — двом
  групам). Якщо так: рядок не дублюємо при мультивиборі (dedup), а «сума по значеннях» законно **більша**
  за загальну — це підписати в Легенді. Скалярне поле тут не працює: фільтр/групування мають ходити по
  **списку**. *(Кейс: `sheet_manager` став рядком «A, B» — фільтр по менеджеру почав давати 1 афіліат
  замість 7, а картка менеджера взагалі не відкривалась.)*
- 🔢 **Популяція (знаменник) кожної вкладки/картки — зафіксуй ЯВНО:** `____`
  На скількох сутностях рахується кожен блок і чи це **та сама множина**? Список / KPI-картка / графік /
  деталізація часто беруть **різні** набори: усі привʼязані · лише активні · лише з рухом у періоді ·
  лише створені до початку періоду. Однакова назва ≠ однакова база.
  *(Кейс кабінету mithras: «Мої трейдери» 554 · Statistic 493 · panel 1134 — і два нових трейдери з обігом
  взагалі не потрапили в список. Арифметика скрізь правильна, розбіжність — у популяції.)*
  → Перевірка: `set(вкладка A) == set(вкладка B)`, а не лише «суми зійшлись»; і окремо — чи потрапляють
  сутності, **створені всередині періоду**.

## 5. Дані / період (якщо змінюються)

- **Що означає ПОРОЖНІЙ фільтр — «усі» чи «нічого»?** ☐ усі (класика) ☐ нічого, доки не обрано → `____`
  Не косметика: змінює перший екран, читання цифр і навантаження на базу. *(Кейс trader-affiliate-native:
  слайсери «Менеджер»/«Афіліат» перевели на «нічого» — звіт мав бути інструментом по конкретному афіліату.)*
- **Змінюється джерело/період/TZ/грейн?** ☐ ні ☐ так → `____`
- **Якщо чіпаємо вікно/грейн — зафіксуй семантику явно:**
  ☐ **as-of дата (включно)** + пресети ☐ закриті періоди ☐ довільний From–To → `____`
  - чи входить **поточний неповний день**? (як позначено в UI — не лишай неявним)
  - ⚠ чи є **найдрібніший грейн еталона** (зазвичай **день**)? без нього неможливо звірятись день-у-день із джерелом
  - ⚠ зміна семантики вікна **зсуває всі історичні цифри** — попередь у §4 (регресія)
> 🔗 **ЗВІТ ТОРКАЄТЬСЯ АФІЛІАТІВ ЧИ АФФ-МЕНЕДЖЕРІВ? Спитай про джерело мапінгу.**
> Прив'язка «афіліат → менеджер» у наших звітах приходить **не з бази платежів**, а із
> **зовнішніх 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 немає — це не привід пропустити
> питання: рішення вже ухвалене, і звіт, зроблений «як було», доведеться переробляти.

- **Якщо додається ЗОВНІШНЄ джерело (Google-таблиця, чужий API, файл-мапінг)** — одразу зафіксуй **режим оновлення**:
  ☐ **крон** (авто, вказати TTL/розклад) ☐ **вручну на запит** (команда + хто запускає) ☐ разовий імпорт.
  І де лежить знімок. *(Кейс: мапінг афф-менеджерів — замовник свідомо обрав ручне оновлення, «крона не треба»;
  читання по `mtime` дало підхоплення без рестарту.)* Ця розвилка стосується будь-якого мапінгу, не лише
  контент-розділів.
- **Нові таблиці/поля:** `____` → каталог §7
- **Нові поля provider-settings:** `____` → каталог §5 (уточни точну назву поля в supportzones)

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

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

> 🚫 Цей бриф **не викочує правку**. Результат — файл `ТЗ_edit_<slug>.md`, який ти надсилаєш власнику звітів.
- **Терміновість:** `____`  ·  **Хто приймає:** `____`
- ❗️ **Не роби сам:** не правь код звіту, не чіпай сервери, не проси доступів. Реалізація — на боці власника.

---

## 7. ✅ Self-check скілом + крос-чек (перед ТЗ)

| Параметр | Значення | Ок? |
|---|---|---|
| Звіт і id ідентифіковані | `____` | ☐ |
| Кожен пункт має тип + очікуваний результат | `____` | ☐ |
| Зміни формул зіставлені з каталогом §2/§3 | `____` | ☐ |
| **📌 Формули дані ПОСИЛАННЯМ (§ + версія каталогу), а не переписані текстом; метрики поза каталогом мають статус «НОВА» + хто затвердив** | `____` | ☐ |
| **🧭 У КОЖНОГО пункту заповнено «поточний стан у коді» (нове / є-не-показано / **є-але-рахує-інакше** / є-і-правильне) — дивились у код, не вгадували** | `____` | ☐ |
| **🕰️ Для кожної метрики з Δ перевірено наявність історії; для SNAPSHOT без історії Δ винесено в окрему задачу зі зрізами** | `____` | ☐ |
| **🧮 Для кожної метрики зафіксовано, чи вона агрегується (середні й перцентилі — ні); якщо просять розріз — вирішено, звідки береться грейн** | `____` | ☐ |
| **🎛️ Матриця «слайсери × нові блоки» (§4) заповнена; для агрегатів RLS ріже ДО підсумків** | `____` | ☐ |
| **🕳️ Для кожного нового показника визначено, що показуємо на порожньому/недостатньому наборі (`—` vs `0`)** | `____` | ☐ |
| Фінформули звірені з knowledge + оновлено `## Історія` | `____` | ☐ |
| supportzones-поля звірені з каталогом §5 | `____` | ☐ |
| Регресія (§4) зафіксована повністю | `____` | ☐ |
| **⚖️ Кожен % / avg — зважений чи середнє по рядках?** Для КОЖНОЇ частки й середнього явно зафіксовано базу: `Σчисельник/Σзнаменник` (зважено) vs `mean(per-row)` (незважено). Незважене середнє легітимне лише якщо так і підписано на картці | `____` | ☐ |
| **🔢 Популяція (§4) кожної вкладки/картки збігається** — множини сутностей однакові, сутності, створені всередині періоду, не загубились | `____` | ☐ |
| План сверки після правки узгоджено | `____` | ☐ |
| **Онлайн-звірка (§9): формула в CH hermes + цифри з halleg (прод) зійшлися 1:1 (до копійки/пункту)** | `____` | ☐ |
| **Незмінні цифри (§4) наживо НЕ зрушились після правки** | `____` | ☐ |
| **`brief-gap-check` прогнано після КОЖНОЇ секції** (а не лише наприкінці) | `____` | ☐ |
| **§10 поставлено: «закриваємо / є правки / перерва»** — сесія не завершується мовчки | `____` | ☐ |
| **🚦 ТЗ (§9) не містить ЖОДНОГО відкритого питання / припущення / TBD** | `____` | ☐ |

> 🔴 **Ворота «джерела»:** для кожної зміненої/доданої метрики має бути зафіксовано **джерело формули**
> (навіть якщо це halleg за замовчуванням). І дай відповідь: **чи звірявся з альтернативним джерелом?**
> Тригери: CR не сходиться → **eye-of-god** · статуси/ліміти/**комісії** → **supportzones** ·
> метрика про співробітників → **BAM** · ліквідність/сеттли/курси → **lab.dsfrogboard** ·
> фінансова формула → **canonical knowledge**. Жодної звірки з альтернативою — це сигнал, не норма.
> Результат пиши в ТЗ: `звірено з <джерело>: <збіг N/N | розбіжність + причина>`.

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

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

1. `____` *(типово: точна нова формула / поріг)*
2. `____` *(типово: які цифри мусять лишитись незмінними)*
3. `____` *(типово: з чим звіряти після правки і який допуск)*
4. `____` *(типово: розбіжність код↔очікування — напр. яка таксономія виміру: sheet-manager vs provider_group)*

---

## 9. 📄 Чисте ТЗ-на-правку — ВИДАЙ У ЧАТ ОДНИМ БЛОКОМ

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

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

## 1. Звіт і контекст
- URL/id: <...> · сервер: halleg (прод) · файл: <...>
- Навіщо правка: <...>

## 2. Зміни (нумерований список, кожна — самодостатня)
1. [<тип>] Було: <...> → Стало: <...>
   - Формула (якщо є): <фінальна> · крос-чек: наша <...> / нова-звірена
   - Як перевірити: <...>

## 2-bis. Макет
- Затверджено: `<файл/лінк>` · версія `<N>` ⚠️ макет = вигляд, не цифри

## 3. Регресія (інваріанти правки)
- Незмінні метрики/цифри: <...>
- Незмінні фільтри/RLS: <...>
- Суміжні звіти, які не має зачепити: <...>

## 4. Дані (якщо змінюються)
- Джерело/період/грейн: <ні / деталі> · нові поля: <...>

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

- Виконує **власник звітів** за цим ТЗ · терміновість: <...> · приймає: <...>


## 6. Definition of Done
- [ ] **Сесію брифа закрито явно** — питання «закриваємо чи є ще правки?» поставлено і отримано відповідь. Перехід до реалізації/деплою сесію НЕ закриває
- [ ] **Вкладення замовника збережені** (скріни/експорти/лінки) і перелічені в ТЗ з прив'язкою до пункту — або явно вказано «вкладень немає»
- [ ] Кожна зміна дає очікуваний результат
- [ ] **У кожного пункту заповнено «Як перевірити»** — спостережуваний артефакт, не опис наміру.
      *(Аудит 14 ТЗ: заповнено в 1 із 14, хоча протокол п.7-bis прямо каже «не приймай пункт без нього».
      Правило жило лише в протоколі — і не спрацьовувало.)*
- [ ] **Для кожного пункту зафіксовано ПОТОЧНИЙ СТАН у коді:** `НОВЕ` · `Є НА БЕКЕНДІ, НЕ ПОКАЗАНО` ·
      **`Є, АЛЕ РАХУЄ ІНАКШЕ`** · `Є І ПРАВИЛЬНЕ`. *(Найнебезпечніший — третій: візуально все на місці,
      тому ніхто не перевіряє, і розбіжність живе до першої звірки з іншим звітом.)*
- [ ] **Макет «до → після» затверджено замовником** — лінк/файл: `____`
- [ ] **Матриця «слайсер × блок»** заповнена для нових/змінених блоків
- [ ] Онлайн-звірка пройдена: формула (CH hermes) + цифри (CH hermes + halleg прод) збігаються **1:1** — до копійки/₹ і до пункту (нульова розбіжність)
- [ ] Жодна метрика з §3 не змінилась (регресія-чек наживо)
- [ ] **Регресію міряно на ЗАКРИТОМУ періоді (минулий місяць), старий код проти нового** — не на поточному
      *(на живому періоді дані доїжджають: напрям розбіжності перевертається між прогонами і дає хибний
      сигнал. На закритому — детерміновано; у нас так вийшло 0 розбіжностей, 12 метрик × 184 афіліати)*
- [ ] **Перевірку «Як перевірити» виконано по ОБОХ вікнах — поточному і попередньому**
      *(кейс: денний грейн клав у «попереднє» цілий місяць замість обрізаного вікна — колонка таблиці була
      правильна, а KPI-картка й матриця ні, розбіжність ~21%. Не спіймалось, бо перша звірка перевіряла
      лише поточне вікно)*
- [ ] **Метрики з історії подій прогнано двічі — результат ідентичний** (перевірка на недетермінізм)
- [ ] **RLS перевірено під реальним користувачем ролі `user`, включно з вигрузкою** — не лише симуляцією
- [ ] Пройдено verify-report (звірка з ТЗ + hermes + PBI)

<!-- Для контент/довідкового розділу (зовн. джерело) — DoD інший: не «1:1 цифри», а «= джерело»: -->
## 6-bis. Definition of Done — контент/довідник (замість §6, якщо зміна = контент-розділ)
- [ ] **Сесію брифа закрито явно** — питання «закриваємо чи є ще правки?» поставлено і отримано відповідь. Перехід до реалізації/деплою сесію НЕ закриває
- [ ] **Вкладення замовника збережені** (скріни/експорти/лінки) і перелічені в ТЗ з прив'язкою до пункту — або явно вказано «вкладень немає»
- [ ] **Per-entry звірка з ОРИГІНАЛОМ (обов'язково, авто-аудит):** для КОЖНОГО запису заголовок/кроки/текст/картинки —
      правильні й на своєму місці; **не перетікають** у сусідній; **нема фантомних/дубльованих** записів; кроки **не губляться**;
      картинки **не каскадять** (к-сть на запис = як в оригіналі). Прогнати скрипт-аудит (дублі, порожні кроки/fix, >N картинок).
- [ ] Кожен елемент джерела на місці (усі терміни/помилки/картинки; нічого не загублено, **нічого не вигадано/не перекладено** без узгодження)
- [ ] Групування/структура = як домовились (одним списком / по вкладках джерела — окремо по кожній частині)
- [ ] Варіант доставки (A ендпойнт+кеш / B статик) реалізовано; для A — egress з halleg перевірено, TTL виставлено
- [ ] Розмір відповіді/сторінки в межах бюджету (картинки); lazy-load не тормозить решту звіту
- [ ] Регресія: метрики/CH-дані/RLS **не зачеплені** (розділ лише додано)
```


---


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

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

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


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

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

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

🔁 **Рішення змінилось після перегляду реалізації — ПЕРЕВИДАЙ ТЗ.** Замовник побачив живий екран і
передумав (нормальний сценарій) → ТЗ миттєво розходиться з тим, що в проді, а власник міг уже його
переслати. Видай **ред. 2** із поміткою, який пункт замінено й чому, і лише тоді закривай.
*(Кейс 2026-08-17: узгодили «порожній стан = заглушка», після перегляду замовник попросив «блоки з
нулями» — документ і прод розійшлися на добу.)*

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

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


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

Після видачі ТЗ асистент **додає 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).
- **Деплой:** виконує **власник звітів** (замовник ТЗ сервери не чіпає).