qvib.pro
EN

~4 мин чтения · профи

Циклы: как движок гонит работу до результата (loop-until-dry, кап итераций)

Что это

Цикл — это ограниченная петля «сделай → проверь → если не готово, почини класс проблемы → проверь снова». Движок гонит её сам, но с жёсткими тормозами, чтобы не крутиться вечно. В спеке есть два уровня этой механики, и важно их не путать.

Находка BLOCKот призмыПочини КЛАССgrep всех местПере-судтолько затронутыеСухо?нет criticalДальшек сдачене сухо · кап 2 круга → отчёт
Цикл «правь класс → пере-суди», пока не сухо — с жёстким капом

Уровень 1 — orchestration.loopUntilDry (ревью-петля)

Это петля вокруг ревью призмами. Пока есть находка critical/block — чинить и пере-судить. Экспортируется буквальным текстом-рецептом в пакет:

Если BLOCK — почини КЛАСС проблемы (не точечный симптом) и пере-суди ТОЛЬКО затронутыми углами. Повторяй до «сухо» (нет critical/block), максимум N итераций; после капа — эскалируй владельцу.

В Quest maxIterations: 4. Ключевое слово — класс проблемы: не залатать один симптом, а починить всю категорию и пере-проверить только затронутыми углами.

Уровень 2 — engine.cycles[] (общие ограниченные петли)

Статус. engine.cycles[]слой конструктора: поддержан схемой и экспортёром, но в базовый флагманский сид Quest пока не входит (в разработке для флагмана). Уровень 1 (loopUntilDry, maxIterations: 4) в Quest есть и работает из коробки; уровень 2 — то, что вы добавляете сами в конфигураторе, когда вам нужны более широкие петли. Всё в этом разделе описывает форму слоя, а не поведение купленного пакета.

Это отдельный примитив для более широких сценариев, с пятью видами: until-goal, for-each-work-item (проход по бэклогу), refine-until-quality, retry-with-backoff, reconcile-config. Здесь maxIterationsобязательное поле без дефолта, потолок 12. Это сделано намеренно: петля без жёсткого потолка на слабой модели может крутиться бесконечно, поэтому «нельзя описать цикл, не назвав его предел».

Три предохранителя от runaway

  1. Счётчик — из файла, не из памяти. Рецепт прямо говорит: считай итерации из файла-леджера .engine/cycles/<name>.count, а НЕ по памяти — «память врёт про счётчик». Каждый круг: инкремент в файле → работа → выполненный verify.
  2. Токен-бюджет обязателен. Схема отклоняет спеку на сохранении, если есть cycles[], но не задан permissions.budget.tokensSoftCap. Формально: «петля без токен-капа может сжечь неограниченную стоимость на слабой модели».
  3. STOP-правило. Достиг maxIterations ИЛИ бюджета токенов → ОСТАНОВИСЬ и эскалируй владельцу. «Не крути цикл дальше на всякий случай».

Есть и защита соседей: у цикла есть offLimits — пути, которые петля НЕ трогает никогда, чтобы runaway-цикл не затёр смежное.

Когда применять слой cycles

  • for-each-work-item — прогнать бэклог из N задач до verify-green по каждой.
  • reconcile-config — привести конфиги/сервисы к желаемому состоянию (этот вид обязателен, если в спеке есть setup.systemConfig — иначе сохранение отклоняется).
  • refine-until-quality — полировать артефакт, пока не пройдёт планку.

Как это выглядит на слое конструктора

Пример ниже показывает форму слоя engine.cycles[] — как он описывается в конфигураторе. Это не сценарий базового Quest (там из коробки есть только уровень 1 loopUntilDry); это то, что делает движок, в который вы добавили cycles.

Вход: бэклог из 3 багов, движок с engine.cycles: [{ name: 'backlog', kind: 'for-each-work-item', maxIterations: 6 }] и tokensSoftCap: 400000.

Что делает движок с таким слоем (буквально по рецепту в пакете):

  1. Создаёт .engine/cycles/backlog.count = 0.
  2. Круг 1: инкремент → .count = 1. Берёт баг №1, чинит класс, запускает verify (npx tsc --noEmit, npm test). Зелено — фиксирует.
  3. Круг 2 → .count = 2. Баг №2. Тест падает — чинит, пере-verify до зелёного.
  4. Круг 3 → .count = 3. Баг №3. Готов.
  5. Бэклог пуст (цель for-each достигнута) — цикл завершается на 3 из 6 итераций, штатно.

Альтернативный исход (важный): если бы баг №2 не чинился и .count дошёл до 6 — движок останавливается на капе и пишет честный отчёт: «достиг maxIterations на баге №2, вот что осталось», а не имитирует, что всё исправлено. Точно так же он остановится, если токены пересекут 400000.

Результат: три бага закрыты с выполненным verify на каждом; счётчик итераций объективен (из файла); стоимость под контролем бюджета; на нерешаемом — эскалация, а не бесконечный цикл.

Честная оговорка

Сначала о статусе: уровень 1 (loopUntilDry) есть в базовом Quest; уровень 2 (engine.cycles[]) — слой конструктора, во флагманский сид пока не входит (в разработке). Раздел про пять видов циклов и worked-пример относятся к движку, в который вы этот слой добавили сами.

Дальше о границе гарантии. Дисциплина леджера и STOP-правила — ADVISORY проза (инструкция целевой модели), потому что счётчик в файле сам по себе не блокирует код выхода. ENFORCED тут — только те verify-команды внутри круга, у которых есть реальный код выхода (тесты, tsc, линтер). Токен-кап и кап итераций как правила схемы блокируют сборку невалидного движка, но на этапе выполнения их соблюдение обеспечивает следование рецепту.