- scripts/workflow-jobs.mjs: разбор job-уровня validate.yml (id, name,
inline matrix) без зависимостей; незнакомые формы — громкая ошибка.
- ci-proof: JOB_RULES по id job с именем-контрактом; число и точные имена
экземпляров — из матрицы YAML (константы count: 6 нет); resolveJobRules
на каждом evaluateCiProof — переименование даёт failed с названной job;
jobContractProblems — сверка в обе стороны, UNCONSUMED_JOBS для proof.
- validate-gate: MUTANT_JOB_PREFIX — из контракта ci-proof.
- e2e-gate: E2E_JOB_NAME — зеркало name: из houseplan-e2e e2e.yml
(43899da5), распознавание выводится из шаблона; missing называет
завершённые прогоны без job по контракту.
- тесты: фикстуры имён выводятся из validate.yml; новый
test/workflow-jobs.test.mjs; 5 мутантов в реестре.
Issue: #622
User-Visible: no
`test/release-gate.test.mjs` строил ожидания из живого состояния репозитория, а
`localEvidence` читает `baselines.reviewedRun` из сообщения HEAD-коммита. Пока
вершиной был обычный коммит, поле оставалось `null` и всё сходилось. Как только
вершиной становился коммит приёмки эталонов, поле становилось непустым, и
`evaluateCiProof` законно требовал объявленный прогон — которого заглушка
`loadContext` не отдавала. Тест краснел на всей ветке, ради которой #573 и
писалась, и вместе с ним краснел бы `dev` сразу после слияния такой ветки.
Воспроизведено на `issue/594-form-kit-room@68bb7558`: 2776 pass / 1 fail, из-за
чего конвейер ревью не запустился вовсе.
Продуктовый код не менялся: контракт «объявленный прогон обязан существовать,
быть завершённым, не отменённым и быть Validate» — ровно то, что задумано, и он
намеренно не требует `success`, потому что приёмка эталонов по определению
ссылается на прогон, где golden покраснел.
Что изменено:
- зелёный путь теста считается на evidence с `baselines.reviewedRun: null` и
больше не зависит от того, каким коммитом оказалась вершина;
- контракт объявленного прогона вынесен в отдельный тест и проверяется с трёх
сторон: завершённый неотменённый Validate с `conclusion: 'failure'` — зелёно;
прогона нет — отказ; прогон отменён — отказ;
- мутант `declared-baseline-review-run-never-checked` обнуляет объявление и
краснеет на новом тесте.
Доказательство на фикстуре «вершина — приёмка эталонов»: старый тест 8 pass /
1 fail, новый 10 pass / 0 fail.
Issue: #595
User-Visible: no
Приёмка эталонов на beta.3 (`ad4000f9`) стоила второго полного Validate —
22 минуты, из них 17–22 на шард мутантов. Причина одна: корпус отпечатка
(`source-fingerprint.mjs`) называет `demo/golden` строкой-каталогом, а
замыкание входов раскрывало каталог во все текстовые файлы под ним, включая
`baselines-index.json`. Индекс становился входом smoke, performance_smoke и
каждого гарда через `serve.mjs`: на реальной паре C→B ключи smoke/perf были
DIFFERENT, отпечатки 181 из 183 браузерных свидетелей менялись, журнал их не
пропускал.
- `check-inputs.mjs`: `BASELINE_OVERLAY` — раскрытие каталога не выдаёт
overlay; явный корень golden и явная ссылка на файл — как были. На паре
C→B: ключи smoke/perf/parity/backend same, golden DIFFERENT; отпечатки
743 из 744 равны; план мутантов B с журналом C — 0–1 на шард вместо 38–44
- `ci-proof.mjs`: составное evidence — product tree без overlay, overlay
(tree, sha256 индекса, run из `Baseline-Reviewed`), content-ключи всех
реюзных job (исполненных тоже); `evaluateCiProof({expected, reviewedRun})`
сверяет с локальным расчётом, fail-closed на ключ, tree, индекс, reviewed
run, маркер с чужим ключом; proof без evidence при ожиданиях — stale
- `release-gate.mjs` / `release-prerelease.mjs`: ожидания считаются на
checkout кандидата (`candidateExpectations`), чужой checkout — notice
- мутанты: `baseline-overlay-leaks-into-every-key`,
`proof-trusts-evidence-it-could-verify`,
`reused-marker-key-unchecked-against-candidate`,
`product-tree-identity-counts-baselines`; перенацелен
`ci-proof-ignores-run-attempt`
- docs: TESTING (правило overlay), DEVELOPMENT (evidence в release proof),
STATUS
Issue: #573
User-Visible: no
The gate used to require every Validate run on the tag SHA to be green:
a cancelled duplicate or a red flake that a later re-run had fixed kept
the stable release blocked (v1.73.0, 09.09 — released by hand). Now the
verdict comes from the newest run that was not cancelled: not completed →
wait, success → pass, anything else → fail, no run → wait. The same rule
is documented for the perf workflow and the release runbook.
Mutants: release-gate-counts-cancelled-runs, release-gate-oldest-run-wins.
Issue: #511
User-Visible: no