Gates и то, как они измеряются
Gate — это точка процесса, в которой работа останавливается, пока кто-то — человек, агент или скрипт — не скажет, что она может продолжаться. В продукте их тринадцать, и до FASE 15a ни один из них не существовал в виде списка: они были разбросаны по чистым правилам, сценариям использования, тестам и workflow CI. Чтобы ответить на вопрос «какие gates существуют», нужно было прочитать шесть файлов.
docs/gates.yml — это и есть такой список. Этот документ объясняет, что
он собой представляет и — что важнее — чем он не является.
Реестр описывает, а не исполняет
Ни один gate не начинает проверяться из-за этого файла. Изменение
severidade в нём не меняет никакого поведения: merge по-прежнему
блокирует decide.ts, PR по-прежнему оценивает агент QA, backmerge
по-прежнему проваливает workflow CI.
Это предположение легче всего сделать неверно, поэтому стоит повторить:
реестр — это индекс, а не политика. Политика веток по-прежнему живёт в
branching-policy.md; поля entrada и
entregavel — это фраза на португальском, а onde указывает на код,
который на самом деле управляет.
Реестр даёт другое: делает gates перечислимыми. Урок Фаз 10 и 13 — метрика извлекается скриптом, а не записывается вручную — неприменим к тому, что невозможно перечислить.
Два поля, которые стареют, если оставить их неопределёнными
verificacao отвечает на вопрос как доказывается, что этот gate
пройден, а не кто его оценивает. script означает, что есть
механический артефакт — событие, тест, job, — служащий доказательством.
Поэтому qa-verificada — это script, даже когда вердикт выносит LLM:
артефакт — это pr.gate_changed с veredito. Тот, кто оценивает, живёт в
dono.
aprovacao_humana: true означает, что решение принимает
пользователь — напрямую, кликом, или делегированно, через политику,
которую пользователь написал сам. Именно это позволяет acao-aprovada
сосуществовать со status: auto_approved: политика, решающая
самостоятельно, — это пользователь, решивший заранее. merge-protegida —
случай, когда нет даже делегирования, потому что потолок понижает
auto_approve до require_approval даже при включённой автономии.
У четырёх gates, которые конституция объявляет ручными, это поле инвариантно и зафиксировано тестом (RN-071).
Три формы доказательства, потому что не всякое доказательство живёт в логе
| тип | когда | что содержит локатор |
|---|---|---|
event_log | исход становится доменным событием | типы событий + фильтр payload |
teste | гарантия — это утверждение | путь к spec |
ci | gate живёт в репозитории | workflow + spec |
Два случая, которые заставили это поле появиться:
merge-protegidaне порождает собственного события. Это потолок, применяемый последним поверх уже вычисленного вердикта, который понижаетauto_approveдоrequire_approval; исход выглядит как ожидающийproposed_action.created, неотличимый от любого другого ожидания. Блокировку гарантирует тест.backmergeживёт вне приложения: это обязательная проверка с версионируемым состоянием в.release/gate.jsonнаmain.
Без evidencia правило «gate block без доказательства проваливается»
навсегда оставило бы две самые жёсткие блокировки продукта красными.
Две проверки, и только одна из них работает в production
Реестр проверяется в два слоя, и это разделение не косметическое:
| слой | что утверждает | кто запускает |
|---|---|---|
validarRegistro | утверждения о содержимом — block требует script, четыре человеческих gate остаются человеческими, active требует доказательства, нет дублирующихся id, нет осиротевших entrada | любой, кто читает реестр, включая api во время выполнения |
validarLocalizadores | целевой файл teste/ci существует | тест против реального файла и фаза 2 validacao-gates.ts — никогда api |
Первый действует везде, где читается реестр. Второй — утверждение о репозитории, и он имеет смысл только внутри checkout.
Когда-то обе были одной функцией, и загрузчик api её вызывал. Последствие
было измерено на установленной v6.1.0: GET /gates отвечал 500 с
RegistroDeGatesInvalido, перечисляя одиннадцать целей как «не
существует», — двенадцать ответов 5xx примерно за 55 минут. С реестром
всё было в порядке. Образ production несёт /app/docs/gates.yml и больше
ничего из docs/; apps/api/test/, scripts/ci/ и .github/ в него
никогда не попадают, поэтому утверждение о репозитории, проверяемое
против этого дерева, проваливает каждый gate в каждой установке.
Ничего не было ослаблено. Правило по-прежнему существует, покрывает те же
цели, и исчезнувшая цель по-прежнему проваливается — оно просто
выполняется там, где вопрос имеет смысл. Это удерживают два стража: тест,
который поднимается к корню репозитория и проверяет каждый локатор, и
тест загрузчика, который воссоздаёт на диске дерево образа (корень с
docs/gates.yml и ничем больше) и утверждает, что реестр загружается.
Возврат проверки в загрузчик делает второй тест красным с ровно тем же
сообщением, которое выдала установка.
И теперь api, отдающая реестр, проверяется против образа production:
docker/smoke.sh вызывает GET /gates и GET /internal/gates (service
token) и требует реестр в ответ в обоих. Проверка тела — shell-функция
checar_registro_de_gates, которую scripts/ci/smoke-gates.spec.ts
прогоняет против трёх тел без подъёма стека. Ни один модульный набор не
смог бы поймать исходный сбой — vitest и ExUnit запускаются из checkout,
где цели существуют.
Ловушка общего типа
qa-verificada и secops-segura не являются двумя типами событий:
оба записывают pr.gate_changed, различаемые по payload.gate. Хуже
того — тот же тип записывается, когда gate открывается, без
veredito.
Реестр, хранящий только имя типа, посчитал бы открытие прохождением и
намерил бы вдвое больше, чем произошло на самом деле. Отсюда фильтр и
отсюда сторож veredito: presente. То же самое верно для двух gates
PR инфраструктуры, которые делят infra.gate_changed.
Тест утверждает, что ни одна пара (event_types + filtro) в реестре
не повторяется. Именно эта уникальность придаёт измерению смысл.
Фильтр намеренно достаёт только до payload: принимать произвольную
колонку в YAML означало бы открыть весь запрос. Кто продвинул историю —
человек или PO, — хранится в колонке actor_kind и остаётся вне
декларативного словаря.
Измерение
pnpm --filter api validacao:gates # full report
pnpm --filter api validacao:gates -- --sem-banco # registry and locators only
pnpm --filter api validacao:gates -- --projeto <uuid>
Три фазы, в таком порядке: реестр (загружает и проверяет),
локаторы (существует ли цель teste/ci?) и event log
(последнее прохождение, с id события). Первые две никогда не трогают
базу — именно это делает скрипт полезным в CI без Postgres.
Фаза 2 сообщает о каждом локаторе реестра, а не только о локаторах
gates active+block. Когда-то у неё был собственный, более узкий цикл,
а у загрузчика api — более широкий, и rag-acertivo — active, warn,
ссылающийся на workflow и тест, — никогда не появлялся в таблице. Теперь
это одно правило, validarLocalizadores, общее с тестом.
| код выхода | когда |
|---|---|
0 | реестр корректен, локаторы на месте |
1 | нарушен инвариант, отсутствует цель или gate без прохождения, если передан --projeto |
2 | неверное использование |
Асимметрия намеренна. Реестр и локаторы — утверждения о
репозитории: они действуют всегда. Доказательство из event log —
утверждение о выполнении, и оно существует, только если выполнение
было, — без --projeto фаза 3 лишь отчёт. Требование прохождения в
только что созданной базе заставляло бы скрипт всегда выходить с 1, а
скрипт, который всегда проваливается, становится шумом, который никто не
читает.
Что перечисление уже окупило
Ещё до каких-либо измерений составление списка нашло две вещи, которых никто не замечал:
- оценку QA и SecOps для PR инфраструктуры, у которой собственный
путь (
infra.gate_changed, без задачи backlog за ней) и которой не было в spec; promotion-checkбыл обязательной проверкой без собственного spec, в отличие отpr-policeиapproval-ladder. Доказательство указывало на скрипт, с пробелом, записанным прямо там, и пункт ушёл в триаж — фаза объявляет и измеряет, а не исправляет. Триаж его закрыл:scripts/ci/promotion-check.spec.tsсуществует, и доказательство gate теперь указывает на него. Это цикл, работающий так, как задуман, — перечисление нашло, триаж расставил приоритеты, другая фаза исправила.
Третья находка пришла из первого запуска измерителя: фильтр
story-promovida указывал на actor_kind, который является колонкой,
а не payload, и gate отображался как «никогда не проходил» в базе, где
он прошёл несколькими часами ранее. Реестр, который никто не запускает, —
реестр, который лжёт.
Активация gate planned — такая же работа, как любая другая
implementavel (владелец dev-lead) родился planned в FASE 14d вместе
с остальной целевой оргструктурой, которую docs/gates.yml/docs/fluxo.yml
уже описывали без кода за ней.
ADR 0090 его
активировал — и это упражнение полезно как пример, потому что в форме
реестра ничего не изменилось: status: planned → active плюс блок
evidencia (указывающий на proposed_action.created, отфильтрованный по
actionType: assess_implementability) — ЕДИНСТВЕННЫЕ две строки, которые
gate warn должен получить, чтобы перестать быть стремлением.
severidade остаётся warn — комментарий в файле уже гласил «рождается
warn даже при активации», а активация — не то же решение, что повышение до
block (для этого пришлось бы сначала измерить реальные прохождения, та
же дисциплина, что в FASE 15a).
Настоящая работа лежит вне файла: Dev Lead понадобился новый инструмент
(assess_implementability), предлагающий оценку как proposed_action, а
QA-стратегия — до того роль proposto в docs/fluxo.yml, с уже
записанным там критерием разделения («может быть самим qa-lead на втором
этапе», в оригинале «pode ser o próprio qa-lead num segundo momento»), —
должна была появиться, чтобы питать оценку реальным планом тестирования.
Реестр лишь начал описывать то, что теперь делает код.
Этот вход не пережил реального использования.
ADR 0192
(RN-674) перенёс план тестирования на время
ПОСЛЕ сдачи dev — написанный ДО dev, он не имел кода для чтения и исчерпывал
итерации, — и entrada gate сменилась с plano-de-teste на
module_map: Dev Lead оценивает историю и карту модулей, которые у него
уже есть. План теперь питает qa-verificada как вход единственного
вердикта, который этот gate и так выносит. Тот же урок в обратную
сторону: строка реестра изменилась, потому что изменилось КТО ЧТО КОМУ
ПЕРЕДАЁТ, а не для того, чтобы gate прошёл.
Реестр может устареть в неверную сторону — устаревший, а не неактивный
implementavel — пример gate, которому понадобился новый код, чтобы выйти
из planned. paralelismo-autorizado — противоположность: механизм
(RequestParallelizationUseCase,
RN-083) работает в production со
времён FASE 14d — отстал именно реестр, объявляя planned о том, что уже
было active в родственном docs/fluxo.yml и в коде. Аудит fluxo.yml ×
код (находка A1/B5,
auditoria-fluxo-vs-codigo.md) обнаружил
расхождение; исправлением были те же две строки, которые получил и
implementavel, — status: active плюс evidencia, указывающая на
proposed_action.created, отфильтрованный по actionType: parallelize.
Стоит отметить, почему это ничего не изменило в дорожке PR на панели
команды: paralelismo-autorizado — это fluxo: execucao, а экран выводит
этапы только из gates с fluxo: pr (см. «Потребление» ниже), — gate может
стать active в реестре, не появившись нигде в интерфейсе, если его поток
другой.
Gate может родиться сразу active — когда механизм и реестр приходят вместе
implementavel и paralelismo-autorizado — два примера gate, который уже
существовал (объявленный planned или устаревший) и получил запись в
реестре ПОЗЖЕ. necessidade-validada
(RN-406,
ADR 0095) — третий шаблон:
механизм и реестр родились в ОДНОМ И ТОМ ЖЕ изменении, потому что самого
gate не было нигде — ни planned в файле, ни кода за ним.
Выбор severidade: warn следует тому же рассуждению, что и у
implementavel, но по другой причине. implementavel — warn, потому что
повышение до block потребовало бы сначала измерить реальные прохождения
(FASE 15a). necessidade-validada — warn, потому что НИЧТО в продукте
сегодня не проверяет его прохождение, прежде чем позволить PO продолжить,
— handoff Criativo→PO уже происходит внутри confirm_readiness, ещё до
появления этого gate. block обещал бы блокировку, которой нет; warn
описывает ровно то, чем gate является: измерением того, что человек
подтвердил ценность потребности, а не gate, блокирующим следующий шаг.
workspace-verificado (RN-423,
ADR 0104)
— четвёртый пример и самый наглядный из четырёх в том, почему warn — не
«слабая блокировка»: то, что действительно блокирует ЗДЕСЬ, — не gate, а
явный отказ в
Engine.Actions.TerminalExecutor.decisao_de_execucao/1 — без проверенного
workspace или подключённого runner команда отвергается безусловно, в
глубине кода, минуя реестр. Gate существует лишь для того, чтобы дать
ДОКАЗАТЕЛЬСТВО того, когда произошло подтверждение
(project.workspace_verified в event log); повышение его до block
описывало бы блокировку, которая уже существует в другом месте, так же
как necessidade-validada солгал бы о блокировке, которой нет нигде.
rag-acertivo (RN-490/RN-498,
ADR 0132/ADR 0138)
— пятый пример, и причина, по которой он остаётся warn, однажды сменила
форму, что стоит рассказать целиком. Он родился warn по самой простой
из пяти причин: не было CI, способного запустить реальную LLM, —
golden-set, который он измеряет (17 вопросов, составленных из реальных
RN/ADR и сверяемых с реальным курируемым корпусом настоящим
HybridSearchUseCase), запускался только вручную (mix golden_set.rag),
та же позиция, что у golden-set QA Automation
(ADR 0123) и
сегодня. Это перестало быть верным: .github/workflows/golden-set-rag.yml
теперь по-настоящему запускает mix golden_set.rag против реального
сервиса Ollama — он вызывает только модель embedding, на CPU,
детерминированно, никогда не чат-модель, выносящую суждение.
(Утверждение, что golden-set QA нельзя обработать тем же способом, было
измерено позже, и время не оказалось препятствием — см. следующий
раздел.) Устареть severidade: warn помешало не то, что gate остался
ручным, — а то, что workflow плановый (ночной cron плюс
workflow_dispatch), никогда не запускаемый по pull_request. block
по-прежнему обещал бы проверку, удерживающую merge, а плановый запуск
ничего не удерживает: регрессия становится видна в течение суток, а не в
момент PR. Причина сменилась с «никакой CI не может это запустить» на
«этот CI не стоит на пути merge», — warn по-прежнему описывает ровно то,
что верно, только уже не ту же истину, что раньше.
Golden-set QA в CI: время укладывается, инструмент измеряет, и он запускается еженедельно
Golden-set семантической оценки агента QA Automation
(ADR 0123) — то, что
дало бы семантическому этапу qa-verificada сигнал тенденции. ADR
0123 и ADR 0138
утверждали, не измеряя, что запуск в CI требует секрета LLM по API или
новой инфраструктуры (runner с GPU). 2026-09-13 (AT-067) это было
измерено, точно по образцу golden-set-rag.yml: ubuntu-latest, без GPU,
те же закреплённые образы сервисов pgvector и ollama,
qwen2.5-coder:latest, скачанный в Ollama самого job (с
OLLAMA_KEEP_ALIVE=1h/OLLAMA_CONTEXT_LENGTH=16384 из compose dev), api
и engine, поднятые как серверы, — engine — это то, что добавлено по
сравнению с job RAG, потому что именно engine, вызываемый api через
POST /internal/actions/execute, запускает npm test, который запрашивает
QA.
| запуск | загрузка модели | подготовка до задачи mix | mix golden_set.qa | весь job | счёт |
|---|---|---|---|---|---|
| 34769447405 | 15 с | ~3 мин | 19 мин 15 с | 22 мин 09 с | 0/6 |
| 34770869429 | 13 с | ~3 мин | 22 мин 53 с | 25 мин 45 с | 0/6 |
Чат-ход 7B-модели на CPU этого runner'а занимал в медиане ~20 с (10–34 с на 23 ходах, которые сохранил лог api). Время укладывается: ночной или еженедельный job меньше получаса, без секрета, без GPU. Половина «новая инфраструктура» старой фразы измерением опровергнута.
Не укладывается инструмент. Оба запуска дали 0/6, ниже записанной
нижней границы 1/6, — и не из-за суждения модели. Event log второго
запуска показывает почему: каждый npm test, предложенный QA, был
автоматически одобрен (permissions.json allow: npm test), а затем
отвергнут engine, потому что начиная с
RN-502
(ADR 0143,
2026-09-04) проект container без контейнера в running нигде не
выполняет терминальные команды. Скрипт seed датирован 2026-08-30 и
никогда не регистрирует контейнер. Поэтому ни один случай не может
увидеть exit 0, а emit_qa_verdict отвергает approved без него — три
из шести ожидаемых вердиктов стали невозможны. Сообщение об отказе гласит
"suba o container do projeto (a Infra propõe container_start)", и модель
сделала то, что прочитала: в трёх случаях попросила у терминала
container_start или docker-compose up (осталось pending, затем
awaiting_approval), а в одном повторила npm test шестьдесят раз до
toolloop.limit_reached (blocked(modelo)). Оставшиеся два застряли на
первом вызове с пустым command — ненадёжность, которую ADR 0123 уже
фиксировал для этой модели, независимо от RN-502. Ничего здесь не
специфично для CI: тот же отказ действует и при локальном запуске.
Именно поэтому никакой golden-set-qa.yml не был закоммичен. Плановый
workflow, краснеющий при каждом запуске по причине, не связанной с тем,
что он измеряет, хуже, чем никакого, — фоновый красный учит людей
игнорировать красный. Измеренная схема осталась в истории репозитория
(workflow в том виде, как он был в коммите e7d7d1b16, включая временный
диагностический шаг). Чтобы он снова измерял, нужно было решить, как
golden-set запускает набор под RN-502, и это было решение для человека.
Ослабление отказа ради прохождения harness никогда не было одним из
вариантов: оно ослабило бы изоляцию, ради которой существует RN-502, чтобы
тест прошёл.
Решение и второе измерение (AT-076)
2026-09-27 сопровождающий выбрал первый путь: seed поднимает реальный
контейнер через broker и регистрирует его как running, путём
production. RN-502 остаётся нетронутым; изменилось то, что seed теперь
выполняет её предусловие. Для каждого случая, после клонирования
заготовки в управляемую папку проекта,
apps/api/scripts/golden-set-qa-container.ts проходит те же сценарии
использования, что проходит реальный проект, в собственной сессии
consultiva: CreateModuleMapUseCase (один модуль),
RouteModulesToInfraUseCase (один образ-кандидат), ProposeActionUseCase
для container_start с актором agent/infra и ApproveActionUseCase от
имени владельца проекта. Одобрение запускает
ExecuteContainerStartUseCase, который записывает выбор как новый
artifact.project_image через DecidirImagemDoProjetoUseCase (так что
образ оценивает validarDecisaoDeImagem), вызывает
ContainerBrokerPort.start и регистрирует provisioning → running через
машину состояний. Seed предлагает и одобряет, а не вызывает сценарий
выполнения напрямую, потому что два правила живут на стороне предложения
— именованный 409 sem_broker_na_instalacao
(RN-591) и политика decide() для
container_start, — и harness, который их пропускал бы, перестал бы
следовать production в тот день, когда любое из них изменится.
Образ — node:24.11.1-bookworm-slim, закреплённый digest
мультиплатформенного индекса в seed (правило ADR 0159, хотя этот файл
остаётся вне деревьев, которые обходит imagens-pinadas.ts). Debian, а не
Alpine, потому что broker держит контейнер живым через sleep infinity.
Геометрия, благодаря которой все три стороны видят один и тот же код:
worktree является управляемой папкой проекта (projectScopeRoot),
broker монтирует <PROJECT_WORKSPACES_HOST_ROOT>/<workspace_dir_name> в
/work, а engine переводит cwd QA в /work перед exec. В job CI api,
engine и broker работают нативно на runner, поэтому
PROJECT_WORKSPACES_ROOT (api, engine) и PROJECT_WORKSPACES_HOST_ROOT
(broker) — один и тот же путь; broker — единственный процесс, говорящий с
Docker.
Случай, контейнер которого не поднимается, теперь проваливает seed с
ContainerDoGoldenSetNaoSubiuError, называя шаг и причину, которую дал
продукт, а модуль ExUnit проваливается, когда api поднята, но seed
падает. Раньше он возвращал {:skip, _} из тела теста — что ExUnit не
считает пропуском: тест проходил, зелёным, ничего не измерив.
Измерено временным workflow, запускаемым по push в ветку (удалён до PR,
как сделала AT-067):
| запуск | загрузка модели | загрузка образа | контейнер поднят (предложение → выполнено) | mix golden_set.qa | весь job | счёт |
|---|---|---|---|---|---|---|
| 36291440108 | 15 с | 6 с | 0,20–0,28 с на случай | 48 мин 29 с | 52 мин 38 с | 3/6 |
| 36294037297 | 15 с | 6 с | 0,22–0,30 с на случай | 83 мин 16 с | 86 мин 16 с | 3/6 |
По трём критериям, в обоих запусках: в event log ноль отказов RN-502
(ни одно терминальное действие и ни одно событие сессии их не упоминает);
npm test выполнился внутри контейнера с exit 0 в пяти из шести случаев
в первом запуске и во всех шести во втором (пробелы — пустые command,
которые broker отверг с названием причины, — ненадёжность модели, которую
ADR 0123 уже фиксировал); и счёт сообщается как есть, 3/6 дважды, против
нижней границы 1/6.
| случай | ожидалось | 36291440108 | 36294037297 |
|---|---|---|---|
rf-covered | approved | ✓ approved | ✓ approved |
rf-uncovered | changes_requested | ✓ changes_requested | ✓ changes_requested |
rf-single-clean | approved | ✗ changes_requested | ✗ blocked(modelo) |
rf-mismatched-filename | approved | ✗ changes_requested | ✓ approved |
rf-partial-coverage | changes_requested | ✗ blocked(modelo) | ✗ blocked(modelo) |
rf-skipped-test | changes_requested | ✓ changes_requested | ✗ approved |
Тот же итог, разные случаи: ошибки теперь — это суждение модели и его
дисперсия, а именно это golden-set и существует, чтобы измерять, —
rf-skipped-test один раз одобрил, засчитав test.skip как покрытие, а
rf-single-clean во втором запуске выполнил npm test 30 раз, прежде чем
завершиться blocked(modelo).
Запуски стали дольше (48 и 83 мин против 19–23 мин), потому что случаи
теперь выполняют работу: читают файлы и рассуждают о покрытии, а не
останавливаются на отказе, и повторяющийся случай теперь повторяет
реальные выполнения. Плановому job нужен timeout-minutes выше
наблюдённых здесь 86 мин. Две вещи были замечены и здесь не изменены:
вызов api→broker имеет потолок 5 с для любой операции, включая start,
поэтому container_start, образа которого ещё нет в daemon, превысил бы
время, пока загрузка продолжается (job скачивает образ заранее; без этого
не измерялось); и heartbeat engine закрыл простаивающие сессии
(heartbeat_timeout) примерно через 30 с после seed, пока QA продолжал
предлагать и выполнять в них команды (QA работает в процессе mix, вне
какого-либо процесса сессии), без видимого эффекта на прогон.
По расписанию, еженедельно (AT-149)
2026-09-27 сопровождающий выбрал периодичность: еженедельно
(ADR 0168).
.github/workflows/golden-set-qa.yml — это схема AT-076, ставшая
постоянной, — api, engine и broker нативно на runner, только broker
говорит с Docker, одна и та же управляемая папка на всех трёх сторонах, —
на schedule плюс workflow_dispatch, никогда pull_request, та же
позиция, что и у golden-set-rag.yml.
Еженедельно, а не еженощно, как job RAG, по двум измеренным причинам.
Запуск RAG стоит ~20 минут и детерминирован, поэтому вчерашнее падение —
реальное падение. Этот стоит от 52 до 86 минут и варьируется: два
запуска AT-076 пришли к одним и тем же 3/6 через разные случаи. Ночной
запуск тратил бы часы runner'а в неделю, чтобы читать ту же тенденцию с
большим шумом; одного запуска в неделю достаточно, чтобы храповая нижняя
граница показала падение, а workflow_dispatch отвечает сразу после
изменения промпта QA или seed. Он запускается в субботу в 01:17 UTC:
подальше от ежедневных 06:00 RAG, от воскресных 04:00 доказательств
свойств и от понедельничных 06:00 проверки внешних ссылок; в субботу —
чтобы красный запуск дождался начала недели; в 17-ю минуту — потому что
Actions задерживает, а иногда и отбрасывает задания на целый час.
timeout-minutes — 150: измеренные 86 мин плюс запас на запуск, где
больше одного случая упирается в потолок итераций. Это дисперсия модели,
и job не измеряет время.
Вердикт — вердикт самого теста: ниже passRate, который floor.json
записывает для модели, ExUnit проваливается. Workflow добавляет лишь два
стража инструмента — строка счёта должна существовать (тест пропускается,
когда api недоступна, а пропуск в запуске без надзора — зелёный, который
ничего не измерил), и в event log не должно быть отказа RN-502 (сигнатура
AT-067), — а при сбое печатает логи, как делает job RAG. Образ для
предварительной загрузки читается из самой константы seed
(IMAGEM_DO_GOLDEN_SET_QA), а не из второго литерала: docker run broker
выполняется под контрольным потолком 30 с порта Docker
(RN-604), и job, скачивающий один образ, в
то время как seed просит другой, поместил бы загрузку внутрь этого
потолка.
Проверочный запуск, с временным триггером push в ветке, удалённым до
PR:
| запуск | загрузка модели | загрузка образа | mix golden_set.qa | весь job | счёт |
|---|---|---|---|---|---|
| 36327281774 | 15 с | 5 с | 10 мин 13 с | 13 мин 41 с | 3/6 |
Ноль отказов RN-502 в event log, шесть контейнеров running до первого
случая. Снова те же 3/6, через ещё один, другой набор случаев —
rf-covered, rf-uncovered и rf-skipped-test угадали;
rf-single-clean и rf-mismatched-filename получили changes_requested
там, где ожидалось approved, а rf-partial-coverage был одобрен с
частичным покрытием. Запуск занял шестую часть времени запусков AT-076
(10 против 48–83 минут mix golden_set.qa): на этот раз ни один случай не
повторялся до потолка итераций. Таймаут по-прежнему рассчитан по самому
медленному измеренному запуску, а не по самому быстрому.
Gate может отсутствовать в реестре, пока fluxo.yml уже его назначает
deployavel — четвёртый шаблон и наименее лестный: он был объявлен как
gate_saida области area-infra в docs/fluxo.yml и полностью
отсутствовал в docs/gates.yml — ни planned, ни устаревший, его там
просто не было. Модель команды состоит из трёх частей (каталог, поток,
реестр), и ничто их пока не сопоставляет, поэтому gate может быть
результатом в одной и не существовать в другой. Это вторая находка
такого вида; первой был paralelismo-autorizado, устаревавший в
противоположную сторону.
Он входит как planned/warn, без блока evidencia и без скрипта
проверки, потому что RN-070/
RN-071 запрещают block без него, — и
потому что блокирует его не код. Инфраструктура существует (overlays,
образы в GHCR по digest, скрипты smoke/rollout/restore); не хватает
production-окружения с реальным трафиком и того, кто будет его
поддерживать. См.
ADR 0153,
который также заставляет десять документов перестать ссылаться на
DEPLOY_ENABLED так, будто это переменная, существующая в коде.
Потребление: экран выводит, а не повторяет
Дорожка PR на панели — Dev → QA → SecOps → Você — была списком,
записанным внутри компонента. Начиная с FASE 15b она берётся из реестра,
через GET /gates.
На практике меняется свойство, а не внешний вид: gate, ушедший из реестра, сам уходит с экрана. Раньше отключение SecOps оставляло мёртвый этап на дорожке, пока кто-нибудь не вспоминал отредактировать код, — ровно тот вид устаревания, ради которого реестр и появился.
Три решения, которые стоит объяснить:
Маршрут отделён от внутреннего. /internal/gates — сервис-сервис, с
service token, и обслуживает скрипт измерения; /gates — для вошедшего
пользователя и обслуживает экран. Один реестр, разная аудитория.
Без projectId. Реестр — факт о ПРОДУКТЕ: одни и те же gates
действуют для любого проекта. Привязка к проекту намекала бы, что можно
иметь разные gates для разных проектов, а именно это ADR 0054 намеренно
оставил открытым.
Только gates active. Gate planned описывает будущую роль
(dev-lead, platform). На экране, говорящем о том, что происходит сейчас,
он выглядел бы так, будто происходит.
Что экран по-прежнему решает сам
Подпись каждого этапа (QA, Você) и какой этап представляет
каждый gate остаются в компоненте. Это не непоследовательность: реестр
описывает политику, а как называть вещи для пользователя — решение экрана.
Из него ушёл СПИСОК — какие этапы существуют.
Gate PR, который реестр приносит, а экран ещё не умеет рисовать,
игнорируется, и это утверждает тест: этап undefined посреди дорожки
был бы хуже его отсутствия.