Skip to content

Композитная risk-модель закрытия задачи (v15-risk-model)

Статус: принято (v1.5, эпик v15-evidence-attestation) Реализация: scripts/risk_model.py · Потребители: v15-risk-compute-on-done (расчёт при task_done), v15-l3-risk-trigger (high → обязательный L3 adversarial review), v15-risk-surface-metrics (metrics/status).

Зачем

QG-2 отвечает бинарно: гейты прошли или нет. Но «прошли» бывает разным: scoped-прогон с одним реальным гейтом и тысячестрочным диффом по auth-коду — не то же самое, что полный прогон на пятистрочном фиксе документации. Risk-score сводит эту разницу в одно число 0.0–1.0, чтобы downstream-механика (L3-ревью, метрики, аудит) могла реагировать ступенчато, а не вкл/выкл.

Формула

score = Σ wᵢ · fᵢ ,  fᵢ ∈ [0, 1],  Σ wᵢ = 1

Взвешенная сумма, не max(): риск закрытия накапливается из независимых слабостей. Один посредственный фактор не должен доминировать (за это отвечает вес), но три посредственных вместе обязаны поднимать уровень — max() это компаундирование игнорирует.

Каждый фактор нормализован так, что 0.0 = безопасно, 1.0 = максимально рискованно.

Факторы и веса

ФакторВесЧто измеряетНормализация
gate_coverage0.25Доля сконфигурированных verify-гейтов, которые НЕ выполнились реально (skipped)1 − ran/total; пустой список = 1.0
test_delta0.20Изменение исходников без пропорционального изменения тестовsource=0 → 0.0; tests/source ≥ 0.5 уже даёт ≤0.2
ac_evidence0.20Доля AC-критериев без явного evidence1 − covered/total; нет AC = 1.0
security_hits0.20Затронуты ли security-чувствительные файлы (security_pattern)0 хитов = 0.0; иначе пол 0.5 + 0.5·доля
code_churn0.15Размер диффа (log-шкала)log10(lines+1)/3, кэп 1.0 (≈1000 строк)

Обоснование весов

  • gate_coverage 0.25 — максимальный. Вся цепочка evidence-attestation (подписанные receipts) заверяет только гейты, которые РЕАЛЬНО выполнились; skipped-гейты исключаются из receipt by design. Непокрытый гейт — самый сильный одиночный сигнал, что «зелёное» закрытие ничего не доказывает.
  • test_delta 0.20. Изменённый код без изменённых тестов — классический предвестник тихой регрессии. Чуть ниже гейтов, потому что scoped-прогоны легитимно пропускают несвязанные suites, а доли тестов в диффе зависят от жанра задачи.
  • ac_evidence 0.20. AC без evidence = «сделано» со слов агента. QG-2 уже требует --ac-verified, но парсер evidence показывает, какая доля критериев подтверждена ссылками на тесты/прогоны.
  • security_hits 0.20. Стоимость дефекта на security-поверхности кратно выше. Пол 0.5 («любое касание ≥ medium») синхронизирован с политикой verify-кэша: security-файлы всегда re-verify, кэшу не доверяем.
  • code_churn 0.15 — минимальный. Большой дифф прячет дефекты, но сам по себе слабейший предиктор: рефакторинг-переименование на 800 строк безопаснее 20 строк в парсере денег. Лог-шкала, чтобы 30 и 100 строк отличались заметно, а 2000 и 5000 — уже нет.

Уровни

УровеньДиапазонРеакция downstream
lowscore < 0.33нет
medium0.33 ≤ score < 0.66подсветка в metrics/status
highscore ≥ 0.66обязательный L3 adversarial review (v15-l3-risk-trigger)

Границы попадают В уровень выше (0.33 → medium, 0.66 → high): спорный балл эскалируется, а не амнистируется.

Контракт ошибок

  • Неизвестное имя фактора, значение вне [0,1], NaN/Inf → ValueError. Тихий clamp маскировал бы баг интегратора и портил исторические ряды.
  • Отсутствующий канонический фактор → консервативный дефолт 1.0 + имя в списке defaulted в выводе. Интеграция, не умеющая измерить фактор, не должна выглядеть безопаснее той, что измерила его как плохой (fail-visible, не fail-open).

Известные ограничения

  1. Веса априорные, не обученные. Калибровка на исторических verification_runs/usage_events возможна после накопления риск-строк (v15-risk-compute-on-done пишет их в БД).
  2. test_delta считает файлы, не assert'ы. Переименование тестового файла выглядит как «тесты менялись». Сознательный компромисс: дёшево, детерминированно, без парсинга AST.
  3. code_churn не различает жанры диффа (генерированный код, lock-файлы). Частично купируется малым весом.
  4. Формула линейна — нет взаимодействий факторов (security × churn). Усложнение без данных для калибровки = ложная точность.
  5. Score описывает закрытие задачи, не проект. Агрегация по эпику/сессии — отдельная задача (v15-risk-surface-metrics).