Skip to content
Все посты /

Один проход — недостаточно: как Signum научился исправлять свой код

Верификация AI-кода как цикл, а не гейт. Итеративный аудит, самокритика контрактов и общий контекст между задачами в Signum v4.6.

#context-engineering #claude-code #verification #iterative-audit #agents

Первая версия Signum работала в один проход: CONTRACT → EXECUTE → AUDIT → PACK. Если аудит находил проблему — блок. Человек разбирается.

Это честный процесс, но ограниченный. Представьте код-ревью, где ревьюер может только комментировать, а автор не может ничего исправить. Финдинг уходит обратно в очередь, контекст теряется, цикл начинается заново. Signum v4.6 закрывает этот разрыв: пайплайн теперь зацикливается на трёх уровнях — код, контракт и проектный контекст — прежде чем собрать финальный proofpack.

Проблема: one-shot верификация

В предыдущих постах я разбирал контракт как точку истины и proofpack как артефакт верификации. Архитектура работала: спецификация → ослеплённая реализация → мультимодельный аудит → proof artifact. Но в продакшн-использовании проявился паттерн.

Большинство находок аудита в наших ранних прогонах — не архитектурные проблемы. Это пропущенный edge case в обработке ошибок. Забытый null check. Тест, который не покрывает один из acceptance criteria. Вещи, которые инженер-агент мог бы исправить за секунды — если бы получил шанс.

Вместо этого Signum ставил AUTO_BLOCK, человек смотрел на находку, перезапускал пайплайн. Полная пересборка контракта, полная реализация, полный аудит — для бага, который чинится одной строкой.

Цикл 1: код — итеративный аудит

Signum v4.6 добавляет цикл ремонта, связывающий AUDIT и EXECUTE. Когда аудит находит MAJOR или CRITICAL проблемы, вместо блокировки инженер возвращается чинить:

AUDIT → findings → re-enter EXECUTE (repair) → AUDIT → ... → PACK

После первого прохода аудита (механик + Claude + Codex + Gemini), если есть actionable находки, инженер-агент получает repair_brief.json:

{
  "iteration": 1,
  "findings": [
    {
      "id": "F-1",
      "severity": "MAJOR",
      "file": "src/api/tokens.py",
      "line": 42,
      "description": "Missing error response when rate limit storage is unavailable",
      "source": "codex"
    }
  ]
}

Важно: repair_brief.json содержит только наблюдаемые симптомы дефектов из видимых критериев и детерминированных проверок. Провалы holdout-сценариев описываются как поведенческие наблюдения (“функция возвращает 200 вместо ожидаемого 429”) без раскрытия скрытых критериев приёмки. Data-level blinding из оригинального контракта сохраняется — инженер никогда не видит raw holdout text.

Инженер чинит. Полный аудит перезапускается — не на diff от ремонта, а на всю реализацию от начала. Затем PACK собирает финальный proofpack, как и раньше.

Ключевые решения:

Best-of-N, не last-of-N. Пайплайн хранит артефакты каждой итерации в .signum/iterations/NN/. Если итерация 3 хуже итерации 2 (ремонт сломал что-то другое), Signum откатывается к лучшему кандидату. Не слепая вера в то, что последний ремонт — лучший.

Diff progression. На первом проходе ревьюеры видят полный патч. На проходе 2+ — полный патч плюс дельту итерации с инструкцией фокусироваться на том, что изменилось в ремонте. Это экономит токены и снижает шум. Если дельта >80% от полного патча — fallback на полный diff (ремонт слишком большой, чтобы рассматривать инкрементально).

Early stop. Если две итерации подряд не улучшают результат — остановка. Максимум 20 итераций (настраивается через SIGNUM_AUDIT_MAX_ITERATIONS). На практике сходимость за 2-3 прохода.

Finding fingerprints. Каждая находка получает отпечаток на основе файла, диапазона строк и типа проблемы. Между итерациями Signum классифицирует каждый финдинг как resolved, persisting или new. Синтезатор использует это для оценки реального прогресса — не просто “меньше находок”, а “какие конкретно проблемы исправлены и какие появились”.

Фильтрация галлюцинаций. Если ревьюер ссылается на строку, которой нет в diff, или на файл вне scope — находка отбрасывается. Это тот же механизм, что описан в посте об экосистеме: каждый AI-финдинг валидируется против реального diff перед попаданием в repair loop.

Цикл 2: контракт — самокритика

Цикл кода чинит реализацию. Но что если проблема выше — в самом контракте? Идеальная реализация ошибочной спецификации — всё ещё провал.

Для задач среднего и высокого риска контрактор теперь запускает 4-проходную самокритику перед тем, как показать контракт человеку:

  1. Ambiguity review — сканирует goal, acceptance criteria и scope на неоднозначные формулировки
  2. Missing-input review — проверяет недостающие предусловия, записывает решения по уточнениям
  3. Contradiction review — ищет противоречия между целью, scope и уровнем риска
  4. Coverage review — реконструирует цель из критериев приёмки, проверяет покрытие, документирует происхождение допущений

Максимум 2 раунда авторевизии. Если после второго раунда вердикт "no-go" — эскалация к человеку. Задачи низкого риска пропускают все 4 прохода.

Результат записывается в контракт:

{
  "readinessForPlanning": {
    "verdict": "go",
    "summary": "All ambiguities resolved. AC3 coverage gap closed in round 1."
  },
  "ambiguityCandidates": [...],
  "contradictionsFound": [],
  "clarificationDecisions": [...]
}

Человек видит и вердикт, и весь путь к нему. Не “контрактор решил, что контракт хороший” — а какие проблемы были найдены и как устранены.

Цикл 3: проект — общий контекст между контрактами

Цикл кода итерирует внутри одной задачи. Цикл контракта итерирует внутри одной спецификации. Но предыдущие версии Signum работали с контрактами изолированно. Каждая задача — отдельная вселенная. В реальном проекте задачи связаны: трогают одни файлы, зависят от одних решений, используют одну терминологию.

Три новых слоя:

Project intent. Файл project.intent.md в корне проекта — цель, возможности, non-goals, персоны. Контрактор читает его перед генерацией контракта. Non-goals проекта становятся ограничениями scope контракта. Для средне- и высокорискованных задач отсутствие intent — блокирующий вопрос.

Glossary. project.glossary.json определяет канонические термины и запрещённые синонимы. glossary_check сканирует контракт на использование алиасов, terminology_consistency_check ловит расползание синонимов между активными контрактами. Оба — WARN, не блок.

Cross-contract coherence. overlap_check находит пересечения inScope между активными контрактами (два контракта трогают один файл — конфликт?). assumption_check ищет противоречия в допущениях между связанными контрактами. adr_check предупреждает, когда релевантные ADR существуют, но не упоминаются в контракте.

Плюс upstream staleness detection: контрактор хэширует содержимое project.intent.md и глоссария при создании контракта. Если upstream-файлы изменились к моменту выполнения — предупреждение (или блок, если настроено stalenessPolicy: "block").

Архитектура v4.6.1: checks как отдельные скрипты

Бонус последнего рефакторинга: 6 inline-проверок, которые жили внутри оркестратора, вынесены в отдельные testable-скрипты в lib/:

lib/glossary-check.sh      — сканирование запрещённых синонимов
lib/terminology-check.sh   — расползание терминов между контрактами
lib/overlap-check.sh       — пересечение inScope
lib/assumption-check.sh    — противоречия в допущениях
lib/adr-check.sh           — проверка релевантных ADR
lib/staleness-check.sh     — устаревание upstream-артефактов

Все скрипты: JSON stdout, stderr для диагностики, exit 0 для любого результата проверки (non-zero только для инфраструктурных ошибок). Оркестратор вызывает скрипты и сам решает, блокировать или предупредить. Разделение ответственности: скрипт проверяет, оркестратор решает.

Что это меняет

Signum v3 отвечал на вопрос “корректно ли это?” одним да/нет. v4.6 отвечает на вопрос “можно ли это сделать корректным?” — и если да, делает.

В наших ранних прогонах заметная доля задач, которые v3 блокировал с AUTO_BLOCK, v4.6 доводит до AUTO_OK за 2-3 итерации без участия человека. Задачи, которые всё ещё блокируются — как правило реальные проблемы спецификации или архитектуры, то есть именно то, что и должно эскалироваться к человеку.

Верификация — не гейт в конце пайплайна. Это цикл. Тот же принцип, что и в человеческом код-ревью: находка → исправление → перепроверка. Разница в том, что AI может пройти этот цикл за секунды, а не за дни.

Ссылки