Skip to content

Releases: digitable-lol/ouroboros

v0.6.1

Choose a tag to compare

@the-homeless-god the-homeless-god released this 10 Sep 12:22

What changed since 0.6.0.

The history speaks English, in Conventional Commits. All 103 commits were
rewritten in one pass: every message translated by hand, not by machine — the
reasoning and the numbers in the bodies are what the messages were written for,
and a dictionary pass would have thinned them out. type(scope): subject, a body
wrapped at 80 columns, BREAKING CHANGE: on the seven commits where behaviour
changed on the outside. The original Russian messages are kept unchanged on the
branch archive/history-in-russian, which also carries the v0.6.0 tag; the
tree itself is byte-for-byte identical to it.

The rule is written down. CONTRIBUTING.md names the shape, the nine types
and the scopes this tree uses. Nothing enforces it — there is no
commit-message hook and no step in scripts/qa.sh that reads the log — and the
file says so rather than implying a check that does not exist.

The front page says what a trace gives a model reader. Four responders,
twelve programs, every question asked with a trace and without: for the weak
model 44.0 % correct without the trace against 78.3 % with it, and for the strong
one no gain — with the reason stated, that twelve fifty-line programs are too
easy for it.

A guard holds the front page's numbers equal to the measurement. The table
on the landing page is compared with the measurements page row by row, and the
self-test feeds it an inflated number, a recaptured measurement and a missing
row — it catches all three.

A release of the language no longer breaks the trunk. The emitted-brain
guard returns code 1 when the emission has drifted from its source and code 2
when it could not compare at all; only code 1 fails the step now, while code 2
prints a warning with the command to re-emit. The tree is emitted with flang
0.7.16.

Ouroboros-Logger 0.6.0

Choose a tag to compare

@the-homeless-god the-homeless-god released this 08 Sep 21:59

The rules that read a trace are no longer merely tested — they are checked by a compiler.

The reading rules moved to flang. What counts as a line, how an in is paired with its out, when a call counts as unfinished, where a value gets cut — all of it now lives in ouroboros/brain/trace_brain.flang and is printed into Python at build time. flang check: 39 functions, termination proved for every one of them. flang test: 91 examples out of 91. flang check --proof: 5 claims, 5 proved, 0 taken on trust. Answers are byte-for-byte what they were.

Directories are called draft and clean. A project created by an earlier version keeps its old directory names, is used exactly as it is, and the tool says so out loud instead of quietly starting a second one beside it.

Everything the tool prints is in English — the command line, the MCP server, and the guards whose output lands in the Actions log of somebody else's pull request.

The gate got stricter. 710 lint rules instead of 221, across the whole tree instead of two directories; strict types on 77 files instead of 33; 1248 checks; coverage is measured by the same run that gates, with the threshold in the config — so "100 %" can no longer fall behind the tree unnoticed, which is exactly what it had done before.

Eight guards were found to be silently not checking anything and were repaired, among them one that turned a network hiccup into a stack trace and one that stopped looking at the longest page in the repository.

Install: uv tool install git+https://github.com/digitable-lol/ouroboros, brew install digitable-lol/tap/ouroboros, or as an asdf plugin.

Ouroboros-Logger 0.5.0

Choose a tag to compare

@the-homeless-god the-homeless-god released this 30 Aug 02:55

Восемь языков: Python, JavaScript/TypeScript, C, C++, Elixir, Go, Java, C#.

Что нового

Java — обмазка через try/catch/finally. Разборщик доставать неоткуда не надо: это компилятор, который уже лежит внутри любого JDK (javax.tools вместе с com.sun.source). Ввоза помощника не вставляется вовсе, ни одна врезка не содержит перевода строки — номера строк и след вызовов у необработанного исключения совпадают построчно.

C# — то же устройство. Roslyn берётся из самого пакета .NET SDK: путь выясняется из dotnet --list-sdks, качать нечего. Пять видов частей не обмазываются, потому что обмазанный вариант не собрался бы (yield, возврат по ссылке, указатели, ссылочные структуры, свойства с телом-выражением), и про каждую обмазка возвращает предупреждение с причиной.

Починена старая поломка в JavaScript. Обмазка портила файлы, где есть знак вне базовой плоскости: babel считает смещения в единицах UTF-16, питон режет по кодовым точкам, и один смайлик в комментарии делал обмазанный файл несобираемым. Молча, с сообщением об успехе. Было во всех прежних выпусках с обработчиком JavaScript.

Две проверки схемы записи ничего не проверяли. Проверка про опознаватели вызовов пересобирала программу между прогонами и тем сбивала условие, которое проверяла. Проверки на монотонные часы не было вовсе ни у одного языка — теперь есть.

Цена, снятая одним прогоном на всех восьми

язык добавка на вызов байт на вызов
C# 15,6 мкс 251,4
C 19,9 мкс 248,4
JavaScript 20,4 мкс 236,5
Java 20,9 мкс 251,4
C++ 22,2 мкс 269,4
Go 28,9 мкс 243,4
Python 58,2 мкс 252,5
Elixir ~190 мкс 254,4

Состояние

991 проверка, покрытие с ветвями 99,59 %. Набор равенства — 151 программа на восьми языках: каждая гоняется дважды, без обмазки и с обмазкой, и три исхода сверяются побайтово.

Страницы: https://digitable-lol.github.io/ouroboros/

Ouroboros-Logger 0.4.0

Choose a tag to compare

@the-homeless-god the-homeless-god released this 30 Aug 01:54

Шестой язык — Go

Уроборос умел пять языков; теперь шесть. Разборщик Go лежит в самой поставке
языка
, поэтому доставлять нечего: нужна только команда go — и, в отличие от
Node с Elixir, нужна она и на обмазке, а не только на сборке.

ouroboros wrap-file main.go
go build -o app main.go ouroboros_runtime.go
OUROBOROS_DEBUG_INFO=./debug.info ./app
{"p":"in","t":"2026-08-30T00:59:13.333","id":"6fd9c942-…","ci":-1,"th":"3787200.1","fn":"(*Счётчик).Добавить","a":"2","k":""}
{"p":"out","id":"6fd9c942-…","fn":"(*Счётчик).Добавить","r":"2","d":0.000001}

Схема записи та же, что у остальных пяти, до байта: те же ключи, те же две
строки на вызов, тот же смысл каждого поля. Разбор трассы у вас один на все
языки.

Как это сделано

Отложенное замыкание вместо try/finally. У Go нет ни надстроек над
функцией, ни try/finally, зато есть то, ради чего оба и нужны: defer.
Замыкание, поставленное первым же действием в теле, отрабатывает на любом
выходе — на каждом return, на панике, проходящей сквозь кадр, на достижении
конца тела.

Возвраты не переписываются вовсе. Вместо этого всем возвращаемым значениям
даётся имя прямо в подписи, и замыкание читает их уже после того, как return
их присвоил:

func add(a, b int) (__ouro_r0 int) {
	__ouro_ctx := _ouroEnter("add", a, b)
	defer func() {
		if __ouro_p := recover(); __ouro_p != nil {
			_ouroPanicked(__ouro_ctx, __ouro_p)
			panic(__ouro_p)
		}
		_ouroReturned(__ouro_ctx, __ouro_r0)
	}()
	return a + b
}

Отсюда return f(), пробрасывающий сразу несколько значений чужого вызова,
работает без единого исключения из правила — тогда как C и JavaScript приходится
переписывать каждое место возврата. Тип подписи от именования не меняется, так
что вызывающие, интерфейсы и функции-значения ничего не замечают.

Строки ввоза нет ни одной. Помощник — файл того же пакета, а не подключаемая
библиотека: Go не умеет ввозить соседний файл. Поэтому над заголовком файла
ничего не вставляется, и //go:build вместе с описанием пакета остаются на
своих местах, как того требует язык. Плата за это — помощник обязан объявлять
пакет обмазанного файла, иначе рядом с файлом библиотеки ляжет package main,
который не собирается.

Границы — прочтите до того, как включать

Функции-значения не обмазываются. func(x int) int { … }, записанная в
переменную или переданная доводом, пропускается, и её вызовов в записях
не будет вовсе. Решение то же, что и пропуск lambda у Python, но разница
для читателя настоящая: у Python вложенная def обмазывается и в записи видна
(outer.<locals>.inner), а в Go такой же по смыслу код молчит. Обмазываются
только объявленные функции и методы. Отличить «не звали» от «звали, но не
записано» по записи нельзя.

Непойманная паника печатает не то же самое. Это единственное место во всех
шести языках, где обмазка меняет вывод программы. Чтобы записать вид и текст
паники, замыкание вызывает recover(), а затем panic() заново — иначе поле
x осталось бы без вида и без текста. Поэтому программа, чью панику никто не
ловит, печатает в поток ошибок

panic: bad [recovered, repanicked]

вместо panic: bad. Проверено прогоном: код возврата тот же (2), обычный вывод
тот же, и всякая паника, пойманная самой программой или вызывающей, приходит тем
же значением
— меняется ровно текст сообщения о необработанном падении. Он и
без обмазки был бы другим: в след вызовов входят номера строк, а обмазка их
сдвигает. Убрать это, не потеряв поле x, нельзя: узнать значение паники, не
перехватив её, Go не даёт.

Ещё три мелочи, снятые прогонами: получателя метода в доводах нет (как у C++ с
this; Python, наоборот, пишет self); go vet находит на одну придирку
больше, если через обмазанную функцию идёт по значению структура с замком
внутри — на go build и go test это не влияет; изображение значения идёт
через %v, а он зовёт у значения его собственный String()/Error().

Чем это подтверждено

  • 102 программы равенства вместо 79: каждая собирается и запускается дважды,
    без обмазки и с обмазкой, и сверяются три исхода — код возврата, обычный вывод,
    вывод ошибок. Из них 23 на Go: несколько возвратов, наказ defer, меняющий
    именованный возврат, паника пойманная вызывающим и пойманная собой, горутины,
    os.Exit(3), обобщённые функции, //go:build, кириллица в именах.
  • 835 проверок вместо 742, покрытие с ветвями 99,55 %; у обработчика Go
    оно стопроцентное вместе с ветвями.
  • 20 нарочных порч внесены в код Go и все 20 пойманы. Две из них с первого
    раза прошли — и это были настоящие дыры: не проверялся предел длины одного
    значения и монотонность часов. Обе закрыты новыми проверками.
  • Схема сходится с остальными пятью: tests/test_schema_parity.py гоняет
    один и тот же вызов на всех шести языках и сверяет поля.
  • Инструмент обмазывает сам себя: 21 файл собственного исходника, 227
    функций, обмазанная копия проходит 834 проверки из 835. Единственная упавшая
    упала не от поломки — проверка велит обмазанному инструменту писать записи в
    тот самый файл, который даёт ему читать, и он честно дописывает туда два своих
    вызова.

Чего это стоит

Замеры пересняты целиком, все шесть языков одним прогоном на одной машине
(scripts/measure/run.sh), 20 002 вызова:

язык добавка на вызов байт на вызов
C 19,8 мкс 250
JavaScript 20,3 мкс 238
C++ 22,0 мкс 270
Go 30,0 мкс 244
Python 63,5 мкс 255
Elixir 179,7 мкс 255

Go не попал в тесную полосу 20–22 микросекунд, и разница разобрана: 3,3
микросекунды из тридцати стоит поле th.
Узнать номер горутины стандартная
библиотека даёт единственным способом — разобрать runtime.Stack, — а он обходит
весь стек: 2,655 мкс на пустом и 87,562 на глубине 300. Платится это один раз на
вызов, на строке входа, и до того, как запускаются часы длительности, поэтому
в поле d не попадает.

Установка

brew install digitable-lol/tap/ouroboros
# или
asdf plugin add ouroboros https://github.com/digitable-lol/ouroboros.git
asdf install ouroboros 0.4.0

Оба пути проверены не «команда не выругалась», а работой, и с пустого места:
хранилище формул отцеплено и 0.3.1 снята, затем короткая строка подключила
хранилище сама и поставила 0.4.0; brew test прошёл. Тем же поставленным
инструментом обмазан настоящий файл на Go, собран, запущен — вывод совпал с
необмазанным, — и записи прочитаны им же. Для asdf то же на чистом каталоге
данных.

Если в вашем ~/.gitconfig есть правило insteadOf, переписывающее
https://github.com/ на git@github.com:, то и brew tap, и
asdf plugin add упадут на «Permission denied (publickey)»: клонируют они
сами, и правило переписывает адрес до того, как он до нас доходит. Обход —
снять правило на время либо клонировать вручную.

Образ packaging/Dockerfile собран с golang-go и проверен изнутри: обмазка,
сборка и запуск программы на Go прошли на Debian-овском Go 1.19.8, то есть
помощник собирается не только свежим Go.

Страницы

Языки ·
Границы ·
Замеры

Ouroboros-Logger 0.3.1

Choose a tag to compare

@the-homeless-god the-homeless-god released this 29 Aug 10:34

Починка безопасности — ради неё выпуск и вышел

Обмазка C и C++ больше не читает за пределами объекта.

В 0.3.0 довод типа const char * печатался содержимым (%s в C,
std::string(s) в C++). Тип этого не обещает: функция

static int put_one(const char *p) { return (int)*p; }

вызванная как put_one(&c) для одного знака на стеке — обычный правильный код.
Обмазанная копия читала за c до первого нуля где-то дальше в памяти.

Показано измерителем границ, а не рассуждением. Необмазанная программа чиста и
печатает 120; обмазанная в 0.3.0:

ERROR: AddressSanitizer: stack-buffer-overflow ... READ of size 2
  #1 vsnprintf
  #2 _ouro_enter ouroboros_runtime.h:654
[32, 33) 'c' <== Memory access at offset 33 overflows this variable

Это плохо дважды: инструмент обещает не менять поведение измеряемой программы —
а вносил в неё неопределённое поведение; и он ломал тех, кто гоняет свои проверки
под измерителем границ, который показывал бы на обмазку, а не на их ошибку.

Теперь любой указатель печатается адресом, и обе программы под измерителем границ
чисты.

Чего это стоило. Содержимое строк было самым полезным в записи на C, и его
больше нет. Безопасно вернуть нельзя: типа со значением «здесь точно строка с
нулём на конце» в C не существует, а ограничение длины не спасает. Как сделать
это необязательным поведением — записано в FEATURE_REQUESTS.md.

Что ещё вошло с 0.3.0

  • C и C++ говорят с libclang через отдельный процесс. Разбором занят один
    родной помощник, общий для обоих; кода, трогающего разборщик, было 127 строк из
    458, стало 3 из 204. Поблажек по модулям в mypy --strict не осталось ни одной.
    Заголовки llvm не нужны — помощник объявляет нужный кусок обычая вызовов сам, и
    верность объявлений проверяется сборкой в обе стороны.
  • Покрытие с ветвями 94 → 99,51 %, проверок 580 → 742. Проверено не только
    число: 15 нарочных порч в файлах со стопроцентным покрытием — все 15 пойманы.
  • Замеры по пяти языкам, снимаемые одной командой scripts/measure/run.sh.
  • Пять проверок документации вместо двух: ссылки на строки исходника, ссылки
    между страницами, числа состояния, слова со смешанными алфавитами и таблица
    полей записи, печатаемая прогоном пяти языков.

Установка

brew install digitable-lol/tap/ouroboros
# или
uv tool install git+https://github.com/digitable-lol/ouroboros

Ouroboros-Logger 0.3.0

Choose a tag to compare

@the-homeless-god the-homeless-god released this 29 Aug 09:06

Одинаковость поведения теперь проверяется, а не предполагается

79 программ на пяти языках (Python, JavaScript, C, C++, Elixir) прогоняются
дважды — без обмазки и с обмазкой — и три исхода сверяются: вывод, код возврата,
исключение.

Расхождений было 16, стало 0. Отдельно порода .mjs: было 16 отказов из 17,
стало 0.

Инструмент обмазывает сам себя

19 файлов, 206 функций, ни одной поломки — и обмазанная копия проходит все 580
проверок
.

Числа

было стало
проверок 167 580
покрытие с ветвями 66,3 % 94 %
расхождений при обмазке 16 из 79 0
столкновений опознавателей в C++ 20 из 20 пар 0

Что починено

  • Сервер MCP больше не отвечает успехом на неудачу. Если помощник времени
    выполнения не удавалось записать, ответ был ok: true, а исходник оставался
    переписанным и несобираемым.
  • Помощник кладётся рядом с файлом, а не в корень черновика. Обмазанный код
    ищет его возле себя, поэтому запись в src/main.c давала несобираемый файл —
    при ответе ok: true. Ломались три языка из пяти: C, C++ и JavaScript.
  • Длительности в C перестали включать время собственной записи — медиана
    односточной функции была 10,00 мкс, стала 0,00.
  • Опознаватели вызовов в C++ перестали повторяться между процессами.
  • Записи влезают в PIPE_BUF. Вызов с 30 доводами давал строку в 6208 байт,
    ядро её рвало, и разборщик молча считал обе половины испорченными.
  • Шесть средств MCP для C и C++ (symbol_search, document_symbols,
    references, call_hierarchy, describe_symbol, lint_file) перестали быть
    объявленными и нерабочими: образ не содержал clangd и clang-tidy, а текст
    указаний их не называл.
  • JavaScript больше не теряет "use strict", C++ собирается на
    return {1, 2, 3}, Python не ломается о #! и строку кодировки.

Что остаётся платой, а не поломкой

  • C++ не записывает возвращённый объект классового типа: иначе теряется пропуск
    копирования, который язык гарантирует.
  • Имён доводов нет в записи ни у одного из пяти языков — иначе одна схема на пять
    языков не сходится.
  • Обмазка Python добавляет кадр стека на вызов: глубокая рекурсия мельчает вдвое.

Каждое — с замером и разбором в Границах.

Установка

brew install digitable-lol/tap/ouroboros
# или
uv tool install git+https://github.com/digitable-lol/ouroboros

Полный справочник 17 средств MCP снят с живого сервера:
docs/mcp-tools.md.

Уроборос 0.2.1

Choose a tag to compare

@the-homeless-god the-homeless-god released this 29 Aug 00:10

Исправление, без которого установка была бесполезна для ИИ-агента.

Что чинилось. Пакет просил mcp>=1.2.0 без верхней границы, и любая свежая установка приезжала с mcp 2.1.1, где FastMCP переименован. Команда ouroboros-mcp падала сразу с ModuleNotFoundError: No module named 'mcp.server.fastmcp'. Ломалось молча: командная строка при этом работала. Теперь mcp>=1.2.0,<2.

Поставить:

uv tool install git+https://github.com/digitable-lol/ouroboros@v0.2.1

Другие способы — https://digitable-lol.github.io/ouroboros/install.html

Уроборос 0.2.0

Choose a tag to compare

@the-homeless-god the-homeless-god released this 29 Aug 00:04

Первый выпуск, в котором в хранилище лежит сам инструмент, а не только его описание.

Что внутри: пакет ouroboros-logger 0.2.0 — командная строка на 17 команд, сервер MCP на 17 средств, пять языков (Python, JavaScript/TypeScript, C, C++, Elixir), черновик с историей изменений, стенд.

Поставить:

uv tool install git+https://github.com/digitable-lol/ouroboros@v0.2.0

Другие способы — https://digitable-lol.github.io/ouroboros/install.html

Архив исходников этого тега (Source code (tar.gz)) — то, что ставят формула Homebrew и плагин asdf.