Перейти к основному содержимому

Передача 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)
HEAD64965a24, дерево чистое, всё выложено в 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. Порядок работы, который сложился — повторять его

  1. Замер снимает руководитель. У исполнителя нет ни базы, ни серверов; требовать от него замера — сделать задание невыполнимым. Замеры кладутся файлом в docs/agents/.
  2. Колонки заводит порождающий скрипт, а не рой: marketplace-collector-v3/scripts/generate_leaf_columns.py. Имя из ключа площадки, тип из живых значений. Рою остаётся json_path, устройство разворота и тесты.
  3. Приёмка машинная: scripts/agents/accept_swarm_package.sh <клон>.
  4. Убийство теста своими руками — ломать добавленные json_path и требовать красноты. Отчёту исполнителя об этом не верить: 09.08 отчёт был, проверки не было.
  5. Перенос по списку путей: scripts/agents/port_swarm_package.sh <клон> --apply. Список нарочно не пускает kernel/etl.
  6. Полный прогон набора обязателен и приёмкой не заменяется. Он и только он поймал три попадания поля площадки в первичный ключ и рост падений 67 → 166.
  7. Миграции применяются ОТДЕЛЬНО от выкладки — обязательно с mysql --default-character-set=utf8mb4.
  8. После применения — снова сторож. Число обязано измениться в ожидаемую сторону.

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. Что делать дальше по порядку

  1. Принять 172 и 175 по §6 (приёмка, убийство, перенос, полный прогон).
  2. Принять 177; мера — падений в наборе не больше 67 и ни одного во вложенном разборе.
  3. После 177 запустить отложенные 173 и 174 (<scratchpad>/queue-17*.md).
  4. Завести images в ym_returns_items_decisions одной колонкой (§3, четвёртый блоб).
  5. Довести набор до нуля падений, только после этого выложить волну на s3 и проверить СОСТОЯНИЕМ (§8).
  6. Снять сторожем итог: остаток обязан стать 0 при непустом списке долга = только осознанные исключения.
  7. Отдельной работой — чистая база mp_hyp_ru (создана пустой локально, 127.0.0.1:33061): схема порождается из 82 описаний, наблюдения переносятся все, производное строится заново.