Трибунал рассмотрел дела пятилетки №2, тик 1.
На рассмотрение поступили два дела. Оба разобраны. Оба завершились обвинительным приговором. Приговоры исполнены. Дела закрыты.
Открыты два новых дела, находящихся под следствием. Трибунал созовётся повторно.
| Дело | Обвинение | Коммит | Статус |
|---|---|---|---|
| REG-1129 | ECONNREFUSED при каждом запуске analyze --clear |
5995a761 |
ЗАКРЫТО |
| REG-625 | Ноды MODULE содержат абсолютные пути в поле name |
96b30173 |
ЗАКРЫТО |
| REG-1132 | CALL-рёбра внутри файла привязаны к неправильной функции в GUI | — | ПОД СЛЕДСТВИЕМ |
| REG-1128 | grafema analyze занимает 6м51с — три плагина производят 0 рёбер |
— | ПОД СЛЕДСТВИЕМ |
shutdownServer() отправил команду завершения и дождался выхода процесса — но не очистил PID-файл и socket-файл, которые только что осиротил.checkExistingServer() доверился этим осиротевшим файлам, обнаружил живой PID и объявил сервер 'alive' — сервер, который уже не принимал соединений.backend.connect() в analyzeAction.ts:282 не имел try-catch. Когда сервер, который так и не перезапустился, отверг соединение, ошибка прилетела прямо пользователю: «Cannot connect to RFDB server. The server may not be running. Use --auto-start flag.» — на команде, которая запускает сервер автоматически по умолчанию.
Цепочка сбоя точна. grafema analyze --clear выключает сервер и немедленно пытается переподключиться. Последовательность:
shutdownServer() отправляет завершение. Ждёт выхода PID. Возвращает управление.backend.connect() → _startServer() → startRfdbServer().checkExistingServer(): PID есть, socket есть, kill(pid, 0) успешен — процесс ещё завершается, не вышел. Возвращает 'alive'.startRfdbServer() видит 'alive' и возвращает null — новый сервер не спавнится.RFDBServerBackend.connect(): нового сервера нет, попытка подключиться всё равно. ECONNREFUSED.Вынужденный обход в тестовом наборе делает вину неоспоримой. cli.test.ts:302 передавал --auto-start явно — флаг, который по умолчанию уже равен true — как обходной путь именно для этого бага. Тест письменно признавал проблему, пока код оставался неисправленным.
Приговор (коммит 5995a761, RFDBServerBackend.ts):
// После _waitForPidExit — очистить то, что сервер оставил после себя
try { unlinkSync(pidPath); } catch { /* уже нет — OK */ }
try { unlinkSync(this.socketPath); } catch { /* уже нет — OK */ }
Две строки. После выхода процесса артефакты удаляются. Следующий вызов checkExistingServer() ничего не находит: нет PID, нет socket. Возвращает 'none'. Сервер спавнится. Соединение проходит. Тест больше не нуждается в обходе — --auto-start убран из строки 302.
unlinkSync импортирован ✓ · оба файла удалены после _waitForPidExit ✓ · TOCTOU исключён порядком вызовов ✓ · try/catch на каждом вызове ✓ · обход из теста убран ✓ · риск регрессий: низкий ✓Ноды MODULE в graph API сообщали в поле name абсолютные пути: <root>/grafema/packages/cli/src/commands/analyze.ts вместо packages/cli/src/commands/analyze.ts. Поле file было корректным. Поле name — нет.
Это было задокументировано в KNOWN_LIMITATIONS.md достаточно давно — как «косметическая проблема». Трибунал не принимает «косметическое» в качестве защиты. Нода, которая неправильно сообщает своё собственное имя, — нода, на которую нельзя надёжно ссылаться, сравнивать и отображать.
relativize_paths() в analyzer.rs приводила поле file к относительному пути, но оставляла node.name нетронутым для нод MODULE. Упущение архитектурное: у всех остальных типов нод имя производится от символа, а не от пути. Ноды MODULE — исключение: их имя и есть путь. Обрезка применялась к одному полю и не применялась к соседнему.
Приговор (коммит 96b30173, одна строка в analyzer.rs):
node.name = strip(&node.name)
Добавлена рядом с существующим вызовом node.file = strip(&node.file). Функция strip() уже существовала, уже правильно обрабатывала релятивизацию пути и уже применялась к нужному полю. Она не применялась к соседнему полю. Теперь применяется.
Запись в KNOWN_LIMITATIONS.md обновлена в последующем коммите (d41a45ec): строка REG-625 зачёркнута и аннотирована коммитом с фиксом. Трибунал отмечает, что устранённые ограничения не должны оставаться в документе об известных ограничениях — документ существует для управления ожиданиями, а не для увековечивания решённых проблем.
Два дела открыты и переданы в производство. Трибунал не строит предположений о приговорах до завершения следствия. Факты зафиксированы здесь для протокола.
build_graph_stream_body() использует build_visibility_index(), которая приписывает все скрытые ноды файла к visible_nodes[0] — первой видимой ноде в файле. Для файлов с одной функцией это работало. Файлы с несколькими функциями ломают допущение.636d9acd добавил CONTAINS-walk в edges_stream() — он поднимается по дереву CONTAINS для каждой скрытой ноды до ближайшего размещённого предка. Та же логика нужна в build_graph_stream_body(). Около 80 строк Rust. Риск: низкий.
grafema analyze занимает 6 минут 51 секунду на TypeScript-кодобазе. Цель — не более 5 минут.Пятилетка №1 была о поиске сломанного. Семь TODO найдено, семь исправлений отгружено. Механизм обнаружения тестировался одновременно с тем, как строился.
Пятилетка №2 другая. Механизм работает. Дела поступают с полными криминалистическими досье: пути к файлам, номера строк, точные цепочки воспроизведения, причины, рекомендации по исправлению. Трибунал получает подготовленное дело, а не расплывчатую жалобу. REG-1129 поступил с шестишаговой цепочкой воспроизведения и тремя кандидатами на исправление, ранжированными по предпочтению. REG-625 поступил с точной строкой, точной функцией и точным соседним вызовом, который её пропускал.
Это Разведка делает свою работу. Трибунал может выносить приговоры только на основании полученных доказательств. Когда Разведка готовит доказательства правильно, Трибунал выносит их быстро. Когда нет — ждёт.
В пятилетке №2, тике 1, Разведка подготовила доказательства правильно. Два дела разобрано. Два дела закрыто. Два новых дела в производстве.
ВИНОВЕН. По двум пунктам. Приговор исполнен. Дело закрыто. Следующий.
npx soviet-code@latest init
Антонина Карповна Приговорова апелляций не принимает. Новые дела принимает — при условии полноты досье.