Files
dotfiles/agents/.agents/skills/claude-subagent-playbook/references/lessons.md
T

51 KiB
Raw Blame History

Эмпирика прогонов

Датированные факты: что поймал каждый этап конвейера, казусы, цифры. Дописывай после каждого прогона в конец файла — это и есть «выжимка опыта», ради которой скилл существует. Правку старых записей согласуй с пользователем: это долговечная эмпирика.

Журнал накопительный, поэтому у него есть архив: записи с 2026-07-06 по структурный рефакторинг 2026-07-31 включительно лежат в lessons-archive.md — они описывают заметно другую редакцию скилла. Живые хвосты тех записей перед переездом сведены в очередь на механизм (запись 2026-08-03). Когда файл разрастётся снова — подвинь границу и перенеси старое туда же: архив вне пути чтения прогона, поэтому его длина ничего не стоит, а дистилляция стоила бы потерянных историй.

2026-07-31: одёргивания за день прогонов (Opus 5-координатор, старая редакция)

Фидбек пользователя после дня работы со скиллом; ценно тем, что это перечень мест, где координатора приходилось останавливать руками.

  1. Повторное ревью отревьюенной постановки. Координатор ревьюит готовый тикет, уже прошедший ревью на этапе постановки. Правило «одно ревью на версию артефакта» в этапе 2 ЕСТЬ (записано 2026-07-21) — и не держит. Класс отказа тот же, что с вкусовой линзой (запись 2026-07-31, в архиве): правило в прозе не исполняется, нужен механизм — например, перед этапом 2 координатор обязан назвать, какая версия тикета когда ревьюилась, и пропустить этап, если версия не менялась.
  2. Переоценка сложности. Полный цикл ревью там, где хватило бы саморевью. Шкала риска этапа 4 есть — и тоже не держит: дефолт скилла «полный состав минус пропуски» (opt-out), координатор склонен к максимальной церемонии. Кандидат на следующий заход: перевернуть дефолт — минимальный хребет (постановка → исполнитель → саморевью → приёмка) + явные триггеры эскалации (opt-in); добавить слой дешевле, чем отменить запущенный.
  3. Код Кодекса нельзя показывать ученикам. Формально задачу решает хорошо, но «не хочу их учить так писать» — это не дефект кода, это дефект назначения исполнителя. Боевое подтверждение оси вкуса, принятой этим же днём: в учебном репозитории почти всё читает человек → почти вся реализация уходит craftsman'у, Codex — на обвязку и инфраструктуру. Проверить следующим прогоном, снимает ли новый разрез эту боль.
  4. Скилл зовут ради механики субагентов, не ради конвейера. Регулярный сценарий: дать агенту знание «субагенты существуют, вот как их звать» для специальной задачи — а вызов тянет весь конвейер. Это отдельная ветка использования: дисциплина делегирования (Роли, определения, файлы-вместо-пересказов) полезна вне цикла разработки. Кандидат: выделить её в лёгкий скилл или отдельную точку входа, чтобы «субагентная грамотность» не стоила 300 строк процесса.

Пункты 1–2 и 4 — открытый вопрос следующего структурного захода (сегодняшний рефакторинг закрыт коммитом 3e0498e, не расширять задним числом).

2026-08-01: триаж судит находку, но не лечение (clickstream v2, тикет #36)

Конфигурация редкая: оркестратор и есть исполнитель — плейбук позвали поверх готовой работы /implement, третьей линией (Кодекс, high) к двум уже прошедшим Клод-линиям. Пересылать индекс некому, триаж вёлся в голове; статусы по ID появились файлом только ради перепроверки, задним числом.

  • Все три находки верны, одно лечение — перебор. REV-1 «имена не с чем сверить» я закрыл записью 47 имён второй копией — то есть прибил гвоздями решение чужого слоя (имена атрибутов складывает модель DDS, спека задала только вид имени). Поймал владелец вопросом «ты прибил гвоздями?», не процесс. Триаж в скилле судит находку (FIXED / REJECTED / …), а лечение проезжает без суда — тем более что колонка «как проверить» у Кодекса фактически лечение и предписывает.
  • NEEDS_DECISION был доступен и не сработал. Починка замораживала решение будущего тикета — ровно его случай. Не опознал, потому что правка выглядела дешёвой: 47 механических строк. Урок-обобщение: дешевизна починки ничего не говорит о том, чьё это решение.
  • Перепроверка тем же ревьюером перебор не ловит структурно: вернула ALL_CLOSED, подтверждая реализацию собственного предписания.
  • Промах в промпте ревью: перечислены источники истины, но не «какие решения ещё открыты и кем владеются» — ревьюер физически не мог знать, что имена DDS не наши. Кандидат: обязательный блок открытых решений.
  • Цифры: ревью 124 команды, 3 находки (2 MAJOR, 1 MINOR); перепроверка на medium — 46 команд. Одна находка фактическая (комментарий рекламировал торговые события, от которых спека отказалась) — её обе Клод-линии пропустили: подтверждение ценности другой родословной.

2026-08-02: перепроверка толкает лечение в обе стороны (clickstream v2, тикет #38)

Конфигурация та же, что 08-01: оркестратор он же исполнитель, Кодекс (high) третьей линией к двум прошедшим Клод-линиям. Снова три находки, все верны; снова другая родословная дала класс, мимо которого прошли обе Клод-линии — честность тестов: сторож сравнивал день −N с днём N, а сталкиваются −N и N−1.

  • Перепроверка тем же ревьюером не судит соразмерность лечения ни в одну сторону. 08-01 она вернула ALL_CLOSED, подтвердив исполнение собственного предписания; здесь на ту же починку — NOT_CLOSED с требованием недостижимого (полная неприкосновенность запомненной когорты: в numpy владелец памяти размораживается всегда, а вид всегда показывает .base). Одна дыра, две стороны. Приём: на NOT_CLOSED проверять не только факт, но и существует ли закрывающее состояние, и мерить цену альтернативы — здесь 16 МиБ копий на день вместо 2 мс, то есть размен реального свойства на защиту от умысла.
  • Долг 08-01 не закрыт: статус вешается на находку, не на лечение. Здесь лечение судилось только потому, что владелец потребовал этого в постановке («Кодекс коварен, польза бывает очевидна на первый взгляд»). Механизма по-прежнему нет.
  • Новый класс: сломан не код, а измерительный прибор. Цикл мутаций «правка → прогон → откат» внутри секунды оставлял устаревший .pyc (свежесть — по времени правки и размеру), тесты шли по байт-коду мутанта и падали на восстановленном дереве. Чуть не «починил» работающее. Линии ревью смотрят на код и такого не ловят; конвейер молча допускает, что проверялка оркестратора исправна. Приём: мутации гонять с PYTHONDONTWRITEBYTECODE=1, первым и последним прогоном — чистое дерево.
  • Польза линии пришла как «изменил, что я проверяю»: находка развернула батарею мутантов на дерево зёрен, которое я считал очевидным, — там нашлись ещё два слепых сторожа. Слить ветвь дней с ветвью состава можно было, не уронив ни одного из 297 тестов.
  • Цифры: ревью 68 команд, 3 находки (1 MAJOR завышен — сценарий требует сознательно снять флаг у массива, 2 MINOR); перепроверка на medium — 12 команд; батарея 17 мутантов, выживших нет.

2026-08-02: долг «суда над лечением» закрыт шапками (clickstream v2, тикет #39)

Штатная конфигурация: реализация — Опус-мастеровой, линия кода — Кодекс (high) свежим тредом, линия задачи и вкуса — свежий Опус. Долг двух прошлых прогонов закрыт руками: в шапку триажа и в шапку перепроверки дописаны абзацы про соразмерность лечения (заготовки прогона; перенесены в шапки 2026-08-03).

  • Ручка сработала трижды за один прогон. Перепроверка вкусовой линии сама оценила правку спеки на перебор (вышла за границу промпта — принята с доводом: четыре фразы без поведения, чинят противоречие, внесённое этим же изменением), приняла закрытие маршрутом, отличным от подсказанного («запрет был не той половиной, которую надо чинить»), и обосновала соразмерность нового модуля. Без ручки всё три проехали бы молча, как в #36.
  • 10 FIXED, 0 REJECTED — повод посмотреть на лечения, а не приговор. Перебора не нашлось, но узнал я это проверкой, а не по отсутствию отказов.
  • Пересечение линий — ноль из десяти, и каждая принесла свою дыру в честности тестов. CODE-1: слепок детерминизма не включал ни порядок колонок, ни публичные швы для соседнего тикета — перестановка колонок через вызов проходила все три сторожа. TASK-4: сторож правил нарезки ловил склейку двух визитов, но не разрез одного. Разные тесты, разные механизмы.
  • Тонкий лог перепроверки — повод проверить MAJOR самому. 8 команд против 58 в самом ревью; закрытие единственного MAJOR перепроверил своим зондом (перестановка колонок, правка каждого шва, стабильность того же дня) — закрыт честно. Цена приёма — один скрипт оркестратора.
  • Два отказа вызова Кодекса, оба от чтения правил без разбора. < /dev/null по правилу «фоновому запуску закрывай stdin» убивает промпт, который сам идёт через stdin (No prompt provided via stdin), — правило не про этот случай. И codex exec resume (0.145.0) не принимает -s/-C: среда треда задана при создании и на продолжении не переуказывается; остальные ключи (-m, -c, -o, --json) те же, что у exec. Уточнено 2026-08-03: хвост «песочница только через -c sandbox_mode=…» был неверен — ключ разбирается, но среду продолжения не меняет. Оба отказа закрыты по месту: codex-exec.md (рецепт resume и правило stdin), SKILL.md этап 6 (правило < /dev/null сужено условием «нечего читать со входа»).
  • Цифры: два ревью — 58 и 36 команд/инструментов, 10 находок (1 MAJOR, 8 MINOR, 1 NIT); триаж — 10 FIXED; перепроверки на medium — 8 и 12; тесты 297 → 353.

2026-08-02: шапки соразмерности сработали в третий раз (clickstream v2, тикет #40)

Конфигурация редкая по исполнителю: тикет залезал на территорию трёх соседних (план состава #38, воронка и каталог #39), и вкусовую реализацию вёл не craftsman, а deep-reasoner — решение пользователя, обоснование: не письмо по готовой форме, а калибровка «померил — подкрутил — померил» под вилки мира. Оправдалось: 41 минута, 26 новых тестов, числа мира легли в вилки с первого захода. Линия кода — Кодекс (high) свежим тредом, линия задачи и вкуса — свежий Опус.

  • Пересечение линий — одна находка из девяти, и обе линии описали её по-разному. Полуночная потеря покупки при выжившей странице подтверждения: Кодекс сконструировал сценарий и дал MAJOR, Опус померил 14 канонических дней, нашёл ноль случаев и записал в необязательные соображения. Не спор, а два свидетельства об одном латентном дефекте (окно ~7 с на заказ). Приём: сводя вердикты, ищи не совпадение формулировок, а совпадение явления — разная оценка достижимости у линий нормальна и полезна.
  • Шапка соразмерности лечения окупилась на MAJOR'е. Исполнитель отклонил маршрут ревьюера («проверь симптом там, где он виден») и починил слоем выше — граница суток даёт подтверждению место под торговый хвост. Довод сильный: починка внутри commerce двигала бы просмотры страниц при правке торгового подпотока, то есть ломала бы ровно то свойство, о котором вторая находка того же ревью. Без шапки предписание проехало бы молча, как в #36.
  • 9 FIXED / 0 REJECTED — и одно ослабление порога всё же было. TASK-3: группа стала строже (пары исключены), измеренный лифт упал ×4,7 → ×3,0, и порог теста опущен > 3 → > 2. Обосновано, но это ровно тот ход, от которого предостерегает шапка, — судить его отдал перепроверке той линии, что находку и подняла. Вернулась CLOSED с воспроизведёнными числами.
  • Тонкий лог перепроверки Кодекса (10 команд) — снова повод проверить MAJOR самому. Свой зонд оркестратора: 14 дней × два режима, включая стресс с часовым торговым хвостом (окно, в котором дефект жил) — ноль подтверждений без покупки и ноль покупок без подтверждения. Приём из #39 подтверждён.
  • Приёмка глазами нашла то, чего не даёт ни один вердикт: урок виден в данных. Распечатал один визит целиком и померил разрыв: у 157 покупок из 272 сумма по разобранным массивам не равна purchaseRevenue (2680 против 2679,8). Ради этого копейки в каталог и заводились — без своего прогона осталось бы верить отчёту.
  • Цифры: реализация 41 мин / 328k токенов, саморевью 8/8; ревью — Клод 33 инструмента, Кодекс 120 команд; 9 находок (2 MAJOR, 5 MINOR, 2 NIT), пересечение 1; триаж 9 FIXED за 13 мин; перепроверки 13 и 10 команд, обе ALL_CLOSED + 1 новый NIT (повтор фразы в спеке, внесённый починкой TASK-2) — поправил сам по месту. Тесты 353 → 383.
  • Долг скилла остаётся тот же: абзацы про соразмерность лечения третий прогон подряд пишутся руками. Закрыт 2026-08-03.

2026-08-02: сломанный прибор доезжает до коммита (clickstream v2, тикет #50)

Продолжение #40 после грилинга: реализация — deep-reasoner (калибровка чисел мира), линия кода — Кодекс (high) свежим тредом, линия задачи и вкуса — свежий Опус. Ревью постановки — одна линия (Кодекс), вторая пропущена: решения уже прогриллены и лежат в спеке.

  • Ошибка измерения всплыла трижды на трёх уровнях, и ни разу её не поймал тот, кто мерил. (1) Оркестратор мерил на зерне 0, а весь мир и все тесты живут на CANONICAL_SEED — поймало ревью постановки; (2) в знаменателе «просмотры карточек» сидели строки событий корзины, потому что событие садится на ту же страницу, — поймал исполнитель; (3) маска «строка с массивом товаров» захватывала и строку покупки — поймал сам оркестратор, сверив арифметику отчёта. Два из трёх уже уехали в коммит спеки. Класс дефекта не новый (#38 — устаревший .pyc), но новое здесь то, что прибор ломается на чтении данных, а не на прогоне, и потому переживает и тесты, и ревью кода. Приём: числа, попадающие в постановку и спеку, сверять арифметикой независимо от источника — сумма частей, тождества между колонками; и называть зерно явно везде, где написано «канонический день».
  • Наблюдение владельца по итогам дня: Кодекс дрейфует к букве. Прогон его уточняет, а не подтверждает целиком. На постановке Кодекс работал ровно по смыслу — половина из девяти находок это охота на формальность («критерий закрывается одним визитом», «требование живёт в прозе, а не в чекбоксе»). На коде он вернул 1 MINOR за 120 команд и прошёл мимо главного дефекта прогона — дефекта цели, видного только тому, кто держит в голове, зачем тикет заведён. Часть промаха назначена линзой оркестратора («не дублируй вкусовую линию»), но остаётся устойчивый осадок из #36/#38/ #40: лечения Кодекса буквальны даже внутри его линзы. Кандидат в механизм (не решение): требовать от линии Кодекса перед индексом один абзац — «что сделает эту правку провальной, даже если все критерии зелёные».
  • Шапка соразмерности сработала дважды за прогон. Исполнитель отверг предложенный ревьюером ряд вероятностей, перемерив его (давал 1,84 при требуемых 2), и закрыл находку другим маршрутом; перепроверка поймала необязательный сдвиг порога (0,05 → 0,03 при измеренных 7,25% — старый пол проходил). Второе — новый факт: порог, сдвинутый и честно объявленный, всё равно бывает лишним; объявление снимает скрытность, но не заменяет проверку необходимости.
  • Ревьюер признал своё предложение неверным на перепроверке — раньше такого не встречалось. Перепроверка, которой явно поручено судить лечение, судит и собственное предписание.
  • Приёмка глазами снова дала то, чего не даёт ни один вердикт: один распечатанный визит показал новый сюжет целиком (шесть карточек, две в корзину, корзина не открыта) и заодно, что кладут не только магниты, — то есть главная правка видна на одном случайном примере.
  • Казус: make в промпте звался из generator/, а Makefile лежит в корне — три «красных» прогона на пустом месте, и ревью постановки этого не поймало (команды проверки оно не запускало).
  • Цифры: ревью постановки 32 команды / 9 находок, все приняты; реализация ~31 мин, саморевью 3/3; слепые ревью — Опус 28 инструментов / 7 находок, Кодекс 120 команд / 1, пересечение ноль; триаж 8 FIXED + второй круг на 1 новую находку, 0 отклонённых; перепроверки 15 и 18; тесты 383 → 394.
  • Долг четвёртый прогон подряд тот же: шапки соразмерности так и не перенесены. Закрыт 2026-08-03.

2026-08-03: долг соразмерности закрыт, и найдена его настоящая причина

Сессия без прогона: разбор lessons.md по /writing-great-skills и правка шапок. Долг, висевший с #39, закрыт — правки лежат в prompts/triage-header.md и prompts/recheck-header.md, содержание там, здесь не дублирую.

  • Причина не в размере починки, а в весе слова (диагноз пользователя). Пока код писал Кодекс, а ревьюил Опус, послушный исполнитель был прав: ревьюер сильнее как архитектор. После разворота ролей — вкусовая реализация Опусом, ревью Кодексом — послушание стало вредным: Кодекс очень сильный исполнитель, но слабее как мыслитель-архитектор, настойчив, склонен к формализации без учёта высокоуровневой цели и продавливает Опуса на свои решения. Перекос усиливает разница ступеней: Опус на medium против Сола на high уступает ещё и по «званию».
  • Цифра под диагнозом: #39, #40, #50 — около 28 находок, ноль отклонений.
  • Два кандидата сняты сверкой с эмпирикой, а не по вкусу. Требование объявлять ослабление порога отдельной строкой — против него сам урок #50 («объявление не заменяет проверку необходимости»), а оба случая (#40, #50) поймала перепроверка. Пункт «оценивай довод исполнителя, а не факт несогласия» — дубль строки, уже стоявшей в шапке.
  • Правило об арбитре в споре не писалось намеренно: спора ни разу не было (ноль отклонений), правило под несуществующую ситуацию есть перерегулирование. Ждём первого отклонения — новые шапки как раз должны его породить.
  • Разбор самого файла: 783 строки, 22 записи; собственное правило «поглощён урок — сожми до строки-факта» сработало один раз (07-12) и с тех пор нет — записи после него по 30–80 строк, часть держит вторую копию действующих правил SKILL.md. Дистилляция архива вынесена отдельным заходом.

Очередь на механизм (сведена сюда, чтобы не искать по всему файлу): блок открытых решений в промпте ревью — «какие решения ещё открыты и кем владеются» (#36); абзац от линии Кодекса «что сделает эту правку провальной, даже если все критерии зелёные» (#50); приёмы против сломанного прибора оркестратора (#38 — PYTHONDONTWRITEBYTECODE=1 и чистое дерево крайними прогонами; #50 — арифметика чисел, попадающих в спеку, и явное зерно; #39/#40 — тонкий лог перепроверки как повод проверить MAJOR своим зондом; #40/#50 — один пример целиком на приёмке); мелочи по месту — сведение вердиктов по совпадению явления, а не формулировки (этап 5), ревью постановки не запускает команды тикета (этап 2), оговорка про < /dev/null рядом с приёмом в codex-exec.md; старьё — ранжирование правил задачи (07-07), эхо файла задачи (07-13), A/B на gpt-5.6-terra (07-13), пп. 1–2 и 4 одёргиваний (07-31).

2026-08-04: находки холодного ревью долечены; запрет против значения

Сессия без прогона, продолжение 08-03: триаж находок холодного ревью самого скилла. Что именно поправлено — в коммитах от c18472e, здесь не дублирую.

  • Правило, нарушаемое рассуждением, лечится значением, а не ещё одним запретом. «Не режь ревьюеру read-only» стояло в двух местах — и было нарушено при сборке команды для этого же холодного ревью: не по незнанию, а доводом «ревью документа рук не требует». Промпт при этом сам обещал ревьюеру опыты во временных файлах, которые песочница запрещала, — тред пришлось убить и перезапустить. Вылечено тем, что песочница названа значением с причиной при нём, а ветке ревьюера дан целый рецепт: не осталось повода собирать команду руками.
  • Случай третий той же формы (два прежних в архиве: 07-22 канал обёртки, 07-30 danger-full-access «на всякий случай» из-за оговорки «боем не обкатано»). Отказ скилла — не отсутствующее правило, а правило, оставляющее рассуждению право на исключение; оговорка в рецепте читается как приглашение к самодеятельности.
  • Скретчпад поднимается из транскрипта сессии. Ребут стёр каталог; шесть документов восстановлены fast-worker'ом из JSONL сессии (~/.claude/projects/<проект>/<id>.jsonl): вызовы Write отдают тело дословно, файлы чужих процессов — из эха Read с обрезкой номеров строк. Не поднимаются промпты, ушедшие в cat ... | на stdin: их тело в транскрипт не попадало. Цена — 48k токенов субагента, три минуты.
  • Вывод под этим: пережить сессию должно то, что лежит вне скретчпада. Индексы находок целиком не переносим — в handoff идут открытые, с номером и доводом линии; закрытые читаются по коммитам.
  • Диагноз пользователя: краткость исполнителя ломает решение. Метку NEEDS_DECISION ставит тот, кто единственный держит код в голове, а наверх уходит голый вопрос — координатор кода не читал по устройству ролей, владелец тем более. Дословно: «Опус пишет так, как будто я очень хорошо разбираюсь в коде и проблеме, а так бывает далеко не всегда». Отсюда требование вариантов, их цены и рекомендации — и к исполнителю, и к координатору, когда он несёт развилку владельцу.

2026-08-06: вывод Кодекса теряет харнесс, а не версия

Запись переписана в тот же вечер: первая редакция валила отказ на версию codex-cli 0.146.0 и была неверна. Развели две проверки — холодная A/B (изолированная установка 0.146.0 рядом с глобальной 0.146.1, обе штатны) и контрольные прогоны изнутри харнесса, где отказ живёт и на 0.146.1.

  • Версия ни при чём. Чистые 0.146.0 и 0.146.1 на этой же машине пишут в перенаправленный файл штатно: codex --version > f — 18 байт, --json — семь строк JSONL с thread.started и command_execution. Списать отказ на версию — первый соблазн: он объясняет всё и не требует ничего. Цена проверки — десять минут на установку в отдельный префикс, и она стоит того, чтобы гипотеза не уехала в журнал утверждением.
  • Ось — файл против канала. Изнутри харнесса T3 Code (SDK-хост) на 0.146.1: codex --version > f — ноль байт и код 0, codex --version | cat > g — 18 байт. То же на полном вызове: exec --json … > log — пусто, он же через 2>&1 | cat > log — семь строк, thread_id в первой, command_execution дважды. Отчёт -o пишется в обоих случаях: файлы, которые Кодекс открывает сам, целы — пропадает унаследованный дескриптор, когда за ним файл. Stderr так же: Error loading config.toml… виден только через канал. Отключение песочницы Bash ничего не меняет.
  • Тот же разрез объясняет и вход. exec - < prompt.md изнутри даёт No prompt provided via stdin и код 1, cat prompt.md | работает; вне харнесса работают оба. Одна причина на обе находки первой редакции — и обе снимаются каналом.
  • Не всякий харнесс — этот харнесс. В Claude Code CLI 2.1.223 на той же машине и той же 0.146.1 всё штатно: файл на выходе — 18 байт, лог — семь строк, - < prompt.md читается. Ярлыку «я в агенте» не верь, спроси среду сама — самотест стоит пять секунд и живёт предполётом в codex-exec.md.
  • Лечение живёт в рецептах, не здесь. Форма сбора — каждый поток своим каналом в свой файл — стоит там же, в предполёте, и включается по результату самотеста; сами рецепты не правились. Три вещи, которые стоили отдельной проверки. 2>&1 | cat короче и подкладывает ошибку разбора: строка stderr в JSONL ломает jq и может встать первой вместо thread.started. Канал глотает код возврата (false | cat даёт ноль) — отсюда set -o pipefail в форме. Псевдотерминал из первой редакции не нужен: script давал тот же JSONL ценой \r и непереносимости.
  • Механизм не вскрыт. Известно поведение, не причина: файл на входе и выходе дочернего процесса пуст, канал цел, песочница ни при чём. Пишу это свойством среды и не выдаю за объяснение.
  • Цена неверной атрибуции — коммит в репозиторий скилла. Правило «пока причина не снята запуском с одним изменением, она гипотеза» у меня было — и не спасло: внутри серии я честно менял по одному параметру, но вся серия целиком стояла в одной среде, и согласованность её прогонов читалась подтверждением. Отсюда добавка к правилу: среда — тоже параметр, и первый кандидат на подозрение, когда наблюдение расходится с документацией инструмента. Сняла ошибку чужая проверка, не моя.
  • Уцелевшая ловушка: запуск из недоверенного каталога (пробовал из /tmp) даёт Not inside a trusted directory and --skip-git-repo-check was not specified. Снаружи это видно в stderr и коде 1; изнутри харнесса сливается со всеми прочими отказами в пустоту и код 1.
  • Цифры: два захода контрольных прогонов, промпт односложный («выполни pwd»), model_reasoning_effort="low"; отчёт -o — 46 байт в каждом, лог через канал — семь строк.

2026-08-27: три линии по нулям на ошибке формы (clickstream v2, #158)

Прогон DQ-контура: реализация Codex, линия уместности Fable, две слепые резервные линии. Все три дали ноль находок; ошибку увидел пользователь одним вопросом «почему DQ на Python, а не SQL».

  • Находка жила выше диффа. Агрегация ключевого зерна шла в Python: 57 567 ключей сессий ехали из ClickHouse в процесс Airflow ради суммирования. Код опрятный, ни один из двенадцати запахов не задет — линза, читающая код, пройти мимо была обязана.
  • Промах не был бесплатным. Та же граница была причиной саги с памятью: OOM планировщика в 640 МиБ, восемь ресурсных ручек в трех дагах. Ревью объяснило сагу теснотой коробки — гипотезой из постановки — и не спросило, почему задача, пишущая три десятка строк сводки, ест сотни мегабайт.
  • Механизм промаха — порядок чтения. Ревью от диффа принимает раскладку исполнителя как оглавление; внутри чужой рамки видно кривое имя, но не то, что раскладка не та. Фронтирный исполнитель пишет чистый код вокруг неверных решений: ошибки живут в форме, не в отделке — то же наблюдение, что в прогоне #18.
  • Лечение — механизм, не правило в прозе (класс отказа записан 2026-07-31): в line-taste.md добавлен shape-проход шагами до чтения диффа — набросок ожидаемой формы из одной постановки, список недиктованных решений, вердикт каждому. Артефакты предъявляются как checks run даже при нуле находок — слот уже был в review-boilerplate.md. Запрет «не читай дифф первым» не произносится: его исполняет порядок шагов.
  • Проверить следующим прогоном: ловит ли shape-проход ошибку класса #158 и не сжигает ли ревьюер бюджет до диффа (граница «a few lines suffice»).

2026-09-09: аудит плюс реализация одним усилием (t2-ml-forecast, #22 + #18)

Прогон: разведка Explore, аудит Codex high, гриллинг с владельцем в чате, ревью постановки deep-reasoner, реализация Codex high, саморевью, две слепые линии (craftsman и Codex), триаж, перепроверка, коммит, трекер.

  • Аудит перевернул постановку. Тикет просил «проверить, что умеет механизм»; прогон показал, что чинить его дороже, чем заменить (ошибка на схеме заказчика, нет окна действия). Объединение с тикетом-решением в одну сессию сэкономило цикл передачи контекста; владелец такое объединение приветствует, формализм границ ему не нужен.
  • Ревью постановки поймало блокер, который тесты не поймали бы: инвариант Slice.coherent отвергал любой результат с условиями, и без него приёмка была недостижима. Ни разведка, ни постановка этого не знали. Этап 2 окупился одним этим.
  • Sol без вкуса, подтверждено. Код верный и полный, форма первая попавшаяся: ручная валидация на 100 строк рядом с pydantic, хвост из семи аргументов, assert как страж. Разрез «вкусовая часть сначала» был пропущен: постановка предлагала «dataclass или pydantic», и исполнитель выбрал буквальный путь. Контракты и разбор входа — Opus.
  • Накрутки Codex, два случая за прогон. По находке ревью он завёл четыре теста, которые читают markdown и проверяют формулировки документов; по находке про формулы в Excel запретил символы ещё и в поле, которое не выгружается (перепроверка вернула OVERREACH). Лечение строкой в шапке триажа: «тесты только на поведение, не на текст документов; чини ровно то место, что названо».
  • Перепроверка тем же ревьюером принесла новую находку, не только подтверждение: OVERREACH и указание, что дыра сидит в экспорте, а не в условиях. Заведён отдельный тикет, в скоуп не тянули.
  • Два писателя в одном дереве без конфликта. Владелец в соседней сессии правил другие файлы; коммитил только файлы из отчёта исполнителя по явному списку. Ревьюерам список «шум дерева» подшит в параметры отдельным блоком, иначе они ревьюят чужое.
  • T3 Code: bwrap RTM_NEWADDR в песочнице Codex не фатален. Четыре ошибки на apply_patch, обошёл сам через heredoc; 58 команд прошли.
  • AGENTS.md-правило «перечитывай issue после сжатия» заставило саморевью Codex перечитать GitLab и запустить сверку витрин через bpc_f2_export (минуты). Полезно, но долго; сторож на застой необходим.
  • Расход контекста оркестратора: два полных чтения (разведка 419 строк, вердикт ревью постановки) дали больше всего. Разведку просить индексом до 80 строк с путями; вердикты читать только строками находок через grep.

2026-09-11: накладные расходы конвейера против «сделать самому» (t2-ml-forecast, #36)

Тикет #36: условие с инерцией, семь файлов, все развилки закрыты ответами бизнеса и оркестратором до старта. Конвейер развёрнут по плейбуку: файл-задача, craftsman исполнителем, Astra ревьюером.

  • Замер контекста оркестратора (тысячи токенов, оценка). Конвейер: чтение плейбука и справочников 22, сбор фактуры 8, файл-задача и промпты 5, приёмка ~10, итого ~45. Своими руками: чтение кода 15, правки и тесты 10, приёмка 5, итого ~30. Разница целиком в фиксированной плате за плейбук: вторая задача в той же сессии стоит конвейером ~20.
  • Слова владельца. У Astra как оркестратора накладные расходы на постановку, оркестрацию и приёмку во многих случаях превышают экономию от делегирования; её оркестрация тяжелее, чем у Fable, хотя и мощнее. Экономика субагентов полезна для ревью, где нужен чистый контекст, и редко для разработки.
  • Следствие для вводных скилла (переписать позже): одиночная задача с закрытыми развилками до десятка файлов делается оркестратором самостоятельно, конвейер сводится к слепому ревью. Исполнитель-субагент оправдан при нескольких задачах в сессии или при открытых решениях, когда контекст оркестратора нужен для них, а не для кода.
  • Лишняя плата этого прогона: часть фактуры ушла на ловушки Codex (snap uv, зависание TestClient в песочнице), хотя исполнителем в итоге выбран Claude. Исполнителя выбирать до чтения справочника по механике вызова.