Записывает, как код на самом деле исполнялся: вызовы, доводы, результаты, исключения, длительности
Исходник отвечает на вопрос «что может случиться». Он показывает все ветви, все объявленные доводы, все возможные пути. Он не показывает, какие из них случаются.
Вопрос, который на деле стоит перед человеком, почти всегда другой: что здесь происходит на самом деле? Какие функции живые, с чем их зовут, что они возвращают, где оно висит. Ответа на него в исходнике нет ни в каком виде, и вычитать его нельзя — сколько ни читай.
Прогон отвечает. Уроборос делает этот ответ дешёвым: одна команда до, одна после.
Прочитать сорок тысяч строк дорого. Прогнать и посмотреть, какие функции реально зовутся и с чем, — дёшево. Часто выясняется, что живых путей три, а остальное не исполняется никогда, и три четверти чтения были не нужны.
По шагам — Прологировать чужой код.
Отслеживание стека говорит, где сломалось. Запись говорит, с чем позвали — и позвали не только сломавшуюся функцию, но и ту, что её позвала, и ту, что позвала эту.
Пример из README:
стек показывает деление на ноль в average. Записи показывают, что average
позвали с [], а report, который её позвал, тоже получил []. Ошибка не в
average — она там, где родился пустой список.
Отдельный случай той же задачи: программа идёт часами и молчит. Строка входа
без парной строки выхода — вызов вошёл и не вернулся. Вопрос «где оно висит»
превращается в «найди непарные id», и инструмент уже это сделал: они лежат в
поле in_flight.
Прогнали до правки, прогнали после, сравнили записи. Ответ получается не «тесты красные», а вот эти сорок вызовов теперь ведут себя иначе — с доводами и результатами обеих сторон. Особенно полезно там, где тесты зелёные, а поведение изменилось: тест проверяет то, что кто-то придумал заранее, записи — то, что было.
По шагам — Обычная разработка.
Не профилировщик. Он меряет время; здесь записывается поведение. Поле d
меряет обмазанный прогон и настоящей ценой не является.
Не отладчик с точками останова. Программа не останавливается, ничего не ждёт человека, прогон идёт как обычно. Это важно там, где остановиться нельзя: под нагрузкой, в ядре системы, в чужом окружении.
Не покрытие. Покрытие говорит, что строка исполнилась. Записи говорят, с чем её позвали и что вышло.
Не доказательство правильности. Это самое важное различие, и ему посвящена отдельная страница.
Стоит сказать сразу, потому что это измерено. На задаче, где ошибка целиком видна по итоговому выводу, инструмент не даёт ничего: агент, которому его дали, не воспользовался им ни разу из трёх прогонов — и был прав. Когда ожидаемый вывод лежит рядом, сверить вывод дешевле.
Числа и разбор — bench/RESULTS.md.
Правило простое: прежде чем обмазывать, спросите, локализуется ли ошибка по выводу и по отслеживанию стека. Если да — не тратьтесь.
Название не украшение. У инструмента есть замысел за пределами разбора кода, и он в том, что цепочка замыкается:
программа работает
↓
записи фиксируют, как она себя вела
↓
из записей получаются примеры
↓
примеры делают спецификацию проверяемой
↓
спецификация проверяет ту же программу
Программа порождает материал для собственной проверки. Ничего не надо придумывать — надо перестать выбрасывать то, что и так происходит.
Мост, который делает третий шаг, — надстройка, описанная в навыке
skill/SKILL.md;
в это хранилище он не входит. Здесь лежат первые два шага: обмазка и записи.
Мысль, ради которой всё затевалось: инвариант говорит, чего не бывает, а пример показывает, что бывает, — и обобщать удаётся по второму. Навык подкрепляет её числами из опыта по синтезу спецификаций; здесь они не приводятся, потому что воспроизвести их этим хранилищем нельзя.
И оговорка, без которой предыдущий абзац опасен: примеры наследуют распределение той нагрузки, на которой сняты, и ошибки той программы, с которой сняты. Границы.