Прогон проб 7×7 — проверка обвязки как процесса на задачах, поставленных ради проверки: не взятых из работы, но таких, какие разработчик мог бы поставить. Проба это одна такая задача; раунд семь проб; прогон семь раундов. Проверяется, отвечает ли база, инструмент, правила и тесты верно, останавливают ли они правдоподобно-неверное действие и чем именно это удержано. Ловит ли свод по планке дефект в коде, меряет не он, а bar-probe. Вызывать по «прогон 7×7», «прогон проб», «проверь обвязку проба...
Installs into .claude/skills of the current project.
Sponsors
SSkills DirectorySkills Directory
Know which skills are safe — weekly.
SSkills DirectorySkills Directory
Is that skill safe to install?
Are you the author of Probe?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/igor-lx-probe)
---
name: probe
description: Прогон проб 7×7 — проверка обвязки как процесса на задачах, поставленных ради проверки: не взятых из работы, но таких, какие разработчик мог бы поставить. Проба это одна такая задача; раунд семь проб; прогон семь раундов. Проверяется, отвечает ли база, инструмент, правила и тесты верно, останавливают ли они правдоподобно-неверное действие и чем именно это удержано. Ловит ли свод по планке дефект в коде, меряет не он, а bar-probe. Вызывать по «прогон 7×7», «прогон проб», «проверь обвязку пробами». Отдельная ось — «проба по холодному контуру»: то, чего не касается ежедневная петля проверок; она идёт проходами по видам дефекта и выборкой из 21 пробы.
user-invocable: true
---
# Прогон проб: 7 × 7
Обвязка проверяется не прогоном её собственных проверок, а **тем, что она даёт
на задачах, которых сегодня не ставили**. Прогон подставляет такие задачи и
устанавливает два факта: даёт ли обвязка верный ответ и останавливает ли она
правдоподобно-неверное действие, — и третий: чем это удержано.
**Место среди проверок обвязки.** Их четыре, у каждой свой вопрос: режим
`falsify` — ловит ли каждая сверка свой предмет; этот скилл — справляется ли
обвязка как процесс с живой задачей и чем это удержано; скилл `bar-probe` —
находит ли свод по планке дефект в коде; скилл `audit` — правильно ли обвязка
устроена. Проба вида «поймает ли свод такое-то нарушение в коде» здесь не
делается: её оформляют посадкой в книгу `.claude/tools/bar-probes.json`, и
меряет её `bar-probe`. Свод по планке пробуется здесь только как механизм:
протокол, печать, слова страниц, ворота конца хода.
## Определения — считать буквально
| Что | Сколько | Смысл |
| --- | --- | --- |
| **Проба** | 1 задача | задача, поставленная ради проверки обвязки: не взятая из работы, но такая, какую разработчик мог бы поставить по этому проекту. Содержит один вопрос к обвязке и способ его проверить |
| **Раунд** | 7 проб | каждая проба раунда относится к **своей** точке соприкосновения; две пробы одного раунда не проверяют одну точку с разных сторон |
| **Прогон** | 7 раундов = 49 проб | законченная проверка горячей оси; результат у него один — чист или нет |
| **Проход** | 1 вид дефекта | вид, заданный признаком, который ищется командой, — перечислен командой целиком; у каждого места исход, у вида — держатель. Только холодная ось |
| **Прогон холодной оси** | проходы + выборка 21 проба | хотя бы один проход на семейство, затем по 3 пробы на семейство с добором по находке — раздел «Из чего складывается прогон холодной оси: проходы, затем выборка» |
**Прогон чист, когда находок ноль.** Это определение исхода, а не основание для
повтора: «почти чисто» результатом не является, а решение о повторном прогоне
принимает разработчик.
## Параметры вызова — их задаёт разработчик
Сколько прогонов делать и когда остановиться, **говорит он**, а не скилл. По
умолчанию — **один прогон**: сделать, доложить исход, ждать решения.
**Ось по умолчанию — горячий контур:** то, чьё поведение проверяется между
правкой и коммитом, то есть попадает в петлю «правка → полный набор проверок →
коммит». Вызов без уточнения идёт по ней. Холодный контур — вторая ось со своим
разделом ниже, и включается он **только прямым вызовом**: в обычный прогон
холодная ось не добавляется, иначе каждый прогон тратил бы часть проб на
область, которая между прогонами не менялась.
| Как сказано | Что делать |
| --- | --- |
| «прогон проб», «прогон 7×7» — без уточнения | один прогон по **горячему контуру**, доложить исход и остановиться |
| «два прогона», «ещё раз» | названное число прогонов подряд, каждый новыми пробами |
| «повторяй до чистого» | повторять, пока прогон не даст ноль находок |
| «пройди по такой-то области» | те же 49 проб, но ось «точка соприкосновения» сужена до названного |
| «проба по холодному контуру» | прогон по **второй оси** — раздел ниже: проходы по видам дефекта, затем выборка. Предметы берутся не по областям, а по свойству «этого не касается ежедневная петля». Только по прямому вызову |
Находка чинится в том же заходе по общим правилам — это от числа прогонов не
зависит. Если после починки прогон не повторяется, в отчёте прямо стоит, что
последний прогон был **не чист**, и чем именно.
### Указание «повторяй» означает прогоны подряд, без остановки на отчёт
**Ход завершается текстом, за которым не следует вызов инструмента.** Отсюда всё
остальное: любой отчёт — хоть по раунду, хоть по прогону — останавливает работу,
если после него ничего не вызвано. Фраза «иду в следующий прогон», написанная в
конце такого отчёта, — обещание, а не действие: она не запускает ничего, и
работа стоит до нового сигнала разработчика.
**Стоп-условий ровно три, и других нет:**
1. прогон чист — цель стоячего указания достигнута;
2. нужна развилка, которую выбирает разработчик (см. общие правила о том, когда
решение не за исполнителем);
3. действие необратимо и требует подтверждения.
Ни «раунд кончился», ни «прогон кончился», ни «отчёт написан» стоп-условиями
**не являются**. Отчёт по прогону пишется — и **следующий вызов инструмента идёт
в том же ходе, сразу за ним**.
Промежуточные заметки — одна-две строки «что нашлось, что чиню» — пишутся между
вызовами инструментов, а не вместо них.
**Это стоило времени дважды, и оба раза по одной причине — правило было уже, но
формулировка оставляла лазейку.** Сперва работа вставала после каждого раунда;
правило написали «не останавливаться между раундами» — и работа стала вставать
после каждого прогона. Поэтому здесь критерий не в размере куска работы, а в
стоп-условиях: их три, они перечислены выше, и всё, чего нет в списке, —
основанием остановиться не является.
## Где идёт прогон
В проекте — в нём самом. В мастерской обвязки пакетов и базы нет, и
площадку готовит одна команда из корня мастерской:
```
node .claude/tools/graph.mjs seat <пустая папка вне репозитория> --shop
```
Она сажает текущую обвязку, ставит пакеты, кладёт поверх магазин — живое
приложение, на котором пробе есть что править, — и кончается сверкой базы;
устройство — `.claude/tools/graph.md`, режим `seat`. Код не ноль — она
печатает причину и команду разбора, и прогон до неё не начинается. Обвязку
правят в репозитории мастерской и переносят в посадку повторным вызовом той
же команды без `--shop`; окно свежести и коммит прогона — в репозитории
мастерской.
## Что такое проба и когда она пройдена
Проба — это **поставленная задача, а не запуск команды**: «мне надо починить вот
это», «мне надо добавить вот такой узел», «пользователь жалуется на вот такое».
Дальше проверяется, что обвязка на неё отвечает.
**Пройдена:** база, документация, правила или инструмент дали верный ответ —
либо остановили правдоподобно-неверное действие.
**Провалена:** промолчали; ответили неверно; ответ утонул в шуме; ответ
пришлось выводить самому, хотя он обязан был лежать записанным.
Отсутствие ответа — тоже провал пробы, и он менее заметен: прогон, в котором
проверка отработала и ничего не нашла, по выводу не отличается от прогона, в
котором она не сработала вовсе.
**У каждой пробы, кроме исхода, — графа «чем удержано»**: что именно дало
верный ответ или остановило неверное действие. Варианты: сверка (имя
ёлочками), звено цепочки, ворота перед коммитом, ворота конца хода, хук среды;
раздел правила — файл и заголовок, и входил ли он в ярус чтения этой задачи;
шаг скилла; запись базы; тест. Последний вариант — **ничто, кроме внимания
сессии**: машина промолчала, раздел не входил в ярус, а неверного действия не
случилось, потому что сессия помнила. Такая проба — находка, хотя исход
«пройдена»: следующая сессия может не вспомнить. Её класс уходит в аудит
кандидатом в признак «держится одной прозой, хотя могла бы машина».
**Держатель-машина подтверждается, а не предполагается**: сломать его и
убедиться, что неверное действие теперь проходит. Без этого «удержано сверкой»
читается так же, как «удержано случайно».
**Точку, которую держит машина, пробой руками не берут — её держателя
проходят списком.** Когда на вопрос пробы отвечает сверка, режим
инструмента, ворота или хук, проба сводится к фальсификации держателя, а
её для сверок целиком делает режим `falsify`, для режимов — тесты
инструмента. Поэтому прогон один раз проходит перечень таких держателей,
взятый командой: сверки — таблица `.claude/tools/checks.md` против вывода
`falsify` (он называет сверки без рецепта); режимы — заголовки третьего
уровня `.claude/tools/graph.md` против тестов инструмента; хуки — таблица
«Хуки обвязки» в `.claude/rules/environment.md`. Держатель без
фальсификации — находка, он получает тест. Пробы идут на то, что держится
правилом по ярусу и вниманием, и на хуки: их исполняет только живая сессия,
и пробу на них ставит работник (раздел ниже). Решено разработчиком
`2026-11-06`.
## Работник и наблюдатель
Проба процесса ставится не сессией, ведущей прогон, а **работником**:
отдельной сессией `claude -p`, стартовавшей в папке площадки и не знающей,
что её проверяют. Сессия, которая сама изображает задачу, знает, что
проверяется, и проходит шаги, которые живая сессия пропустила бы.
- **Вызов** — как у сессии свода в скилле `bar-probe`, шаг 2: задание первым
словом после `-p`, `--session-id` с номером от
`node -e 'console.log(crypto.randomUUID())'`, в фоне, по одной за раз.
Работнику сверх `node` и `git` разрешают `npm` и `npx`.
- **Задание** — одна строка задачи, как её поставил бы разработчик, без
подсказок о порядке работы: проверяется, держит ли порядок обвязка.
- **Наблюдатель** — сессия, ведущая прогон. Журнал целиком она не читает:
читает его сводку, итоговый вывод работника и те файлы площадки, которые
называет перечень ниже. Отказы разрешений — поле `permission_denials`
вывода.
Сводка журнала — строка на вызов инструмента: имя, путь или команда, окно
чтения. Журнал — `~/.claude/projects/<путь площадки, косые черты заменены
дефисами>/<uuid>.jsonl`.
```
node -e 'const fs=require("fs");for(const l of fs.readFileSync(process.argv[1],"utf8").split("\n")){if(!l)continue;let e;try{e=JSON.parse(l)}catch{continue}const c=e.message&&e.message.content;if(!Array.isArray(c))continue;for(const b of c){if(b.type!=="tool_use")continue;const i=b.input||{};console.log([b.name,String(i.file_path||i.command||i.pattern||i.path||"").replace(/\s+/g," ").slice(0,160),i.offset!=null?"@"+i.offset+"+"+(i.limit??""):""].join(" | "))}}' <журнал>
```
**У каждого шага перечня наблюдатель ставит исход и держатель**: машина —
ворота, хук, сверка, режим; правило, вошедшее в ярус задачи; одно внимание
работника. Шаг без признака в сводке или в дереве не судится. Шаг,
пропущенный работником либо выполненный одним вниманием, — находка: класс
уходит в кандидаты на машинного держателя по разделу «Найденное лечится как
класс, а не как пример».
**Общее для задачи с диффом кода:**
| Шаг | Доктрина | Признак |
| --- | --- | --- |
| вход: карта и факты базы | `entry.md`, «Первыми же действиями, до любых действий с кодом»; `loop.md`, «Соразмерность: что читать под задачу» | чтение `.context/00-map.md` и `.context/01-facts.md` до первой правки |
| область очерчена | `loop.md`, «Соразмерность: что читать под задачу» | `graph.mjs brief` либо `plan` до первой правки |
| область прочитана целиком | `loop.md`, «Петля: база → код → сверка → записи → задача → записи», шаг 2 | чтение каждого файла области без окна либо окнами, покрывающими файл |
| тесты: новое закрыто, изменённое пересмотрено | `code.md`, «Тесты правятся вместе с кодом, а не после него» | правка в `tests/` той же правкой; тесты тронутых файлов открыты |
| цепочка проверок | `close.md`, «Как проверяется сделанное» | `npm run check` с кодом `0` после последней правки |
| мутационный прогон по новому и правленому | `code.md`, «Мутационный прогон — машинная форма критерия о падающем тесте» | вызов Stryker либо `graph.mjs mutated` без долга по тронутым файлам |
| свод по планке | `quality.md`, «Сверка — обязательный шаг, а не настрой» | `graph.mjs bar` до печати; страницы `bar-read` открыты |
| база и документация | `code.md`, «Как заводится новый узел», пункты 4 и 5; `loop.md`, петля, шаг 6 | `graph.mjs tested`; записи карты и `08-tests.md` о новых файлах; документы по списку `tested` |
| коммит через ворота | `safety.md`, «Коммиты: атомарно по смыслу, а на риске — дробнее» | коммит без `--no-verify` и без подмены `core.hooksPath` |
| отчёт | `close.md`, «Отчёт» | числа прогона, нарушения объявлены `⚠`, блок долга, если долг есть |
**По роду задачи — сверх общего:**
| Род | Доктрина | Признак |
| --- | --- | --- |
| новый узел | `code.md`, «Как заводится новый узел» и «Раскладка проекта» | замер до письма: `plan` и `brief` по точке встраивания, `levels`; файл лежит по раскладке; строка гарантии, если возможность видна снаружи (`J12`) |
| рефактор без смены поведения | `code.md`, «Тесты правятся вместе с кодом, а не после него», пункт 4 | старые тесты достают до перенесённого: поломка узла роняет именно их |
| удаление узла | `code.md`, «Как убирается узел» | потребители проверены (`brief`, `dead`); тесты и записи базы удалены; документы о нём сняты |
| багфикс | `code.md`, «Тесты правятся вместе с кодом, а не после него» | падающий тест раньше правки кода |
| правка теста | `code.md`, «Тесты правятся вместе с кодом, а не после него», пункт 3 | тест усилен, а не ослаблен; поломка кода роняет его |
| правка значения-настройки | `documentation.md`, «Пять видов документов, и что в каждый не пишется» — настройки; `quality.md`, `E5` | документ настроек обновлён; ответ на краевом входе не изменён без записи |
| правка стиля | `styles.md`, «Как компонент разговаривает со своими стилями» | три шага схемы; сверка имён классов без новых нарушений |
| работа по пункту долга перехода | `loop.md`, «Найденное чинится сразу, в том же заходе»; `seat/transition.md`, «Что идёт в долг перехода» | пункт правлен той же правкой: число меньше либо строка удалена; храповик обнулён |
| задача чтения | `loop.md`, «Петля: база → код → сверка → записи → задача → записи» — род задачи | правок кода нет; свод `bar <путь>`; находки названы с судьбой «предложено»; прочитанная область без записи в карте записана |
| правка одной документации | `loop.md`, «Петля: база → код → сверка → записи → задача → записи» — род задачи | свода не требуется; найденное в коде названо, а не починено |
## Как выбираются пробы
Пробы не выдумываются подряд из головы: так они сползают в одну область.
Раскладываются по осям, и **раунд обязан задеть разные значения хотя бы двух
осей**.
- **Тип задачи:** объяснить поведение · ревью без правки · багфикс по
симптому · рефактор без смены поведения · новый узел · удаление узла ·
правка значения-настройки · правка стиля · правка теста · правка
документации · правка самих правил · посадка в пустой и в живой проект ·
переход · обновление обвязки в проекте, посаженном прежней редакцией.
Пробу на проект, посаженный прежней редакцией, ставят, только когда такой
проект есть: пока обвязку не сажали ни в один рабочий проект, предмета у
неё нет. Решено разработчиком `2026-10-31`.
- **Точка соприкосновения:** правила (каждый файл, включая вложенные) · база
(каждый её файл) · **документация: устройство слоёв, решения, витрина
возможностей, комментарии в коде** · инструмент (каждый режим) · свод по
планке как механизм: протокол, печать, страницы чтения, модель по уровням ·
ворота перед коммитом и конца хода, страж обхода, хуки среды · посадка:
инструкция, карта, семена · переход и его долг · гарантии поведения ·
рецепты фальсификации · тесты · браузерный смоук · мутации · окружение и
разрешения · скиллы. **Перечни берутся командой, а не памятью**: режимы —
заголовки третьего уровня в `.claude/tools/graph.md`, сверки — таблица
`.claude/tools/checks.md`, файлы правил и базы — содержимое их папок.
Перечень в этой строке отстаёт от обвязки при первой новой подсистеме,
команда — нет.
- **Направление сверки:** прямое (предмет есть — описан ли он?) и **обратное**
(описание есть — существует ли предмет?). Обратное даёт больше находок:
односторонняя сверка покрывает одно направление и печатает ноль после
исчезновения предмета.
- **Способ проверки:** чтение (утверждение базы против строки кода) · запуск
(режим инструмента, команда) · **фальсификация** (внести ошибку → убедиться,
что упала именно нужная проверка → откатить → убедиться, что прогон снова даёт
код ноль). Посадка нарушения в код ради свода по планке — способ
`bar-probe`, а не этого скилла.
**Логический пробел, найденный аудитом, входит в прогон отдельной пробой.**
Аудит читает текст и потому находит то, что не проявляется при работе:
висящее правило, противоречие двух формулировок, неисполнимый шаг. Проба
отвечает на другой вопрос — держится ли это чем-нибудь, кроме внимания, — и
ответ на него чтением не получить. Обмен идёт в обе стороны: класс, найденный
здесь, и проба, удержанная одним вниманием, дописываются в список признаков
скилла `audit` тем же заходом.
### Неповторение — про протухание, а не про запрет
**Проба не повторяется, пока её предмет не менялся.** Повтор по неизменившемуся
предмету покупает то, что уже куплено. Но проба закрепляет **пару** — предмет и
его состояние, — и состояние меняется: если предмет изменился со времени
прошлого прогона, прошлый результат к текущему состоянию не относится, и новая
проба по нему не повтор, а перезамер. Такой перезамер **обязателен**, а не разрешён.
**Запрет и отбор здесь легко перепутать, а разница решающая.** Запрет требует
памяти обо всём, что пробовали, — а её нет и не будет: пройденная проба следа не
оставляет вовсе, след оставляет только находка, и лежит он рядом с починенным
местом. Значит по памяти можно избежать ровно того, что уже укреплено, и нельзя
узнать о том, где проба когда-то прошла, а предмет с тех пор сгнил. Память
устроена наоборот тому, что нужно, — поэтому вопрос ставится не «пробовали ли
это», а **«менялось ли это с тех пор»**, и на второй вопрос отвечает машина.
**Угол — это значение оси, а не формулировка.** Тот же предмет с другого угла
допустим всегда, и «другой угол» означает другое значение хотя бы одной оси: тип
задачи, точка соприкосновения, направление сверки, способ проверки. Например, в
прошлый раз проверяли, что сверка краснеет на отсутствующем файле, в этот раз —
что она не срабатывает на неоднозначном имени. Пересказ той же пробы другими словами
углом не является: иначе правило нельзя нарушить ни одним наблюдаемым способом,
а неопровержимое правило не исполняется.
**Изменившееся пробуется обязательно.** Без этого правило запрещает ровно то,
ради чего горячая ось и существует, — свежую правку в месте, которое когда-то
уже пробовали. Сколько прогона на это отводится и почему не весь — раздел
«Из чего складывается прогон: четыре ручья».
### Окно свежести считается, а не вспоминается
Прогон с находками закрывается коммитом, и коммит несёт пометку прогона, —
значит прошлый прогон **адресуем**, и «что изменилось с тех пор» отвечает
история, а не память. Прогон начинается с одного вызова оболочки:
```
git diff "$(git log --grep="^Probe-run: hot" -1 --format=%H)" --stat
```
**Пометка ищется своей оси**, а не любая. У осей свои прогоны и свои окна: чужая
пометка сдвинула бы окно вперёд, и правки, сделанные до чужого прогона, но после
своего, не попали бы в отбор — без признака этого в выводе. Это ошибка в
**опасную** сторону, в отличие от отсутствующей пометки, поэтому ось указана в
образце команды.
Вывод и есть вход отбора: в нём предметы, изменившиеся после прошлого
прогона, и они берутся в пробы обязательно. Пометки нет — git отказывает
строкой `ambiguous argument ''` и ненулевым кодом; тогда окно берут шире, а
не пропускают отбор. **Кавычки вокруг подстановки несущие:** без них пустое
место вместо коммита делало команду `git diff --stat` — окном одной
незакоммиченной правки, без признака подмены. **Переменной оболочки в команде нет**:
подстановку `${…}` среда сессии спрашивает при любом списке разрешений
(`environment.md`, раздел «Что списком не снимается»). Держит это тест
обвязки «окно свежести и залежавшееся не молчат».
**Диапазона `..HEAD` здесь нет намеренно.** С ним сравниваются два коммита, и
незакоммиченная правка в окно не попадает — замерено: диапазон печатал пусто, пока в
рабочем дереве лежали изменённые файлы. Без него сравнение идёт с деревом, то
есть окно видит и то, что ещё не закоммичено.
**Первый предел — в безопасную сторону:** чистый прогон коммита не оставляет,
поэтому окно считается от последнего прогона, у которого **были находки**. Оно
шире нужного — перезамеров выйдет больше, а не меньше.
**Второй предел — граница репозитория, и он в опасную сторону.** История
отвечает про файлы проекта; глобальные правила, окружение, версии зависимостей и
возможности агента лежат вне его, и окно их не видит вовсе. Их берут не окном, а
поводом: сменилась среда, правились глобальные правила, обновились зависимости —
это предмет пробы независимо от того, что показал диф.
**Чем держится остаток.** Машина отвечает, что протухло; что прогон этот ответ
прочитал и взял протухшее в пробы, не проверяет ничто — наблюдать ход прогона
нечем, как и ход любого шага, который спрашивают, а не проверяют. Остаток
записан формой в каталоге ограничений — семя `07-invariants.md`, раздел
«Обещания обвязки, которые не держит машина», — а не оставлен
подразумеваемым.
### Из чего складывается прогон: четыре ручья
Одно окно свежести даёт перекос в обратную сторону, и это не догадка, а счёт:
бюджет прогона фиксирован, а изменившееся права на весь бюджет не имеет — иначе
большой диф забирает его целиком. Тогда прогон превращается в ревью диффа: проб
сделано столько же, отчёт честно скажет «прогон чист», а стабильное — записи,
которых давно никто не трогал, и обратные направления сверок — не пробуется
вовсе. Именно этот класс дефекта здесь самый живучий, и диф его не предлагает
никогда.
Поэтому бюджет разложен на доли, и ни один ручей не забирает чужую. **Доли ниже
— про горячую ось.** У холодной своя разбивка, и почему она другая, сказано в её
собственном разделе.
| Ручей | Откуда берутся предметы | Доля |
| --- | --- | --- |
| **свежесть** | окно: изменилось со времени прошлого прогона | два раунда |
| **регресс** | предметы находок прошлого прогона — другим углом | один раунд |
| **залежавшееся** | та же история наоборот: дольше всех не тронуто | два раунда |
| **покрытие** | оси выбора проб, без оглядки на историю; повтор разрешён | два раунда |
**Регресс стоит отдельным ручьём не для симметрии.** Починка родилась из красной
пробы, и проба, которая обнаружила бы её регресс, — та же самая. Запрет на неё
как на повтор означает, что починки не проверяются вовсе; починка сверки при
этом опаснее прочих, потому что результат сверки используется как основание.
**Потолок вместо «обязательно всё».** Свежесть имеет первое право **внутри своей
доли**, а не на весь прогон. Что не поместилось — названо в отчёте и в пометке
коммита, и следующий прогон начинает с этого. Без переноса непокрытое выпадает
без признака: коммит сдвигает точку отсчёта, и окно следующего прогона считается уже от него.
**Переливание идёт вниз.** В ручье кончились предметы — раунд отдаётся
«покрытию», а не заполняется выдуманным. Пустой ручей — факт о состоянии
проекта, а не повод изобретать пробу.
**«Залежавшееся» — не холодный контур, и путать нельзя.** Холодный определён
свойством «этого не касается ежедневная петля»; здесь признак другой — давность
последнего касания. Предмет бывает горячим и залежавшимся разом: лежит внутри
ежедневной петли, проверяется ею каждый день и при этом не правился дольше всех
в проекте. Список даёт та же история, что и окно, только прочитанная наоборот:
```
node -e 'const git=(a)=>require("child_process").execFileSync("git",a,{encoding:"utf8"});const files=git(["ls-files","--error-unmatch",...process.argv.slice(1)]).split("\n").filter(Boolean);console.log(files.map((f)=>git(["log","-1","--format=%cs","--",f]).trim()+" "+f).sort().slice(0,10).join("\n"))' <область>
```
Без `--error-unmatch` область, которой нет в индексе, даёт пустой список с
кодом ноль — то есть «залежавшегося нет»; с ним команда называет промах.
## Ось «холодный контур» — вызывается отдельно
Обычный прогон идёт по тому, чьё поведение проверяется между правкой и коммитом, и потому находит
быстро: свежая правка ещё горячая, и вокруг неё всё живое. Но у обвязки есть
вторая половина, и она устроена наоборот.
**Определение — по свойству, а не по списку папок.** В холодный контур входит
всё, чьё поведение не проверяется ничем между правкой и коммитом: ежедневная
петля — это «правка → полный набор проверок → коммит», и что в неё не попало, не
проверяется ни разу за день. Список папок тут не годится принципиально: он
устареет, а свойство — нет.
**Почему отдельным вызовом.** Этот пласт расходится с действительным **медленно**: его правят редко,
и находки в нём копятся месяцами, а не за неделю. Гонять его каждый прогон —
платить за то, что почти не изменилось; не гонять никогда — держать на
внимании то, у чего нет ни одного машинного свидетеля. Поэтому у него своё имя
и свой вызов, а не строчка в общем списке осей.
### Куда бить — семь семейств, и все они про одно свойство
| Семейство | Признак | Чем обычно оказывается находка |
| --- | --- | --- |
| **Другая машина** | предмет обещает переносимость, а проверяется только здесь | закреплённая версия среды, кэши вне репозитория, пути, работающие лишь на одной ОС |
| **Другой проект** | заготовка исполняется не у нас, а у принимающей стороны | шаблоны конфигов, инструкция посадки, состав того, что уезжает; файл уехал, а объяснения к нему нет; проект, посаженный прежней редакцией, после обновления — семена вне `.claude` старые, таблица скиллов называет удалённый (только когда такой проект есть — ось «Тип задачи») |
| **Другое состояние вызова** | поведение зависит от того, что уже поднято, собрано или лежит рядом | переиспользование поднятого сервера, устаревший артефакт сборки, отсутствующий отчёт, испорченный реестр |
| **Другой человек и другой день** | путь исполняется раз в месяцы и не нами | публикация, первый запуск в чистом клоне, генератор данных, ручной скрипт, который сессия не запускает |
| **Ветка, в которую не входят** | код есть, а сценария нет | обработка отказа, вырожденный вход, пустая коллекция, дев-слой, не уезжающий в сборку |
| **Вторая копия** | предмет прочитан один раз и больше не перечитывается | спящие копии, парные форки, побайтные двойники, реэкспорт, которым никто не пользуется |
| **Проза, которую не исполняет ничто** | утверждение о коде, не имеющее машинного крючка | счёт сущностей, обоснование исключения, приписывание гарантии не тому механизму, якорь без цитаты |
**Семейство «другой проект» пробуется командой посадки, а не прозой.**
Команда `seat .` и есть инструкция для всех трёх сценариев — пустой папки,
знакомого проекта и чужого (`seat/seat.md`, раздел «Посадка — одной командой,
сценариев три»), — и проба этого семейства выглядит так: свежий клон полки с
гита, проект нужной формы, та же команда. Каждую форму, которую посадка
обязана встретить, держит корпус посадок в горячем наборе
(`graph.modes.corpus.test.mjs`): проект получает свой сценарий, посадку кодом
ноль и сверку базы кодом ноль, чужой — свой перечень «не проверено».
Найденная форма ложится в корпус проектом тем же заходом; тогда следующая
посадка её не найдёт заново. Круги посадок сессией по прозе как поиск
находок отменены: решено разработчиком `2026-10-30`.
### Вопрос, который задаётся на каждой пробе
Не «верно ли это?», а **«чем это держится и когда в последний раз
исполнялось?»**. Ответы «правилом», «комментарием», «помним» означают отсутствие
машинной опоры и являются находкой. Ответ «этой проверкой» подтверждается
фальсификацией: проверку ломают и убеждаются, что она падает.
**Принадлежность проверяется, а не предполагается.** Здесь чаще всего
оказывается, что предмет держится **не тем**, чем кажется: не тестом, а типом;
не тестом, а конфигом компилятора; не байтами, а объявленной пометкой. Проходящий
тест на подсадке в этом пласте — не пробел, пока не проверено, что не упал
никто другой.
### Чем находка отличается от находки обычного прогона
Тем, **когда** она выстрелит. В горячей части дефект ловится ближайшим
прогоном; здесь — у принимающей стороны, после публикации, на чужой машине или
через полгода, когда причина забыта. Отсюда две поправки к обычному порядку:
- **цена ошибки считается по моменту проявления**, а не по размеру правки:
строчка в инструкции посадки дороже строчки в тесте, потому что вторую
поймает прогон, а первую — никто;
- **починка по возможности переносится в горячую петлю.** Гарантию, которую
можно выразить тестом обычного набора, туда и кладут: тогда она перестаёт
быть холодной. Сверка базы или запись — второй выбор, а не первый.
### Из чего складывается прогон холодной оси: проходы, затем выборка
Холодная ось ищет дефекты там, где их не проверяет ничто, и пространство
поиска не перечислено. Пробы наугад по нему к чистому прогону не сходятся:
шесть прогонов подряд по `49` проб находили от `5` до `12` классов каждый,
чистого не было ни одного. Поэтому прогон идёт двумя частями, и порядок у
них один: сперва проходы, затем выборка. Решено разработчиком `2026-11-04`
и `2026-11-06`.
**Проход — вид дефекта, перечисленный командой целиком.** Вид задан
признаком, который ищется командой: «каждый ненулевой выход инструмента»,
«каждая команда и путь, названные в прозе», «каждый читатель сохранённого
состояния, от которого зависят ворота». Порядок:
1. Назвать вид одной фразой и семейство, к которому он относится.
2. Перечислить все места вида командой — скриптом, поиском, режимом
инструмента. Команда идёт в отчёт.
3. Сверить метод перечисления на заведомо известном примере (`J4`):
посадить место вида, убедиться, что перечень его называет, снять.
4. Дать исход каждому месту: держится — чем; не держится — находка, и она
чинится классом (раздел «Найденное лечится как класс, а не как пример»).
5. Закрыть вид держателем. Первый выбор — тест полного набора, который
краснеет на новом месте вида без теста либо без исключения с причиной;
второй — сверка. Вид, чьи места машине не перечислить, держит дата либо
одно место, и причина записана. Держатель фальсифицирован четырьмя
действиями.
**Пройденный вид второй раз не проходят**: его держит тест на каждом полном
наборе, а жив ли держатель, отвечают мутационный прогон и фальсификация.
Коммит, закрывший вид, несёт строку на вид:
```
Probe-pass: <вид одной фразой> — <держатель>
```
Перечень пройденных видов берётся командой, а не памятью:
```
git log --grep="^Probe-pass:" --format=%B | grep "^Probe-pass:"
```
Выведено: проходы `2026-11-04` легли только прозой сообщений, и следующий
заход не мог узнать, какие виды закрыты, не перечитав историю.
**Проходов — хотя бы один на семейство** за прогон, по видам, которых в
перечне ещё нет. Кандидаты — колонка «Чем обычно оказывается находка»
таблицы семейств и классы прошлых находок. Вид, который командой не
перечислить, идёт в выборку.
**Выборка — `21` проба, по `3` на семейство, по целям вне пройденных
видов.** Её дело — найти вид, о котором никто не подумал. Проба, попавшая в
пройденный вид, не засчитывается: берут другую. **Добор по находке:**
семейство, где выборка нашла новый вид, получает проход по нему и ещё `3`
пробы; чистое семейство не добирается — цена растёт там, где есть что
ловить.
Почему `21`, а не `7` и не `49`. Вид, на который натыкается доля p проб, `n`
проб находят с вероятностью 1 − (1 − p)ⁿ: при p = `10` % семь проб — `52` %,
`21` — `89` %, `49` — `99` %. Чистая выборка из `n` проб ограничивает долю
сверху числом 3/n: `43` %, `14` % и `6` %. Три пробы на семейство — нижний
предел, при котором семейство с плотностью `20` % пропускается реже, чем в
половине случаев (`51` % против `80` % у одной пробы). `49` после проходов
покупают сдвиг границы с `14` % до `6` % ценой `28` проб, а выборка из `49`
к чистой не сходилась и до проходов.
**Прогон холодной оси чист**, когда каждый проход закрыт держателем и
выборка не нашла нового вида. Утверждение итога: «виды такие-то пройдены
целиком и держатся тем-то; выборка из стольких проб нашла столько новых
видов»; чистая выборка добавляет «доля неизвестного не выше 3/n на пробу».
**Шаг — семейство**: его проходы, затем его пробы, починка найденного, тесты
по имени. Ручьёв горячей оси здесь нет: «свежесть» почти всегда пуста —
пласт правят редко, потому он и вынесен отдельной осью, — а когда не пуста,
она и есть повод для вызова; «залежавшееся» здесь всё. Окно свежести берётся
от горячей оси дословно: та же точка отсчёта, та же команда, только пометка
своей оси (`Probe-run: cold`).
**Норма фальсификаций: не меньше семи на прогон**, и обязательно — держатель
каждого прохода и всё, что заведено или изменено со времени прошлого
прогона **этой оси**. Сверка, объявленная рабочей и ни разу не покрасневшая,
— это утверждение, а не проверка.
**Когда вызывать.** По прямому вызову; после правки самого этого пласта; как
редкая профилактика — реже, чем горячий прогон, потому что предмет меняется
реже. Признак, что пора: с прошлого раза правились заготовки, инструкция,
конфиги сборки или публикации.
## Что считается находкой
Не только прогон с ненулевым кодом. Находкой считается:
- сверка напечатала ноль там, где предмет исчез или разошёлся с записью;
- инструмент ответил неверно или ответил про другое;
- правило разошлось с тем, как всё устроено на самом деле;
- запись базы или документации разошлась с кодом;
- число в правилах или базе устарело;
- один и тот же текст записан в двух местах и разойдётся при первой правке;
- проверка, которая **не может** упасть ни при каком состоянии предмета;
- проверка, падающая на законном тексте: её результат используется как
основание, и при срабатывании не по делу вывод перестают читать целиком;
- проба пройдена, но удержана одним вниманием: верный исход держится на том,
что сессия помнила, а не на записанном правиле или машине.
### Расхождение записи с замером — ещё не находка
Сперва устанавливается, что неверна **запись**, а не измеритель. Планка требует
сверять метод на заведомо известном примере — J4 «Метод проверки сверяется
на заведомо известном примере», — и на пробу это правило
распространяется в первую очередь: проба и есть измерение.
**Это не осторожность, а счёт.** За один прогон семь подозрений подряд
оказались ошибками замера, ни одного дефекта: регулярное выражение не поймало
одну форму импорта; счётчик считал импорты имени вместе с его вызовами; таблица
подавалась без заголовка и потому не разбиралась — а инструмент был прав. Ещё
раньше в том же дне разбор через оболочку выдал счёт втрое меньше настоящего.
**Цена ошибки здесь несимметрична.** Пропущенная находка оставляет дефект на
месте. Ложная — заставляет «починить» **верную** запись в неверную, и тогда
прогон, задуманный как проверка, сам становится источником лжи, причём с
пометкой «проверено пробой».
Порядок, прежде чем называть находкой:
1. **Перемерить другим способом** — иным инструментом, иным признаком.
2. **Прочитать, что именно утверждает запись.** Часто она говорит не то, что
померил измеритель: называет файлы поимённо, а не считает их; говорит про
группы, а не про вызовы.
3. **Только после этого** — находка, и в отчёте называются оба числа.
Признак, что измерялось не то: расхождение малое, значения близки. Расхождение
записи с кодом обычно велико — предмет отсутствует, имя переименовано, счёт
отличается кратно.
## Найденное лечится как класс, а не как пример
Проба показывает **экземпляр**, а чинить надо **класс**. Это главное правило
прогона, и нарушается оно незаметно: заплатка под конкретный пример выглядит как
починка: прогон после неё даёт код ноль, а обвязка остаётся в прежнем состоянии —
просто в этой точке ей подложили подпорку.
Порядок после каждой находки:
1. **Назвать класс словами**, прежде чем править. Не «режим звал ставить якорь
на `<такой-то файл>`», а «в список документации попадает то, что
документацией не является». Не сформулировался класс — значит причина ещё не
понята, и править рано.
2. **Найти все места рода — перечнем, а не впечатлением.** Признак класса
превращается в поиск: инструмент Grep по признаку — нет его, `grep` одной
командой без трубы, — либо режим инструмента, а не имя файла из пробы. В
отчёт идёт перечень мест вместе с командой, которой он получен. «Других
мест нет» без команды поиска ответом не является.
Места рода ищутся и по связям найденного места, а не только по сходству
признака: кто читает то, что оно пишет, и откуда оно читает само.
Поломка в одном звене обычно стоит на предпосылке, которую делят соседи
по потоку данных: читатель той же записи примет ту же порчу, а другой
писатель того же предмета её повторит. Соседей называет команда:
`graph.mjs brief` по файлу места (кто его берёт и что берёт оно) плюс
таблица связей мимо импортов. В отчёте каждый сосед получает исход.
3. **Свести места рода в один механизм, где они делают одно и то же:** одна
функция, один читатель, одна точка входа, а у мест — вызов её. Частные
фильтры, уже стоящие под ту же мысль, снимаются, а не дополняются: три
заплатки под три случая означают, что четвёртый случай потребует
четвёртой. Где свести нельзя — названа причина (разная логика, разный
владелец), и тогда каждое место из перечня проверяется отдельно, с исходом
у каждого.
4. **На механизм — один тест, который бьёт по всем случаям рода сразу:** все
формы на всех местах, а не пример из пробы. Тест фальсифицирован четырьмя
действиями.
5. **Проверить обе стороны:** класс закрыт (пример больше не срабатывает) и
ничего законного не сломано (что проходило, то проходит).
**Сведённый механизм меняет смысл следующей пробы.** Проба в теме, где места
сведены, проверяет механизм, и её чистота переносится на все места рода. В
теме, где места не сведены, чистая проба закрывает только своё место: семейство
холодной оси — это ситуация, в которую попадают разные механизмы, а не один
механизм. Выведено: проба «посадка без имени git» прошла чисто, а `handoff`
без временной папки — тоже «другая машина» — оставлял непроверенный снимок, и
места, берущие временную папку, после починки одного перечнем не прошли.
Держится графой отчёта и пометкой прогона (ниже); машинной проверки нет —
верен ли выбранный класс, решает чтение.
**Признаки, что лечили пример, а не класс:** правка называет конкретный путь,
имя или число из пробы · рядом уже есть похожее исключение · починка звучит как
«здесь не считать» без ответа на вопрос «а почему вообще считали» · класс нельзя
назвать одной фразой, не сославшись на пробу.
**Правится обвязка, а не проба.** Исправляется то, что дало неверный ответ:
правило, инструмент, запись базы, тест, настройка среды. Изменение пробы под
ответ обвязки, чтобы проба прошла, оставляет дефект на месте — следующая задача
того же вида придёт от разработчика, а не от прогона.
## Гигиена: после прогона дерево такое же, как до
- Всё, что проба посадила, **откатывается сразу**, и после отката делается
прогон с кодом ноль — иначе «откатил» остаётся утверждением, а не установленным фактом.
- Правки для фальсификации ставятся точечно и по одной: два дефекта сразу
допустимы, только если каждый ловится **своей** секцией вывода.
- Опасные пробы (удаление файла, снос папки) делаются переносом, а не
удалением, и возвращаются тем же способом.
- В конце: состояние репозитория сверяется с тем, каким было до прогона.
Разошлось — это находка, и она про сам прогон.
## Прогон с находками закрывается коммитом
**Нашлось хоть что-то — прогон кончается коммитом, и до начала следующего.**
Прогон чист — в мастерской коммитить нечего, и коммита нет; в проекте коммит
один — строка журнала, раздел «Журнал прогонов», с той же пометкой.
**Это не инициатива, и глобальному правилу «коммит только по прямой команде» не
противоречит.** Вызов прогона и есть та прямая команда: заказана проверка
обвязки вместе с починкой найденного, а работа, которую нельзя откатить одним
шагом, не закрыта. Нарушением здесь было бы обратное — оставить прогон
незакоммиченным. `push` под это не подпадает: он остаётся отдельной командой.
**Цена накопления замерена на живом случае, а не предположена.** Девять прогонов
подряд без единого коммита дали один диф на `43 файла`, где правка кода, правка
инструмента, правка базы и правка правил смешались в кашу. Разделить их задним
числом уже нельзя: сверки сцепляют их между собой — маркер решения в коде
требует записи в реестре, запись базы требует типа в коде, новая сверка требует
строк в обеих таблицах, — и любая граница между ними даёт коммит с красным
`verify`.
**И это ломает сами инструменты прогона.** `tested`, `twins` и `mutated` читают
`git status`: они отвечают на вопрос «что тронула **эта** правка». После второго
незакоммиченного прогона они отвечают про всё накопленное, то есть перестают
отвечать на свой вопрос. В том же случае `tested` показывал файлы, тронутые
четырьмя раундами раньше, как тронутые сейчас, и вопрос «а пересмотрены ли их
тесты» задавался вхолостую.
**Порядок — общий, без послаблений:** полный набор проверок даёт код ноль → вывод
**прочитан** → коммит отдельным вызовом. Сообщение по существу прогона: что
нашлось и что с этим стало.
**И последней строкой сообщения — пометка, которой прогон себя называет:**
```
Probe-run: hot; классов: N, сведено: M, не сведено: K; не поместилось: <предметы или «ничего»>
Probe-run: cold; проходов: P; выборка: S, новых видов: X; классов: N, сведено: M, не сведено: K; не поместилось: <предметы или «ничего»>
```
Шаг холодной оси, закоммиченный до конца прогона, несёт не `Probe-run:`, а
`Probe-family: cold <номер>/7; проходов: P; проб: S; классов: N` — иначе
окно следующего прогона сдвинулось бы на незаконченный.
**Ось пишется латиницей**, потому что по ней выполняется поиск: он идёт по
сообщениям коммитов через оболочку, а сравнение кириллицы там зависит от
кодировки окружения — результат бывает не приблизительным, а противоположным
верному.
Она нужна не истории, а следующему прогону, и несёт две вещи. По первой он
находит своё окно свежести — что изменилось с прошлого раза и, значит, обязано
быть перепробовано. Вторая переносит то, что не влезло в долю свежести: коммит
сдвигает точку отсчёта, и без переноса непокрытое выпало бы из окна без признака.
Коммит без пометки делает прошлый прогон неадресуемым, и следующий берёт окно
наугад, то есть возвращается к отбору по памяти.
**Дробление внутри прогона — по общим правилам проекта.** Граница прогона их не
отменяет, а добавляет к ним одну обязательную. Если находки сцеплены так, что
каждая граница даёт красный промежуточный коммит, — коммит один, и причина
названа в его сообщении.
## Журнал прогонов
В проекте каждый прогон, и чистый тоже, — строка в `14-probes.md` базы, тем
же заходом: когда, оси, сколько проб, что найдено и починено, сколько проб
удержано одним вниманием; у холодной оси — пройденные виды с держателями. Читает журнал следующий прогон: ручей «регресс»
берёт предметы находок прошлого. В мастерской базы нет, и журнал — пометки
`Probe-run:` в коммитах; чистый прогон там следа не оставляет, и окно
считается от последнего прогона с находками — шире нужного, в безопасную
сторону.
## Стоимость: прогон не дороже пользы
- Полный набор проверок **не гоняется после каждой пробы** — только после
правок, и один раз в конце. По ходу — тесты затронутого, по имени. Что
входит в полный набор мастерской и сколько стоит каждая его часть, записано
в одном месте — справочник `tools/graph.md`, режим `seat`: вторая копия
цены расходилась с первой без признака.
- Пробы батчатся: одна команда, отвечающая на несколько вопросов, лучше пяти.
- Мутационный прогон ради пробы не запускается: он аудит, а не проверка
обвязки. Исключений два: правка пробы задела область прогона и оставила
долг; проба ведёт свод до печати на правке файла, до которого доходят
тесты, — без свежего замера печать «чисто» по `J10` не ставит, и прогон по
файлу правки тогда часть пробы.
- Условие остановки внутри раунда: следующая проверка не меняет ответ.
## Отчёт по прогону
- По раундам, а не сплошным списком: видно, какие области закрыты. У
холодной оси — по семействам: проходы с командой перечня, числом мест и
держателем вида, затем пробы выборки.
- По каждой находке — что не так, **исправлено / не исправлено, потому что… /
отложено**, и как именно.
- По каждой находке — четыре графы класса: класс одной фразой; перечень мест
рода с командой поиска; «сведено: где» либо «не сведено: почему»; имя теста
на механизм. Счёт классов и сведённых идёт в пометку прогона: находка без
графы видна там разницей чисел.
- У каждой пробы — исход и чем удержано; в конце — сколько проб удержано
машиной, сколько правилом по ярусу, сколько одним вниманием.
- Фальсификация записывается **всеми четырьмя действиями**: внёс такую-то ошибку
→ упала такая-то проверка → откатил → прогон дал код ноль. Запись
«фальсифицировано, всё прошло» приводит только последнее из четырёх.
- Числа, а не оценки: какие проверки прогнаны и с каким результатом.
- В конце — прямое утверждение: **прогон чист** либо **прогон не чист, находок
столько-то**. Следующий шаг — за разработчиком, если он не задал его заранее.