Перейти к основному содержимому

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
cigate живёт в репозитории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.

запускзагрузка моделиподготовка до задачи mixmix golden_set.qaвесь jobсчёт
3476944740515 с~3 мин19 мин 15 с22 мин 09 с0/6
3477086942913 с~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счёт
3629144010815 с6 с0,20–0,28 с на случай48 мин 29 с52 мин 38 с3/6
3629403729715 с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.

случайожидалось3629144010836294037297
rf-coveredapproved✓ approved✓ approved
rf-uncoveredchanges_requested✓ changes_requested✓ changes_requested
rf-single-cleanapproved✗ changes_requested✗ blocked(modelo)
rf-mismatched-filenameapproved✗ changes_requested✓ approved
rf-partial-coveragechanges_requested✗ blocked(modelo)✗ blocked(modelo)
rf-skipped-testchanges_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счёт
3632728177415 с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 посреди дорожки был бы хуже его отсутствия.

Ссылки​

  • ADR 0054 — решение
  • ADR 0048 — решение в event log, без которого нечего было бы измерять
  • RN-070, RN-071