#!/usr/bin/env bash
# accept_swarm_package.sh <путь-к-клону> — машинная приёмка работы роя.
#
# Проверяет шесть классов дефектов, на которых 08.08.2026 подряд отклонены шесть
# работ. Ничего не переносит и ничего не меняет: только считает и печатает.
# Перенос — scripts/agents/port_swarm_package.sh, и только после этой проверки.
#
# Шестой класс — двойники — машинно не проверяется: увидеть, что `nmId` у нас уже
# лежит как `nm_id`, можно лишь сверив смысл. Для этого в задании приложен список
# существующих колонок таблицы (docs/agents/zamer-listya-*/).
set -u
W="${1:-}"
[ -d "$W" ] || { echo "usage: accept_swarm_package.sh <путь-к-клону>"; exit 2; }
cd "$W" || exit 2
files=$(git status --short | grep -v 'docs/agents/runs\|\.task\.md\|_sim_guard' | awk '{print $2}')

echo "=== $(basename "$W")"
echo "--- изменённые файлы"
echo "$files" | sed 's/^/    /'
specs=$(echo "$files" | grep '^specs/products/' || true)
tests=$(echo "$files" | grep 'tests/' || true)
migs=$(echo "$files" | grep 'schemas/migrations/' || true)
# Исполняемое в пакете. Нужно, чтобы отличить волну по документации от волны по
# коду: с первой тесты спрашивать не за что.
kod=$(echo "$files" | grep -E '\.(py|sh|php|js|ts)$' || true)
fail=0
say() { echo "  $1"; }
# 1. блоб заменён блобом
if [ -n "$specs" ]; then
  # Сравниваем НАБОР имён блоб-колонок до и после. Диффом нельзя: перестройка
  # описания под intakeByKind переносит существующие блобы, и они выглядят как
  # добавленные, хотя это те же самые.
  hit=""
  for f in $specs; do
    before=$(git show HEAD:"$f" 2>/dev/null | grep -oE '[a-zA-Z_]+(_json|_payload):' | sort -u)
    after=$(grep -oE '[a-zA-Z_]+(_json|_payload):' "$f" | sort -u)
    added=$(comm -13 <(echo "$before") <(echo "$after"))
    [ -n "$added" ] && hit="$hit$f: $(echo $added | tr '\n' ' ')\n"
  done
  if [ -n "$hit" ]; then say "❌ 1 НОВЫЕ JSON-колонки в описании:"; printf "       $hit"; fail=1
  else say "✅ 1 новых JSON-колонок нет (существующие блобы сохранены как доказательство)"; fi
else say "⚠ 1 описания не менялись — проверить, откуда тогда возьмутся значения"; fi
# 2/3. схема и цели литералом в тесте
if [ -n "$tests" ]; then
  # Проверка про схему имеет смысл ТОЛЬКО когда работа сама создаёт колонки: тогда
  # тест обязан сверяться с настоящим SQL, а не со словарём из головы автора.
  # С 09.08.2026 колонки заводит порождающий скрипт руководителя, а снимок схемы
  # снимается с живой базы — выдумывать нечему, и требовать схему в тесте, где нет
  # миграции, значит отклонять работу за требование, которого больше нет.
  if [ -z "$migs" ]; then say "— 2 миграции в работе нет: схему сверять не с чем, проверка не применяется"
  elif grep -q 'columns_from_paths\|columns_from_sql' $tests 2>/dev/null; then say "✅ 2 схема берётся из SQL"
  else
    # Не блокирует, и вот почему. Смысл проверки — чтобы миграция и ожидания теста
    # не разошлись. С 09.08.2026 у руководителя есть доказательство сильнее: миграция
    # применяется к ЖИВОЙ базе, и сторож покрытия обязан показать ожидаемое падение
    # числа непокрытых листьев. Литерал в тесте при этом остаётся недостатком —
    # но отклонять из-за него работу, проверенную на живой базе, значит держать
    # требование выше доказательства.
    say "⚠ 2 схема в тесте литералом — принимать ТОЛЬКО после применения миграции к живой базе и падения числа у сторожа"
  fi
  # 4. тест гоняет значение через настоящий разбор
  #
  # Применимо ТОЛЬКО когда работа трогает описания: имена, которые здесь ищутся,
  # — это функции разбора. У волны, которая правит скрипт на bash, генератор
  # документации или сторожа, таких вызовов нет и быть не должно, а проверка
  # объявляла их отсутствие отказом.
  #
  # 14.08.2026 на этом застряла верная работа: скрипт сброса worktree,
  # проверенный настоящим гитом, тремя нацеленными мутациями и 61 зелёным
  # тестом, получил «ИТОГ: ЕСТЬ ОТКАЗЫ». Отклонять работу за отсутствие вызова,
  # которого в её классе не существует, — то самое невыполнимое требование, из-за
  # которого уходят в лазейку. Гейт по $specs, как у проверки 1.
  if [ -z "$specs" ]; then say "— 4 описания не менялись: настоящего разбора в работе нет, проверка не применяется"
  elif grep -q 'render_intake\|_write_target\|build_collector' $tests 2>/dev/null; then say "✅ 4 тест зовёт настоящий разбор"
  else say "❌ 4 тест НЕ гоняет значение через разбор — проверяет только имена"; fail=1; fi
elif [ -z "$kod" ]; then
  # Волна, изменившая ТОЛЬКО документацию, тестов не добавляет и не должна.
  # Её проверяют иначе: существуют ли пути из страницы, отрабатывают ли
  # приведённые команды, не появилось ли обрывов ссылок.
  #
  # 14.08.2026 на этом застряла волна W3 — две страницы описательного уровня,
  # каждая с прогонами в теле и с честным разделом «не установлено». Требовать
  # от неё pytest значит отклонять работу за требование, которого в её классе
  # не существует; рядом с таким требованием всегда находят лазейку.
  say "— 2/4 работа только по документации: тестов не требуется, проверка иная"
else say "❌ 2/4 тесты не добавлены"; fail=1; fi
# 5. манифест при новых таблицах ЯДРА
#
# ⚠️ ПОПРАВЛЕНО 15.08.2026 — проверка давала ЛОЖНЫЙ ОТКАЗ.
#
# Было два дефекта, и оба мои, а не исполнителя:
#
# 1. Манифеста требовали для ЛЮБОЙ новой таблицы. Но build-all-inputs.yaml —
#    это KernelBuildInputManifest, перечень входов СБОРКИ ЯДРА (dim_product,
#    fact_supplier_stock и подобные). Таблиц мониторинга в нём ноль, и вписывать
#    их туда неверно: сборка ядра их не читает.
# 2. Выражение обрывалось на точке и вытаскивало из
#    `gwptd_monitoring.defect_registry` имя СХЕМЫ вместо имени таблицы —
#    сообщение об отказе называло несуществующую таблицу «gwptd_monitoring».
#
# Цена: волна реестра дефектов 15.08 была отклонена приёмкой за то, что не
# вписала таблицу мониторинга в манифест ядра, куда её вписывать нельзя.
#
# Теперь манифест требуется только для таблиц ЯДРА: схема gwptd_kernel либо
# имя без схемы в миграции ядра. Мониторинг, приём и служебные схемы своего
# манифеста не имеют и в этот не входят.
newtab=$(grep -hoE 'CREATE TABLE (IF NOT EXISTS )?`?([a-z_]+`?\.`?)?[a-z_]+`?' $migs 2>/dev/null \
  | sed -E 's/.*CREATE TABLE (IF NOT EXISTS )?//; s/`//g' | sort -u || true)
kerneltab=$(printf '%s\n' "$newtab" | grep -vE '^(gwptd_monitoring|gwptd_intake|gwptd_raw|gwptd_stage|s2_ref|s20_ref)\.' | sed -E 's/^gwptd_kernel\.//' | grep -v '^$' || true)
if [ -n "$kerneltab" ]; then
  if git diff --stat -- specs/kernel/build-all-inputs.yaml | grep -q build-all-inputs; then say "✅ 5 манифест дописан (новые таблицы ядра: $(echo $kerneltab | tr '\n' ' '))"
  else say "❌ 5 новые таблицы ЯДРА есть, манифест НЕ дописан: $(echo $kerneltab | tr '\n' ' ')"; fail=1; fi
elif [ -n "$newtab" ]; then
  say "✅ 5 новые таблицы вне ядра ($(echo $newtab | tr '\n' ' ')) — манифест ядра им не нужен"
else say "✅ 5 новых таблиц нет — манифест не нужен"; fi
# 6. объявления сторожу
if git status --short specs/registry/json-leaf-targets.yaml | grep -q json-leaf; then say "✅ 6 объявления сторожу тронуты"
else say "⚠ 6 объявления сторожу не менялись (нормально, если renames не нужны)"; fi
echo "  ИТОГ: $([ $fail -eq 0 ] && echo 'машинные проверки пройдены — дальше убийство теста' || echo 'ЕСТЬ ОТКАЗЫ')"
