Передача 09.08.2026 — разворот JSON-блобов приёма в колонки
Это вход для новой сессии и для нового разработчика. Прочитать целиком до первой правки. Постоянный вход в проект — ../../AGENTS.md и ../../for-ai-agents/START-HERE.md.
Предыдущие состояния: HANDOFF-2026-08-09-utro.md — устройство работы и заставы выкладки; HANDOFF-2026-08-08-noch.md — классы дефектов приёмки.
1. Задача и чем она измеряется
Правило владельца: когда приходит JSON от площадки, каждое его поле обязано иметь свою колонку, имя — как называет площадка, побайтово. Кусок сырого JSON в поле — нарушение. Зачем: бренд-менеджер должен видеть в одном месте всё, что отдают площадки; логику отчётов напишут потом сами.
Блоб-колонки при этом сохраняются: только по ним доказуемо, что реально отдала площадка, и только они позволяют переразобрать данные заново.
Мерить ТОЛЬКО сторожем — не глазами и не сторонним скриптом:
ssh -f -N -L 13406:127.0.0.1:3406 s3-int
V3_MYSQL_HOST=127.0.0.1 V3_MYSQL_PORT=13406 V3_MYSQL_USER=root V3_MYSQL_PASSWORD=root \
python3 marketplace-collector-v3/scripts/check_json_leaf_coverage.py --sample 3000
2. ⚠️ Сторож зеленеет от НАЛИЧИЯ колонки — это его главный предел
Он не проверяет, что в колонку что-то доезжает. Из этого 09.08 вышла ложь в моих же числах:
Из 59 дочерних таблиц у семи не было писателя ни в одном описании сбора. У ym_returns
7394 строки, у четырёх его дочерних — по нулю. Их листья числились покрытыми, а данных
там не будет никогда.
Общее у семи одно: массив внутри массива. Движка вложенного разворота в рантайме нет —
у SpecCollector отсутствует _child_mapper, spec_runtime/mapping_helpers.py ходит
только по словарям. Тесты к движку написаны и падают; сам движок не сделан. То есть
покрытие объявили раньше, чем появилась возможность его обеспечить.
Теперь ловится машинно: marketplace-collector-v3/tests/test_declared_child_tables_have_writers.py
(база не нужна, сравнивает реестр с целями описаний). Долг вынесен списком
ALLOWED_WITHOUT_WRITER, и второй тест в файле не даёт держать в нём таблицу, у которой
писатель уже появился.
Доезд значения мерится отдельно: <scratchpad>/mapcheck.py и
../agents/ZAMER-BEZ-PRAVILA-2026-08-09.md.
3. Состояние на вечер 09.08.2026
| Замер | Значение |
|---|---|
| Листьев в блобах приёма | 1266 |
| Не покрыто — в начале работы | 1223 |
| Не покрыто — сейчас | 23 |
| Колонок в приёме | 1950 → 2613+ |
| Дочерних таблиц массивов | 0 → 59 объявлено, у 52 есть писатель |
| Падений в наборе тестов | 67 (было 78; на чистом дереве до всей работы — 26) |
| HEAD | 64965a24, дерево чистое, всё выложено в origin |
| Права писателя | 150 таблиц, выданы живой базе, без прав не осталось ни одной |
Остаток из 23 листьев — четыре блоба, поимённо в ../agents/ZAMER-23-LISTA-2026-08-09.md (каждый лист плюс полный состав колонок целевых таблиц):
ym_stats_orders.items_json— 14, нужныdetails,prices,warehouse;ozon_product_rating.groups_json— 6,conditionsиimprove_attributes;ym_tariffs.raw_json— 2,parameters;ym_returns.return_json— 1,imagesв решениях (однородный массив → ОДНА колонка).
Три из четырёх требуют движка вложенного разворота — без него они невыполнимы.
4. Что идёт прямо сейчас
| Задание | Состояние | Что с ним делать |
|---|---|---|
| 172 тесты читают старую форму | ГОТОВО, 12 правок | принять по §6 |
175 массив ym_tariffs | ГОТОВО, 3 правки | принять по §6 |
| 177 движок вложенного разворота, заход 2 | в работе | принять по §6, мера — падений ≤ 67 |
173 ozon_product_rating, 174 ym_stats_orders | отложены | запускать ПОСЛЕ 177 |
Задания лежат в <scratchpad>/1NN-*.md, отложенные — <scratchpad>/queue-17*.md.
Пакет 176 (первый заход движка) отклонён и откачен. Движок в нём работал — 19 тестов
вложенного разбора зелёные, убийство держит. Отклонён по последствиям: набор вырос с 67
падений до 166. Четыре дефекта, все названы в задании 177: новые таблицы не объявлены
intake_only: в манифесте входов ядра (180 падений), не внесены в словарь прав (4),
сменена подпись _write_target без правки вызовов (17), внесён дубль ключа YAML (6).
Отдельно: ym.returns остался не тронут, а строки долга по нему сняли — сделанным
объявили то, чего не делали.
5. Три перегородки, каждая останавливала работу целиком
5а. Права писателя. Порождаются из проверенного словаря
specs/security/collector-intake-table-vocabulary-v1.json. Нет таблицы в словаре — сбор
падает с ERROR 1142.
⚠️ Областей в миграции 2026-07-16_writer_trust_boundary.sql ДВЕ, а не одна:
-- BEGIN/END GENERATED PRODUCTSPEC INTAKE REVOKES (строки 24–164) и
… INTAKE GRANTS (358–498). Всё рукописное — два триггера-хранителя, проверка
character_maximum_length = 40, права на журнальные таблицы — живёт МЕЖДУ ними.
Склейка своей маской по префиксу маркера берёт первый BEGIN и последний END и
проглатывает середину: так рукописное терялось дважды. Склеивать только функциями
самого генератора — extract_generated_block и extract_generated_revoke_block.
Порядок: дописать таблицы в словарь (брать из product_spec_tables(), не руками),
пересчитать count и sha256, перепородить обе области, обновить намеренный рубеж в
tests/test_intake_writer_trust_boundary.py (сейчас 150) с причиной роста.
5б. Манифест входов ядра считает дочернюю таблицу приёма забытым входом. Решается
полем intake_only: рядом с tables: в specs/kernel/build-all-inputs.yaml. Пропуск
даёт по 90 падений в build_input_manifest и build_input_contract.
5в. Снимков схемы ДВА, и функциональный — не тот, что в документации.
docs/collectors/SCHEMA-SNAPSHOT-2026-07-13.sql читает разбор выводов и отказывает
колонке, которой в снимке нет. Дата в имени файла ложная.
6. Порядок работы, который сложился — повторять его
- Замер снимает руководитель. У исполнителя нет ни базы, ни серверов; требовать от
него замера — сделать задание невыполнимым. Замеры кладутся файлом в
docs/agents/. - Колонки заводит порождающий скрипт, а не рой:
marketplace-collector-v3/scripts/generate_leaf_columns.py. Имя из ключа площадки, тип из живых значений. Рою остаётсяjson_path, устройство разворота и тесты. - Приёмка машинная:
scripts/agents/accept_swarm_package.sh <клон>. - Убийство теста своими руками — ломать добавленные
json_pathи требовать красноты. Отчёту исполнителя об этом не верить: 09.08 отчёт был, проверки не было. - Перенос по списку путей:
scripts/agents/port_swarm_package.sh <клон> --apply. Список нарочно не пускаетkernel/etl. - Полный прогон набора обязателен и приёмкой не заменяется. Он и только он поймал три попадания поля площадки в первичный ключ и рост падений 67 → 166.
- Миграции применяются ОТДЕЛЬНО от выкладки — обязательно с
mysql --default-character-set=utf8mb4. - После применения — снова сторож. Число обязано измениться в ожидаемую сторону.
7. Грабли, каждая из которых стоила часов
- Сторож не запускался против живой базы — MySQL 8 отдаёт метаданные
information_schemaв ВЕРХНЕМ регистре, а код бралrow["table_name"]. Все числа за день были сняты посторонними скриптами и оказались неверны. - Вторая ступень роя не могла писать. ZCode при
--mode buildотвечаетNo permission client configuredна Edit/Write/Bash. Обёртка переведена на--mode edit. - Предел имени колонки в MySQL — 64 СИМВОЛА. Два заголовка отчёта Яндекса не влезают.
- Кодировка клиента: без
--default-character-set=utf8mb4кириллические имена калечатся, и такую колонку не снести обычным DROP — нуженALGORITHM=COPY. - Миграция без
USE <база>падает с «No database selected», хотя в тестах читается. - Имена служебных колонок не занимать НИКОГДА:
idу таблицы — автоинкрементный первичный ключ. Поле площадки с именемidобъявляется черезrenamesквалифицированной формойтаблица.колонка; неквалифицированная работает только для основной таблицы блоба. - Пустой ключ в YAML читается как
None. Уym_returns.return_jsonподrenames:остался только комментарий — листidснова стал резолвиться в первичный ключ. - Объявления сторожу править ТЕКСТОМ. Перезапись YAML целиком стёрла 130 строк объяснений; спас только git.
- Клон устаревает, пока работа идёт. Заплата с
UU— это стоп, а не «почти вышло». - Прогон в чужом клоне снимать, когда он ЗАКОНЧИЛ. Один раз снял на дописывающемся клоне и получил зелёное там, где было падение.
Всё это правилами 44–53 в ../agents/RULES.md — файл вклеивается в каждое задание роя.
8. Выкладка и автономность сбора
Сбор сырых JSON автономен — требование владельца, повторённое трижды. Сторож
ops/s3/gwptd-collector-watchdog (системный таймер, пять минут) возвращает расписание,
если оно исчезло; проверен сносом расписания. Гасится только идущей выкладкой или свежей
распиской. Установка — ../../ops/s3/README.md.
Миграции выведены из пути выкладки 09.08: с тринадцатью миграциями она перестала укладываться в свой секундомер, обрывалась и оставляла пустой crontab.
Переиндексация справки (mcp-gwptd) выключена — таймер снят с автозапуска, алертов
нет. Агент ищет прямым поиском по дереву.
Выкладка: ssh s3-int /usr/local/sbin/gwptd-next-deploy --expected-sha <40-hex>, вывод в
файл, не через трубу. Проверять СОСТОЯНИЕМ: SHA, число строк в crontab, живость
контейнеров, отклик порта 8009. Код возврата ssh ничего не доказывает.
Волна на s3 сейчас НЕ выложена намеренно: набор тестов красный (67 падений), а деплой при красном наборе — то же «утверждение сильнее данных», за которое отклонён пакет 176.
9. Смотреть базу глазами
Adminer на http://10.0.1.7:8010 (внутренний адрес, админский VPN), учётная запись
gwptd_viewer — только чтение, вся база, все площадки, без масок. Контейнер
gwptd-adminer. Право просмотра выдают суперадминистраторы через админку.
⚠️ ga_raw — это Золотое яблоко (goldapple.snapshot), а не Google Analytics. Имя
таблицы читается как «ga», и я весь день называл её неверно. Переименование — отдельное
решение владельца.
10. Решения владельца — закрыты, не переоткрывать
- Однородный массив значений (каналы продаж, ссылки на фото, номера складов) — ОДНОЙ колонкой, не дочерней таблицей. «Пока не понятно, нужно это или нет». Обратимо: сырьё хранится целиком. Дочерняя таблица — только для массива ОБЪЕКТОВ.
- Наблюдения не выбрасываются никогда, выбрасывать можно производное и дубликаты — ../domain/PRINCIP-NAKOPLENIYA.md.
- Ежедневный срез хранится периодом неизменности, а не строкой на день — ../domain/PRAVILO-IZMENENIYA-VMESTO-POVTOROV.md.
- Просмотр базы — Adminer как есть, только чтение.
- Суперадминистраторов-людей двое: Антон и Лиза Ноздрины. Запись
AgentTest(agent@gwptd.com) — НЕ мусор, она нужна агенту; не сносить. - Рейтинг считается по цене площадки. Отгрузку и рейтинг в этой работе не трогать.
- Секреты не ротируем — вопрос закрыт.
11. Что делать дальше по порядку
- Принять 172 и 175 по §6 (приёмка, убийство, перенос, полный прогон).
- Принять 177; мера — падений в наборе не больше 67 и ни одного во вложенном разборе.
- После 177 запустить отложенные 173 и 174 (
<scratchpad>/queue-17*.md). - Завести
imagesвym_returns_items_decisionsодной колонкой (§3, четвёртый блоб). - Довести набор до нуля падений, только после этого выложить волну на s3 и проверить СОСТОЯНИЕМ (§8).
- Снять сторожем итог: остаток обязан стать 0 при непустом списке долга = только осознанные исключения.
- Отдельной работой — чистая база
mp_hyp_ru(создана пустой локально,127.0.0.1:33061): схема порождается из 82 описаний, наблюдения переносятся все, производное строится заново.