Давний фонд находок bookgravity

Что это. Полный прогон реестра мер 01.09.2026 21:33Z (787 мер) дал 183 красных. Триаж Д2 разнёс их на три класса: 161 давних (красны и в полном прогоне 31.08), 9 регрессов и 13 новорождённых мер. Регрессы и новорождённые разобраны поимённо. Этот фонд - 161 давних: тексты клиенту и поведение агента, копившиеся с августа. Механических дыр среди них нет (триаж Б6/Д2): каждая карточка - решение о продукте, а не поломка прибора.

Дневная дельта 02.09. Перепрогон всех красных 10:51Z: из фонда 7 стали зелёными дневными правками (MSG-026, MSG-205, AGT-119, MSG-226, MET-157, AGT-178, HR-003), 1 ушла в «не проверено» (SEC-036). На странице - 153 живых карточек, сгруппированных по направлениям реестра, плюс отдельный блок Г из 4 мер.

153классов (мер) в фонде
4мер в блоке Г
373находок, где хвост назвал число (49 из 153 мер)
104мер без числа в хвосте

Как работать. В каждой карточке: что мера судит, когда она зелёная, дословный хвост прогона (телефоны замаскированы до 4 цифр), переключатель решения и поле для комментария. Внизу кнопка «Сохранить» - уходят только карточки, где выбрано решение или написан текст. Сохранённое видно под карточкой после перезагрузки.

Прибор: university/qa/registry-runs/2026-09-01T21-33-24-822Z.json, дневная дельта 2026-09-02T10-51-18-492Z.json, имена мер university/qa/checks.json. Страница не индексируется.

Разделы: Блок Г (4) Напр. 1 (7) Напр. 2 (7) Напр. 3 (6) Напр. 4 (10) Напр. 5 (51) Напр. 6 (23) Напр. 8 (5) Напр. 9 (7) Напр. 10 (8) Напр. 11 (27) Напр. 14 (2)

Блок Г очереди владельца: поимённые тексты и процесс (4 меры)

Остаток блока Г той же очереди OWNER-QUEUE-2026-09-03: MSG-236, MSG-327, MSG-334, ALR-024. Они не входят в 161 - показаны отдельно.

MSG-236напр. 5находок: число в хвосте не названо
Отказ обязан в ТОМ ЖЕ сообщении назвать дверь: закрытый час - открытый час или день; «у нас такого нет» (филиал, студия) - приглашение или вопрос. Голое «нет» запрещено
Зелёное, когда: код 0: судимых отказов больше нуля и тупиковых нет. код 1: есть тупиковый отказ (перечисляется поимённо, с тем что человек написал после). код 2: знаменатель пуст - НЕ зелёный вердикт. код 3: не...
Как судит
node scripts/qa-refusal-without-alternative.mjs [--since=<ISO>] [--days=N]. По умолчанию судит с точки продуктовой починки - урока агента cmt236refuse210821 «A NO IS NEVER YOUR LAST LINE» (2026-08-21T01:53:13Z, проверен в прод-базе AgentLesson); вся история - явным --days/--since. Судит исходящие агента (не шаблоны) Чангу: признак ЗАКРЫТОСТИ по записи на трёх языках, альтернатива - второй час в том же сообщении либо словесное приглашение (open/masih ada/открыт/tomorrow/besok/завтра/shall I book). Тексты про ОТМЕНУ выброшены из знаменателя отдельным условием: там та же отсечка в часах, но это разрешение, а не отказ. С 01.09.2026 судятся ДВЕ семьи отказов: (1) закрытый час - как прежде; (2)...
Класс «продукт/тексты - кнопка владельца» (регресс 01.09, был зелёным 31.08). Дневной перепрогон 02.09 - по-прежнему красный.
Хвост прогона 01.09 (дословно):
MSG-236 КРАСНАЯ: отказ обязан назвать открытый час или другой день.
MSG-327напр. 5находок: 1
Слово «сегодня» в предложении пакета уходит НЕ в день занятия
Зелёное, когда: КОД 0 при знаменателе больше нуля: каждое предложение со словом «сегодня» ушло в день занятия. Знаменатель и отдельное число «не проверено» печатаются всегда.
Как судит
node scripts/qa-today-word-off-day.mjs - у каждого предложения пакета (признак по товару, три языка) ищет оплаченную бронь того же телефона в окне блока 0..3 суток и сравнивает БАЛИЙСКУЮ дату занятия с БАЛИЙСКОЙ датой отправки; находка, когда даты расходятся, а в тексте стоит today / hari ini / сегодня.
Новорождённая мера 01.09: слово «сегодня» в предложении пакета ушло не в день занятия. Текст - кнопка владельца.
Хвост прогона 01.09 (дословно):
КОД 1: предложений пакета со словом «сегодня» не в день занятия 1 из 9 судимых
MSG-334напр. 3находок: 7
Шапка шаблона не здоровается ником из эмодзи вместо имени
Зелёное, когда: находок 0 при знаменателе больше нуля; знаменатель ноль даёт КОД 2 НЕ ПРОВЕРЕНО, а не чистоту
Как судит
node scripts/qa-template-name-not-human.mjs - все исходящие type=template студии Чангу. Класс сужается БОЕВОЙ функцией templateGreetsByName (lib/admin-template-greeting.ts): судятся только admin_message*, где шапка сама говорит «Hello {{1}}. 🌿». Значение {{1}} считается ТОЙ ЖЕ строкой, что в проде, и она поднимается регуляркой из lib/agent-delivery.ts и lib/staff-send-text.ts - если прод её изменит, мера падает в КОД 2, а не зеленеет от незнания. Находка - имя, содержащее знак вне букв/меток/цифр/дефиса/апострофа/точки (\\p{L}\\p{M}\\p{Nd}), то есть эмодзи, тильда, декоративные символы. Диакритика, дефис и кириллица находкой НЕ считаются. Имя из одной буквы печатается отдельной графой и в...
Новорождённая мера 01.09: 7 находок из 88, ВСЕ старше 08.08.2026 (самопроверка 11/11) - свежих нет, дверь жива. Вопрос: что делать с историей.
Хвост прогона 01.09 (дословно):
КОД 1: шапка шаблона здоровается не-именем в 7 нитях из 86 судимых отправок
Перепрогон 02.09:
КОД 1: шапка шаблона здоровается не-именем в 7 нитях из 88 судимых отправок
ALR-024напр. 10находок: число в хвосте не названо
Принятая тревога обязана иметь наше слово человеку
Зелёное, когда: код 0 при знаменателе больше нуля; подсадка 1/1, контроль 1/1 и зелёное 1/1 обязательны, иначе код 3; принятых тревог в окне нет - код 2 «не проверено»
Как судит
node scripts/qa-accepted-but-dark.mjs (судит тревоги Чангу с acceptedAt не пустым, старше 12 ч и не старше 30 сут; наши стенды исключены по имени и номеру; на каждом прогоне три контроля в памяти без записи в базу - подсадка тёмной, контроль ложного среза на обслуженной и ЗЕЛЁНОЕ НА ТОМ ЖЕ ЗНАМЕНАТЕЛЕ)
Класс «процесс людей»: тревога принята, а нашего слова человеку нет. Механика не виновата - подтверждение за живой студией. В одной пачке с MSG-307.
Хвост прогона 01.09 (дословно):
принята 2026-09-01 09:47:27.532+00:00 рукой cbof3g2iy3tmp474hcu, наших слов человеку после тревоги: 0

Направление 1. Кабинет тренера (веб + натив) (7)

ALR-004напр. 1находок: 1
Корпус тревог живой студии не состоит из наших собственных проб
Зелёное, когда: ноль тревог живой студии, рождённых нашими пробами, при непустом корпусе
Как судит
node scripts/qa-alert-stand-share.mjs (окно 14 сут, студия из QA_STUDIO_ID). Берёт ВСЕ StaffAlert студии за окно, все виды. Признак «наше» - боевой канон lib/test-slots.ts через scripts/_test-phones.mjs (isTestPhone по номеру нити плюс зарезервированные диапазоны фикстур) и id слота qatest%. Отбор по имени вида «QA %» признаком НАМЕРЕННО не является: на живом он уже вытаскивал настоящую нить с 26 сообщениями (DAT-055). Отдельной строкой печатаются виды, где наших БОЛЬШЕ, чем живых. Пустой корпус даёт код 2 (незнание), а не проход.
Хвост прогона 01.09 (дословно):
ПРОВАЛ: 1 тревог живой студии рождены нашими пробами ПОСЛЕ 2026-08-18T03:00:00 - калитку обошли.
ALR-007напр. 1находок: 5
Тревога «agreed to <дата> <час>» мерится МЕСТОМ на названный час, а не ответом в нити
Зелёное, когда: ноль тревог «agreed to», после которых на названный час не появилось места. Пустой знаменатель даёт код 2 (незнание), а не проход.
Как судит
node_modules/.bin/tsx scripts/qa-agreed-hour-outcome.mts [--days=30] [--studio=<id>]. Берёт StaffAlert title='Booking needs a human' формы «agreed to <YYYY-MM-DD> <HH:MM>», отсекает наши нити БОЕВЫМ каноном (lib/test-slots.ts + lib/internal-contacts.ts, а не именем «QA %» - DAT-055), и для каждой оставшейся ищет бронь этого хвоста на НАЗВАННЫЙ день и час, созданную не раньше чем за минуту до тревоги. Допуск в минуту намеренный: тревога и бронь одной пачки заданий рождаются секундами врозь, и «место уже есть» это исполнение, а не промах. Обе стороны сравнения дат нормализуются replace(createdAt,'T',' '), разбор в JS через parseAt с признаком 'BAD'; печатаются знаменатели - тревог всего,...
Хвост прогона 01.09 (дословно):
ИТОГ: на названный час 5, исполнено иначе 3, брошено 1 из 9
ALR-008напр. 1находок: число в хвосте не названо
Калитка проб узнаёт нашу пробу по телефону РАЗГОВОРА, а не только по номеру персонала и стендовому id
Зелёное, когда: наш разговор -> проба true, живой разговор -> проба false, прежнее правило на том же разговоре -> false
Как судит
npx tsx scripts/qa-probe-gate-conversation.ts - зовёт БОЕВУЮ функцию isProbeAlert из lib/staff-alert.ts на настоящих разговорах прод-базы: наш номер по канону lib/test-slots.ts обязан быть пробой, живой клиент обязан пробой НЕ быть. Отдельной строкой печатается прежнее правило на том же разговоре: если бы оно ловило случай, чинить было бы нечего. Пустая пара разговоров даёт код 2 (незнание).
Хвост прогона 01.09 (дословно):
ПРОВАЛ: калитка не различает пробу и живого по телефону разговора
LNG-012напр. 1находок: число в хвосте не названо
Платная студия не говорит клиенту «ты»: вежливая форма проверяется на ВЫХОДЕ цепочки перевода, а не в промпте одного движка
Зелёное, когда: 0 фамильярных обращений нашей механики живым людям при знаменателе больше нуля; пустой знаменатель - код 1 (ноль от незнания); несошедшаяся подсадка или ложный срез - код 1 (неисправен сам прибор)
Как судит
node --import tsx scripts/qa-polite-second-person.ts [--дней=60] [--since=<ISO>]. Судит по тому, что видел клиент: coalesce(translatedBody, body). Язык берётся по САМОМУ ТЕКСТУ (кириллица - ru или uk по буквам іїєґ), поля sentLang/detectedLang/clientLanguage - лишь подсказка: sentLang родился 17.08 и пуст у 3052 исходящих из 3213 за 60 суток, отбор по нему судил бы 95 строк вместо 1095. Неопознанное печатается отдельным числом, а не пропадает. Фамильярность ищется списком местоимений и притяжательных для девяти языков с различием форм (ru, uk, de, fr, es, it, pl, pt, nl); ссылки выбрасываются - в пути URL встречается tu и ti. Ручные сообщения персонала (fromAgent=0, без шаблона и без...
Хвост прогона 01.09 (дословно):
Lo siento, tu mensaje llegó incompleto, lo leí mal. He notado espondilolistesis L4-L5. En clase todo es a través de la relajación y sólo el propio peso corporal hace el trabajo; El entrenador pregunta
MET-116напр. 1находок: число в хвосте не названо
Прибор присмотра отделяет наше от клиентского только боевым каноном lib/test-slots, а не своим списком номеров
Как судит
node_modules/.bin/tsx scripts/qa-promise-backlog.mts --only=MET-116 - импортирует isTestPhone из lib/test-slots и печатает число пропущенных тестовых рядом со знаменателем. Разница до и после перехода на канон: тестовых 564 против 333, тёмных обещаний 9 против 11. ПОПРАВКА 23.08.2026: --only=MET-116 обязателен - без него общий код выхода красил эту мету чужой находкой PRM-003 (тёмные обещания); режим проверяет канон фактом: все 20 хвостов TEST_PHONE_TAILS ловятся, живой номер нет, своих литералов номеров в приборе ноль.
Хвост прогона 01.09 (дословно):
ЗНАМЕНАТЕЛЬ MET-116: пунктов проверено 4, хвостов канона 24
MSG-227напр. 1находок: число в хвосте не названо
Новое обещание срока поверх тревоги, застрявшей в fallback - молчание повторится по той же причине
Зелёное, когда: Код 0. Ноль нитей, где новое обещание срока лежит поверх непринятой тревоги.
Как судит
node scripts/qa-promise-over-fallback.mjs (флаг --since= сдвигает окно обещаний, --self-test подсаживает случай). Прибор берёт StaffAlert со status='fallback' и acceptedAt IS NULL, у каждой смотрит исходящие ПОЗЖЕ fallbackAt и ищет обещание срока по узкому списку оборотов. Печатает знаменатель (тревог всего / в fallback без acceptedAt) и называет автора обещания - агент, названный человек или человек из приложения площадки.
Хвост прогона 01.09 (дословно):
✗ Обещание срока лежит поверх тревоги, которую никто не принял. Второе молчание наступит по той же причине, что и первое.
STD-001напр. 1находок: число в хвосте не названо
Учётка стенда не живёт внутри боевой студии
Зелёное, когда: подсадка 1/1 (учётка стенда в живой студии краснит), контроль ложного среза 0/1 (та же учётка в стендовой студии зелёная), знаменатель 21 учётка живых студий, разрешённых пропущено 1 (тренер Gr)
Как судит
node scripts/qa-stand-account-in-live-studio.mjs. Судит таблицу User: учётка со следом приёмки (приставка идентификатора, почта-заглушка, имя с меткой) внутри студии, не помеченной как стендовая. Признак ставится ТОЛЬКО по наличию метки - отсутствие устройств признаком не считается. Разрешённые жители перечислены поимённо с основанием и датой и печатаются вслух, чтобы пропуск не был тихим.
Хвост прогона 01.09 (дословно):
✗ qatest_canggu_admin | роль ADMIN | «QA Canggu Admin» qatest-canggu-admin@qatest.invalid | студия studio_canggu_1778764028263 | устройств 0 | признак: приставка идентификатора

Направление 2. Клиентский путь (запись, вход, коды, отмена, перенос, пакеты, промокоды) (7)

AGT-147напр. 2находок: число в хвосте не названо
Дожим не уходит тому, кто уже записан или уже был
Зелёное, когда: подсадка (дожим при живой брони) 1 из 1 и контроль ложного среза (та же строка, броней ноль) 0 из 1 на боевой форме строки байт в байт; знаменатель после точки отсчёта непустой, иначе код 2; мимо...
Как судит
node scripts/qa-nudge-already-booked.mjs [--since=<ISO>]. Судимы строки EventLog scope='agent:followup' message='дожим ушёл' по студии Чангу за 60 суток. Красным считается дожим, у чьего клиента есть подтверждённая бронь этой студии на день дожима или накануне (факт из Booking+TimeSlot, не из переписки). История до точки отсчёта печатается отдельно и на код НЕ влияет.
Хвост прогона 01.09 (дословно):
✗ 2026-08-28T01:03:14.983+00:00 variant=silent | Made Astika …8441 | подтверждённых броней с кануна: 1
AGT-153напр. 2находок: число в хвосте не названо
Дверь дожима не видит того, кто пришёл не из рекламы
Зелёное, когда: 3 пробы из 3; первый боевой прогон 23.08.2026 - знаменатель 233, рекламных 175, СЛЕПЫХ 58, нечитаемых дат 0, код 1
Как судит
node scripts/qa-nudge-organic-blind.mjs [--studio=<id>] [--days=60]. Судит живых лидов студии, замолчавших после НАШЕГО слова дольше 48 ч и не имеющих ни одной брони: видит ли их хоть одна дверь возврата. Двери списаны с app/api/cron/agent-autopilot/route.ts - silent (adReferralAt непуст плюс окно lastInboundAt 18-36 ч) и promised (клиент сам назвал день). Отсеиваются служебные собеседники по списку lib/internal-contacts.ts, номера тренеров и стенд. Оба исхода доказываются НА ВЫДУМАННЫХ строках боевой формы, без единой записи в базу: подсадка 1/1, контроль ложного среза 1/1 (реклама, обещание, бронь, ждёт нас, служебный), контроль свежести 1/1. Код 1 - слепые есть; код 0 - слепых нет при...
Хвост прогона 01.09 (дословно):
Правку делать нельзя без кнопки владельца: снятие условия adReferralAt = автоматическое касание живых людей.
ALR-034напр. 2находок: число в хвосте не названо
Непринятая тревога тоже обязана кончиться нашим словом человеку
Зелёное, когда: подсадка (не принята, слов ноль) 1 из 1 и контроль ложного среза (та же строка, слово есть) 0 из 1 - оба на боевой форме строки байт в байт; знаменатель непустой, иначе код 2; тёмных 0 из знаменателя.
Как судит
node scripts/qa-unaccepted-but-dark.mjs. Судимы тревоги Чангу за 30 суток, у которых acceptedAt пуст, есть conversationId и возраст больше 12 часов. Красным считается тревога, после рождения которой в нити НЕТ ни одного нашего исходящего. Судим по слову клиенту, а не по закрытию задачи.
Хвост прогона 01.09 (дословно):
- … | нить 12…2279 | звонков 0 | digested/-
MET-152напр. 2находок: 3
Дочерний node-процесс запускается с явным cwd репозитория
Зелёное, когда: код 0 при знаменателе больше нуля (мест запуска 10, без cwd 0). Пара фактов встроена: подсадка формы без cwd 1/1, зелёное на той же форме с cwd 0/0, контроль ложного среза (внешняя команда, не наш...
Как судит
node scripts/qa-child-cwd.mjs - разбирает scripts и scripts/tmpqa (1689 файлов), считает места запуска НАШЕГО node по балансу скобок и краснеет на тех, где нет cwd. Запуски самого node с флагом (node --check файл) не судятся: они ничего из проекта не читают. Свой файл из выборки исключён - иначе прибор находил бы собственные образцы и был бы красным вечно.
Хвост прогона 01.09 (дословно):
КОД 1: ЗАПУСК НАШЕГО NODE БЕЗ cwd: 3 из 21. Такой ребёнок не найдёт .env и умрёт до первой строки.
MET-168напр. 2находок: число в хвосте не названо
Подтверждённая бронь, о которой клиент не узнал ни разу (билет не уходил)
Зелёное, когда: нет броней без единого билета; знаменатель непустой; подсадка (бронь без билета) даёт 1/1, контроль (билет ушёл) 0/1; блок срока даёт подсадку (занятие 08:00 при калитке 09:00) 1/1 и контроль (11:00,...
Как судит
node scripts/qa-booking-ticket-dark.mjs - по каждой CONFIRMED брони живого человека ищет в переписке хоть одно наше сообщение с её билетом; группа на один номер и слот считается одной строкой. Красным делает бронь, у которой билета не было НИ РАЗУ, и отдельно помечает те, у кого занятие ВПЕРЕДИ. У броней впереди печатает СРОК: калитку починки MET-160 (09:00 Бали) против НАЧАЛА ЗАНЯТИЯ, запас в минутах, и живой замер отклика на напоминание class_today (доля ответивших за 30 мин и за 2 ч со знаменателем).
Хвост прогона 01.09 (дословно):
КОД 1: есть люди с бронью, которым мы ни разу не сказали, что они записаны.
MET-169напр. 2находок: 1
Живая бронь, не прошедшая ни через одну дверь продукта (нет строки авторства)
Зелёное, когда: нет живых броней без строки авторства; знаменатель непустой; подсадка (бронь без следа) 1/1, контроль (бронь со следом) 0, контроль стенда 0
Как судит
node scripts/qa-booking-no-authorship.mjs [--since=2026-08-17] - по каждой живой брони студии ищет строку EventLog scope=action с её entityId и action=create. Красным делает бронь, у которой такой строки нет ВООБЩЕ: значит её завели прямой записью в базу, минуя API.
Хвост прогона 01.09 (дословно):
КОД 1: БРОНЬ МИМО ВСЕХ ДВЕРЕЙ - 1 из 29. У такой брони тёмные сразу три исхода: билет клиенту, тревога персоналу, зов.
Перепрогон 02.09:
КОД 1: БРОНЬ МИМО ВСЕХ ДВЕРЕЙ - 1 из 32. У такой брони тёмные сразу три исхода: билет клиенту, тревога персоналу, зов.
MET-195напр. 2находок: число в хвосте не названо
Сегодняшний час назван словом «сегодня», а не именем дня недели (спасение отмены + дожим)
Зелёное, когда: код 0 - сегодняшние часы названы словом «сегодня»; код 1 - живой человек читал имя дня недели там, где речь о сегодняшнем дне; код 2 - оба знаменателя пусты, это «не проверено», а не «чисто»
Как судит
node scripts/qa-rescue-day-word.mjs. ДВЕ ДВЕРИ, две графы, оба знаменателя печатаются отдельно. Дверь 1 - живые события EventLog scope='cancel:rescue' (структурные слоты). Дверь 2 - исходящие Чангу, где в английском исходнике body встречается образец «<День недели> ЧЧ:ММ» (дожим lib/followup-texts.ts и ответы ассистента). В знаменатель случай попадает, только когда названный день совпадает с СЕГОДНЯШНИМ днём студии И названный час ещё НЕ наступил: если час прошёл, речь заведомо о следующей неделе и двусмысленности нет. Тестовая нить QA-цикла отделяется ДО любой цифры. Самопроверка судьи: --selftest, 10 проб на боевом виде строки байт в байт.
Хвост прогона 01.09 (дословно):
КОД 1: живой человек читал имя дня недели там, где речь шла о сегодняшнем дне.

Направление 3. Кабинет администратора (+sadmin) (6)

AGT-148напр. 3находок: число в хвосте не названо
Человек назвал свой день, день настал, а ни одна дверь дожима его не видит
Зелёное, когда: проба обоих исходов в памяти, без единой записи в базу: подсадка 1/1 (срок настал, поле убрано, входящее древнее - обе двери слепы), контроль двери promised 1/1, контроль двери silent 1/1, срок...
Как судит
node scripts/qa-promise-unreachable.mjs [--studio=<id>] [--days=30]. Названный срок берётся из ДВУХ мест сразу: живого поля WhatsAppConversation.promisedReturnAt и записей EventLog scope='promise:cleared' (там срок сохранён дословно вместе с причиной уборки). Краснеет, когда срок наступил, после срока мы человеку ничего не написали, и его не видит ни дверь promised (promisedReturnAt в окне [now-3сут, now]), ни дверь silent (adReferralAt непуст и lastInboundAt в окне [now-36ч, now-18ч]) - окна списаны с app/api/cron/agent-autopilot/route.ts и печатаются в выводе.
Хвост прогона 01.09 (дословно):
Писать этим людям - решение владельца: это касание живого человека после долгого молчания, присмотр сам не пишет.
AGT-181напр. 3находок: число в хвосте не названо
Наш собственный фиксированный текст отказа не заявляет места, которого нет
Зелёное, когда: код 0 при знаменателе больше нуля: ни одна строка, произносимая при ОТКАЗЕ взять место, не говорит о месте в прошедшем времени
Как судит
node_modules/.bin/tsx scripts/qa-fallback-claims-seat.mts - поднимает из БОЕВОГО lib/sales-agent.ts обе константы отказа автоброни (AUTOBOOK_FALLBACK и AUTOBOOK_FALLBACK_NIGHT) и судит каждую строку связкой «предмет записи» + «уже приведён в состояние нами» (is/has been set aside|put aside|held|kept|saved|blocked out|locked in|reserved|secured). Результат боевого claimsSeatTaken печатается СПРАВОЧНО и в вердикт не идёт
Хвост прогона 01.09 (дословно):
Текст видит клиент - меняется только кнопкой владельца (правило про клиентские тексты).
ALR-045напр. 3находок: число в хвосте не названо
Согласился на конкретный час - место обязано существовать в базе, а не в чьей-то памяти
Зелёное, когда: код 0 при знаменателе больше нуля: у каждого обещанного часа есть место в базе
Как судит
node scripts/qa-agreed-hour-no-seat.mjs - судит тревоги «<кто> agreed to <дата> <час>, but the assistant could not take the seat»: для каждой ищет бронь ТОГО ЖЕ человека в ЭТОЙ студии, созданную позже тревоги. Стенды отсеиваются по имени, ветка «тело не разобрано» громкая, код 2 отдельный на пустой знаменатель (это НЕ зелёный вердикт)
Хвост прогона 01.09 (дословно):
тревога принята: ПАКЕТНАЯ МЕТКА, НЕ ЧЕЛОВЕК (batch:owner-20.08 2026-08-20T09:50:30.365Z), звонков 0
MET-055напр. 3находок: 1
Сторож не судит по полю «сейчас» без верхней границы окна
Зелёное, когда: «MET-055: выполнено 3 из 3». Ноль названных в контрольном окне означает потерянную границу, а НЕ отсутствие дефектов.
Как судит
npx tsx scripts/qa-drifting-gate.mts - две прослойки. СЛОЙ 1, форма кода: в lib/lost-booking-request.ts считаются ВСЕ ворота с отсевом по Booking.updatedAt (их двое - мёртвая задача вывода и невыполненное обещание), у каждых обязана быть верхняя граница lte; ворот меньше двух = проверка ослепла, а не дефект исчез. СЛОЙ 2, живое контрольное окно: findUnkeptStudioPromises с 10.08 04:00Z и боевой отсрочкой 20 минут обязана назвать оба дверных сообщения нити 6s8r8f3z и не назвать остальные 160 исходящих окна.
Хвост прогона 01.09 (дословно):
MET-055: ПРОВАЛ, 1 слоя(ёв)
SCH-040напр. 3находок: число в хвосте не названо
Расписание не кончается раньше, чем его спрашивают
Зелёное, когда: подсадка 8 из 8; на живых данных знаменатель непустой, иначе код 2
Как судит
npx tsx scripts/qa-schedule-horizon.mts [--подсадка] [--недель=N]. Считает ЛИНИЮ расписания (день недели плюс время начала) и её горизонт - дату последнего неотменённого занятия. Красное, если у живой линии нет продолжения на N недель вперёд. Линия, которую перестали вести намеренно (меньше трёх занятий или оборвалась больше месяца назад), тревогой не считается.
Хвост прогона 01.09 (дословно):
ЗНАМЕНАТЕЛЬ: студий 4, линий короче 3 недель: 8
SEC-020напр. 3находок: 1
Проверка не ходит по проду под именем живого человека
Зелёное, когда: Ноль файлов, чеканящих личность под живым человеком; ноль машинных сессий живых людей после запрета. Пустой знаменатель (ноль файлов или ноль сессий) - код 1. Замер рождения 17.08.2026: файлов 632,...
Как судит
npx tsx scripts/qa-identity-guard.mts. Смотрит С ДВУХ СТОРОН: по КОДУ - все файлы scripts/**, которые чеканят личность (encode/NATIVE_JWT_SECRET/createHmac), и сверяет КАЖДЫЙ найденный идентификатор с таблицей людей в базе, а не с формой строки; по ДАННЫМ - сессии живых сотрудников с машинной подписью клиента. Отсчёт по данным от момента запрета 2026-08-17T01:00. Флаг --проба подсаживает синтетического нарушителя с именем живого человека из базы: прогон обязан быть красным.
Хвост прогона 01.09 (дословно):
ПРОВАЛ: 1

Направление 4. Архитектура и данные (схема, миграции, инварианты) (10)

DAT-009напр. 4находок: 14
Реальные телефоны клиентов и секреты не живут в коде/примерах
Зелёное, когда: 0 находок
Как судит
node scripts/qa-no-client-phones.mjs (прибор существовал, реестр звал меру словами; связь подтверждена запуском 21.08.2026) | прежнее описание: grep дерева репо (телефоны, .p8/.p12, токены)
Хвост прогона 01.09 (дословно):
НАХОДКА DAT-009: 14 из 552 живых номеров лежат в дереве
Перепрогон 02.09:
НАХОДКА DAT-009: 17 из 554 живых номеров лежат в дереве
DAT-012напр. 4находок: число в хвосте не названо
Телефон из репозитория не совпадает ни с одним живым номером клиента или тренера
Зелёное, когда: 0 совпадений вне списка тестовых номеров
Как судит
node scripts/qa-repo-phones.mjs (0/1/2; --подсадить): git grep 10-14-значных литералов x хвосты (9) из Booking/WhatsAppConversation/Trainer; стендовые - боевой канон _test-phones, внутренние контакты - боевой lib/internal-contacts.ts; по дизайну исключены chat-watch и sales-psychology; вывод только с маской
Хвост прогона 01.09 (дословно):
НАХОДКА (журнал): university/qa/pending/patch-alr051-empty-addressee-lock-2026-08-29.md
Перепрогон 02.09:
НАХОДКА (журнал): university/partners/outreach/wave-A-2026-08-10.md
DAT-082напр. 4находок: 0
Дата в боевой базе хранится С ПОЯСОМ: наивную строку Prisma читает как местное время Бали и сдвигает на 8 часов назад
Зелёное, когда: наивных значений 0 и мест записи идиомом 0
Как судит
npx tsx scripts/qa-naive-datetime.mts (прибор существовал, реестр звал меру словами; связь подтверждена запуском 21.08.2026) | прежнее описание: АВТОМАТОМ: npx tsx scripts/qa-naive-datetime.mts - две половины сразу. (1) ЗАМЕР по базе: во всех колонках, по которым система СУДИТ о времени (SendLo
Хвост прогона 01.09 (дословно):
ВЕРДИКТ: НАЙДЕНО. наивных значений 0 (-), мест записи идиомом 4
DAT-087напр. 4находок: число в хвосте не названо
Один человек не заведён дважды из-за формата номера (местный против полного)
Зелёное, когда: пар «81xxx и 6281xxx в одной студии» среди живых людей ноль
Как судит
node scripts/qa-phone-format-twins.mjs
Хвост прогона 01.09 (дословно):
Слить вслепую нельзя: у нитей могут быть разные закреплённые тренеры - решает человек.
INF-084напр. 4находок: число в хвосте не названо
Порог допуска работы не может быть НИЖЕ p90 самой работы
Зелёное, когда: подсадка 6 из 6; на живых данных p50 18,3с, p90 25,7с, порог 32000 мс - код 0
Как судит
node scripts/qa-lesson-window.mjs [--дней=7] [--подсадка]. Считает p50 и p90 по удавшимся заданиям добычи уроков за окно и сравнивает с порогом допуска, ПРОЧИТАННЫМ ИЗ lib/sales-agent.ts (копия порога в мере разошлась бы молча). Порог ниже p90 - красное. С 27.08.2026 вторая половина живьём: порог обязан быть ДОСТИЖИМ в своём доме - окно дома читается из deploy/launchd/com.bookgravity.lessonmine.plist (AGENT_LESSON_WAIT_MS), порог выше окна - красное. Печатает исходы по всем видам заданий, чтобы здоровый путь ответов клиенту не смешивался с больным.
Хвост прогона 01.09 (дословно):
КОД 1: порог 60000 мс НИЖЕ p90 (81366 мс) - каждая десятая допущенная попытка обречена по построению
MET-091напр. 4находок: число в хвосте не названо
Фикстура прогона живёт в тестовой студии; попавшая в живую - убирается в тот же день
Зелёное, когда: тестовых сущностей вне тестовой студии ноль; ни одна не видна клиенту
Как судит
node scripts/qa-instrument-liveness.mjs (прибор существовал, реестр звал меру словами; связь подтверждена запуском 21.08.2026) | прежнее описание: Два прогона рядом: npx tsx scripts/qa-test-slot-public.mjs (ни одно тестовое занятие не видно клиенту) и npx tsx scripts/qa-money-invariants
Хвост прогона 01.09 (дословно):
приборов без двери стало больше базы (3) - новый прибор написан мимо правила
MET-128напр. 4находок: 5
«Работа не сделана» судится по ВСЕМ дверям этой работы, а не по одному полю: у извещения тренера дверей две - колонка Booking и тревога в приложении
Зелёное, когда: ноль броней, где одна колонка сказала бы «не извещён», а тревога в приложении существует; либо каждая такая бронь названа и разобрана
Как судит
node scripts/qa-trainer-notified-doors.mjs [--days=30] [--since=<ISO>]. Для каждой брони живой студии (studioId NOT LIKE 'qatest_%') с waNotifyTrainerStatus='not_sent' спрашивает ВТОРУЮ дверь: есть ли StaffAlert kind='booking' по её slotId позже создания брони. Есть - утверждение «тренер не извещён» ложно, код 1 с поимённым списком. Нет - тишина настоящая, называется поимённо тоже. Даты нормализуются с обеих сторон (MET-063), рядом со знаменателем печатается счётчик нечитаемых дат. Подсадка (not_sent + тревога обязана ловиться) и два контроля ложного среза (not_sent без тревоги, skipped с тревогой) - при несовпадении код 2 и вердикт недействителен. Пустой знаменатель тоже код 2, а не...
Хвост прогона 01.09 (дословно):
КРАСНЫЙ: 5 броней, про которые одна колонка сказала бы «тренер не извещён», а тревога в приложении существует. Судить обеими дверями.
MET-129напр. 4находок: 1
Уборка пробы ходит только по СВОИМ идентификаторам и никогда по общему префиксу семейства; создание подсадки лежит внутри finally
Зелёное, когда: код 0 при знаменателе больше нуля; приёмка самого сторожа: снять фикстуру из базы - обязан дать КОД 1 и назвать её имя, вернуть - КОД 0, и зависимая приёмка trainerless-class-watch-selftest.mjs снова...
Как судит
node scripts/qa-standing-fixtures.mjs. Держит ЯВНЫЙ список стоячих фикстур - тех, что никто не создаёт по ходу прогона, но на которые по имени ссылается чужая приёмка. Для каждой сверяет наличие в базе и печатает, кто её ждёт. ОБЕ автоматические формы вывода списка из кода были написаны и измерены на приёмке, обе врут: «в файле есть хоть какой-нибудь INSERT» дало 1 ПРОПУСК и ровно тот самый (f5 удалена, а сторож печатал КОД 0, потому что упоминающий её selftest создаёт по соседству свои selftest_tl_*), а «имя в окне 6 строк от INSERT» дало 27 ЛОЖНЫХ, потому что пробы объявляют идентификатор константой вверху и вставляют далеко внизу. Поэтому список ведётся рукой: во всём репозитории такая...
Хвост прогона 01.09 (дословно):
КОД 1: пропавших стоячих фикстур 1 из 1 - приёмки, которые их ждут, зелены ОТ НЕЗНАНИЯ
MET-131напр. 4находок: число в хвосте не названо
Готовая к отправке фраза в досье называет конкретный час или относительный день - к моменту отправки она указывает на чужой день недели
Зелёное, когда: код 0 и ноль протухающих фраз в действующих редакциях
Как судит
node scripts/qa-dossier-stale-hours.mjs - судит ТОЛЬКО текст в ёлочках под заголовком «Что сказать ...» (проза вокруг - разбор, а не отправляемое), и только действующую редакцию: всё до последней метки «ОТМЕНА ПРЕЖНЕЙ ГОТОВОЙ ФРАЗЫ» пропускается, иначе append-only досье краснело бы вечно. Метка {ЧАСЫ} = осознанный отказ от часов, такую фразу не трогает. Печатает знаменатель (досье всего / с цитатной фразой) и отдельный код 2 для «проверять было нечего».
Хвост прогона 01.09 (дословно):
... и ещё 53
Перепрогон 02.09:
... и ещё 60
MSG-271напр. 4находок: число в хвосте не названо
Решение агента «нечего отвечать» оставляет след и не держит очередь пробкой
Зелёное, когда: код 0 и обе пробы 1/1 и 0/0 в выводе
Как судит
node scripts/qa-cork-in-queue.mjs - человеческие входящие Чангу (нажатия кнопок отсеяны), у которых НЕТ своей карточки и своего задания по inboundMessageId, и перед которыми в той же нити в пределах двух часов лежит такое же бесследное входящее. Две пробы на чистой функции разбора: подсадка пары «пробка без следа -> задавленное» обязана краснеть, контроль ложного среза (у пробки след есть) обязан молчать.
Хвост прогона 01.09 (дословно):
ЗАДАВЛЕНО 2026-08-28T01:51:18.000+00:00 text «di coretax, nama SVITLANA MOLODTSOVA ini sebagai PIC, ini siapa ya?»

Направление 5. Цепочка сообщений (вебхук -> инбокс -> агент -> перевод -> доставка -> статусы -> кнопки -> EventLog) (51)

AGT-093напр. 5находок: число в хвосте не названо
Канон «я подумаю»: слова владельца закреплены словарём и не подменены - ни правкой, ни машинным переводом
Зелёное, когда: exit 0 при счётчике проверок > 0; ноль отправок в живой части печатается явно и зелёным по этой части не считается
Как судит
npx tsx scripts/qa-deferral-canon.ts - эталон в проверке против DEFERRAL_DOOR_TEXTS посимвольно (4 двери x 3 языка), стиль строк, наличие DEFERRAL DOORS и поля deferral в боевом промпте, отсутствие отставленных формулировок (открытое расписание на отсрочку, перевёрнутый приоритет CLOSE BY TWO SMALL STEPS), механика applyDeferralDoor, живые отправки канона (SELECT: body=канон EN -> translatedBody=словарь языка).
Хвост прогона 01.09 (дословно):
- прод money/ru: клиент получил слова владельца - ушло: ""
AGT-152напр. 5находок: 1
Агент не выдаёт окно расписания за правило недели: говорит о ДАТАХ, а не о том, что студия «всегда» или «никогда» делает по такому-то дню
Зелёное, когда: код 0 при ненулевом знаменателе судимых исходящих; код 2 = не проверено (живых исходящих после точки отсчёта ещё не было) и зелёным не считается
Как судит
node scripts/qa-week-rule.mjs - исходящие Чангу после точки отсчёта 2026-08-23 00:20 (нити стенда отсеиваются по хвостам 5347/546405 и именам QA/Test), в тексте, который ВИДЕЛ клиент (coalesce(translatedBody,body)). Правилом недели считается день недели во множественном числе, «по воскресеньям занятий нет», «we run Mondays...», «no Sunday classes», «hari Minggu tidak ada kelas», «satu-satunya hari tanpa kelas», «every week at 09:00...». День недели рядом с ЧИСЛОМ ДАТЫ правилом НЕ считается - назвать конкретную дату можно и нужно. Доказательство прибора: node scripts/qa-week-rule.mjs --подсадка.
Хвост прогона 01.09 (дословно):
КОД 1: ПРАВИЛО НЕДЕЛИ СКАЗАНО ЖИВОМУ ЧЕЛОВЕКУ 1 из 500
Перепрогон 02.09:
КОД 1: ПРАВИЛО НЕДЕЛИ СКАЗАНО ЖИВОМУ ЧЕЛОВЕКУ 1 из 511
AGT-161напр. 5находок: 1
Русская форма привычки «по понедельникам и средам» судится как правило недели наравне с английским Mondays
Зелёное, когда: подсадка: найдено 26 на корпусе 2458 (было 21), контроль правильных строк с датами 7 из 7 молчит, прежняя дыра 6 из 6 закрыта; боевой прогон красный на живом случае
Как судит
node scripts/qa-week-rule.mjs (признак «по <дням> (дательный мн.)»). Ловит дательный множественного после предлога «по»; именительный «ближайшие занятия - понедельник и среда» (законный ход лестницы закрытия) НЕ трогает - разделяет падеж, а не число дней.
Хвост прогона 01.09 (дословно):
КОД 1: ПРАВИЛО НЕДЕЛИ СКАЗАНО ЖИВОМУ ЧЕЛОВЕКУ 1 из 500
Перепрогон 02.09:
КОД 1: ПРАВИЛО НЕДЕЛИ СКАЗАНО ЖИВОМУ ЧЕЛОВЕКУ 1 из 511
AGT-177напр. 5находок: 1
Отказ в приватe при открытых часах: мы не закрываем день, который стоит свободным
Зелёное, когда: ноль отказов при открытых часах; знаменатель непустой
Как судит
node scripts/qa-private-refusal-vs-schedule.mjs - нити Чангу с просьбой о приватe (direction ЗАГЛАВНЫМИ, MET-133, стенд отсеян боевым isTestPhone); находка = наше исходящее с формой отказа за целый день или период («will not work for a private», «cannot open a private», «приват не получится», «tidak bisa privat») при том, что в семи сутках от этого сообщения стоял хотя бы один открытый час (слот живой, с тренером, публичный, броней ноль). Судится то, что прочитал человек: coalesce(translatedBody, body)
Хвост прогона 01.09 (дословно):
КОД 1: отказов в приватe при открытых часах 1 в 1 нит(и/ях).
AGT-186напр. 5находок: число в хвосте не названо
Блок авто-уроков не съедает боевой промпт агента: у него есть потолок доли и скорости роста
Зелёное, когда: КОД 0: доля авто-уроков в собранном боевом промпте каждой судимой студии меньше 50% И прирост активных уроков за 10 суток меньше 20. КОД 1: порог пройден - печатаются поимённо студия, знаки промпта,...
Как судит
node_modules/.bin/tsx scripts/qa-prompt-weight.mts
Хвост прогона 01.09 (дословно):
КОД 1: блок авто-уроков занимает половину и больше боевого промпта либо растёт быстрее 20 уроков за 10 суток. Мера не утверждает, что ответы уже испорчены - она называет рост, у которого нет ни потолк
AGT-187напр. 5находок: число в хвосте не названо
Два соседних рекламных лида не читают одну и ту же фразу
Зелёное, когда: КОД 0 - соседние лиды получили разные фразы. КОД 1 - повтор есть, пары названы поимённо с цифрой сходства. КОД 2 - НЕ ПРОВЕРЕНО, соседних пар после точки отсчёта ещё нет (знаменатель печатается...
Как судит
node scripts/qa-ad-relief-rotation.mjs - берёт первый наш ответ каждому индонезийскому рекламному лиду Чангу ПОСЛЕ точки отсчёта (выката механизма), срезает приветствие и сравнивает вступительную фразу с фразой ПРЕДЫДУЩЕГО лида по доле общих слов (порог 0.6). Самопроверка 9 из 9 в каждом прогоне: две подсадки (дословный повтор и переставленные слова), два контроля (разные по смыслу углы не считаются повтором; приветствие срезается) и пять проб самого механизма выбора угла из lib/ad-relief-angle.ts.
Хвост прогона 01.09 (дословно):
КОД 1: ДВА СОСЕДНИХ ЛИДА ПРОЧИТАЛИ ОДНУ И ТУ ЖЕ ФРАЗУ - механизм угла не сработал либо угол один на двоих.
AGT-207напр. 5находок: число в хвосте не названо
Повторяющийся недельный распорядок назван доступностью, когда ближайший экземпляр этого дня отменён
Зелёное, когда: ноль сообщений, называющих часом отменённый экземпляр дня недели; знаменатель непустой
Как судит
node scripts/qa-weekday-hours-vs-cancelled.mjs - наши исходящие Чангу (direction ЗАГЛАВНЫМИ, MET-133; стенд и персонал отсеяны каноном scripts/_test-phones.mjs, без сита вердикт не выносится вовсе). Находка = в тексте рядом стоят имя дня недели БЕЗ даты и час, а ближайший от минуты отправки экземпляр этого дня имеет названный час отменённым. Языки en+id+ru. Судится то, что прочитал человек: coalesce(translatedBody, body). Состояние мира восстанавливается на минуту отправки: cancelledAt <= отправки
Хвост прогона 01.09 (дословно):
КОД 1: названо доступным то, что было отменено на минуту наших слов - 1 из 145.
Перепрогон 02.09:
КОД 1: названо доступным то, что было отменено на минуту наших слов - 1 из 150.
ALR-028напр. 5находок: число в хвосте не названо
Прибор лестницы читает реестр имён из прода, а не хранит копию
Зелёное, когда: проба зелёная И живой прогон печатает строку «реестр имён прочитан из lib/staff-alert.ts: N» с N больше нуля. Разбор не состоялся - код 3 «судить нельзя»; молчаливый пустой список запрещён, он даёт...
Как судит
node scripts/qa-alert-escalation-ladder.mjs --проба - ЛОВУШКА ФОРМЫ ЗАКРЫТА 23.08.2026: точка предложения после флага склеивалась исполнителем в аргумент «--проба.», скрипт его не узнавал и гонял живой режим вместо пробы. Функция titlesFrom разбирает lib/staff-alert.ts: константы *_TITLE плюс список NEEDS_HUMAN_ALERT_TITLES. Проба гоняет четыре сцены на НАСТОЯЩЕМ исходнике: прод разобран (не меньше 4 имён); подсадка пятого имени в исходник даёт +1 и включает подсаженное; реестр убран из исходника - null; имя списка не разрешается в константу - null.
Хвост прогона 01.09 (дословно):
(после выката лестницы первая запись появится на ближайшем тике автопилота)
ALR-046напр. 5находок: число в хвосте не названо
Лестница тревоги «нужен человек» успевает до НАЧАЛА ЗАНЯТИЯ, а не только по возрасту тревоги
Зелёное, когда: КОД 0: у всех судимых тревог все три ступени успевают до начала занятия
Как судит
node_modules/.bin/tsx scripts/qa-ladder-vs-class.mts - берёт тревоги реестра NEEDS_HUMAN_ALERT_TITLES со слотом, считает начало занятия по TimeSlot (Бали +8) и печатает, какие ступени лестницы (лично +2ч, админам +6ч, владельцу +24ч - числа читаются ИЗ боевого lib/alert-repeat.ts) физически выпадают уже ПОСЛЕ начала занятия. Стенд отсеивается боевой дверью lib/test-slots. Три самопроверки: подсадка «занятие через 23,6 ч» даёт ровно «владельцу», подсадка-2 «через 4,6 ч» даёт «админам, владельцу», контроль «через 12 суток» даёт пусто; нечитаемая дата уходит в отдельный счётчик «не проверено», а не в находки
Хвост прогона 01.09 (дословно):
2026-08-30T07:04:06.206Z тревога cmtfgrh32003b04l7lxqneonn | занятие 2026-08-31 13:00 (PRIVATE) | до занятия 21.9 ч | НЕ УСПЕВАЮТ: владельцу | нить cmtdz3qdt000104juwqbpfxu0
ALR-047напр. 5находок: 2
Обещали место, места нет - у обещания обязан быть ИСХОД, а не только тревога
Зелёное, когда: КОД 0: у каждого судимого обещания есть исход. КОД 1: есть обещание без единого исхода. КОД 2: знаменатель ноль - не проверено.
Как судит
node scripts/qa-promised-no-seat-unrung.mjs - знаменатель это тревоги Чангу «Booking needs a human» от отказа автоброни (could not take the seat), у которых в теле НАЗВАН номер клиента и номер не стендовый. По каждой ищутся четыре исхода: живое слово персонала (acceptedAt, но НЕ пакетной рукой batch:*), срочный зов urgent_here по той же нити или занятию, появившаяся бронь ЭТОЙ студии, либо место у человека уже было на согласованный день (перенос). Судья один на прогон и на пробы. Отсев печатается числами: моложе порога, стендовые, БЕЗ НОМЕРА В ТЕЛЕ, нечитаемые даты - «не проверено» отличимо от «чисто». Две пробы: подсадка (живой случай Jacob 25.08) и контроль ложного среза (той же записи...
Хвост прогона 01.09 (дословно):
КОД 1: обещаний без единого исхода 2 из 9.
ALR-050напр. 5находок: число в хвосте не названо
Принятый зов «обещание без места» обязан кончиться местом или живым словом
Зелёное, когда: КОД 0: у всех принятых зовов появилось место или живое слово. КОД 1: есть принятый зов, за которым нет ни того, ни другого. КОД 2: знаменатель ноль - НЕ ПРОВЕРЕНО. КОД 3: не доказана пара фактов...
Как судит
node scripts/qa-promise-call-accepted-no-seat.mjs [--hours=3] [--days=60] [--studio=<id>]. Берутся StaffAlert kind=urgent_call с заголовком «promise without a seat», принятые ЖИВОЙ рукой (пакетная метка batch:* приёмом не считается и в знаменатель не входит). По каждому спрашивается: появилась ли ПОСЛЕ ПРИЁМА бронь CONFIRMED этого телефона в ЭТОЙ студии либо живое слово человеку в нити (fromAgent=0, templateName пуст). Даты нормализуются, нечитаемые печатаются отдельным числом, стенды отсеиваются боевым каноном isTestPhone.
Хвост прогона 01.09 (дословно):
тело зова: «We told them the studio takes it from here. Still no booking 0h 31m later.»
ALR-051напр. 5находок: 8
Замок от повторов тревог не обходится пустым адресатом
Зелёное, когда: КОД 0: пар, обходящих замок, нет при непустом знаменателе. КОД 1: есть. КОД 2: знаменатель пуст - НЕ ПРОВЕРЕНО. КОД 3: не доказана пара фактов. Подсадка обоих исходов: ALR051_SELFTEST=fail |...
Как судит
node scripts/qa-staff-alert-twins.mjs [--days=30] [--seconds=60]. Берутся StaffAlert за окно, отсеиваются студии-стенды и стендовые номера боевым каноном isTestPhone (пустой адресат НЕ отсеивается - он и есть искомое). Ищутся пары с ОДИНАКОВЫМ studioId+kind+body в окне секунд, у которых РАЗНЫЙ toPhone, и среди них считаются те, где одна сторона пустая: именно по toPhone ключуется замок подавления lib/staff-alert.ts:344, поэтому пустой адресат его не совпадает и вторая строка ложится в ленту студии.
Хвост прогона 01.09 (дословно):
КОД 1: замок от повторов обойдён 8 раз - одинаковое тело легло в ленту студии дважды
Перепрогон 02.09:
КОД 1: замок от повторов обойдён 10 раз - одинаковое тело легло в ленту студии дважды
ALR-052напр. 5находок: 2
Дверь зова «обещали место» судит по КЛАССУ повода, а не по одной формулировке
Зелёное, когда: КОД 0 при знаменателе больше нуля. КОД 1 - есть обещание без исхода. КОД 2 - знаменатель ноль, это НЕ проверено. Внутри две пробы: подсадка синтетической тревоги боевой формы через ту же razobrat...
Как судит
node scripts/qa-promised-no-seat-unrung.mjs. Знаменатель - тревоги StaffAlert kind=booking ОБЕИХ формулировок одного повода: тело содержит «could not take the seat» (отказ автоброни) ИЛИ errorDetail = «the assistant promised a studio handoff nobody picked up» (обещали передачу, никто не подхватил). Признак повторяет боевую функцию alertIsPromisedNoSeat из lib/urgent-call.ts поле в поле - её же зовут дверь решения и свип. Красное - тревога старше порога без единого из четырёх исходов: живое слово (не batch:*), срочный зов по нити или занятию, бронь этой студии, место на согласованный день.
Хвост прогона 01.09 (дословно):
КОД 1: обещаний без единого исхода 2 из 9.
ALR-054напр. 5находок: 3
Наша собственная досылка присмотра не гасит зов живому тренеру
Зелёное, когда: КОД 0: гашений нет при знаменателе больше нуля. КОД 2 - знаменателя нет либо канон служебных имён пуст (это НЕЗНАНИЕ, а не чистота). Самопроверка до вердикта доказывает три исхода: подсадка нашей...
Как судит
node scripts/qa-alert-hushed-by-our-injection.mjs. Знаменатель - последние 80 StaffAlert Чангу, у которых есть нить. Находка: после тревоги в нити есть исходящее, подписанное СЛУЖЕБНЫМ именем (учётки User с name like 'QA %' или почтой-заглушкой - канон берётся ФАКТОМ ИЗ БАЗЫ, не переписывается в прибор), и при этом НИ ОДНОГО исходящего, подписанного живым человеком студии. Именно по признаку transcriptWho===TRAINER боевой lib/alert-repeat.ts:216 считает тревогу отвеченной, а колонка authorName у нашей досылки (scripts/send-freeform.ts) заполнена - значит для гасителя мы неотличимы от тренера.
Хвост прогона 01.09 (дословно):
КОД 1: НАША ДОСЫЛКА ГАСИЛА ЗОВ ЖИВОМУ ТРЕНЕРУ в 3 из 80 тревог.
CRN-029напр. 5находок: число в хвосте не названо
Бронь в терминальном окне «только что забронировал» не остаётся без ЕДИНОГО напоминания
Зелёное, когда: код 0 при знаменателе больше нуля; есть бронь без обоих напоминаний - код 1 с поимённым списком; знаменатель пуст или пороги не прочитаны - код 2 «не проверено», а не зелёное; самопроверка не сошлась...
Как судит
node scripts/qa-reminder-fresh-hole.mjs --days=90 [--studio=<id>] --samoprover. Окно считается не на глаз: час крона читается из vercel.json (0 11 * * *), порог свежести - из lib/booking-guards.ts (REMINDER_MIN_BOOKING_AGE_MS). Знаменатель - живые неотменённые брони, созданные в [часКрона-свежесть, часКрона) UTC на класс СЛЕДУЮЩЕГО дня. Нарушение - у такой брони пусты И reminderSentAt, И todayReminderSentAt.
Хвост прогона 01.09 (дословно):
КРАСНЫЙ
DOS-001напр. 5находок: число в хвосте не названо
Имя досье клиента канонично и однозначно
Зелёное, когда: все имена каноничны при знаменателе больше нуля - код 0; есть неканоничные - код 1 с перечислением и близнецами; досье не найдено ни одного - код 2 (НЕ ПРОВЕРЕНО, а не чистота); проба провалилась -...
Как судит
node scripts/qa-dossier-name-canon.mjs. Судит ИМЕНА файлов в university/qa/clients: канон - ровно 6 цифр хвоста номера (так названы 361 из 367 на день заведения), либо ig-<id>, либо служебный файл с ведущим подчёркиванием. Для каждого неканоничного имени печатает, с каким каноном оно путается (имя-суффикс). Встроенная проба обязательна и идёт ПЕРЕД боевым вердиктом: чистая папка в боевом формате имён даёт 0, подсадка двух сирот (6898, 8992407) даёт 2 с названными близнецами; проба не прошла - код 3, боевому вердикту верить нельзя.
Хвост прогона 01.09 (дословно):
Память о человеке разложена по двум файлам: тик, открывший одно имя, второго не видит.
DOS-002напр. 5находок: число в хвосте не названо
Один человек - одно живое досье (раскол между двумя каноничными именами)
Зелёное, когда: расколов ноль при знаменателе больше нуля - код 0; есть расколы - код 1 с перечислением пар и числом строк в каждой половине; досье не найдено - код 2; проба провалилась - код 3.
Как судит
node scripts/qa-dossier-name-canon.mjs, второй вердикт того же прибора. Ищет пары «ig-<id>.md и <последние 6 цифр id>.md», где ОБА файла живые. Половинка, переведённая в указатель («ПЕРЕЕХАЛ» / «ЭТОТ ФАЙЛ ДУБЛЬ»), соперником не считается. Проба обязательна и доказывает ОБА исхода: чистая папка с половинкой-указателем даёт 0 расколов, подсадка живой половинки вместо указателя даёт 1 с названной парой.
Хвост прогона 01.09 (дословно):
Память о человеке разложена по двум файлам: тик, открывший одно имя, второго не видит.
DOS-003напр. 5находок: 41
Одно досье на человека судится НИТЬЮ ИЗ ШАПКИ, а не формой имени файла
Зелёное, когда: обе графы пусты при знаменателе больше нуля - код 0; есть безключевые или расколы - код 1 с перечислением; досье не прочитано ни одного - код 2 (НЕ ПРОВЕРЕНО, а не чистота).
Как судит
node scripts/qa-dossier-identity.mjs. Две графы. ГРАФА 1 - досье, чья ШАПКА (первые 6 строк) не называет ни одного conversationId: ключа опознания нет, следующий тик своего же досье не найдёт. ГРАФА 2 - разные файлы, чьи нити ИЗ ШАПКИ совпали: один человек в двух живых досье. Ключ намеренно берётся только из шапки: первый заход брал все нити файла и дал 11 расколов, ручная проверка убила два из трёх проверенных (417555 Annabelle против 430564 Justinas, 058838 Bethesda против 546405 Sancho - РАЗНЫЕ люди, общая нить пришла из УПОМИНАНИЯ чужого разговора в теле). Упоминание нити доказывает упоминание, а не тождество. Самопроверка 5 проб в обе стороны идёт ПЕРЕД вердиктом: живой случай Padma...
Хвост прогона 01.09 (дословно):
КОД 1: досье без ключа 41, расщеплённых нитей 3 из 459 досье.
Перепрогон 02.09:
КОД 1: досье без ключа 41, расщеплённых нитей 3 из 460 досье.
LOC-001напр. 5находок: число в хвосте не названо
Человек, идущий на занятие, получил АДРЕС студии хотя бы одним сообщением до начала
Зелёное, когда: ноль броней без карты до начала занятия при числе судимых больше нуля
Как судит
node scripts/qa-map-before-class.mjs (окно --дней=, по умолчанию 30). Берутся неотменённые брони с датой занятия в окне, отсеиваются стендовые студии и наши номера (числа печатаются). Карта считается ушедшей по ДВУМ признакам: (а) исходящее ЭТОЙ студии на хвост телефона со Studio.locationUrl в coalesce(translatedBody, body) до начала занятия; (б) до начала занятия ушёл не-failed шаблон, чьё ОДОБРЕННОЕ Метой тело несёт блок «📍» (lib/template-bodies.json, синк из Меты) - потому что копия шаблонного исходящего до 0f5780c7 (23.08 11:44) писалась зашитым текстом БЕЗ адреса и с неверным именем шаблона, и судить только по копии значит врать (Annabelle 24.08: party2 read с адресом у неё, копия...
Хвост прогона 01.09 (дословно):
КРАСНЫЙ: человек с бронью после правки LOC-002 шёл на занятие, ни разу не получив адрес студии.
MET-079напр. 5находок: 12
Список НЕЗАКРЫТЫХ подозрений не сужается окном по возрасту: кандидат уходит из списка только по факту разрешения, а не по времени
Зелёное, когда: счётчик 'из них старше окна' печатается всегда; кандидат, найденный прошлым прогоном, либо присутствует в новом, либо его исчезновение объяснено фактом разрешения
Как судит
node scripts/qa-sent-never-delivered.mjs 7 studio_canggu_1778764028263 - эталон класса. Окно держит ЗНАМЕНАТЕЛЬ (сколько всего исходящих за N суток), но выборка застрявших и суд над ними идут по ВСЕЙ истории, и рядом печатается счётчик 'из них старше окна' - именно он называет, скольких прежние прогоны теряли по возрасту. Правило распространяется на любую мерку, где строка означает НЕЗАКРЫТУЮ беду (недоставленное сообщение, невыполненное обещание, неотвеченный клиент, незакрытый долг).
Хвост прогона 01.09 (дословно):
КОД 1: кандидатов на потерю самого сообщения 12 - это находка, а не незнание
MET-092напр. 5находок: 5
«Дожимали ли этого человека» мерить тем ключом, который пишется при отправке, а не тем, что читает код
Зелёное, когда: Пересечение двух хранилищ БОЛЬШЕ НУЛЯ, либо в коде явно написано, какое из них мертво и почему его нельзя убрать. Ноль пересечения без такой пометки - красное.
Как судит
node scripts/qa-trainer-notified-doors.mjs - SELECT count(*) FROM EventLog WHERE scope='ad:followup' -- и -- SELECT count(*) FROM SendLock WHERE id LIKE 'ad:followup:%' -- и пересечение множеств. | прибор scripts/qa-trainer-notified-doors.mjs НЕ вписан (21.08.2026): его тема - двери извещения тренера о брони, а эта мера про ключ дожима ad:followup; номер в шапке совпал случайно
Хвост прогона 01.09 (дословно):
КРАСНЫЙ: 5 броней, про которые одна колонка сказала бы «тренер не извещён», а тревога в приложении существует. Судить обеими дверями.
MET-175напр. 5находок: число в хвосте не названо
Живая нить Чангу, которую не назвал НИ ОДИН тик присмотра
Зелёное, когда: КОД 0: каждая созревшая живая нить (последнее входящее старше 45 мин) названа хотя бы одним тиком присмотра - по идентификатору либо по хвосту номера. КОД 1: есть нить, которой не назвал никто,...
Как судит
node scripts/qa-thread-never-watched.mjs 3
Хвост прогона 01.09 (дословно):
КОД 1: живой человек писал студии, и ни один тик присмотра его не назвал. Разобрать нить и записать её в журнал; причина щели ищется отдельно (окно выборки, упавший тик, спящий мак).
MET-182напр. 5находок: число в хвосте не названо
Закрывая день, слот не прячут вместо отмены: живой слот с publicVisible=0 в дне, где есть отменённые
Зелёное, когда: КОД 0 при знаменателе больше нуля. КОД 2 (знаменатель ноль) - НЕ проверено, а не чисто.
Как судит
node scripts/qa-day-cancel-partial.mjs (окно 90 суток назад + 14 вперёд). Знаменатель - дни студий, где есть хотя бы одна отмена. Признак находки: cancelledAt пуст, publicVisible=0, витрина снята РУКОЙ (updatedAt позже createdAt более чем на секунду - фикстуры прогонов рождаются скрытыми сразу). Самопроверка scripts/qa-day-cancel-partial-selftest.mjs доказывает оба исхода.
Хвост прогона 01.09 (дословно):
КОД 1: день закрывали, а слот спрятали вместо отмены - для механизмов он идёт.
MSG-023-bisнапр. 5находок: число в хвосте не названо
Зеркало подтверждения брони = тело шаблона из кабинета Меты, а не наша локальная копия
Зелёное, когда: код 0 при ЗНАМЕНАТЕЛЕ больше нуля; подсадка 1/1 и контроль 1/1 обязательны, иначе код 3; судимых записей в окне нет - код 2 «не проверено», зелёным не считается
Как судит
node scripts/qa-mirror-matches-cabinet.mjs [--since=ISO] (по умолчанию судит с выката починки двери 0f5780c7, 2026-08-23T04:00:00Z; вся история - явным --since=2026-01-01) (судит OUTBOUND с templateName LIKE 'booking_confirmed%', только те имена, у которых тело есть в снимке lib/template-bodies.json; подсадка боевой формы и контроль ложного среза выполняются в памяти на каждом прогоне, без единой записи в базу)
Хвост прогона 01.09 (дословно):
КОД 1: зеркало врёт - персонал и присмотр читают текст, которого человек не получал.
MSG-031напр. 5находок: число в хвосте не названо
Падение отправки клиенту доносит на себя фактом, а не намерением
Зелёное, когда: 5 веток из 5 и ноль оставшихся фикстур; тестовой студии нет - код 3 «не проверено»
Как судит
npx tsx scripts/qa-delivery-alarm-live.mts (стенд qatest_cab_studio, своя нить и номер канона; 5 веток на каждом прогоне: падение ставит метку инбокса и строку журнала delivery:failed, повтор в 15 минут донос не удваивает, наш замок объёма провалом не считается, успешная отправка доноса не поднимает)
Хвост прогона 01.09 (дословно):
ЗНАМЕНАТЕЛЬ: веток проверено 5, провалов 1; фикстур осталось 0
MSG-113напр. 5находок: число в хвосте не названо
Язык готового исходящего сверяется с языком разговора; ответ на чужом языке не может уйти клиенту
Зелёное, когда: скан: 0 расхождений на пути агента после 2026-08-10; в EventLog нет строк «ответ был на чужом языке - откат», а строки «перехвачен и переведён заново» разбираются как срабатывание защиты
Как судит
npx tsx scripts/qa-outbound-language.ts (прибор существовал, реестр звал меру словами; связь подтверждена запуском 21.08.2026) | прежнее описание: Два слоя. Скан: npx tsx scripts/qa-outbound-language.ts [дней] [studioId] - язык разговора ведётся прод-судьёй languageSample, язык ответа d
Хвост прогона 01.09 (дословно):
Done 😊 Your booking has been canceled. We'd love to welcome you back on any day that's convenient for you!
MSG-121напр. 5находок: 2
Ни одного системного исходящего в тихие часы Бали - дожимы и приглашения молчат с 20:00 до 09:00
Зелёное, когда: 0 находок; знаменатель непустой (иначе это не чистота, а «не проверено», код 2)
Как судит
node scripts/qa-quiet-hours.mjs --дней=1 - судит ФАКТ по WhatsAppMessage: исходящее живой студии в тихие часы по поясу студии (20:00-09:00). Законны реплики в живом разговоре (входящее свежее 30 минут), ОДИН запоздалый ответ на остывшее входящее, квитанции по свежей оплате (MonoInvoice.status='success') и шаблоны по расписанию занятия. Знаменатель печатает каждый отсев отдельной цифрой; нечитаемые даты - своим числом
Хвост прогона 01.09 (дословно):
КРАСНЫЙ: 2 системных исходящих в тихие часы Бали.
Перепрогон 02.09:
КРАСНЫЙ: 1 системных исходящих в тихие часы Бали.
MSG-161напр. 5находок: число в хвосте не названо
Утверждение о новизне в клиентском тексте опирается на событие в данных
Зелёное, когда: КОД 0 - каждая отправленная фраза о новизне подтверждена событием не старше суток при знаменателе больше нуля; КОД 1 - назвать поимённо человека, слот и разрыв в сутках; КОД 2 - судимых дожимов за...
Как судит
node scripts/qa-followup-slot-claims.mjs - берёт дожимы студии за 90 суток, вынимает из утверждённого текста притязание на новизну ('just opened up' / 'только что открылись' / 'baru saja terbuka') и сверяет его с фактом по каждому названному слоту: TimeSlot.createdAt и последняя правка против даты отправки. Отдельно судится притязание на срок ('на этой неделе') - назван ли день той самой недели. Самопроверка обоих исходов идёт ДО вердикта: подсадка недельного утверждения 1/1, контроль ложного среза 0/0; число нечитаемых дат и отсеянных стендовых строк печатается всегда. ПУТЬ ПЕРЕПИСАН 27.08.2026 (присмотр 13:30 Бали): здесь стоял 'grep клиентских текстов ... Замер по истории: node...
Хвост прогона 01.09 (дословно):
• 2026-09-01T01:01:47.991+00:00 🤍Wen31_🐝mahendr4🤍 (studio_canggu_1778764028263): сказано «только что открылись» про Wednesday 11:00, а слот 2026-09-02 заведён за 10 суток до отправки
MSG-162напр. 5находок: число в хвосте не названо
«Передал студии» обязано кончаться ЖИВЫМ ответом, а не только поднятой тревогой
Зелёное, когда: ноль тревог старше 12 часов без живого ответа и без брони (код выхода 0)
Как судит
node scripts/qa-handoff-unanswered.mjs [часов=12] [studioId] - берёт StaffAlert «Booking needs a human»/«Assistant had no answer» с привязкой к разговору и ищет после неё исходящее от живого человека (fromAgent=0) ЛИБО появившуюся бронь этого телефона. Тестовый номер стенда исключён поимённо. Чьё слово последнее - подсказка, не вердикт: просьбу читать в теле тревоги.
Хвост прогона 01.09 (дословно):
последнее слово: наше (подсказка, не вердикт - просьба в теле тревоги выше)
MSG-174напр. 5находок: 12
Исходящее WhatsApp не остаётся вечно на 'sent': застрявшие старше 2 часов разделены на «потерян статус» и «потеряно сообщение», Instagram судится отдельно
Зелёное, когда: кандидатов на потерю самого сообщения ноль; ненулевое число разбирается пофамильно - человек либо получил строку другим путём, либо его поднимают
Как судит
node scripts/qa-sent-never-delivered.mjs [дней] [studioId]. Берёт OUTBOUND со status='sent' старше 2 часов (нормализация replace(createdAt,'T',' ') по MET-063), ИСКЛЮЧАЕТ канал Instagram (clientPhone LIKE 'ig:%') и делит остальное надвое: если ПОСЛЕ строки человек писал сам или получал другое исходящее delivered/read - связь жива, застрял СТАТУС; если после неё тишина - кандидат на потерю самого сообщения. Знаменатель по каналам и статусам печатается всегда, голый ноль запрещён.
Хвост прогона 01.09 (дословно):
КОД 1: кандидатов на потерю самого сообщения 12 - это находка, а не незнание
MSG-178напр. 5находок: число в хвосте не названо
Одноразовая эскалация к человеку повторяется, пока условие живо: один пуш навсегда неотличим от исполнения
Зелёное, когда: у каждого механизма с ЯВНЫМ условием маркер посуточный и ни одного живого условия старше суток без повторного зова; у механизма со СЛАБЫМ признаком (никто не открыл инбокс) повтор разрешён ТОЛЬКО...
Как судит
node scripts/qa-escalation-marker-scope.mjs (прибор существовал, реестр звал меру словами; связь подтверждена запуском 21.08.2026) | прежнее описание: ШАГ 1, ОБЯЗАТЕЛЬНЫЙ ПЕРЕД ШАГОМ 2: померить, ЧЕМ вызван зов - поднять последнее входящее каждого живого условия и посчитать, скольким из них человек д
Хвост прогона 01.09 (дословно):
КОНТРОЛЬ: механизм с выкаченной правкой прошёл (promise:unservable) - значит мерка умеет и зеленеть, а не красит всё подряд
MSG-179напр. 5находок: число в хвосте не названо
Вопрос клиента, на который никто никогда не ответил, виден отдельно от вежливых концовок
Зелёное, когда: каждая строка в графе «с настоящей просьбой» либо отвечена, либо названа в журнале присмотра с причиной, почему автономно ответить нельзя. Само по себе ненулевое число дефектом не является - дефект в...
Как судит
node scripts/qa-open-questions.mjs [часов=24] [studioId]. Берёт разговоры студии, где последнее слово за клиентом и оно старше порога, и делит их надвое: настоящая просьба (вопросительный знак или просительные обороты tolong/boleh/minta/kirim/let me know/how much/berapa/apakah) против вежливой концовки (ok, sip, makasih, thanks, noted, yes, эмодзи). Печатает ОБА числа рядом. Умение падать: флаг --probe требует, чтобы эталонный случай ...830062 (mi.cha, «Still no contact from the owner about a collab with my retreat ?») был НАЙДЕН и чтобы хотя бы одна вежливая концовка была отсеяна; иначе код 2. Контроль объёма: ноль разговоров у студии даёт код 3 «проверять нечем», а не зелёное; число...
Хвост прогона 01.09 (дословно):
«Hi Gravity Stretching Canggu team, My name is Aldy from GetYourGuide. I previously attempted to reach you out but still hasn't managed to connect that's why I'm reaching out to you»
MSG-223напр. 5находок: число в хвосте не названо
Застревание на status='sent' судится ПО КАНАЛУ НИТИ: для ig:/fb: это конечное состояние, а не недоставка
Зелёное, когда: ноль WhatsApp-исходящих в sent старше порога при знаменателе WhatsApp больше нуля; соцсетевые sent в ставки не попадают никогда
Как судит
node scripts/qa-stuck-sent-by-channel.mjs [--hours=N] [--days=N] [--probe] [--control]. Канал берётся из clientPhone (префикс ig:/fb: против номера). Ставкой считается ТОЛЬКО WhatsApp-исходящее в статусе sent старше порога; соцсетевые печатаются отдельным числом как отсеянные. Знаменатели обязательны: всего исходящих, нечитаемых дат, по каждому каналу total/sent/дошло дальше sent/failed. Код 2 при пустом знаменателе - «не проверено», а не «чисто». Подсадка --probe объявляет соцсетевую строку WhatsApp-нитью и старит её: прибор обязан её найти. Ложный срез --control объявляет соцсетевыми ВСЕ строки: ставок обязано быть ноль.
Хвост прогона 01.09 (дословно):
НЕ СТАВКИ, отсеяно по каналу: соцсетевых sent в окне 2 - для них sent конечное состояние, тревоги по ним ложные
MSG-238напр. 5находок: число в хвосте не названо
Наше собственное обещание вернуться не остаётся тёмным
Зелёное, когда: тёмных 0 при знаменателе больше нуля - код 0; есть тёмные - код 1 с поимённым списком; знаменатель пуст - код 2 «не проверено». Ключ --подсадить обязан дать четыре исхода на боевом формате: тёмное...
Как судит
node scripts/qa-our-promise-watcher.mjs [--days=30] [--hours=6] [--подсадить]. Знаменатель - ходы АГЕНТА (fromAgent=1), где МЫ берём ход на себя дословным оборотом («I have passed this to the studio», «goes to the studio», «the studio will confirm», «I will chase it», «sudah saya sampaikan», «передал студии»), и которым уже дали --hours часов. Тёмным считается ход, после которого в нити нет НИ нашего слова, НИ слова человека. Даты читаются функцией с признаком нечитаемости (MET-063), их число печатается рядом со знаменателем.
Хвост прогона 01.09 (дословно):
ВЕРДИКТ: КРАСНЫЙ
MSG-251напр. 5находок: число в хвосте не названо
Дата и время в билете - на языке получателя, а не только рамка шаблона
Зелёное, когда: КОД 0 при знаменателе больше нуля. 24.08 первый прогон дал КОД 1: билетов за 90 сут 537, НЕ англоязычных 6, чужой формат внутри у ВСЕХ ШЕСТИ (все шесть стендовые - живому такой билет ещё не уходил)....
Как судит
node scripts/qa-ticket-date-localised.mjs (окно 90 сут, --since=<ISO> для точки отсчёта). Судит ТОЛЬКО билеты booking_confirmed*, чьё тело НЕ английское (кириллица или индонезийские слова рамки), и ищет внутри английский месяц, английский день недели или am/pm. Английские билеты в знаменатель не входят: для англоязычного получателя это его родной формат, а не дефект.
Хвост прогона 01.09 (дословно):
Источник: lib/booking-notify.ts:161/164 и app/api/bookings/route.ts:519/522 - обе двери строят значение по-английски.
MSG-255напр. 5находок: 8
Стена: наших сообщений подряд больше, чем сказал человек, и всё внутри десяти минут
Зелёное, когда: 0 стен; знаменатель непустой
Как судит
node scripts/qa-outbound-walls.mjs --дней=30 - судит ПРЕВЫШЕНИЕ над сказанным человеком, а не длину серии: стена = наших не-билетов подряд больше, чем его слов плюс одно, в окне 10 минут. Билеты броней (booking_confirmed*) вне счёта по правилу владельца, нити персонала и стенда отсеиваются, каждый отсев печатается своим числом
Хвост прогона 01.09 (дословно):
КРАСНЫЙ: 8 стен - человек получил подряд больше, чем сказал сам.
MSG-259напр. 5находок: число в хвосте не названо
У живого тренера нет доступа к нитям тестовых номеров
Зелёное, когда: 0 находок; знаменатель непустой
Как судит
npx tsx scripts/qa-live-trainer-on-test-thread.mts - судит строки WhatsAppConversationAccess, где тренер НЕ стендовый (не QA, не Gr, не демо-учётка ревьюера), а нить принадлежит тестовому номеру по канону lib/test-slots.ts. Убуд считается отдельным числом и красным не делает - студия партнёрская, её доступами распоряжаемся не мы
Хвост прогона 01.09 (дословно):
КРАСНЫЙ: живой тренер видит в инбоксе нашу пробу и принимает её за работу.
MSG-260напр. 5находок: число в хвосте не названо
Перенос брони называется переносом, а не приходит как новая запись
Зелёное, когда: КОД 0: немых переносов нет, знаменатель непустой. КОД 1: есть немой перенос. КОД 3: подтверждений в окне нет - вердикт о чистоте не выносится
Как судит
node --experimental-strip-types scripts/qa-silent-reschedule-notice.mjs - судит ТО, ЧТО ВИДЕЛ КЛИЕНТ: два подтверждения booking_confirmed с ОДНИМ билетом в один разговор, где названо разное время и разрыв больше минуты, а в окне +-10 минут вокруг второго нет ни одного исходящего со словом о переносе (три языка: перенес/сдвин/вместо, moved/rescheduled/changed to, dipindah/digeser). Свои номера и стенды отсеиваются прод-каноном isTestPhone из lib/test-slots.ts, а не списком в приборе
Хвост прогона 01.09 (дословно):
немых переносов: 10
MSG-267напр. 5находок: 5
Один и тот же вопрос не задаётся дважды в пачке: строка-факт «твой последний ответ» видит и ещё не отправленный черновик
Зелёное, когда: КОД 0: после точки отсчёта пачек с вопросом в обоих наших сообщениях больше нуля и повтора одного и того же вопроса среди них нет. КОД 1: человек получил один и тот же вопрос дважды в пределах 180...
Как судит
node scripts/qa-same-question-in-burst.mjs
Хвост прогона 01.09 (дословно):
КОД 1: после точки отсчёта человек 5 раз(а) получил один и тот же вопрос дважды в пачке.
MSG-269напр. 5находок: 3
Переспрос «не дошло, напишите ещё раз» уходит только туда, где слов для нас не было
Зелёное, когда: КОД 0 - после починки все переспросы ушли на нечитаемое при знаменателе больше нуля; КОД 1 - назвать поимённо человека, его слова и нашу строку; КОД 2 - переспросов класса в окне не было (НЕ...
Как судит
node scripts/qa-reask-on-readable.mjs [--days=N] [--since=ISO] - берёт исходящие агента (fromAgent=1) за окно, отбирает СЕМЬЮ формулировок переспроса по английскому ЧЕРНОВИКУ (body, до перевода: «did not come through», «did not quite catch», «images/photos/attachments/video notes do not ...», «write it in a couple/few words»), и для каждого поднимает ближайшее предшествующее ВХОДЯЩЕЕ. Нарушение - когда то входящее было обычным ТЕКСТОМ с непустым телом. Медиа (image, document, media, unsupported, video, audio, sticker, voice) и заглушка [attachment] переспрос ОПРАВДЫВАЮТ даже с подписью: картинку мы правда не видим. Персонал отсеивается каноном staffPhoneTails, стенды - isTestPhone, регистр...
Хвост прогона 01.09 (дословно):
КОД 1: ПОСЛЕ ПОЧИНКИ ПЕРЕСПРОС УШЁЛ НА ЧИТАЕМЫЙ ТЕКСТ 3 раз(а).
MSG-273напр. 5находок: число в хвосте не названо
Кнопка плохой новости в композере кабинета обязана нести конкретику: шаблон роли слома плана с 0 переменных и 0 кнопок = можно отправить только пустоту
Зелёное, когда: КОД 0: у каждой кнопки плохой новости есть переменная или кнопка. КОД 1: есть пустая. КОД 2: композер не предлагает ролей слома - НЕ ПРОВЕРЕНО. КОД 3: самопроверка не прошла или форма боевого файла...
Как судит
node scripts/qa-composer-bad-news-shape.mjs. Читает БОЕВЫЕ файлы: COMPOSER_TEMPLATE_KEYS из app/api/whatsapp/conversations/[id]/messages/route.ts, умолчания ролей из lib/message-templates.ts, формы из lib/template-shapes.json. Роли слома: schedule_change, class_cancelled. Истории не смотрит намеренно - мерит ДВЕРЬ, поэтому не может стать вечно красной от старого случая.
Хвост прогона 01.09 (дословно):
Смена роли в композере - решение владельца (клиентский текст), не правка тестировщика.
MSG-279напр. 5находок: 1
Служебный собеседник, исключённый из автоматики, не остаётся без человека: последнее слово в такой нити не может быть чужим дольше 30 минут
Зелёное, когда: КОД 0: в каждой служебной нити последнее слово наше либо тишина моложе 30 минут. КОД 1: служебный ждёт. КОД 2: список пуст или знаменатель ноль - НЕ ПРОВЕРЕНО, зелёным не считать. КОД 3: проба не...
Как судит
node_modules/.bin/tsx scripts/qa-internal-contact-unanswered.mts [--days=N]. Список служебных берётся из БОЕВОГО lib/internal-contacts.ts (копии нет намеренно). Знаменатель - нити этих номеров с входящим за окно; находка - последнее сообщение нити входящее и старше 30 минут. Даты нормализуются по MET-037, нечитаемые печатаются отдельным числом. Проба гоняет ТОТ ЖЕ разборщик в обе стороны. ДОПИСАНО 29.08 (присмотр 08:15 Бали): у каждой находки печатается ЗАПАС СВОБОДНОГО ОКНА - 24 часа от ПОСЛЕДНЕГО ВХОДЯЩЕГО, и отдельной строкой «из N ждущих окно ещё открыто у K, закрыто у M». Причина: список без срока читается как вечная задача, а у него есть час, после которого обычным текстом ответить...
Хвост прогона 01.09 (дословно):
КОД 1: служебных собеседников без ответа 1 из 3. Автоматика в эти нити не пишет по решению владельца - значит ответить обязан человек, и он должен об этом узнать.
MSG-284напр. 5находок: 1
Человек отказался от КАТЕГОРИИ дней - дожим не зовёт его в эту категорию
Зелёное, когда: КОД 0: после точки отсчёта ни одного зова в отвергнутые дни (доисправочные вердикта не меняют). КОД 1: есть, поимённо с цитатой отказа и цитатой зова. КОД 2: НЕ ПРОВЕРЕНО - отказов от категории в...
Как судит
node scripts/qa-day-category-refusal.mjs [--подсадка] [--дней=60]. Судит по тому, что человек РЕАЛЬНО видел (coalesce(translatedBody,body)); «зов» = день недели ПЛЮС час в одном сообщении; стенд и персонал отсеяны каноном _test-phones; рядом печатается ШИРОКАЯ СЕТЬ. С 30.08 вердикт выносится ТОЛЬКО по зовам позже выката починки: точка отсчёта берётся ИЗ ГИТА (git log -1 -S 'категорияЗанятых' -- lib/promise-date.ts), доисправочные печатаются отдельной строкой как история.
Хвост прогона 01.09 (дословно):
КОД 1: ЗОВ В ОТВЕРГНУТУЮ КАТЕГОРИЮ ПОСЛЕ ПОЧИНКИ 1 из 2 отказов
MSG-287напр. 5находок: 1
Не зовём в занятие, где не осталось ни одного места
Зелёное, когда: КОД 0: ни одного приглашения в занятие без свободных мест. КОД 1: есть, печатаются с числом позванных. КОД 2: НЕ ПРОВЕРЕНО - приглашений с часами в окне не было
Как судит
node scripts/qa-invite-vs-seats.mjs [--probe]. Часы из наших исходящих за 7 суток приписываются БЛИЖАЙШЕМУ живому слоту с этим часом на момент отправки (горизонт 8 суток); стенд отсеивается каноном _test-phones. Вердикт красный только при НУЛЕ свободных мест; теснота (мест 1-2 при десятках позванных) печатается отдельной графой как сведение, а не как брак
Хвост прогона 01.09 (дословно):
КОД 1: 1 занятий, куда мы звали УЖЕ ПОСЛЕ того, как последнее место ушло
MSG-296напр. 5находок: 1
Перевод не меняет лицо: предложение студии не уходит клиенту как просьба студии
Зелёное, когда: код 0; знаменатель печатается двумя числами (переводов и из них вопросов), код 2 честно называет отсутствие события
Как судит
node scripts/qa-translation-person-flip.mjs (знаменатель - переведённые ВОПРОСЫ живых людей за QA_DAYS=30, без шаблонов и агента; находка - первое лицо в переводе при полном отсутствии подлежащего в исходнике)
Хвост прогона 01.09 (дословно):
КОД 1: переводов с подменённым лицом: 1 из 152
Перепрогон 02.09:
КОД 1: переводов с подменённым лицом: 1 из 154
MSG-309напр. 5находок: число в хвосте не названо
Отложенный крючок, рождённый разбором текста, при срабатывании ПЕРЕПРОВЕРЯЕТСЯ сегодняшним кодом у исходного сообщения
Зелёное, когда: код 0 ТОЛЬКО при судимом знаменателе больше нуля; судимо 0 при непустом списке крючков = код 3 «не проверено», а не «чисто»
Как судит
node scripts/qa-stale-promise-hook.mjs (каждый живой крючок Чангу судится по ВХОДЯЩЕМУ, СОДЕРЖАЩЕМУ его цитату, а не по последнему слову нити; стенд отсеян; подсадка и контроль на дословных боевых строках). Боевая калитка - в app/api/cron/agent-autopilot: право цитировать заново зарабатывается у источника, иначе дожим уходит вариантом без цитаты.
Хвост прогона 01.09 (дословно):
КОД 1: крючок противоречит сегодняшнему правилу; снятие - решение владельца, мера не правит.
MSG-313напр. 5находок: число в хвосте не названо
Указание на соседнее сообщение вместо ссылки оплаты, когда кнопок оплаты у человека больше одной
Зелёное, когда: красных 0 при знаменателе больше нуля; если людей с двумя кнопками ноль - печатается «мерить было не на ком», а не чистота
Как судит
node scripts/qa-paylink-pointer.mjs - берёт СВОБОДНЫЕ (не шаблонные) исходящие Чангу в том виде, в каком их видел клиент (coalesce(translatedBody, body)), и красит те, где разом: речь об оплате, речь о ссылке или кнопке, и место названо лентой («above», «di atas», «выше») - но ТОЛЬКО в нитях, где у человека БОЛЬШЕ ОДНОЙ кнопки оплаты. Число кнопок берётся из MonoInvoice по броням этого номера, а не по тексту сообщений. Указание на кнопку ОТМЕНЫ («tap Cancel in the confirmation message above») намеренно НЕ красится - это боевая строка промпта про другую кнопку. Самопроверка обязана дать 8/8 в обе стороны, пробы дословные
Хвост прогона 01.09 (дословно):
«The Pay button in the payment message just above is that link - tap it and the card page opens for ticket 386. If it will not open, you can settle it at the studio before 15:00.»
PRM-003напр. 5находок: число в хвосте не названо
Слово живого человека закрывает обещание, только если оно О ТОМ ЖЕ
Зелёное, когда: тёмных обещаний ноль при знаменателе больше нуля; ветка byHuman не растёт за счёт закрытий не по теме; счётчик «тему назвать нечем» печатается отдельно и зелёным не считается
Как судит
node_modules/.bin/tsx scripts/qa-promise-backlog.mts --days=30 - у каждого обещания передачи студии определяется тема узким словарём (пакет, адрес, цена, время, звонок), и ветка «закрыто словом живого человека» засчитывается ТОЛЬКО когда все темы обещания покрыты текстом живого человека после обещания. Способность падать доказана обеими сторонами: --selftest (2 красных, 2 зелёных на боевых текстах байт в байт).
Хвост прогона 01.09 (дословно):
обещание: "The trainer will take that into account - the first hangs are only a few seconds, and everything goes through relaxation rather than effort."
TPL-028напр. 5находок: 2
У КАЖДОГО клиентского имени, которое система реально может отправить, одобрены все три языка и тело соблюдает правила владельца
Зелёное, когда: ноль имён с нарушением В ЖИВОМ ТЕКСТЕ и ноль имён с устаревшим снимком; печатаются четыре знаменателя: студий, достижимых имён, недостижимых тел, прочитанных тел
Как судит
npx tsx scripts/qa-client-template-canon.mts (прибор существовал, реестр звал меру словами; связь подтверждена запуском 21.08.2026) | прежнее описание: АВТОМАТОМ: npx tsx scripts/qa-client-template-canon.mts (--проба обязана краснеть). Обходит ДВА множества сразу. (1) Имена, достижимые через
Хвост прогона 01.09 (дословно):
ПРОВАЛ: 2
TPL-029напр. 5находок: число в хвосте не названо
Тело одобренного шаблона не содержит формы, запрещённой живым уроком агента (открытый вопрос о времени без единой конкретной точки)
Зелёное, когда: код 0 при знаменателе APPROVED больше нуля; код 1 = найдено красное тело; код 3 = прибор неисправен (подсадка не поймана или контроль ложно сработал); код 4 = знаменатель ноль, вердикт не выносится
Как судит
node scripts/qa-template-open-time.mjs - читает снимок кабинета university/meta/inventory.json, судит КАЖДОЕ тело со статусом APPROVED на трёх языках: открытый вопрос о времени (which day ... suits / какой день ... удобен / hari apa ... cocok) при полном отсутствии конкретной точки (день недели, час, дата). Печатает знаменатель (тел всего / APPROVED) и каждое красное тело целиком. Подсадка 3 фразы, контроль ложного среза 4 фразы - при промахе или ложном срезе прибор объявляет себя неисправным (код 3) и вердикта о живом корпусе не выносит.
Хвост прогона 01.09 (дословно):
! answer_after_wait_v1 [en] builds everything around your back, never through pain. A private is {{2}} for 1.5 hours. Which day this week suits you?
TPL-032напр. 5находок: число в хвосте не названо
Длительность занятия в прямом обращении к клиенту - «75-90 минут», а не «полтора часа»
Зелёное, когда: Код 0: ноль публичных формулировок в прямом обращении. Печатается знаменатель (сколько исходящих вообще называли длительность). Код 2 - длительность за окно не называлась, это НЕ зелёное.
Как судит
node scripts/qa-class-duration-wording.mjs [--days=45]. Сверяет каждый исходящий клиенту (то, что человек ВИДЕЛ - coalesce(translatedBody, body)) с решением владельца 09.08.2026, закреплённым в lib/class-time.ts: прямая работа с клиентом говорит «75-90 минут», публичная поверхность «полтора часа». Соседство «75-90» в том же тексте снимает вопрос. --self-test прогоняет шесть эталонов и заодно все 57 текстов lib/template-bodies.json.
Хвост прогона 01.09 (дословно):
Candice, I owe you one correction before Friday. Your session is with Gravity Stretching Canggu, and I gave you the wrong page earlier - the right one is bookgravity.com/canggu. Friday 15:00 stands, o

Направление 6. Агент продаж (канон, язык, лестница, согласие, автобронь, самообучение) (23)

ADS-001напр. 6находок: число в хвосте не названо
Реклама не жжёт деньги вхолостую: цена лида по дням против своей же медианы
Зелёное, когда: ни одного законченного дня дороже удвоенной медианы и ни одного дня с расходом при нуле лидов
Как судит
node scripts/ads-daily-lead-price.mjs [--дней=14] [--подсадить] - расход Меты по дням (level=campaign) против живых лидов в проде (WhatsAppConversation.adReferralAt). Красное: день дороже удвоенной медианы окна ЛИБО расход больше доллара при нуле лидов. Сегодняшний день по Бали не судится, он не кончился
Хвост прогона 01.09 (дословно):
- 2026-08-24: лид стоил $6.70 при медиане $2.22
Перепрогон 02.09:
- 2026-08-26: лид стоил $3.04 при медиане $1.50
AGT-001напр. 6находок: число в хвосте не названо
Агент не предлагает классы без тренера, несуществующие и прошедшие
Зелёное, когда: 0 выдуманных/пустых/прошедших предложений
Как судит
node scripts/qa-agent-live-checks.mjs --days=16 --only=AGT-001 (детектор AGT-001/039: «сегодня + час + маркер предложения» против TimeSlot - нет такого занятия / закончилось / началось / внутри отсечки) + npx tsx scripts/qa-past-class-unit.mts (границы правила classAlreadyOver и боевой tryAutobook, боевая половина только 09:00-21:30 по студии) ПОПРАВКА 23.08.2026: --only обязателен - без него прибор выносит ОБЩИЙ вердикт по всем семи детекторам, и попадание чужого детектора красило эту меру (23.08 четыре меры покраснели от находок AGT-001/039 и MSG-058).
Хвост прогона 01.09 (дословно):
ИТОГ (--only=AGT-001): ЕСТЬ ПОПАДАНИЯ - AGT-001/039
AGT-059напр. 6находок: число в хвосте не названо
В инструкциях агента нет пар «одно велит - другое запрещает» без названного приоритета
Зелёное, когда: Слой 1: 0 пар при НЕНУЛЕВОМ числе извлечённых запретов. Ноль запретов = код 2 и вердикт «НЕ чисто»: это сломанный разбор, а не чистый промпт. Слой 2: 0 пар без явного слова о приоритете.
Как судит
npx tsx scripts/qa-prompt-contradictions.mts (прибор существовал, реестр звал меру словами; связь подтверждена запуском 21.08.2026); на 21.08.2026 краснеет ПО ДЕЛУ: живые пары запрет-указание в промпте, вписана как есть | прежнее описание: СЛОЙ 1 (машинный, дешёвый и точный) - npx tsx scripts/qa-prompt-contradictions.mts [--studio=canggu]. Собирает БОЕВОЙ промпт целиком через b
Хвост прогона 01.09 (дословно):
но говорится в промпт строка 489: WHY THE MISTAKE IS EASY TO MAKE: the payment message names the amount but NOT the ticket. Only the booking confirmation directly above it carries the ticket number. S
Перепрогон 02.09:
но говорится в промпт строка 491: WHY THE MISTAKE IS EASY TO MAKE: the payment message names the amount but NOT the ticket. Only the booking confirmation directly above it carries the ticket number. S
AGT-100напр. 6находок: 1
Личный ход живого человека в нити не открывает лестницу продажи: голое согласие после него получает пустой черновик
Зелёное, когда: попаданий 0 при НЕПУСТОМ знаменателе; пустой знаменатель даёт код 2 - незнание, а не зелёное
Как судит
node scripts/qa-agent-social-thread.mjs --hours=720 [--studio=<id>] - берёт голые согласия клиента (короткая реплика без цифр и без вопроса, либо одни эмодзи), у которых ПРЕДЫДУЩЕЕ наше исходящее является личным ходом живого человека (OUTBOUND без fromAgent и без templateName - шаблон под именем тренера человеком не является, урок MET-072), и ловит ответ агента в пределах часа, где стоят И названный день или час, И зов выбрать. Тестовые хвосты читаются из боевого lib/test-slots.ts, служебные тапы CONFIRM:/CANCEL: исключены, судится то, что видел клиент
Хвост прогона 01.09 (дословно):
ИТОГ: ПОПАДАНИЙ 1
AGT-132напр. 6находок: число в хвосте не названо
Прямой вопрос о квалификации тренера закрывается фактом или передачей человеку, но не описанием метода
Зелёное, когда: Код 0: ноль уклонений. Контроль объёма печатается всегда (просмотрено входящих, попало в класс). Код 2 - за окно вопросов класса не было, это НЕ зелёное.
Как судит
node scripts/qa-trainer-credential-question.mjs [--days=45]. Ищет входящие, где спрашивают про ИСПОЛНИТЕЛЯ (trainer/coach/she/pelatih/тренер) и про его опыт, обучение, сертификат, работу с конкретным состоянием; берёт первый свободный исходящий и требует в нём либо факт о квалификации, либо честную передачу живому человеку. Встречный вопрос уклонением не считается, но засчитывается только если хвост нити всё-таки закрыл вопрос. --self-test доказывает, что разбор различает все три исхода.
Хвост прогона 01.09 (дословно):
О: Thank you for sharing this, Kak. It is clear you took that training seriously. Everything has been passed to the owner, and I will write here as soon as there is news about the next step.
AGT-167напр. 6находок: число в хвосте не названо
Отказ по СЕГОДНЯШНЕМУ часу не зовёт на другой день, пока у сегодняшнего дня есть бронируемый час
Зелёное, когда: КОД 0 при знаменателе больше нуля и нуле находок. КОД 1 - находка (перечисляется поимённо с часом, который был свободен). КОД 2 «не проверено» при пустом знаменателе - зелёным не считается. КОД 3 -...
Как судит
npx tsx scripts/qa-today-hour-skipped.ts [--days=60]. Берёт исходящие Чангу (не шаблоны), читает coalesce(translatedBody, body) - то, что видел человек. Признак: сообщение говорит про закрытость/полноту СЕГОДНЯШНЕГО занятия (три языка нити, кириллица без \b) И зовёт на другой день. Дальше по прод-функции isSlotBookableWithAttendees проверяется, был ли на момент отправки бронируемый GROUP-слот того же дня; часы, названные в самом сообщении, и слот с живой бронью ЭТОГО же клиента из альтернатив исключаются. Свои номера - прод-канон isTestPhone. Самопроверка: --selftest.
Хвост прогона 01.09 (дословно):
а СЕГОДНЯ (2026-08-24) было бронируемо: 15:00 (2/6)
AGT-168напр. 6находок: число в хвосте не названо
Служебная пометка расписания не пересказывается клиенту дословно (внутренний порог записи)
Зелёное, когда: КОД 0 при знаменателе больше нуля и нуле пересказов. КОД 1 - пересказ (перечисляется поимённо, с долей от знаменателя). КОД 2 при пустом знаменателе - НЕ зелёный. КОД 3 - самопроверка не сошлась.
Как судит
node scripts/qa-internal-note-quoted.mjs [--days=60]. Знаменатель - исходящие Чангу (не шаблоны) живым клиентам, где вообще говорится про закрытую или полную запись; числитель - те из них, что называют человеку внутренний порог («2.5 hours' notice», «2,5 часа», «2,5 jam sebelum», «needs N hours notice»). Кириллица без \b. Свои номера - прод-канон isTestPhone через scripts/_test-phones.mjs. Самопроверка: --selftest.
Хвост прогона 01.09 (дословно):
[2026-08-24T02:30:48.820+00:00] cmt6g1avh000304juynl1580g …3383: Today's 13:00 is already closed for booking - the studio needs 2.5 hours' notice. If that does not work, tap Cancel in the confirmation
AGT-172напр. 6находок: число в хвосте не названо
Урок без адресата не несёт местного факта (QRIS, KTP, рупии, малайский, соседи Бали)
Зелёное, когда: код 0 и в выводе три числа знаменателя
Как судит
node scripts/qa-lesson-local-facts.mjs [--подсадить]. Судит СОДЕРЖАНИЕ, а не происхождение: слова Canggu, Bali, Ubud в ссылке на случай признаком НЕ считаются, их несёт почти каждый урок. Красным делает только факт, ложный или пустой вне Индонезии. Знаменатель печатает три числа: активных уроков, без адресата, с адресатом.
Хвост прогона 01.09 (дословно):
Лечение: проставить studioSlug (список слагов через запятую, например «canggu,ubud»).
AGT-176напр. 6находок: 9
Наш вопрос брошен ради встречного вопроса клиента
Зелёное, когда: 0 находок ПОСЛЕ точки отсчёта при знаменателе больше нуля; подсадка 1/1 и контроль ложного среза 1/1 на том же боевом знаменателе, иначе код 3. Пустой знаменатель или отсутствие троек после точки -...
Как судит
node scripts/qa-own-question-abandoned.mjs - судит тройку подряд в одной нити: наш ход С ВОПРОСОМ -> входящее клиента, которое само содержит вопрос -> наш следующий ход. Находка = наш ход БЕЗ вопроса, то есть свой мы не вернули. Судится ТОЛЬКО то, что позже точки отсчёта (--since = момент активации урока les_ownq_0930_2508), история печатается отдельно и на код не влияет. Отдельным числом - те, после кого человек не написал больше ни разу. Стенды отсеиваются, судится то, что видел человек (translatedBody у исходящих, оригинал у входящих)
Хвост прогона 01.09 (дословно):
КОД 1: ПОСЛЕ УРОКА СВОЙ ВОПРОС БРОШЕН 9 раз(а).
AGT-183напр. 6находок: число в хвосте не названо
Просьба записать на названный день оставляет крючок возврата
Зелёное, когда: код 0 при знаменателе больше нуля: у каждой просьбы записать на названный день есть крючок. Код 2 при пустом знаменателе - это НЕ проверено
Как судит
node scripts/qa-ask-to-book-no-hook.mjs - зовёт БОЕВОЙ detectPromisedReturn (lib/promise-date.ts) на входящих Чангу за 90 суток; стендовые отсеиваются каноном scripts/_test-phones.mjs; рядом с узким вердиктом печатается ШИРОКИЙ ПОЛ по любой форме намерения с названным днём
Хвост прогона 01.09 (дословно):
Правку делать нельзя без кнопки владельца: расширение крючка = новые автоматические касания живых людей.
AGT-201напр. 6находок: 5
Наш вопрос не хоронится внутри собственного сообщения - он стоит последним словом
Зелёное, когда: разрыв долей не больше 5 п.п. при знаменателе больше нуля
Как судит
node scripts/qa-question-buried-in-tail.mjs (0 чисто / 1 разрыв долей больше 5 п.п. / 2 знаменатель пуст - НЕ чисто). Берёт наши свободные ответы Чангу с вопросом, делит на «вопрос ПОСЛЕДНЕЕ слово» и «вопрос похоронен серединой», и сравнивает долю ОТВЕЧЕННЫХ. Стенд и персонал отсеяны боевым каноном.
Хвост прогона 01.09 (дословно):
КОД 1: ПОХОРОНЕННЫЙ ВОПРОС ОТВЕЧАЮТ РЕЖЕ - разрыв больше 5 п.п.
AGT-202напр. 6находок: 6
Язык нити против языка карточки и против языка отправки (цель берётся из ДЛИННЫХ входящих, а не из последнего слова)
Зелёное, когда: 0 расхождений в обеих графах при знаменателе больше нуля
Как судит
node_modules/.bin/tsx scripts/qa-thread-lang-vs-card.mts (0 чисто / 1 расхождения / 2 знаменатель пуст / 3 подсадка цели не сошлась). Цель языка нити - большинство длинных входящих (>=12 знаков, не меньше 60% и не меньше двух образцов). ГРАФА 1 судит sentLang нашего ответа, ГРАФА 2 - карточку, по которой выбирается язык ШАБЛОНА, и печатает, сколько шаблонов человеку уже ушло не на его языке. Восемь подсадок цели прогоняются ДО вердикта.
Хвост прогона 01.09 (дословно):
КОД 1: расхождений 6 (ответов 3, карточек 3).
AGT-205напр. 6находок: 6
Дожим не срезает молча день, который мы сами предложили человеку на выбор
Зелёное, когда: КОД 0 при знаменателе больше нуля - ни одного среза. КОД 2 - знаменатель ноль (НЕ зелёное). КОД 3 - не прошли шесть собственных проб: подсадка среза 1/1, контроль «оба дня остались» 1/1, контроль...
Как судит
node scripts/qa-nudge-drops-offered-day.mjs [--days=60] [--до=ISO] [--подсадить]. Берёт исходящие Чангу с текстом дожима SILENT («just opened / baru saja terbuka / только что открылись»), у которых ПРЕДЫДУЩЕЕ сообщение нити тоже НАШЕ (то есть человек с тех пор молчал) и содержит вопрос-ВЫБОР с двумя и более днями недели. Сравнивает набор дней нашего вопроса с набором дней дожима и печатает СРЕЗАННЫЕ. Дни читаются на трёх языках; индонезийское «minggu» берётся как воскресенье только вне оборотов «minggu ini/depan/lalu». Стенд отсеивается каноном _test-phones.
Хвост прогона 01.09 (дословно):
КОД 1: дожим срезал наш же предложенный день 6 раз(а).
AGT-210напр. 6находок: число в хвосте не названо
Час, который МЫ САМИ назвали свободным и клиент принял, бронируется, а не «передаётся студии»
Зелёное, когда: КОД 0 при знаменателе больше нуля: ни одной передачи на свой же названный час. КОД 1: находки с цитатой клиента и совпавшим часом. КОД 2: фраз передачи в окне не было - НЕ ПРОВЕРЕНО, не зелёное. КОД...
Как судит
node scripts/qa-offered-hour-handed-off.mjs [--показать=N] [--самопроверка]. Знаменатель - исходящие Чангу с фразой передачи приватa в студию на трёх языках (set aside with the studio / passed the day and time to them / sudah kami amankan bersama studio / menyampaikan hari dan jamnya kepada mereka / передал день и час). Находка - такая фраза, когда во ВСЁМ пучке входящих подряд перед ней (не старше часа) клиент назвал час, который мы САМИ предложили за предшествующие сутки; час извлекается из четырёх форм записи (13:00, 13.00, 1pm, jam 1300, с разбором am/pm). Клиент судится по тому, что видел: coalesce(translatedBody, body) у исходящих, оригинал body у входящих. direction сравнивается...
Хвост прогона 01.09 (дословно):
мы:     Your private session is set aside with the studio - I have passed the day and time to them, and they confirm it right here.
AGT-222напр. 6находок: число в хвосте не названо
Перенос своей же брони агент не делает - человек переписывает руками
Зелёное, когда: 0 ручных перезаписей при знаменателе больше нуля (знаменатель = закрытые агентские брони). Пустой знаменатель - код 2 «НЕ ПРОВЕРЕНО», а не зелёное; провал самопроверки судьи - код 3
Как судит
node scripts/qa-move-done-by-hand.mjs [--days=60] [--до=ISO] [--подсадить] - ищет РУЧНУЮ ПЕРЕЗАПИСЬ: агентская бронь (метка AUTOBOOK, без метки MOVED) на дату D закрыта, и тот же телефон в окне -5..+60 минут завёл подтверждённое место на ту же дату в ДРУГОЙ час. Переносы машиной отсеиваются по метке MOVED на закрытом месте. Стендовые номера снимаются каноном lib/test-slots.ts. Печатает три числа - пар мест, людей, случаев человек-дата, потому что пара на пару даёт четыре сочетания и голое число прочиталось бы как число людей
Хвост прогона 01.09 (дословно):
ЧТО ЭТО ЗНАЧИТ: место делал агент, а переносил человек руками. Ступень 3b автоброни недостижима для своих же броней - замок стоит на дате занятия и берётся раньше неё.
ALR-041напр. 6находок: число в хвосте не названо
Служба доставки принимает отправку на КАЖДОЙ платформе, где у нас есть живые адреса
Зелёное, когда: на каждой платформе служба отвечает «принято» либо «адрес мёртв» (это ответ живого канала); отказ по учётным данным - красное
Как судит
node scripts/qa-push-really-delivers.mjs
Хвост прогона 01.09 (дословно):
- android/170d6d43c0ac: служба ОТКАЗАЛА - InvalidCredentials: Unable to retrieve the FCM server key for the recipient's app. Make sure you have provided a server key as directed by t
MET-158напр. 6находок: число в хвосте не названо
Полное молчание канала учителя отличается от смерти канала: тревога поднимается, только когда расписание молчание НЕ объясняет
Зелёное, когда: код 1 в обоих немых случаях (учить не у кого), но ТЕКСТ вердикта разный; код 0 только когда учитель говорил; код 2 - источник не ответил
Как судит
node scripts/qa-teacher-silence.mjs [--часов=24]. При нуле сообщений за окно читает расписание студии: слоты на её ЗАВТРА и брони на её СЕГОДНЯ, у которых нет ни attendanceConfirmedAt, ни checkedIn (условие повторяет alreadyKnown из lib/today-reminders.ts). Ноль и ноль - «красный для учёбы, но не тревога, канал проверять не надо». Иначе прежняя тревога «идти смотреть канал». Подсадка: --подсадка=немой-объяснимо | немой-необъяснимо (плюс прежние молчит|говорит).
Хвост прогона 01.09 (дословно):
КРАСНЫЙ: за окно живые люди Убуда не написали НИ ОДНОГО свободного текста. Уроки за эти сутки выводить не из чего - цикл учёбы обязан сказать это вслух, а не выдумывать приём и не пересказывать уже ра
MET-184напр. 6находок: 1
Наш ответ не начинается с радости, когда ПОСЛЕДНЕЕ слово человека в пачке говорит о боли, симптоме, диагнозе или неработающем лечении
Зелёное, когда: 0 находок при знаменателе больше нуля
Как судит
node scripts/qa-glad-on-pain.mjs (0 чисто / 1 радость поверх боли / 2 НЕ проверено - пустой знаменатель или сам прибор не прошёл свои пробы). Знаменатель - наши живые сообщения Чангу, шаблоны не судятся (они не пишутся под реплику), стенд и персонал отсеяны каноном _test-phones. У исходящих судится ИСХОДНИК до перевода (body), у входящих - НАШ перевод (translatedBody), иначе индонезийское описание боли не попадёт ни в один английский шаблон. Смотрится ПОСЛЕДНЕЕ сообщение пачки, а не первое - в этом и состоит дефект
Хвост прогона 01.09 (дословно):
КОД 1: РАДОСТЬ В ОТВЕТ НА БОЛЬ - 1 на 1751 наших сообщениях
Перепрогон 02.09:
КОД 1: РАДОСТЬ В ОТВЕТ НА БОЛЬ - 1 на 1758 наших сообщениях
MSG-146напр. 6находок: 1
Односложное слово клиента не меняет язык ответа и не переписывает карточку разговора
Зелёное, когда: 0 случаев после отсечки выката И объём проверки не нулевой (просмотрено хотя бы одно короткое входящее)
Как судит
npx tsx scripts/qa-lang-flip-guard.ts [дней] [ISO-отсечка]: боевым судьёй languageSample отбираются входящие, которым нет доверия (короче 12 букв, тап по кнопке), и сверяется, ушёл ли следующий наш ответ на языке ЭТОГО слова вместо языка последней доверенной фразы человека
Хвост прогона 01.09 (дословно):
ПРОВАЛ: 1 случаев у ЖИВЫХ клиентов ПОСЛЕ выката починки
MSG-303напр. 6находок: 2
Отказ по цене не перекрывается предложением брони - ни дожимом, ни живым ответом агента
Зелёное, когда: КОД 0 при знаменателе больше нуля: отказы по цене есть, ни один не перекрыт предложением брони. КОД 1: наши предложения названы поимённо, каждое подписано [ЖИВОЙ ОТВЕТ АГЕНТА] или [АВТОДОЖИМ]. КОД 2:...
Как судит
node scripts/qa-offer-after-price-refusal.mjs. Знаменатель - входящие Чангу со словами отказа ПО ЦЕНЕ (over my budget, too expensive, can't afford, terlalu mahal, kemahalan, дорого); отказ по ДНЮ в знаменатель не входит, у него своя калитка AGT-213. Находка - наше исходящее в течение 72 ч после отказа, зовущее занять место (Shall I book you in, записать вас, mau saya pesankan), БЕЗ новой ценовой вести (скидка, акция, местная цена по KTP). Клиент судится по тому, что видел: coalesce(translatedBody, body) у исходящих, оригинал в body у входящих. Стендовая нить …5347 отсеивается ДО любой цифры. Флаг --стенд судит стендовую нить отдельно, --позже=<ISO> вводит точку отсчёта
Хвост прогона 01.09 (дословно):
КОД 1: отказ по цене перекрыт предложением брони - 2 наших предложений при 8 отказах в 4 нитях
MSG-304напр. 6находок: число в хвосте не названо
Дословно та же строка не уходит второй раз, даже если человек написал между нашими ходами
Зелёное, когда: КОД 0 при знаменателе больше нуля. КОД 1: дословные повторы поимённо, с репликами человека между ними. КОД 2: знаменатель ноль - НЕ ПРОВЕРЕНО. КОД 4: самопроверки ядра не сошлись, о проде не судим
Как судит
node scripts/qa-verbatim-repeat-after-inbound.mjs [--days=N]. Знаменатель - наши пары исходящих в одной нити внутри 10 минут, МЕЖДУ которыми есть входящее (это ровно то, что нынешнее сужение окна стража dup_text пропускает: dupSince обрывается на последнем слове клиента). Находка - совпадение ДОСЛОВНОЕ по тому, что видел человек (coalesce(translatedBody, body)). Первая строка пары в статусе failed в находки НЕ идёт и печатается отдельным числом: повтор после отказа доставки законен. Стенд и персонал отсеиваются каноном _test-phones.mjs. Ядро вынесено функцией najdi() и гоняется шестью самопроверками - две ловящих (дословный повтор; повтор, видимый только в переводе) и четыре молчащих...
Хвост прогона 01.09 (дословно):
КОД 1: живые дословные повторы есть
MSG-312напр. 6находок: число в хвосте не названо
Ответ, посланный тику присмотра, не дойдёт никогда
Зелёное, когда: 31.08 боевой КОД 1: знаменатель 16 писем, разобрано 16, не разобрано 0, без транскрипта 3; мёртвых тиков 12 (лежат 36-83 ч). Контроль на подставном ящике с живым получателем КОД 0, подсадка на нём же...
Как судит
node scripts/qa-mail-to-dead-tick.mjs - вскрывает ~/.claude/chat-mail (ящик держит ТОЛЬКО недоставленные), по каждому письму ищет транскрипт получателя и сравнивает mtime транскрипта с mtime письма; получатель, не сделавший ни одного хода ПОСЛЕ письма, и при этом опознанный как прогон присмотра (в транскрипте есть «ПРИСМОТР ЗА ЧАТАМИ» или «bg-qa-orchestrator»), даёт КОД 1. Пустой ящик и нулевой знаменатель дают КОД 4 «не проверено», а не «чисто». Флаг --ящик=<путь> для подставного ящика, --подсадка для проверки красной ветки
Хвост прогона 01.09 (дословно):
ВЕРДИКТ: есть ответы, посланные одноразовым прогонам - их не прочитает никто
PRD-018напр. 6находок: число в хвосте не названо
Тестовая сборка не считается боевым устройством при выкате по воздуху
Зелёное, когда: оба исхода доказаны подсадкой 30.08.2026
Как судит
node scripts/ota-publish.mjs --channel=production --dry-run - сторож отсеивает устройства с variant='test' и ПЕЧАТАЕТ их число и имена вслух. Приёмка обоими исходами: снять пометку - код 1 «рантайм остался без обновления», вернуть - код 0 и строка «не считаю боевыми N устройств(а) с ТЕСТОВОЙ сборкой»
Хвост прогона 01.09 (дословно):
например: --message="поиск клиента в расписании"

Направление 8. Рутины и расписания (Vercel cron, Hermes, launchd) (5)

CRN-014напр. 8находок: число в хвосте не названо
Замок напоминания не ставится ДО отправки: убитый инстанс не должен оставлять 'отправлено' при нулевой отправке
Зелёное, когда: 0 строк
Как судит
node scripts/qa-reminder-lock-truth.mjs [--days=7] (0/1/2; --подсадить): каждый todayReminderSentAt живой брони обязан иметь один из двух следов - исходящее в разговорах номера в +-5 мин ИЛИ строку журнала reminders:today 'already known'; замок без обоих = «отправлено» при нулевой отправке (DEF-3)
Хвост прогона 01.09 (дословно):
cmt896e43002c04ju06xmoq4y | Magdalena (2/2) | замок 2026-08-27T00:31:26.161+00:00 | ubud - «отправлено» при нулевой отправке
CRN-100напр. 8находок: число в хвосте не названо
Молчащий сторож: задача числится живой, а ничего не пишет
Зелёное, когда: подсадка 5 из 5; на живых задачах ноль молчунов при знаменателе больше нуля; судимых задач нет - код 2
Как судит
npx tsx scripts/qa-watchers-alive.mts (судит НЕ «запускается ли», а когда задача последний раз что-то написала; молчание дольше двух её собственных периодов - красный; --подсадка проверяет само правило на пяти случаях без обращения к диску)
Хвост прогона 01.09 (дословно):
приёмка молчащего: launchctl kickstart -k gui/$(id -u)/<Label>, потом сверить число с ручным прогоном
INF-001напр. 8находок: число в хвосте не названо
Здоровье вывода: claude -p 'ok' с токеном из файла + замер времени; load и зомби: uptime; pgrep -f disclaimer | wc -l
Зелёное, когда: вызов быстрый, зомби нет
Как судит
npx tsx scripts/qa-executor-gate.ts - Живой вызов claude -p 'ok' токеном из пула с замером времени (env -u ANTHROPIC_API_KEY, ввод из /dev/null - см. INF-016) И здоровье площадки: uptime (порог load ~20 по 15-минутному среднему); НАСТОЯЩИЕ зомби `ps axo stat= | grep -c '^Z'` (порог 10) и ОСИРОТЕВШИЕ помощники `ps -o ppid= -p $(pgrep -f disclaimer) | awk '$1==1'` (порог 0). Голое `pgrep -f disclaimer | wc -l` НЕ годится - см. proven. | прибор scripts/qa-executor-gate.ts НЕ вписан (21.08.2026): сам заявляет себя INF-078 (номер сменён 17.08), к здоровью вывода отношения не имеет
Хвост прогона 01.09 (дословно):
ПРИЧИНА: учёток мака 4, из них в лимите 3, помечены мёртвыми 0 (ближайшая освободится 2026-09-02T15:00:00.000Z)
INF-078напр. 8находок: число в хвосте не названо
У задания вывода есть живой исполнитель ИМЕННО его рода: qa и lesson не ставятся в очередь, когда второстепенный узел тёмен
Зелёное, когда: код выхода 0: у lesson и qa доля не взятых никем ниже пятой части на окне 7 суток, знаменатель непустой. Код 2 (за окно не создано ни одного задания) проходом НЕ является. Все шесть проб гвоздя...
Как судит
npx tsx scripts/qa-executor-gate.ts - мерка по родам за 7 суток (создано / выполнено / не взято никем) плюс шесть проб боевой isExecutorAround на подсаженном пульсе (подмена на уровне чтения prisma.eventLog.findMany, настоящий журнал не пачкается) плюс живой опрос прода
Хвост прогона 01.09 (дословно):
ПРИЧИНА: учёток мака 4, из них в лимите 3, помечены мёртвыми 0 (ближайшая освободится 2026-09-02T15:00:00.000Z)
MET-050напр. 8находок: число в хвосте не названо
Время в запросах присмотра сравнивается только через julianday(replace(createdAt,'T',' '))
Зелёное, когда: Наивных сравнений 0; на подсаженном случае наивный теряет, правильный находит.
Как судит
node scripts/qa-naive-time-guard.mjs - две ноги, обе обязательны: (1) грепом -a по scripts/ наивных сравнений createdAt против datetime(now) должно быть 0; (2) на ЖИВЫХ данных наивный запрос «кто ждёт дольше 30 минут» обязан потерять человека, которого находит правильный. Пустой знаменатель ждущих = не проверено, а не успех.
Хвост прогона 01.09 (дословно):
ВЕРДИКТ: ПРОВАЛ

Направление 9. Внешние интеграции (Meta WhatsApp, Google, почта, платежи) (7)

ALR-015напр. 9находок: число в хвосте не названо
Ни одна тревога живой студии не написана мимо продукта (сырой SQL = доставки не было)
Зелёное, когда: ноль тревог с меткой без T за 7 суток при знаменателе больше нуля
Как судит
npx tsx scripts/qa-alert-delivered.mjs (подсадка: --selftest). Мера - формат метки времени: Prisma пишет 2026-08-20T13:43:29.468+00:00, сырой SQL кладёт 2026-08-20 13:57:09. Красный код 1, пустой знаменатель - код 2 (НЕ ПРОВЕРЕНО). ЗАПУСКАТЕЛЬ npx tsx обязателен с 23.08.2026: прибор берёт список NEEDS_HUMAN_ALERT_TITLES из lib/staff-alert.ts (копия убрана), а тот тянет алиас @/lib - голый node падает ERR_MODULE_NOT_FOUND.
Хвост прогона 01.09 (дословно):
2026-08-29T00:57:05.921+00:00 conv=cmqz4qc1x000404l8qz108vdi | Message not delivered
Перепрогон 02.09:
2026-09-01T05:05:18.945+00:00 conv=cmsineoy5003004jtkp2ld74f | Message not delivered
ALR-016напр. 9находок: число в хвосте не названо
Непринятая тревога, ждущая действия человека, не остаётся за списком имён свипа повтора
Зелёное, когда: ноль таких тревог при знаменателе больше нуля
Как судит
npx tsx scripts/qa-alert-delivered.mjs. Берутся тревоги живых студий, привязанные к разговору, старше суток, с заголовком ВНЕ NEEDS_HUMAN_ALERT_TITLES и вне списка извещений (новая бронь, отмена, ростер, неподтверждённые записи, фото клиента). ОГОВОРКА ЧЕСТНОСТИ: поле acceptedAt по ALR-014 не заполняется никогда, поэтому условие «не принята» сейчас выполняется всегда и работу отбирают остальные три признака. ПОПРАВКА 21.08.2026 (ALR-018), не снимать оговорку как протухшую: 20.08 в 09:50:30.365Z ручной проход batch:owner-20.08 проставил acceptedAt у 418 строк одной меткой, поэтому поле теперь ВЫГЛЯДИТ заполненным. По существу оговорка ВЕРНА и остаётся в силе: живой дверью не принято ни одной...
Хвост прогона 01.09 (дословно):
2026-08-29T00:57:05.921+00:00 conv=cmqz4qc1x000404l8qz108vdi | Message not delivered
Перепрогон 02.09:
2026-09-01T05:05:18.945+00:00 conv=cmsineoy5003004jtkp2ld74f | Message not delivered
ALR-018напр. 9находок: 119
Поле acceptedAt не считается принятием, пока его ставит пакет: принятие мерится живой дверью, а не штампом
Зелёное, когда: ноль просьб о действии старше суток без ЖИВОГО принятия, при знаменателе больше нуля. Пустой знаменатель даёт код 2 (не проверено), а не проход. ОТДЕЛЬНО: число принятий пакетом обязано быть видно в...
Как судит
node scripts/qa-unaccepted-action-alert.mjs --selftest. Принятой считается тревога с acceptedAt И acceptedBy НЕ по шаблону batch:*. Отбираются только тревоги, ПРОСЯЩИЕ действия (needs a human / needs you / still not opened / not in the schedule / nobody picked up); осведомительные (New booking, Pre-class roster) намеренно вне знаменателя - их не принимает никто и никогда, и счёт их утопил бы находку в шуме. Рядом со знаменателем печатаются три числа отдельно: взято живым человеком, помечено пакетом, не взято.
Хвост прогона 01.09 (дословно):
ВЕРДИКТ: КРАСНЫЙ - 119 просьб о действии никто не взял, плюс 7 тревог с НЕ УЗНАННЫМ именем
Перепрогон 02.09:
ВЕРДИКТ: КРАСНЫЙ - 121 просьб о действии никто не взял, плюс 10 тревог с НЕ УЗНАННЫМ именем
MET-125напр. 9находок: число в хвосте не названо
Приборы-сироты: мера числится без команды, а прибор для неё уже есть
Зелёное, когда: ноль заявленных связей, не вписанных в реестр
Как судит
node scripts/qa-orphan-instruments.mjs [--слабые] [--проба]. Сопоставляет активные записи реестра без прямой команды со скриптами scripts/, и связь считает заявленной ТОЛЬКО когда номер меры стоит в первых ТРЁХ строках файла - там, где скрипт представляется. Номер глубже печатается отдельно как слабое совпадение и в долг не идёт: упоминание не доказательство, скрипт может ссылаться на соседнюю меру в пояснении. Ничего не правит. Проба гоняет разбор на четырёх случаях, из них два отрицательных (чужой номер в тексте, номер глубже трёх строк).
Хвост прогона 01.09 (дословно):
лечение: вписать команду в method - по одной, каждую подтвердив запуском
MET-190напр. 9находок: число в хвосте не названо
Номер меры, названный в коммите, существует в реестре к дате коммита
Зелёное, когда: код 0; знаменатель печатается (коммитов с ид, ид проверено); код 2 честно называет «судить нечего»; код 3 - реестр или git не прочитан
Как судит
node scripts/qa-measure-id-registry.mjs (git log с точки отсчёта 2026-08-30, ид мер из заголовков коммитов против checks.json: мера обязана существовать и быть рождена не позже даты коммита; красная сторона - QA_TOCHKA=2026-08-29 даёт 3 нарушения на живой истории 29.08)
Хвост прогона 01.09 (дословно):
2fb6c4f4 2026-08-30 называет MSG-298 - меры НЕТ в реестре («Присмотр 02:30: вопрос о языке уходит чужим письмом (урок), мера MSG-2»)
MET-191напр. 9находок: 19
У прибора, который делит находки на «до/после починки», точка отсчёта берётся из гита, а не вписана числом
Зелёное, когда: КОД 0 при знаменателе больше нуля; КОД 1 - нарушители поимённо; КОД 2 - приборов с разделом нет вовсе; подсадка 3/3
Как судит
node scripts/qa-cutoff-from-git.mjs [--подсадка]. Читает scripts/qa-*.{mjs,mts,ts}; знаменатель - файлы, ПЕЧАТАЮЩИЕ раздел «ДО КАЛИТКИ» или «ДО ПОЧИНКИ»; нарушение - такой файл без вызова git за датой. Признак сужен живой калибровкой: широкая редакция (плюс слова «ТОЧКА ОТСЧЁТА») дала 95 из 98, потому что у большинства приборов это слова в комментарии про начало окна выборки, а не раздел до/после - проверено чтением qa-geo-claim и qa-week-rule.
Хвост прогона 01.09 (дословно):
КОД 1: точка отсчёта вписана числом у 19 из 28
Перепрогон 02.09:
КОД 1: точка отсчёта вписана числом у 20 из 30
PRD-017напр. 9находок: число в хвосте не названо
Отпечаток для выката считается в окружении ТОГО профиля, чей это канал
Зелёное, когда: отпечаток канала равен рантайму устройств этого канала
Как судит
node scripts/ota-publish.mjs --channel=staff-qa --message=реестр-PRD-017 --dry-run --ignore-dirty. Канал вписан конкретно (staff-qa - канал телефона приёмки, до 24.08.2026 тут стоял заполнитель <тестовый>, и мера не запускалась ночным прогоном вовсе); --dry-run обязателен, боевой канал этой мерой не трогается никогда. Напечатанный отпечаток обязан совпасть с рантаймом устройств этого канала. И ОТДЕЛЬНО - боевой прогон: после публикации скрипт сверяет опубликованный рантайм с локальным отпечатком и падает при расхождении. Умение падать доказано дважды: без чтения env отпечаток возвращается к продовому; без env в самой команде публикации обновление уходит в чужой рантайм.
Хвост прогона 01.09 (дословно):
в боевом прогоне публикация уже была бы сделана, и вот это осталось бы незамеченным.

Направление 10. Наблюдаемость (EventLog, логи, алерты, отчёты) (8)

AGT-140напр. 10находок: число в хвосте не названо
Правило «запись закрывается за 2.5 часа до начала» не называется причиной по занятию, которое уже началось
Как судит
node scripts/qa-notice-rule-past-class.mjs --days=60 (или --since=<ISO>). Прибор берёт исходящие Чангу, оставляет те, что называют правило 2.5 часов ПРО СЕГОДНЯ (три языка), переводит метку в часы Бали и сверяет с началами групповых занятий этого дня из TimeSlot. Красный - когда все групповые дня уже начались. Флаг --probe: подсадка (то же правило в 15:00 Бали) обязана краснеть 1/1, контроль ложного среза (то же правило в 08:30, когда группы впереди) обязан молчать 0/0.
Хвост прогона 01.09 (дословно):
Good afternoon. Today's group class is already closed for booking - the studio needs 2.5 hours' notice before it starts. Tomorrow, Saturday, 11:00 and 15:00 are open. Which one suits you better?
ALR-020напр. 10находок: 6
Живая бронь проходит дверь оповещения продукта, а не спасается тревогой, дописанной нами руками
Зелёное, когда: ноль живых броней без машинной тревоги по своему слоту в 15 минут, при знаменателе больше нуля
Как судит
node scripts/qa-booking-notify-door.mjs [--since=<дата>] [--window=15] [--studio=<id>]. У каждой живой брони студии в окне ищется StaffAlert ПО ЕЁ ЖЕ slotId (не «хоть какая-то тревога рядом» - первая редакция мерила именно так и Candice пропустила бы, если бы рядом оказалась чужая тревога). Считается задержка от createdAt брони до первой такой тревоги. Наши стенды (qatest_/e2e/probe_ и имена с test) из знаменателя вон. Печатается знаменатель, число нечитаемых дат, подсадка молчаливого слота и контроль ложного среза; пустой знаменатель даёт код 2 «НЕ ПРОВЕРЕНО», а не зелёное.
Хвост прогона 01.09 (дословно):
КОД 1: броней без своевременной тревоги 6 из 42.
Перепрогон 02.09:
КОД 1: броней без своевременной тревоги 6 из 45.
ALR-026напр. 10находок: число в хвосте не названо
Срочный зов укладывается в бюджет и не молчит там, где обязан звать
Зелёное, когда: код 0 при знаменателе больше нуля; больше 6 ног за 14 суток ИЛИ хотя бы один кандидат без зова - код 1 с поимённым списком; ни зовов, ни кандидатов, ни ног - код 2 «не проверено», а не зелёное;...
Как судит
npx tsx scripts/qa-urgent-call-budget.mts (окно 14 суток, но не раньше LIVE_FROM - дня выката дверей рождения; ноги считаются строками журнала urgentcall:step, оканчивающимися на ':wa' - wa-skipped и wa-failed бюджет не тратят; кандидаты считаются по броням и отменам прод-базы с отсевом стенда БОЕВОЙ дверью lib/test-slots, наличие зова - по замку staff:urgentcall:born:*; вердикт вынесен чистой функцией judge, и на каждом прогоне она трижды проверяется в памяти без записи в базу)
Хвост прогона 01.09 (дословно):
ВЕРДИКТ: КРАСНЫЙ
ALR-043напр. 10находок: число в хвосте не названо
У адресной тревоги о новой брони есть живое подтверждение ознакомления тренером в срок
Зелёное, когда: ноль адресных тревог о брони старше срока (24ч или до начала занятия) без живого принятия, при знаменателе больше нуля; число batch:* у новых тревог обязано быть нулём. Пустой знаменатель даёт код 2...
Как судит
node scripts/qa-booking-ack.mjs [--since=2026-08-24T00:00:00.000Z] [--hours=24] [--selftest]. Берутся адресные (userId NOT NULL) тревоги kind=booking живых студий с рождения механизма; живым принятием считается acceptedAt при acceptedBy НЕ по шаблону batch:* (урок ALR-018: пакетный проход 20.08 пометил 418 тревог одной секундой). Срок обоснован числом из прод-базы 24.08 (288 CONFIRMED-броней за 60 суток): p50 лид-тайма 37ч, но 44% броней с лидом меньше суток и 15% меньше 3ч, а тренер Чангу открывает приложение ежедневно (LoginSession, разрывы не больше ~1.3 суток) - поэтому 24 часа, но не позже начала занятия. Учёт задним числом (занятие прошло к рождению тревоги) вне знаменателя с печатью...
Хвост прогона 01.09 (дословно):
ВЕРДИКТ: КРАСНОЕ
ALR-057напр. 10находок: число в хвосте не названо
Число людей не теряется, когда автобронь отказала приватy ИМЕННО из-за компании
Зелёное, когда: проб 6 из 6; находок после рубежа 0 при знаменателе больше нуля. Знаменатель ноль даёт КОД 3 ('событие не наступало'), а не проход; провал двери даёт КОД 2 - боевым числам тогда верить нельзя.
Как судит
node_modules/.bin/tsx scripts/qa-private-party-alert.mts - спрашивает БОЕВУЮ дверь needsHumanAlertBody (lib/staff-alert.ts), а не копию её текста: 6 проб (две подсадки - приват на двоих и на троих; четыре контроля - приват на одного, групповая пара с одним занятым местом, групповая пара с обоими местами, обычный отказ). Затем боевые данные: тревоги 'Booking needs a human' про приват, где сам человек назвал компанию, стенд отсеян боевым isTestPhone. РУБЕЖ ПОЧИНКИ обязателен - без него мера была бы вечно красной на неизменяемой истории; вердикт решают только тревоги после рубежа, более ранние печатаются отдельным числом.
Хвост прогона 01.09 (дословно):
КОД 3: после рубежа ни одной приватной заявки на компанию не было - мерить нечего, доказана пока только дверь
OBS-017напр. 10находок: 2
Журнал действий: ни безымянных строк, ни молчания
Зелёное, когда: Ноль броней мимо журнала, ноль строк без автора. Пустой знаменатель (после починки прод не создал ни одной брони) - код 1 с прямым текстом «судить нечем». Замер рождения 17.08.2026: строк журнала 27,...
Как судит
npx tsx scripts/qa-action-nameless.mts. Две стороны: МОЛЧАНИЕ - брони, созданные в проде после починки, сверяются со строками журнала scope='action' action='create' (журнал судится ФАКТОМ из другой таблицы, а не сам собой); БЕЗЫМЯННЫЕ - строки уровня warn, сгруппированные по двери. Отсчёт от 2026-08-17T02:45 (выкат отказа токену без владельца), не «за N суток». Пробы: --проба подсаживает безымянную строку боевой формы, --проба-молчания обнуляет журнал И подсаживает бронь-свидетеля (без неё падение шло бы по пустому знаменателю, то есть по другой причине).
Хвост прогона 01.09 (дословно):
ПРОВАЛ: 2
STF-005напр. 10находок: число в хвосте не названо
Гора непринятых тревог одного вида сама становится поводом позвать владельца
Зелёное, когда: брошенных разговоров 0, тревог без следа 0, зовов без приёма 0, живых студий без получателя 0
Как судит
npx tsx scripts/qa-staff-alert-queue.mts. Четыре меры со своим знаменателем: (1) зов urgent_here без приёма старше 30 минут - единственный вид, у которого приём существует; (2) тревоги старше 48 часов с разговором, где после тревоги НЕ ушло ни одного исходящего - человека звали и не ответили; (3) тревоги за 7 суток без следа доставки (EventLog alertId либо wamid), кроме settlement_reminder (у него следа нет по построению, см. STF-006); (4) EventLog staff:fallback level=error по ЖИВОЙ студии - «звать некого». Стендовая студия исключается боевой функцией isTestEntityId, не копией списка.
Хвост прогона 01.09 (дословно):
ОЧЕРЕДЬ ЖДЁТ ЧЕЛОВЕКА: красных мер 2
VER-001напр. 10находок: число в хвосте не названо
Журнал версионности не отстаёт от прода
Зелёное, когда: ноль дней с работой без записи в журнале при знаменателе больше нуля; рабочих дней в окне нет - код 2 «не проверено», а не зелёное
Как судит
npx tsx scripts/qa-versionnost.mts --only=VER-001 (окно 7 суток по времени Бали; днём работы считается день с РАБОЧИМ коммитом, отчёты присмотра «присмотр HH:MM» не в счёт; судятся только дни ПОСЛЕ рождения журнала и только ОКОНЧЕННЫЕ - текущий день не судится, запись пишется в конце работы. ПОПРАВКА 24.08.2026: в 04:13 по Бали мера красила «день без записи», у которого прошло четыре часа и работа шла прямо сейчас - мерка обязана отличать «происходит сейчас» от «происходило до починки»; оба исхода суда по дням самопроверяются фикстурами на каждом прогоне. --only обязателен - общий код выхода красил все три меры одной чужой находкой, тот же класс, что LOG-003 23.08)
Хвост прогона 01.09 (дословно):
ЗНАМЕНАТЕЛЬ (VER-001): веток проверено 5, провалов 1; записей 43, дней 4

Направление 11. Измерительный слой (сами чекеры, пробники, судьи) (27)

AGT-185напр. 11находок: число в хвосте не названо
Агент не отказывает в привате: у него нет факта для отказа
Зелёное, когда: 0 отказов; знаменатель печатает число исходящих агента за окно; встроенный контроль обязан поймать три исторические формулировки и не тронуть три честные
Как судит
node scripts/qa-private-refusal.mjs --studio=canggu --days=30 - ищет исходящие агента, где рядом стоят приват и утверждение о невозможности; судит то, что видел клиент (coalesce(translatedBody, body))
Хвост прогона 01.09 (дословно):
КРАСНЫЙ: приватных слотов в расписании нет вовсе, поэтому отказ выдуман. Лечение - передать день и час студии.
AGT-189напр. 11находок: 28
Безличный дожим не уходит тому, кто сам назвал свою боль
Зелёное, когда: КОД 0 - ни один безличный дожим не ушёл назвавшему боль при знаменателе больше нуля; КОД 1 - назвать поимённо человека, его слова и нить; КОД 2 - дожимов Б3 за окно не было (НЕ ПРОВЕРЕНО, а не...
Как судит
node_modules/.bin/tsx scripts/qa-silent-nudge-ignores-pain.mts [--days=N] - берёт дожимы Б3 (утверждённый текст SILENT на трёх языках: «just opened up» / «только что открылись» / «baru saja terbuka», оба поля body и translatedBody) у Чангу за окно и проверяет, называл ли этот человек свою боль во ВХОДЯЩИХ до дожима. Боль опознаёт БОЕВАЯ функция isPainAdPrefill из lib/sales-agent.ts, а не копия регэкспа в приборе (AGT-104): своя копия разошлась бы с продом молча. Регистр direction заглавный (MET-133). Самопроверка обоих исходов идёт ДО вердикта: подсадка (болевой префилл рекламы) 1/1, контроль ложного среза (отбивка «ok thanks kak») 1/1.
Хвост прогона 01.09 (дословно):
КОД 1: у 28 человек была своя боль, а дожим заговорил ни о чём.
AGT-190напр. 11находок: число в хвосте не названо
Категория, на которой держится дверь кода, достижима для модели
Зелёное, когда: КОД 0 - все категории дверей есть в договоре ответа; КОД 1 - назвать дверь и недостижимую категорию; КОД 2 - боевых заданий агента в базе нет (НЕ ПРОВЕРЕНО); КОД 3 - договор не разобран либо...
Как судит
node scripts/qa-category-door-reachable.mjs - берёт ЖИВОЙ системный промпт из последнего InferenceJob (а НЕ из исходника: в lib/sales-agent.ts две ветки промпта, FULL_AUTONOMY и старая, и вывод по исходнику отвечает про не ту), вынимает договор ответа category и сравнивает предложенные модели значения с категориями, на которых держатся двери lib/agent-delivery.ts (HUMAN_DECISION_CATEGORIES, NEVER_SILENT_CATEGORIES). Рядом печатает, сколько раз каждая категория РЕАЛЬНО возвращалась и когда последний раз - ноль от незнания отличается от нуля от отсутствия только этим числом.
Хвост прогона 01.09 (дословно):
Молчание такой ветки неотличимо от «случаев не было». Лечение: либо вернуть категорию в договор, либо снять её из двери - решение владельца, это поведение продукта.
AGT-193напр. 11находок: число в хвосте не названо
Поручения тренера ассистенту исполняет сервер по правам, а серия невыразима по построению
Зелёное, когда: проверено 34, красных 0. Подсадка доказывает падение: правка предиката даёт код 1.
Как судит
npx tsx scripts/qa-trainer-agent-actions.mjs [--подсадка]. Фаза Б2 чата тренера (docs/META_private_and_trainer_chat.md): модель ПРОСИТ действие блоком {"action":{...}}, исполняет сервер с проверкой прав КОДОМ (lib/trainer-agent-rules.ts + lib/trainer-agent-actions.ts). Прибор судит чистые предикаты: свой/чужой слот, permManageBookings, доступ к нити клиента, памятка не в прошлом, и что схема slot_create .strict() ВАЛИТ подсаженный repeatWeekly/seriesId - серия не отклоняется правилом, она невыразима. 34 ветки, у каждой оба исхода; контроль объёма - «проверено 0» даёт код 2.
Хвост прогона 01.09 (дословно):
✗ message: нити нет: поле code ждали "not_found", получили "no_permission"
AGT-220напр. 11находок: число в хвосте не названо
Цикл дожима доходит до конца списка кандидатов, а порядок списка задан явно
Зелёное, когда: КОД 0: за окно цикл хотя бы раз пройден целиком (N>=M) либо необойдённых лидов нет. КОД 1: целиком не пройден НИ РАЗУ И есть необойдённые лиды - голодание хвоста. КОД 2: обрывов и пропусков в окне...
Как судит
node scripts/qa-followup-budget-starves-tail.mjs [--часов=24] [--подсадить]. Две ноги. (1) БОЕВОЙ КОД: в блоке 2d app/api/cron/agent-autopilot считаются ТЕЛА выборок кандидатов (скобочный разбор от prisma.whatsAppConversation.findMany) и наличие orderBy ВНУТРИ каждого; счёт по всему блоку негоден - там есть orderBy соседних запросов. (2) ПРОД: строки EventLog scope=agent:followup за окно - «дожим ОБОРВАН бюджетом: успели N из M» и «блок дожима НЕ ЗАПУСКАЛСЯ». Плюс население: рекламные лиды внутри окна отбора, молчащие дольше суток, и сколько из них без единого касания (EventLog ad:followup или SendLock ad:followup:*), отдельной графой группа «ровно одно входящее».
Хвост прогона 01.09 (дословно):
КОД 1: ГОЛОДАНИЕ ХВОСТА. За 24 ч цикл дожима Чангу не пройден целиком НИ РАЗУ (лучший 11 из 12, обрывов 132, пропусков 22), а порядок кандидатов не задан ни в одной из 3 выборок блока 2d (orderBy: 0).
Перепрогон 02.09:
КОД 1: ГОЛОДАНИЕ ХВОСТА. За 24 ч цикл дожима Чангу не пройден целиком НИ РАЗУ (лучший 10 из 11, обрывов 74, пропусков 19), а порядок кандидатов не задан ни в одной из 3 выборок блока 2d (orderBy: 0). 
DAT-057напр. 11находок: число в хвосте не названо
Явка и оплата приватов отмечаются, иначе статистика неявок ложная
Зелёное, когда: КОД 1, если у типа занятий доля отмеченного прихода НОЛЬ при непустом знаменателе - это не факт о людях, а незаполненное поле
Как судит
node scripts/qa-attendance-fill-rate.mjs --days=90 - доля отметок прихода и оплаты по каждому разрезу студия-тип занятия; красит разрез, где при пяти и более бронях приход не отмечен НИ РАЗУ
Хвост прогона 01.09 (дословно):
КРАСНЫЙ: на этих разрезах любое число про неявки и явку означает «кнопку не нажимали», а не факт о людях.
DAT-058напр. 11находок: число в хвосте не названо
Телефон персонала не числится живым клиентом в наших студиях
Зелёное, когда: КОД 0: в canggu и usa ноль броней на телефон персонала. Брони партнёрского Убуда печатаются отдельной строкой и красным НЕ считаются - его учёт не наш. КОД 1: есть наши. КОД 2: канон персонала...
Как судит
node scripts/qa-staff-phone-as-client.mjs --days=90 - сверяет телефоны броней с каноном персонала (номера тренеров, номер тревог студии, OWNER_NOTIFY_PHONE), поднятым ИЗ БАЗЫ через общую дверь scripts/_test-phones.mjs
Хвост прогона 01.09 (дословно):
КРАСНЫЙ: эти брони искажают и выручку, и явку, и воронку - в них нет живого человека.
MET-011напр. 11находок: число в хвосте не названо
Проверка читает переписку по СТОРОНАМ: у входящих оригинал клиента в body, у исходящих клиентское в coalesce(translatedBody,body). Запрос, берущий coalesce для ОБЕИХ сторон, показывает служебный перевод как слова клиента
Зелёное, когда: 0 запросов, читающих coalesce для входящих
Как судит
node scripts/qa-inbound-original-guard.mjs. (команда вписана 23.08.2026, MET-125: прибор существовал, реестр звал его словами) прогон node scripts/qa-inbound-original-guard.mjs (осматривает ВСЁ дерево scripts/ рекурсивно, включая папку scripts/tmpqa/); ПРЕЖНИЙ способ - grep по шаблону scripts/tmpqa-* - ОТМЕНЁН 22.08.2026: папка разовых приборов переехала в scripts/tmpqa/ ещё 18.08, и шаблон осматривал 4 файла из 685, то есть проверка была зелёной ОТ НЕЗНАНИЯ
Хвост прогона 01.09 (дословно):
coalesce(translatedBody,body) берётся для ОБЕИХ сторон разговора: у входящих это покажет служебный перевод вместо оригинала
MET-065напр. 11находок: число в хвосте не названо
Тревога персоналу, не дошедшая НИ ДО КОГО, обязана быть видна - и слепая студия названа заранее
Зелёное, когда: на живых студиях тревог в никуда 0 И слепых живых студий 0; стенды перечислены отдельно жёлтым, потому что доставка двери на стенде недоказуема осознанно (SEC-009 не ослабляем)
Как судит
node scripts/qa-alert-no-recipient.mjs [суток]. Считает две вещи со знаменателем: (1) строки staff:fallback уровня error - тревоги, у которых получателей оказалось ноль; (2) студии, где не осталось ни одного НЕ тестового ADMIN, то есть у кого широковещательная тревога (userId=null) уйдёт в никуда завтра. Контроль объёма: тревоги есть, а журнала доставки ноль строк - это «не смог прочитать», а не чистота, и это отдельный выход. Красное обязано печатать, чем закрывается. Приёмка: node scripts/qa-alert-no-recipient.mjs 14 --подсадить обязан упасть на подсаженной тревоге ЖИВОЙ студии.
Хвост прогона 01.09 (дословно):
закрывается так: у студии usa_37359fc94f7043da27d3 завести хотя бы одного не тестового ADMIN (сейчас будущих занятий 74, значит тревоги там возможны)
Перепрогон 02.09:
закрывается так: у студии usa_37359fc94f7043da27d3 завести хотя бы одного не тестового ADMIN (сейчас будущих занятий 73, значит тревоги там возможны)
MET-087напр. 11находок: число в хвосте не названо
Детектор выдуманного занятия судит ПРАВДУ, а не приглашение, и не только сегодняшний день
Зелёное, когда: обе подсадки ловятся; знаменатель клауз непустой и сопоставим с числом ответов агента, назвавших час
Как судит
node scripts/qa-agent-live-checks.mjs --days=2 --inject=stale - а после выката правки - подсадка второй строкой боевого формата на ЗАВТРАШНИЙ день («Завтра, в воскресенье, занятие одно - в 22:00») и на русское наличие («есть места в 09:00»). Обе обязаны дать попадание. Отдельно печатается знаменатель клауз против числа ответов агента, назвавших конкретный час: доля меньше половины считается признаком того, что мера снова ослепла
Хвост прогона 01.09 (дословно):
ИТОГ: ЕСТЬ ПОПАДАНИЯ - AGT-001/039, MSG-058
Перепрогон 02.09:
ИТОГ: ЕСТЬ ПОПАДАНИЯ - AGT-001/039, AGT-007, MSG-058
MET-141напр. 11находок: число в хвосте не названо
Мера, ходящая в живые данные, обязана уметь сказать «не проверено»
Зелёное, когда: подсадка 5 из 5; на живых данных знаменатель непустой; слепых мер не больше порога BASELINE (31 на 22.08.2026)
Как судит
npx tsx scripts/qa-not-checked-branch.mts [--подсадка]. Считает меры, которые читают прод-базу, и среди них те, у кого нет ветки «НЕ ПРОВЕРЕНО» либо кода выхода 2. Красное - РОСТ числа против порога, а не само число: вечно красный сторож перестаёт читаться через день.
Хвост прогона 01.09 (дословно):
КРАСНОЕ: слепых мер стало больше порога (33 > 30). Новая мера обязана уметь говорить «не проверено».
MET-144напр. 11находок: 1
Признак, который может проставить массовая правка, не считается доказательством внимания человека
Зелёное, когда: прибор печатает исход ОБЕИХ подсадок защиты (батч пойман 1/1, человек пропущен: судимых 0 при пропущенных 1) и знаменатель больше нуля; ни один прибор реестра не выбрасывает строку из знаменателя по...
Как судит
node scripts/qa-unaccepted-but-dark.mjs --only=MET-144 - у него четыре подсадки на боевой форме строки, две из них про саму защиту: батчевая метка обязана ЛОВИТЬСЯ, человеческая обязана ПРОПУСКАТЬСЯ. Шире по слою: grep -l acceptedAt scripts/qa-*.mjs scripts/qa-*.mts и сверка каждого найденного на наличие проверки приставки batch: (на 22.08 защиту несут 4 из 13, девять названы поимённо строкой долга 2026-08-22T02:41:46.528Z). Эталон защиты в продукте - lib/urgent-call.ts:100. ПОПРАВКА 23.08.2026: --only=MET-144 обязателен - общий код красил мету чужой находкой ALR-034; режим гоняет обе подсадки защиты и свип слоя (прибор с acceptedAt обязан нести защиту batch: либо пометку «MET-144: не...
Хвост прогона 01.09 (дословно):
КОД 1 (MET-144): 1 приборов судят по батч-проставляемому полю без защиты
MET-145напр. 11находок: число в хвосте не названо
Прибор, судящий ТОЛЬКО входящие, читает body; область охвата любой проверки печатается числом
Зелёное, когда: самопроверка доказывает оба исхода; осмотрено > 1000 файлов; 0 приборов, где coalesce применён к строкам, отобранным как INBOUND
Как судит
node scripts/qa-inbound-original-guard.mjs --selftest - отдельно - у каждой проверки печатать «осмотрено N» и считать малое N при большом корпусе признаком отменённого шаблона пути
Хвост прогона 01.09 (дословно):
coalesce(translatedBody,body) берётся для ОБЕИХ сторон разговора: у входящих это покажет служебный перевод вместо оригинала
MET-149напр. 11находок: число в хвосте не названо
У цикла учёбы обязан быть учитель, и его молчание называется вслух
Зелёное, когда: подсадка 2 из 2 в обе стороны; живые прогоны: окно 24ч КРАСНЫЙ (0 живых из 25 сообщений), окно 168ч ЗЕЛЁНЫЙ (6 живых из 117)
Как судит
node scripts/qa-teacher-silence.mjs [--часов=24] [--подсадка=молчит|говорит]. Считает по студии Убуд за окно: сколько сообщений проверено всего, сколько из них ЖИВЫХ свободных текстов человека (не шаблон, не агент, не наша автострока; отдельно печатается, у скольких есть положительный признак - имя автора или штамп человека) и сколько входящих не-кнопок. Двусторонний: ноль живых текстов при живом канале = КРАСНЫЙ «учителя не было», ноль сообщений вообще = КРАСНЫЙ «канал молчит», отказ базы = код 2 «не проверено».
Хвост прогона 01.09 (дословно):
КРАСНЫЙ: за окно живые люди Убуда не написали НИ ОДНОГО свободного текста. Уроки за эти сутки выводить не из чего - цикл учёбы обязан сказать это вслух, а не выдумывать приём и не пересказывать уже ра
MET-156напр. 11находок: число в хвосте не названо
Канон стендовых номеров проверяется ПОВЕДЕНИЕМ на образцах каждого вида, а не наличием экспортов
Зелёное, когда: шесть видов стендовых номеров опознаются, три живых не опознаются, три остальных экспорта отвечают по существу
Как судит
node scripts/qa-canon-behaviour.mjs
Хвост прогона 01.09 (дословно):
ЗНАМЕНАТЕЛЬ: образцов 12, провалов 1
MET-166напр. 11находок: 1
Активная строка реестра называет прибор, которого нет в дереве
Зелёное, когда: 0 строк с несуществующим прибором и 0 с прибором вне гита; знаменатель (активных с именем файла) больше нуля, иначе код 2 НЕ ПРОВЕРЕНО. Самопроверка с 27.08.2026 - ДЕВЯТЬ проб: к прежним шести...
Как судит
node scripts/qa-registry-instrument-exists.mjs - вынимает из поля method каждой АКТИВНОЙ строки путь вида scripts/*.{mjs,mts,ts,js,sh,py} и спрашивает файловую систему и git ls-files. Разделяет две болезни: файла нет вовсе (запуск падает на MODULE_NOT_FOUND) и файл есть, но не в гите (у соседней сессии и в свежем клоне меры не будет). Строки, чей способ описан словами или запросом и имени файла не содержит, считаются отдельным числом, чтобы знаменатель был честным. КАТАЛОГОВ ПРИБОРОВ ДВА, А НЕ ОДИН (присмотр 13:30 Бали 27.08.2026): признак начинался словом scripts/, и прибор, лежащий в university/qa/tools/, был для судьи НЕВИДИМ - не зелёный, а не попавший в знаменатель вовсе. Замер того...
Хвост прогона 01.09 (дословно):
КОД 1: строк с несуществующим прибором 1, с прибором вне гита 3 - из 869 судимых.
Перепрогон 02.09:
КОД 1: строк с несуществующим прибором 0, с прибором вне гита 1 - из 889 судимых.
MET-170напр. 11находок: число в хвосте не названо
Пробники, зовущие claude -p, несут изоляцию личного окружения мака (--settings {} + --strict-mcp-config), как боевой воркер
Зелёное, когда: все вызовы claude -p в пробниках изолированы
Как судит
node scripts/qa-probe-isolation.mjs (0 зелено / 1 вызов без изоляции / 2 не проверено; --подсадить проба падения)
Хвост прогона 01.09 (дословно):
scripts/qa-pain-first-reply.ts: spawn("claude", ["-p", "--model", "claude-opus-5", "--strict-mcp-config", "--mcp-config", '{"mcpServ
MET-181напр. 11находок: 2
Пустая отповедь: канон-фильтр вычистил ответ, человеку ушла одна придерживающая строка и живого слова не пришло
Зелёное, когда: КОД 0 - все пустые отповеди закрыты живым словом в окне; КОД 1 - назвать нить и часы простоя; КОД 2 - за окно таких тревог не было (не проверено, не зелёное); КОД 3 - прибор не доказал оба исхода
Как судит
node scripts/qa-canon-emptied-reply.mjs - берёт тревоги StaffAlert со словами 'canon filter emptied the reply' за 90 суток и проверяет, появилось ли в той же нити исходящее ОТ ЧЕЛОВЕКА (fromAgent=0) в течение 6 часов. Мерит уплату долга, а не молчание агента: агент после пустой отповеди писать может и пишет, но вычищенного содержания не возвращает. Самопроверка: подсадка 1/1, контроль ложного среза 1/1.
Хвост прогона 01.09 (дословно):
КОД 1: ПУСТЫХ ОТПОВЕДЕЙ БЕЗ ЖИВОГО СЛОВА В ОКНЕ: 2 из 3.
MET-186напр. 11находок: 17
Прибор есть, а меры в реестре нет: сирота, которого не запустит ни один цикл
Зелёное, когда: 0 твёрдых сирот при знаменателе больше нуля. КОД 2 - приборов не найдено вовсе. КОД 3 - мера не прошла свои пять проб.
Как судит
node scripts/qa-instrument-unregistered.mjs. Идёт от ПРИБОРА к реестру (обратное направление к MET-125, MET-166, MET-173, которые все идут от реестра к прибору). Твёрдая находка - файл scripts/qa-*.{mjs,mts}, чьё имя не встречается в checks.json ни разу И который в первых трёх строках представляется номером меры, отсутствующим в реестре. Печатает ЧЕТЫРЕ числа: вписаны, твёрдых сирот, мягких без номера, мягких с известным номером, плюс нечитаемые. Признак «первые три строки» взят у MET-125, чтобы обе меры читались одинаково.
Хвост прогона 01.09 (дословно):
КОД 1: 17 прибор(ов) представляются мерой, которой в реестре нет - ни один цикл их не запустит
MET-192напр. 11находок: число в хвосте не названо
Самостоятельную меру дерева зовёт реестр или след прогона (мера, которую не зовёт никто, равна отсутствующей)
Зелёное, когда: КОД 0: сирот нет. КОД 1: сироты названы поимённо. КОД 2: приборов в дереве не найдено - НЕ ПРОВЕРЕНО, а не чисто. КОД 3: подсадка не сошлась, о дереве не судим
Как судит
node scripts/qa-measures-nobody-calls.mjs. Знаменатель - все приборы scripts/qa-*.{mjs,mts,ts} из git ls-files. «Зовут» судится ровно по ДВУМ источникам исполнения: строка в university/qa/checks.json и след настоящего прогона в university/qa/registry-runs/*.json. Журналы присмотра печатаются РЯДОМ отдельным числом как контекст и на вердикт не влияют - иначе отчёт о находке, перечисляющий имена сирот, зеленил бы меру своей же записью. Вспомогательные файлы (unit/selftest/fixture/probe/replay/proof в имени) исключены из вердикта и печатаются своим числом
Хвост прогона 01.09 (дословно):
Решать поимённо: внести строку в реестр ЛИБО похоронить с причиной. Молча оставлять - значит копить вид покрытия без покрытия.
MET-194напр. 11находок: число в хвосте не названо
Сводная графа «мимо двери» согласна со своим же предупреждением о столкновениях
Зелёное, когда: код 0 - восемь проб сошлись и живое окно согласовано; код 1 - прибор перестал различать обход либо сводная графа снова врёт нулём; код 2 - прогон не состоялся или знаменатель живого окна пуст (это НЕ...
Как судит
node scripts/qa-door-bypass-signs.mjs. Гоняет scripts/qa-debts.mjs --json на ПОДМЕНЁННОМ каталоге (QA_DEBTS_DIR) с боевым видом записи байт в байт: чистый день плюс пять подсадок по одному признаку каждая плюс два контроля. Затем читает живое окно и требует, чтобы при непустом collidedStamp сводная графа не печатала ноль.
Хвост прогона 01.09 (дословно):
КОНТРОЛЬ: метка ровно в целую секунду, но с миллисекундами .000 не на нулевой секунде: ждали {"bypassed":0,"roundMinute":0,"alienShape":0}, получили {"total":2,"bypassed":2,"roundMinute":0,"zeroMs":2,
MSG-264напр. 11находок: число в хвосте не названо
Вопрос человека не остаётся без живого ответа, и обещание узнать исполняется
Зелёное, когда: КОД 0: пусто по всем четырём признакам. КОД 1: есть зависшие, каждый назван поимённо с датой и текстом. КОД 2: разговоров в окне нет вовсе - не проверено
Как судит
node scripts/qa-hanging-questions.mjs --studio=canggu --days=30 - судит четыре признака: (1) последнее слово за клиентом дольше порога; (2) вопрос без единого нашего слова после; (3) вопрос, закрытый ТОЛЬКО автоматикой; (4) наше обещание узнать без единого живого ответа после. Служебные нажатия персонала исключены по канону BUTTON_PAYLOAD_RE
Хвост прогона 01.09 (дословно):
НЕРАЗОБРАННЫХ ДАТ: 0
MSG-265напр. 11находок: число в хвосте не названо
Карточка брони не приходит второй раз с ДРУГИМ часом на тот же билет
Зелёное, когда: 0 таких пар; знаменатель печатает, сколько билетов подтверждалось повторно вообще
Как судит
node scripts/qa-ticket-two-hours.mjs --days=30 - берёт все карточки booking_confirmed*, достаёт из ТЕКСТА, ушедшего клиенту, билет и час, и красит билет, у которого часов оказалось два. Судим по тексту, а не по слоту: человек видит текст
Хвост прогона 01.09 (дословно):
КРАСНЫЙ: человек получил две карточки с разным часом и без единого слова о том, что час другой.
MUL-032напр. 11находок: число в хвосте не названо
Прибор, принимающий студию, не берёт умолчание на неузнанном значении
Зелёное, когда: 0 приборов с молчаливым умолчанием; знаменатель печатает, сколько идёт через дверь и сколько разбирает само; встроенный контроль обязан поймать восемь исторических форм и не тронуть переведённые
Как судит
node scripts/qa-studio-arg-door.mjs - судит по СМЫСЛУ строки, а не по списку знакомых форм: есть чтение параметра снаружи (argv/env/arg()) И запасное умолчание с константой Чангу. Строки судятся у ВСЕХ приборов, дверь влияет только на знаменатель
Хвост прогона 01.09 (дословно):
Лечение: перевести на scripts/_studio-arg.mjs - она понимает все три формы и отказывает кодом 2.
MUL-034напр. 11находок: 2
В текстах студии нет пустых мест там, где человек ждёт факта
Зелёное, когда: КОД 0 у каждой студии, которая продаёт занятия. КОД 1 при пустом адресе, неканонном блоке или чужом номере в ссылке отмены хотя бы у одной. КОД 2 если судить нечего (студии нет, у неё нет будущих...
Как судит
npx tsx scripts/qa-studio-dry-texts.ts - БЕЗ аргументов судит ВСЕ студии, которые продают занятия (есть хотя бы одно будущее занятие и это не стенд по канону isTestStudio из lib/test-slots); флаг --studio=<слаг> оставлен для разбора одной. Собирает билет БОЕВОЙ функцией ticketText на настоящих данных студии и её ближайшем занятии, печатает его целиком и судит три вещи: есть ли адрес, канонный ли блок занятия (120 минут = 90 занятия плюс 30 работы тренера), на чей номер ведёт ссылка отмены. Ничего не отправляет. Знаменатель печатается всегда: студий в базе / с будущими занятиями / стендов отсеяно / судимых. Умолчание «все продающие» заведено 31.08.2026: прежде в реестре стоял заполнитель, и...
Хвост прогона 01.09 (дословно):
КОД 1: пустые места в текстах у 2 из 4 - usa: 1; solar: 2
MUL-035напр. 11находок: число в хвосте не названо
Чангу не зашита в исполняемые строки общего кода
Зелёное, когда: Судить список глазами: часть законна (умолчание с переопределением через env, контактные страницы сайта, справочник поясов). Красным считать строку, где общий механизм работает по Чангу для ЧУЖОЙ...
Как судит
node scripts/qa-canggu-hardcoded.mjs - ищет в lib и app шесть опознавательных строк Чангу (идентификатор студии, WABA, номер Меты, видимый номер, слаг строкой, пояс Asia/Makassar) и делит находки на комментарии и ИСПОЛНЯЕМЫЕ строки
Хвост прогона 01.09 (дословно):
КРАСНЫЙ по умолчанию: каждую строку судить глазами - часть законна (умолчание студии, разбор истории), часть это будущая авария второй студии.
TPL-030напр. 11находок: 1
Знаменатель проверки называется целиком, вместе с тем, чего она не читала
Зелёное, когда: имён без прочитанного тела 0, подсадка поймана поимённо
Как судит
npx tsx scripts/qa-client-template-canon.mts --знаменатель - вердикт только о полноте знаменателя: печатает «имён прочитано: N из M», где M = имена снимка ПЛЮС имена, которые прод реально отправлял за 60 суток; имена без снимка судятся по своим ЖИВЫМ отправкам, непрочитанные перечисляются поимённо. Флаг добавлен 24.08.2026: без него команда совпадала с TPL-028, раннер раздавал общий вердикт (sharedWith), и TPL-030 краснела чужим критерием «имён с нарушением» при собственных зелёных.
Хвост прогона 01.09 (дословно):
ПРОВАЛ ЗНАМЕНАТЕЛЯ: 1

Направление 14. Деньги (зарплаты, сейф, касса, промокоды, биллинг) (2)

MSG-295напр. 14находок: число в хвосте не названо
Граница слова \b вплотную к кириллице - мёртвая ветка шаблона (мера слепа на язык, продукт молчит по-русски)
Зелёное, когда: мёртвых веток 0 при непустом знаменателе; самопроверки 3/3 (подсадка кириллицы у границы краснеет, латиница у границы молчит, кириллица без границы молчит); отдельной строкой печатается, сколько...
Как судит
node scripts/qa-cyrillic-word-boundary.mjs - сплошной обход scripts+lib+app: из каждого шаблона вынимается кириллический кусок, стоящий вплотную к границе, и ДОКАЗЫВАЕТСЯ запуском, что граница вокруг него не совпадает с обычной строкой; строки-комментарии пропускаются, их число печатается
Хвост прогона 01.09 (дословно):
/(гр)(thank you|thanks|thx|thank u|terima kasih|makasih|appreciate (it|that)|it was (great|amazing|lovely|wonderful)|loved (it|the class)|great class|amazing cl
MUL-023напр. 14находок: число в хвосте не названо
Живой человек, получивший ссылку на студию-стенд, получает исправление
Зелёное, когда: ноль получателей без исправления при знаменателе больше нуля; подсадка (исправлением считается несуществующая строка) обязана окрасить ВСЕХ, контроль (исправлением считается любое наше исходящее...
Как судит
node scripts/qa-stand-link-uncorrected.mjs [--подсадка|--контроль]. Ставка - исходящее живому человеку живой студии, где есть след стенда (qatest); исправлением считается более позднее наше исходящее с bookgravity.com/canggu либо словами о неверной ссылке. Тестовые номера и нити стенда печатаются числом и не судятся. Пустой знаменатель - код 2 «не проверено».
Хвост прогона 01.09 (дословно):
Написать им - решение владельца (первое касание после долгого молчания), присмотр этого не делает.

Снятые днём 02.09 (не требуют решения)

Стали зелёными: MSG-026, MSG-205, AGT-119, MSG-226, MET-157, AGT-178, HR-003. Ушла в «не проверено» (под замком нет живой учётки): SEC-036.

ничего не изменено