Канон знал 82 таблицы и 995 столбцов, а база — 121 и 1470: не описаны были
рекламный модуль, телеграм-рассылки и весь отдел продаж.
Главное, что выяснилось по дороге: db/schema.sql не документ, а исполняемый
файл — миграция 0001_01_01_000000_load_initial_schema заливает его целиком
первым шагом сборки. Перенести туда DDL модулей нельзя: те же таблицы создают
дельта-миграции, их 77 и 49 из них без стражей. Сборка с нуля падала дважды.
Поэтому канон разбит на два файла: schema.sql исполняется, новый
schema_modules.sql только описывает.
Проверено не глазами:
- сборка с нуля 148 из 148 DONE;
- столбцы 1470 против 1470, ноль расхождений по имени, типу, длине и NULL;
- все четыре счётчика расхождений инструмента — ноль;
- schema.sql отдельно исполняется без единой ошибки, ограничения 495=495,
политики 64=64;
- статанализ 0.
Инструмент сверки усилен: считал только одну сторону и потому не видел
переименования jivo_chat_id в chat_id — старое имя жило в каноне месяц.
Теперь обе стороны, канон из нескольких файлов, понимание RENAME/DROP COLUMN
и DROP TABLE. Починен разбор переносов строки внутри определения столбца.
Сторож SchemaDeltaTest дополнен: файл модулей обязан быть на месте и описывать
39 таблиц, а тело schema.sql их не содержать. Принят вырезанием.
Найдено и НЕ исправлено, нужно решение владельца: задвоенный указатель на
deals и пять политик без NULLIF в миграциях. Оба помечены в тексте канона.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Считать портал уже научился по московским суткам. Показывал он вразнобой:
часть страниц просила браузер явно про Москву, двадцать мест оставляли пояс
на его усмотрение. Человек за пределами Москвы видел у заявки не тот день,
что в отчёте, — при том что фильтры и отчёты считают московские сутки.
Двадцать мест поправлены: заявки, счета, проводки, сессии и смена пароля,
происшествия, запросы субъектов ПДн, ступени цен, интеграция с поставщиком,
проекты поставщика, система, посетители, вход под клиентом, кампании,
поле автоподбора, СМС и прогрев у отдела продаж, карточка клиента.
Одно место оставлено гринвичским НАМЕРЕННО и с объяснением рядом:
firstLeadDate собирает дату из уже московских чисел через Date.UTC, и
повторный перевод сдвинул бы день назад.
Сторож tools/storozh-moskovskogo-vremeni.mjs держит правило дальше: любая
печать времени обязана НАЗВАТЬ пояс. Он отличает время от денег - тот же
вызов toLocaleString печатает и суммы, им пояс не нужен. Приёмка вырезанием
в обе стороны: до правки сторож нашёл ровно 20 мест, после - ноль при 303
проверенных файлах; на пустой папке честно говорит "датчик сломан", а не "чисто".
Своих проверок у сторожа десять, написаны до него.
Прогон интерфейса: 232 файла проверок, 1743 зелёных, 0 падений.
Проверка типов и разметка: 7 замечаний, все были ДО правки и не в моих файлах
(проверено прятанием своих правок и повторным прогоном).
Заодно выметен мусор в корне: файлы с именами 1', 140, 2771, 3042, 3404,
out.txt и app/toArray-скобки - обрывки чужих незакавыченных перенаправлений.
Заодно убрана мёртвая функция dirLabel в экране автоподбора: её никто не звал,
она была там до этой правки, и линтер из-за неё не пропускал весь файл.
Решение владельца. Прогон экрана: 15 файлов проверок, 84 зелёных.
Тест, который пишет в базу и не откатывает за собой, сам остаётся зелёным,
а роняет СОСЕДЕЙ — и обвиняют всегда не того. Так 01.08 девятнадцать чужих
проводок уронили денежную проверку приёмника МТС: искали в приёмнике, а
виновата была рекламная папка рядом.
tools/storozh-chistoty-bazy.mjs спрашивает базу СНАРУЖИ после прогона и
показывает, что осталось. База пересобирается в начале каждого прогона,
поэтому остаток после прогона — вина ровно этого прогона. Минута вместо
двадцати пяти на перебор файлов, и виновная таблица видна поимённо.
Написан по порядку: сперва 11 проверок (краснели, пока кода не было), потом код.
Две вещи сказаны вслух, а не спрятаны:
- пустой ответ базы — это ОТКАЗ, а не успех. Сторож печатает «проверено
таблиц: N» и падает, если N нулевое. На этом проекте трижды обжигались
о сторожей, которые показывали «проверено 0» и это читалось как успех;
- слепое пятно названо прямо в коде. Справочники, которые кладутся при
сборке базы, мусором не считаются — значит лишнюю запись ВНУТРИ самого
справочника сторож пропустит. Поэтому он их показывает списком, чтобы
дрейф был виден человеку.
Запуск после прогона:
node tools/storozh-chistoty-bazy.mjs liderra_testing_prospects
Код возврата: 0 чисто, 2 есть остаток, 1 датчик не сработал.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Сторож требовал, чтобы каждый включённый плагин был поимённо описан в реестре
Прил. Н. ruflo описан там ГРУППОВЫМ именем - одна подсистема, ~20 plugins,
та же модель, что у набора Trail of Bits выше по файлу. Сторож видел только
машинный ключ ruflo-core@ruflo и не мог связать его с групповой записью.
Для ровно таких случаев файл псевдонимов и заведён.
Строки взяты один в один из ветки клиентских СМС, где то же лечение уже
применено 01.08 - чтобы при сведении веток не возникло расхождения.
Приёмка вырезанием, БЕЗ правки чужого settings.json: пробником вызвал
проверяющую функцию сторожа на настоящих данных проекта и подложил в них
выдуманный плагин.
- как есть: ноль расхождений;
- выдуманный плагин: пойман;
- плагин с ТЕМ ЖЕ именем, но из другого источника: тоже пойман.
Третья проверка - главная: она показывает, что псевдоним накрыл ровно один
машинный ключ, а не расширил разрешение на похожие имена.
🔴 Содержательный долг НЕ закрыт и вынесен комментарием прямо в файл:
реестр до сих пор объявляет ruflo «ИЗОЛИРОВАН (dormant) 18.05.2026», хотя
28.07.2026 подсистему разморозили и она включена. Этого расхождения сторож
не проверяет вовсе - чинить через claude-md-management, отдельной работой.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Долг канона db/schema.sql жил в промтах числом "28 столбцов и 3 таблицы".
Число ЗАНИЖЕНО: оно получено выборочной проверкой нескольких таблиц.
Сплошной замер даёт другую картину.
Канон знает 82 таблицы и 997 столбцов. В базе есть и в каноне отсутствуют
50 таблиц и 8 столбцов. Из 50 таблиц 39 - настоящий долг этой ветки (их
миграция лежит в app/database/migrations), а 11 принесла в местную базу
ветка СМС - их в каноне и не должно быть, пока ветка не сведена.
Отсюда вторая граблина, помимо известной "имя миграции не равно именам
столбцов": по одной живой базе считать тоже нельзя. Местная база копит
таблицы ВСЕХ веток, что на ней гоняли, и без отсечки по наличию миграции
долг завышается на четверть.
Инструмент tools/sverka-kanona-shemy.mjs отвечает на вопрос числом и
поимённо за одну команду. 12 тестов, написаны ДО кода - и это себя
оправдало сразу: черновик разборщика соврал дважды подряд. Сперва он
проваливался в первую же таблицу и не выходил из неё, дав "1 таблица в
каноне" вместо 82. Потом не отличал объявление помесячного куска от
настоящей таблицы. Оба случая закрыты отдельными тестами, оба видел
красными.
Приёмка вырезанием в обе стороны: при наличии долга инструмент возвращает
код 2, на паре без долга - 0.
Закрыт заодно хвост И: четыре обломка убраны из корня в папку мусора
сессии, не удалены. get() оказался НЕ пустым - 69 байт вывода хука,
"0 байт" в прежней записи было неверно.
Промт следующей смене выправлен: пункт А закрыт чужими руками, числа
отставания веток перемерены, вписано решение владельца пока не переводить
общую ветку.
Проверено: тесты инструментов 91 из 91, разметка 0, орфография 0.
NB про орфографию: папка docs/superpowers лежит в исключениях cspell,
поэтому промт сторож орфографии НЕ смотрит вовсе - вчерашнее "орфография
ноль" на нём ничего не доказывало. Ноль выше относится к коду инструмента
и к общему списку слов.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Мина из промта подтвердилась, но диагноз вчерашней смены оказался неполным,
а способ её доказательства - неверным. Разбор по замерам.
Что на самом деле убивает статанализ молча - код возврата 1 и ни строки
ни в обычном выводе, ни в канале ошибок:
1. нет файла, перечисленного в scanFiles. Подсказчик _ide_helper_models.php
собирается на месте, в git его нет, на чистой копии проекта его не будет.
2. метка BOM в начале phpstan.neon. Её ставит Set-Content -Encoding utf8
в Windows PowerShell 5.1 - то есть любой, кто правит настройку скриптом,
а не редактором, убивает анализатор. Вчерашнее "доказательство" мины было
именно этим: временную настройку записали с меткой, молчание списали на
отсутствующий файл. Причина была другая, вывод совпал случайно.
Третий случай молча не падает, а врёт убедительнее: подсказчик, собранный
без ключа -M, объявляет ПУСТЫЕ копии поверх настоящих моделей. И четвёртый:
подсказчик просто устарел - не знает моделей, появившихся после сборки.
Замер: старый файл знал 52 модели, свежий - 79. Ровно отсюда расхождение
"1865 замечаний в одной рабочей папке против 42 в другой" на одном коде.
Сделано:
- tools/storozh-statanaliza.mjs - сторож на все четыре случая. Называет
причину человеческим языком и команду починки. Код возврата 2.
- tools/storozh-statanaliza.test.mjs - 13 тестов, писались до сторожа.
- сторож встроен в composer stan и в хук перед larastan через && , так что
при его срабатывании phpstan не запускается вовсе и не путает человека.
- fail_text у larastan переписан: сначала прочитай, не сказал ли сторож,
что ошибок в твоём коде нет.
- composer setup и composer ide-helper теперь собирают подсказчик правильным
ключом. Раньше его нельзя было получить НИ ОДНОЙ описанной командой.
- из baseline убрана устаревшая запись про saas_transactions.credit_target:
столбец настоящий, миграция от 25.07, свежий подсказчик его знает.
- reportUnmatchedIgnoredErrors выключен с объяснением: записи baseline не
должны краснеть оттого, что на этой машине подсказчик полнее или беднее.
Датчик свежести намеренно по СОДЕРЖИМОМУ, а не по времени файла. Сначала
сделал по времени - и он тут же закричал "устарел" на неизменившемся коде,
потому что время правки обновляет любая операция git. Заменено на сверку
списка моделей со списком классов-спутников.
Приёмка вырезанием, каждая половина проверена:
- убрал подсказчик -> сторож назвал причину, код 2, phpstan не запускался;
- поставил метку BOM -> назвал её и способ записи без метки;
- подложил подсказчик без -M -> назвал подмену моделей;
- положил старый файл на 52 модели -> перечислил 27 недостающих поимённо;
- вернул всё -> код 0, статанализ ноль замечаний;
- подложил настоящие поломки в tests -> обе пойманы, значит выключение
reportUnmatchedIgnoredErrors анализатор не ослепило.
Побочно найдено, НЕ закрыто: столбца credit_target нет в db/schema.sql,
хотя миграции на него есть и в CHANGELOG_schema он упомянут. Канон схемы
разошёлся с миграциями - отдельная работа.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Работа 1 промта от 01.08 — оживление сторожей текста. По ходу вскрылся
отдельный дефект в нашем собственном скрипте.
observer-coverage-checker при коммите из рабочей папки ветки печатал
".git/hooks/post-commit not installed". Файл при этом лежал на месте.
Причина: в рабочей папке ветки .git не папка, а файл-указатель, и проверка
пути <корень>/.git/hooks/post-commit смотрела мимо цели. Плюс настройка
core.hooksPath, которая в этом репозитории задана, вообще не учитывалась.
Класс беды тот же, что уже дважды записан в память: сторож, не умеющий
отличить "нет" от "лежит в другом месте", штампует ложные тревоги. Рядом
в той же строке живёт правдивая жалоба про неподключённый обработчик - и
ложная половина приучает не читать всю строку целиком.
Что сделано:
- добавлена postCommitInstalled: сначала ищет общую папку репозитория
(для обычной копии это сам .git, для рабочей папки ветки - по указателю
gitdir и файлу commondir рядом с ним), затем учитывает core.hooksPath
из config и только потом смотрит наличие файла;
- чистое чтение файлов, без запуска сторонних команд - требование
Security Guidance 40 для этого скрипта соблюдено.
Порядок работы соблюдён. Пять проверок написаны ДО правки и покраснели
на отсутствующей функции. Разобраны обе стороны каждой развилки: обычная
копия с хуком и без, рабочая папка ветки с хуком и без, перенос хуков
настройкой. Живой прогон по настоящему репозиторию: из рабочей папки
ветки ложная половина ушла, правдивая осталась.
Прогоны: набор проверок инструментов 14 файлов, 66 проверок, все зелёные.
Отдельно, без правок в git: node_modules был в состоянии оборванной
установки - 121 временная папка распаковки, 90 пакетов без главного файла,
у tinyglobby файл обрезан посреди строки. Причина обрыва - падение сборки
better-sqlite3. Пересобрано начисто из списка версий без сборочных
сценариев; package.json и package-lock.json не тронуты, проверено хешем.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Два дефекта, внесённых предыдущим коммитом, оба мои:
1. cspell-words.txt — в словарь вместе с четырьмя новыми словами попали
служебные строки вывода хука: "[INFO] Recording command outcome: cd" и
"[OK] Command outcome recorded", плюс лишняя пустая строка. Причина —
дописывание словаря через printf с перенаправлением в конец файла:
вывод хука приклеился к тому же файлу. Строки удалены, словарь проверен —
cspell на изменённых файлах даёт 0 ошибок.
2. tools/observer-chain-map.json — файл был переписан скриптом через
json.dumps с отступом 2, из-за чего компактные однострочные массивы
развернулись в многострочные: диф раздулся до 180 добавленных и 56
удалённых строк вместо одной строки по существу. Восстановлено исходное
компактное оформление, узел grilling вставлен строкой после brainstorming.
Чистый итог обоих файлов против состояния до работы над #90 — четыре слова
в словаре и одна строка в карте цепочек. Проверки: observer-chain-map-checker
OK 17 chains in sync, cspell 0 ошибок.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Зарегистрирован вендоренный скил grilling из mattpocock/skills, MIT,
skills/productivity/grilling. Установлен user-level ~/.claude/skills/grilling/
— вне репозитория, единственная копия: проектная удалена во избежание
задвоения.
Роль — безжалостный допрос по УЖЕ имеющемуся плану или решению: обход дерева
развилок ветка за веткой, по одному вопросу за раз, к каждому вопросу свой
рекомендуемый ответ, факты ищутся самостоятельно, работа не начинается до
явного подтверждения заказчика.
Надстройка проекта поверх немодифицированного тела апстрима, отделена
заголовком:
- протокол docs/grilling/ГГГГ-ММ-ДД-тема.md — разделы Решили / Отрезали и
почему / Осталось открытым; пишется по ходу, перечитывается после компакта
контекста. Закрывает класс «договорённость сгорела при компакте»
- порядок обхода «сначала необратимое» — деньги, схема БД, что уходит клиенту
- критерий остановки: пустой фронт развилок с объявлением вслух
- «слушай, не защищай»
Граница ADR-021 GR1 с #55 discovery-interview — разрез по наличию решения:
grilling куёт решение, которое у заказчика уже есть, поэтому наводящий
рекомендуемый ответ обязателен; discovery-interview вскрывает проблему, когда
решения ещё нет, и там наводящие ответы запрещены. GR2 — граница с
brainstorming #19. GR3 — вендоринг без модификации апстрима.
Реестр: узел #90 + контракт, связка L1 между brainstorming и writing-plans,
классификация planning вес 0.8 — ниже первичных решателей. Автотаблицы
перегенерированы через registry-render, карта цепочек дополнена.
Нормативная синхронизация квинтета:
- Tooling Прил. Н v2.26 — новый §4.63, счётчик 87→88 и 107→108, off-phase
+57→+58, футер
- Pravila v1.45 — §13.2 новый абзац, запись в истории версий
- PSR_v1 v3.25 — R10.1 Блок 1 note, запись в истории версий
- CLAUDE.md v2.49 — через плагин claude-md-management, §0 версии квинтета,
§3.4 +#90, §9 запись
Попутно снят застарелый рассинхрон шапки Tooling: числа 84/104 остались от
майской версии, приведены к 88/108, дописаны research-tooling и узлы #87–#90.
Проверки: registry-render --check зелёный на обоих файлах,
observer-chain-map-checker OK 17 chains in sync, загрузчик видит 90 узлов,
markdownlint 0 ошибок, cspell 0 ошибок.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
floor-desync: supreme-gate Δ7 вето-без-сдвига смотрел только classifyDestructive.floor (rm-rf/force-push/migrate), а enforce-floor блокирует шире — content-block правило 8 (node -e/curl/eval), PowerShell, запись в runtime/секрет. Floor-блокируемый-не-destructive шаг (node -e) проскакивал со СДВИГОМ указателя, пол рубил исполнение → шаг терялся безвозвратно (desync, потеря safety-шага). Δ7 расширен на полный предикат floorDecide (пустой escape; escape обрабатывается в decideMode до decide). Order-independent. TDD RED→GREEN, регрессия tools-only 3930 passed + 2 skipped.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Судья/наставник по большой спеке/плану отвечают 25-32с; дефолт callAnthropicAPI 30с давал таймаут→degraded→печать не вставала (спека не запечатывалась gate1 → план не мог встать gate2). HEAVY_LLM_TIMEOUT_MS=90с в router-config, проброшен в callJudgeModel (судья) и buildLlmCall (наставник). TDD RED→GREEN, регрессия tools-only 3928 passed + 2 skipped.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Live-смоук: PostToolUse не запускается на exit≠0 → Post не двигает указатель на RED-шагах. Код возвращён к Pre-advance (3928 GREEN). Спека/план помечены ОТВЕРГНУТО. Настоящий фикс desync = перестановка skill-discipline перед supreme-gate.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Новый enforce-mentor-then-judge.mjs запускает наставника дочерним процессом до конца, потом судью (свежий mentor-GO/вердикт) - убирает гонку параллельных PostToolUse-хуков. Машины enforce-mentor-on-plan-write/enforce-judge-gate байт-в-байт не тронуты. Зарегистрирован в settings.json. TDD +5 тестов.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>