# Контрольные заметки (самопроверка модели, синтетический черновик) Это самопроверка автора расчёта (модели). Она **не** является независимой приёмкой, проверкой преподавателем, рецензией человека или доказательством реальной интеграции. ## Новые проверки этого раунда (наблюдались при последнем запуске; код генератора в этом раунде не менялся) | # | Команда / проверка | Наблюдаемый результат | |---|---|---| | N1 | `python3 output/build_dashboard.py` дважды из корня проекта, сравнение SHA256 пяти файлов | оба **exit 0**, хеши идентичны и равны хешам из предыдущего раздела | | N2 | Отдельная временная копия семи входов и генератора в `output/temporary-check/all-channel/`: `vk_ads` → `all` в `leads.csv` (6 вхождений) и `channel-costs.csv` (1); запуск внутри копии; фактический HTML в headless Chrome | запуск копии **exit 0**; выбор «Канал: all» → одна строка таблицы «Канал: all», одна группа диаграммы денег, воронка 6/6/2/1/1/1, KPI: лиды 6, квалифицированные 2, затраты 80 000 RUB, чистая выручка 0 RUB; reset → «Все каналы (общий срез)», 7 строк, 27 лидов, 300 000 RUB. Подписи «Все каналы (общий срез)» и «Канал: all» различимы | | N3 | Три отдельные временные копии, каждая со свежим пустым `output/` (оригиналы не менялись): перевёрнутый период; `period_start` без часового пояса; `limitations` строкой | во всех трёх **exit 2**, диагностика через `CalcError` в stderr, ни одного из `report.json`/`channel-report.csv`/`dashboard.html`/`interpretation.md`/`source-hashes.json` не появилось | | N4 | Искусственная ошибка рендера (`render_html` подменён на исключение) во временной копии | **exit 3**, «Файлы в output/ не записаны», ни одного из пяти файлов нет | | N5 | Хеши входов против `source-hashes.json`; хеши пяти основных файлов после временных запусков | входы не менялись; основные файлы без изменений | Расположение: копия N2 сохранена отдельно в `output/temporary-check/all-channel/` (входы, её собственный `output/`, `probe.html`, `dom.txt`) и **не является основной версией**; её результаты не смешаны с основными файлами `output/`. Копии N3/N4 после проверки удалены. Основной `source-hashes.json` перечисляет только семь входов проекта. Не перепроверялось в этом раунде: запуск в свежей копии всего проекта (`cwd=/`) — последний раз выполнен в предыдущем разделе (A3) на том же коде. ## Предыдущий прогон текущей версии генератора (A1–A7; до этого раунда) Выполнены уже после последней правки `build_dashboard.py` (проверка контракта до записи, рендер в памяти до записи, идентичность среза по объекту/индексу). Все команды запускались из корня проекта, если не указано иное. | # | Команда / проверка | Наблюдаемый результат | |---|---|---| | A1 | `python3 output/build_dashboard.py` | **exit 0**, пять файлов записаны | | A2 | Повторный запуск и сравнение SHA256 пяти сгенерированных файлов | **exit 0**, хеши идентичны | | A3 | Копия `input/` и генератора во временный каталог, запуск абсолютным путём с `cwd=/`, сравнение SHA256 с проектом | **exit 0**, пять файлов побайтно совпали | | A4 | `source-hashes.json` против независимого `shasum -a 256 input/*` | SHA256 всех семи входов совпали; в `files` семь записей с относительными путями и размерами | | A5 | Структурная проверка: обязательные верхнеуровневые ключи `report.json`; все поля секций у `total` и каждого канала; заголовок `channel-report.csv` (19 колонок, 8 строк, `profit_rub` везде `null`); флаги сверки | все условия истинны | | A6 | Поиск во внешних ссылках `dashboard.html` (`http(s)` в `src/href`, `fetch(`, `XMLHttpRequest`, `@import`) и `/Users` в пяти файлах | не найдено | | A7 | `ls -A output` | только `build_dashboard.py`, `checks.md` и пять сгенерированных файлов; временных результатов нет | Хеши актуального комплекта: `report.json` `df224a57…bb6d`, `channel-report.csv` `b0188b5a…c937b`, `dashboard.html` `5c8b1b86…11b0`, `interpretation.md` `482c3014…91f2`, `source-hashes.json` `fa2bba97…25dd`. Проверки, выполненные ранее в этом же сеансе на текущем коде (после последней правки), и подтверждённые ими свойства — записи 16, 18, 19 журнала ниже: отказ с кодом 2 до записи файлов при перевёрнутом периоде, границе без часового пояса и `limitations` строкой; код 3 без записи при ошибке рендера. В этом новом прогоне они **не** повторялись. ## Что не проверено (актуальные ограничения) - Нет независимой приёмки, оценки преподавателем или рецензии; все записи выше — самопроверка модели. - Мобильная ширина 390px и проход клавишей Tab в этом раунде не проверялись; в предыдущем прогоне не повторялись: браузерные проверки (фильтр, сброс, 390px), негативные сценарии данных; они выполнялись раньше (журнал ниже) и только в headless Chrome на macOS. Другие браузеры, экранные читалки, реальные устройства и ручной проход клавишей Tab не проверялись. - Внешние интеграции (Метрика, CRM, банк, рекламные кабинеты) не использовались. - `checks.md` ведётся вручную и не перегенерируется скриптом. - Контракт обозначает общий срез в `report.json` и `channel-report.csv` значением `channel=all`; при реальном канале `all` строки различимы только позицией/полем `total`. В дашборде коллизии нет. - При чтении входов в вывод попал фрагмент `README.md`; в расчёте не использовался. - Прибыль неизвестна; выручка кассовая; выборка синтетическая и малая. Это самопроверка автора расчёта. Она **не** является независимой приёмкой, проверкой преподавателем или доказательством реальной интеграции. ## Журнал прежних запусков (история, по порядку; относится к промежуточным версиям кода) Эти записи сохранены как история. Они выполнены на более ранних версиях `build_dashboard.py`; актуальной версии они не подтверждают — см. раздел выше. | # | Команда / проверка | Наблюдаемый результат | |---|---|---| | 1 | `python3 output/build_dashboard.py` из корня проекта (Python 3.9.6) | первый запуск упал (`exit 1`, `TypeError`: `Path.write_text` без `newline` в Python 3.9); исправлено; повторный запуск: **exit 0**, пять файлов записаны | | 2 | Повторный запуск генератора и сравнение SHA256 пяти сгенерированных файлов (`report.json`, `channel-report.csv`, `dashboard.html`, `interpretation.md`, `source-hashes.json`) до/после | exit 0, хеши **идентичны** | | 3 | Копия `input/` и `output/build_dashboard.py` во временный каталог, запуск абсолютным путём с `cwd=/` | exit 0, пять файлов **побайтно совпали** с запуском в проекте (SHA256 те же) | | 4 | Сравнение `source-hashes.json` с независимо посчитанным `shasum -a 256 input/*` | значения SHA256 всех семи входов **совпали** | | 5 | `json.load(output/report.json)` | разбирается без ошибок | | 6 | Булевы флаги сверки в `report.json.reconciliation.flags` (лиды, квалификации, события формы, заказы, оплаченные заказы, клиенты, чистая выручка каналов = общий срез; разбиение lead_id; известные затраты каналов = пул) | все `true` | | 7 | Ручная выборочная сверка: число лидов, событий формы и квалифицированных лидов пересчитано по CSV вручную | совпало с отчётом | | 8 | Негативные сценарии на **временных копиях** (исходные входы не менялись): платёж ≠ сумме заказа; возврат > оплаты; возврат без оплаты; конфликтующий дубль transaction_id; флаг `yes`; один customer_id в двух оплаченных заказах; заказ раньше лида; дробная сумма в расходах; ссылка на неизвестный заказ | во всех 9 случаях **exit 2**, понятная диагностика в stderr, файлы отчёта не записаны | | 9 | Headless Chrome, DOM после выполнения скрипта | 7 пунктов фильтра, 18 KPI-карточек, 7 строк таблицы; выбор `unattributed` → 1 строка, затраты «неизвестно», CPL/CPQL/CAC «нет данных»; кнопка сброса возвращает «Все каналы» и 7 строк | | 10 | Встраивание дашборда в iframe шириной 390px (headless Chrome) | `scrollWidth` страницы = 390 = `clientWidth` (нет горизонтальной прокрутки страницы); таблица прокручивается внутри своего контейнера | | 11 | `grep` по `dashboard.html`, `report.json`, `build_dashboard.py` на `/Users`, `http`, ISO-метки времени запуска | совпадений нет | | 12 | Исправление коллизии: настоящий канал `all` совпадал с идентификатором общего среза в JS (`label`, `current`, KPI, диаграмма, таблица, сброс). Срез теперь определяется объектом раздела, значения `option` — `total` / `ch:<индекс>`. Регрессия на временной копии, где `organic` переименован в `all` (входы проекта не менялись) | генератор exit 0; в headless Chrome: общий срез — 27 лидов, 7 строк; выбор канала `all` — подпись «all (отдельный канал)», 1 строка, 5 лидов; сброс — снова общий срез, 27 лидов, 7 строк | | 13 | После исправления: два запуска `python3 output/build_dashboard.py` на реальных входах и сравнение SHA256 | оба exit 0, хеши идентичны; `http`/`/Users` в `dashboard.html` не найдено | | 14 | Вторая правка идентичности: все сравнения по имени в JS убраны (общий срез — идентичность объекта `R.total`, неопределённый источник — последний элемент `R.channels`, выбор — `total`/`ch:<индекс>`); подписи «Все каналы (общий срез)» и «Канал: <имя>». Регрессия на временной копии с каналами `all`, `total`, `ch:0` (входы проекта не менялись): перебор всех 7 пунктов фильтра и сброс | генератор exit 0; общий срез 27 лидов/7 строк/6 групп диаграммы денег; каждый канал — 1 строка и свои лиды (8, 6, 4, 5, 3, 1); подписи различимы; сброс → общий срез. Два запуска на реальных входах: exit 0, хеши идентичны, `report.json`, CSV, `interpretation.md`, `source-hashes.json` не изменились | | 15 | Генератор запущен дважды на оригинальных входах; затем отдельная временная копия семи входов и генератора в системном временном каталоге вне проекта (сохранена, не смешана с `output/`): `vk_ads` → `all` в `leads.csv` и `channel-costs.csv`; запуск и проверка в headless Chrome | оба запуска exit 0, хеши идентичны; временный запуск exit 0; выбор «Канал: all» → 1 строка таблицы, 1 группа диаграммы денег, воронка 6/6/2/1/1/1, затраты 80 000 RUB, чистая выручка 0 RUB (значения совпали с прежним `vk_ads`); сброс → общий срез (27 лидов, 7 строк). Оригинальные входы побайтно прежние | | 16 | Полная проверка `measurement-contract.json`/`company.json` до чтения CSV и до записи файлов (строгий порядок периода, часовой пояс у обеих границ, `limitations` — непустой список строк, обязательные строковые поля, валидный JSON); сборка пяти файлов сначала во временном каталоге и копирование в `output/` только после успеха. Негативные случаи на временных копиях (оригинал не менялся): начало позже конца, начало = концу, граница без пояса (обе), `limitations` строкой, нестроковый элемент `limitations`, нет `attribution`, `period_start` не строка | во всех 8 случаях **exit 2**, диагностика в stderr, в `output/` копии ничего не записано; необработанных исключений нет | | 17 | После правки: два запуска на оригинальных входах и запуск в свежей копии (`cwd=/`) | все exit 0; хеши пяти файлов идентичны прежним (`dashboard.html` `5c8b1b86…`, `report.json` `df224a57…`), входы совпали с `source-hashes.json` | | 18 | Рефакторинг: все тексты (JSON, CSV, HTML, Markdown, хеши) строятся в памяти до любой записи (`render_csv/render_html/render_interpretation`), в `output/` пишется только готовый комплект. Три отдельные временные копии с чистым `output/` (оригинал не менялся): перевёрнутый период; граница периода без часового пояса; `limitations` строкой | во всех трёх **exit 2**, диагностика в stderr, ни одного из `report.json`/`channel-report.csv`/`dashboard.html`/`interpretation.md`/`source-hashes.json` не появилось | | 19 | Искусственная ошибка рендера (`render_interpretation` подменён на исключение) во временной копии | **exit 3**, сообщение «Файлы в output/ не записаны», ни одного из пяти файлов нет | | 20 | Два запуска на оригинальных входах после рефакторинга | оба exit 0, хеши идентичны и равны прежним (`dashboard.html` `5c8b1b86…`, `report.json` `df224a57…`); входы совпали с `source-hashes.json`; в `output/` только основные файлы | SHA256 сгенерированных файлов при повторном запуске: `report.json` `df224a57…bb6d`, `channel-report.csv` `b0188b5a…c937b`, `dashboard.html` `5c8b1b86…11b0` (после исправлений 12–14; остальные четыре файла не изменились), `interpretation.md` `482c3014…91f2`, `source-hashes.json` `fa2bba97…25dd`. ## Прежние заметки об ограничениях (история; частично устарели — актуальные выше) - Нет независимой приёмки, оценки преподавателем или рецензии другим человеком. - Браузерная проверка — только headless Chrome на macOS; другие браузеры, экранные читалки и реальные устройства не проверялись. Клавиатурная доступность обеспечена нативными `select`/`button` и видимым фокусом, но ручной проход клавишей Tab не выполнялся. - Внешние интеграции (Метрика, CRM, банк, рекламные кабинеты) не использовались и не проверялись. - Файл `checks.md` ведётся вручную и не перегенерируется скриптом. - Побочное замечание: при чтении входов в вывод попал фрагмент `README.md` проекта; в расчёте он не использовался. - Входные файлы не изменялись (хеши входов совпали до и после запусков). - Остаточное замечание: по контракту `report.json` и `channel-report.csv` обозначают общий срез значением `channel=all`; при реальном канале `all` там строки различимы только позицией (общий — первая/поле `total`). В дашборде коллизии нет. Свежая копия после исправления отдельно не перезапускалась.