Зеркало process.yml и mutation-gate.yml из dev
Полные бенчмарки производительности / Бенчмарки рендера и геометрии (blend) (push) Failing after 3m41s
Полные бенчмарки производительности / Бенчмарки рендера и геометрии (interaction) (push) Failing after 3m38s
Полные бенчмарки производительности / Бенчмарки рендера и геометрии (plan-snap) (push) Failing after 2m17s
Полные бенчмарки производительности / Бенчмарки рендера и геометрии (overlay) (push) Failing after 2m21s
Полные бенчмарки производительности / Бенчмарки рендера и геометрии (space-default) (push) Failing after 1m6s
Полные бенчмарки производительности / Бенчмарки рендера и геометрии (space-glow) (push) Failing after 1m4s
Проверка (CI) / Классификация изменённых файлов (push) Successful in 31s
Проверка (CI) / HACS: валидация репозитория (push) Skipped
Проверка (CI) / Hassfest: манифест интеграции (push) Skipped
Проверка (CI) / Мутанты по диффу (1/6): затронутые свидетели краснеют (push) Skipped
Проверка (CI) / Мутанты по диффу (2/6): затронутые свидетели краснеют (push) Skipped
Проверка (CI) / Мутанты по диффу (3/6): затронутые свидетели краснеют (push) Skipped
Проверка (CI) / Мутанты по диффу (4/6): затронутые свидетели краснеют (push) Skipped
Проверка (CI) / Мутанты по диффу (5/6): затронутые свидетели краснеют (push) Skipped
Проверка (CI) / Мутанты по диффу (6/6): затронутые свидетели краснеют (push) Skipped
Проверка (CI) / Предполётные проверки: документация, провенанс, процесс (push) Failing after 1m36s
Полные бенчмарки производительности / Бенчмарки рендера и геометрии (isometric) (push) Failing after 14m58s
Проверка (CI) / Переиспользование: это дерево уже проверено (push) Successful in 37s
Проверка (CI) / Бэкенд: pytest в Home Assistant (push) Skipped
Полные бенчмарки производительности / Бенчмарки рендера и геометрии (large-house) (push) Failing after 18m42s
Проверка (CI) / Фронтенд: типы, юниты, мутанты, синхрон бандла (push) Failing after 9m39s
Проверка (CI) / Смоки в браузере (шард 1 из 3) (push) Skipped
Проверка (CI) / Смоки в браузере (шард 2 из 3) (push) Skipped
Проверка (CI) / Смоки в браузере (шард 3 из 3) (push) Skipped
Проверка (CI) / Смоки: все шарды зелёные (push) Skipped
Проверка (CI) / Golden-кадры против принятых эталонов (push) Skipped
Проверка (CI) / Перф-смок: бюджет времени кадра (push) Skipped
Полные бенчмарки производительности / Бенчмарки рендера и геометрии (isometric-stage3) (push) Failing after 1m52s

Оба файла исполняются из ветки по умолчанию: process.yml — по событию
`issues`, mutation-gate.yml — по расписанию. Приводятся к версии dev после
#556 (SHA-пины сторонних Actions, права по job, job-scoped токен у стадии
модели). Гейт workflow_sync в Validate сверяет оба.

Issue: #556
User-Visible: no
This commit is contained in:
Codex
2026-09-13 20:18:09 +03:00
parent c3a749949f
commit a33cf8dacb
2 changed files with 77 additions and 50 deletions
+12 -12
View File
@@ -53,7 +53,7 @@ jobs:
tree: ${{ steps.identity.outputs.tree }}
ref: ${{ steps.identity.outputs.ref }}
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
ref: ${{ github.event_name == 'workflow_dispatch' && inputs.ref || 'dev' }}
fetch-depth: 0
@@ -76,19 +76,19 @@ jobs:
# Час — потолок против зависшего Chromium.
timeout-minutes: 60
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
ref: ${{ needs.material.outputs.sha }}
fetch-depth: 0
- uses: actions/setup-node@v7
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
with:
node-version: 22
cache: npm
- run: npm ci
- uses: actions/setup-python@v7
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7
with:
python-version: '3.14'
@@ -102,7 +102,7 @@ jobs:
- name: Кэш браузеров Playwright
id: pw
uses: actions/cache@v6
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6
with:
path: ~/.cache/ms-playwright
key: playwright-${{ runner.os }}-${{ hashFiles('package-lock.json') }}
@@ -138,7 +138,7 @@ jobs:
--shard=${{ matrix.shard }} --shards=4
- name: Сохранить лог и identity шарда
if: always()
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: mutation-shard-${{ matrix.shard }}-attempt-${{ github.run_attempt }}
path: artifacts/mutation-shard-${{ matrix.shard }}
@@ -153,16 +153,16 @@ jobs:
if: always()
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
# CLI отчётчика принадлежит зафиксированному material. В main лежит
# только workflow и может не быть dev-версии scripts/**.
ref: ${{ needs.material.outputs.sha }}
- uses: actions/setup-node@v7
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
with:
node-version: 22
- name: Забрать evidence всех попыток
uses: actions/download-artifact@v7
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
with:
pattern: mutation-shard-*
path: artifacts/mutation-logs
@@ -191,17 +191,17 @@ jobs:
actions: read
issues: write
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
# Не перечитываем moving dev: код отчётчика берётся из уже
# зафиксированного material. В стабильном main dev-скриптов нет.
ref: ${{ needs.material.outputs.sha }}
- uses: actions/setup-node@v7
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
with:
node-version: 22
- name: Забрать логи шардов
continue-on-error: true
uses: actions/download-artifact@v7
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
with:
pattern: mutation-shard-*
path: artifacts/mutation-logs
+65 -38
View File
@@ -29,16 +29,20 @@ on:
# отменялся первой же посторонней меткой. Теперь посторонняя метка не запускает
# ни одной job (`if` на guard) и в группу не входит.
# #556: права выдаются по job, а не одной строкой на весь workflow. Общий блок
# давал `issues: write` и OIDC даже той стадии, которая ничего не пишет, —
# модели. Ниже минимум на каждую: писать в issue умеют только детерминированные
# стадии, OIDC нужен исключительно `claude-code-action`.
permissions:
contents: read
issues: write
# Обязательно: claude-code-action получает OIDC-токен для авторизации
# GitHub App. Без этого прогон падает с «Could not fetch an OIDC token».
id-token: write
jobs:
guard:
name: "Страж: ребейз на dev и предпосылки ревью"
# Читает и переставляет метки, комментирует отказ.
permissions:
contents: read
issues: write
# Только статусные метки этапов ревью запускают конвейер (#499). Остальные
# события помечаются skipped и не занимают место в группе concurrency.
if: github.event.label.name == 'S4-spec-review' || github.event.label.name == 'S7-code-review'
@@ -56,7 +60,7 @@ jobs:
# scripts/review-doc-guard.mjs — счёт раундов вынесен туда, потому что
# inline-shell не покрывается тестами (#454). Node на раннере
# предустановлен, setup-node не нужен.
- uses: actions/checkout@v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
fetch-depth: 1
ref: dev
@@ -257,6 +261,10 @@ jobs:
prepare:
name: "Ревью: материал и deterministic gates"
# Ребейзит ветку задачи и комментирует возврат автору.
permissions:
contents: read
issues: write
needs: guard
if: needs.guard.outputs.stage != ''
runs-on: ubuntu-latest
@@ -290,7 +298,7 @@ jobs:
id: clock
run: echo "started=$(date +%s)" >> "$GITHUB_OUTPUT"
- uses: actions/checkout@v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
fetch-depth: 0
ref: dev
@@ -320,7 +328,7 @@ jobs:
# кэша, платились из бюджета 45 минут и из лимитов подписки, а ходы модели
# тратились на работу инфраструктуры. В validate.yml кэш стоит на всех
# тяжёлых job, здесь его не было.
- uses: actions/setup-node@v7
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
with:
node-version: 22
cache: npm
@@ -713,7 +721,7 @@ jobs:
- name: Передать подтверждённый материал модели
if: steps.gate.outputs.proceed == 'true' && steps.reuse.outputs.reuse != 'true'
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: ${{ steps.prepared.outputs.artifact }}
path: ${{ runner.temp }}/review-prepared
@@ -732,6 +740,28 @@ jobs:
model_review:
name: "Ревью: работа модели"
# Единственная недоверенная стадия: модель читает материал и кладёт
# запечатанный artifact. Писать в issue и в репозиторий ей незачем — это
# делает `integrate`, проверив происхождение: в репозиторий модель не пишет
# вообще, а в issue — только своим комментарием и отдельным issue по §12.
#
# Права здесь реальны только вместе с `github_token` у шага Review. Без него
# claude-code-action меняет OIDC на собственный App-токен с дефолтом
# `contents/issues/pull_requests: write` (`src/github/token.ts`), и блок ниже
# не ограничивает ничего: токен `ghs_…` от claude[bot] лежит прямо в
# окружении Bash-инструмента модели — это поймало ревью r1 по #556 в
# собственной же сессии. С переданным `secrets.GITHUB_TOKEN` обмена не
# происходит, `id-token` больше не нужен, и этот список становится потолком.
#
# `issues: write` остаётся: процесс требует от ревьюера комментарий с
# вердиктом (§7.2) и отдельный issue на Medium вне скоупа (§12). Снять его
# можно только перенеся и то и другое в `integrate` — это отдельная правка
# конвейера, не эта задача. Что остаётся модели этим правом: комментарий,
# метки, правка тела issue. Чего не остаётся: запись в репозиторий, слияние
# (его решает запечатанный verdict.json в `integrate`), релиз.
permissions:
contents: read
issues: write
needs: [guard, prepare]
if: needs.prepare.outputs.proceed == 'true' && needs.prepare.outputs.reuse != 'true'
runs-on: ubuntu-latest
@@ -748,19 +778,19 @@ jobs:
id: clock
run: echo "started=$(date +%s)" >> "$GITHUB_OUTPUT"
- uses: actions/checkout@v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
fetch-depth: 0
ref: ${{ needs.prepare.outputs.material_sha }}
persist-credentials: false
- uses: actions/setup-node@v7
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
with:
node-version: 22
cache: npm
- name: Получить контракт подготовленного материала
uses: actions/download-artifact@v4
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
name: review-prepared-${{ github.event.issue.number }}-${{ github.run_id }}-${{ github.run_attempt }}
path: ${{ runner.temp }}/review-prepared
@@ -808,7 +838,7 @@ jobs:
# но когда нужен — качать его заново дороже, чем держать в кэше.
- name: Кэш браузеров Playwright
id: pw
uses: actions/cache@v6
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6
with:
path: ~/.cache/ms-playwright
key: playwright-${{ runner.os }}-${{ hashFiles('package-lock.json') }}
@@ -846,7 +876,7 @@ jobs:
- name: Review
id: review
uses: anthropics/claude-code-action@v1
uses: anthropics/claude-code-action@9cdae7f0d995e3ba7c33f226087fdf82a59cd520 # v1
env:
# Вне рабочей копии: восстановление дерева ревьюером не должно
# уничтожать его собственный артефакт (#220).
@@ -855,6 +885,11 @@ jobs:
# Подписка, а не отдельный счёт API: токен выпускается через
# `claude setup-token` (Pro/Max). Действуют лимиты подписки.
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
# Ambient job-scoped токен вместо App-обмена (#556). Со ним
# `permissions:` этой job — настоящий потолок прав модели: ни записи в
# репозиторий, ни постановки метки, ни комментария. Строку нельзя
# снять, не вернув модели право двигать процесс.
github_token: ${{ secrets.GITHUB_TOKEN }}
path_to_claude_code_executable: ${{ steps.claude_bin.outputs.path }}
prompt: |
Ты ревьюер проекта House Plan. Язык ответа — русский.
@@ -1097,7 +1132,7 @@ jobs:
echo "artifact=review-result-${NUM}-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}" >> "$GITHUB_OUTPUT"
- name: Передать результат интеграции
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: ${{ steps.result.outputs.artifact }}
path: ${{ runner.temp }}/review-result
@@ -1116,6 +1151,10 @@ jobs:
integrate:
name: "Ревью: публикация и интеграция"
# Публикует разбор, переставляет метку, сливает проверенный материал.
permissions:
contents: read
issues: write
needs: [guard, prepare, model_review]
if: always() && needs.guard.outputs.stage != ''
runs-on: ubuntu-latest
@@ -1153,21 +1192,21 @@ jobs:
fi
echo "proceed=true" >> "$GITHUB_OUTPUT"
- uses: actions/checkout@v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
if: steps.ready.outputs.proceed == 'true'
with:
fetch-depth: 0
ref: dev
persist-credentials: false
- uses: actions/setup-node@v7
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
if: steps.ready.outputs.proceed == 'true'
with:
node-version: 22
- name: Получить результат модели
if: steps.ready.outputs.proceed == 'true' && needs.prepare.outputs.reuse != 'true'
uses: actions/download-artifact@v4
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
name: review-result-${{ github.event.issue.number }}-${{ github.run_id }}-${{ github.run_attempt }}
path: ${{ runner.temp }}/review-result
@@ -1175,8 +1214,14 @@ jobs:
- name: Проверить полноту и происхождение результата
id: result
if: steps.ready.outputs.proceed == 'true' && needs.prepare.outputs.reuse != 'true'
# #556: единственное, что недоверенная стадия модели может передать
# дальше, — этот artifact, и принимается он как ввод противника: полный
# набор файлов, сходящиеся суммы, совпадение КАЖДОГО поля паспорта с
# тем, что посчитала детерминированная `prepare`. Проверка вынесена из
# inline-shell в `scripts/review-result-gate.mjs` ради враждебных
# фикстур — в YAML её нельзя прогнать ни одним отрицательным случаем.
env:
NUM: ${{ github.event.issue.number }}
ISSUE: ${{ github.event.issue.number }}
STAGE: ${{ needs.guard.outputs.stage }}
CYCLE: ${{ needs.guard.outputs.cycle }}
BRANCH: ${{ needs.prepare.outputs.branch }}
@@ -1192,27 +1237,9 @@ jobs:
SPEC_BODY_DOC: ${{ needs.prepare.outputs.spec_body_doc }}
SPEC_BODY_RECORDED: ${{ needs.prepare.outputs.spec_body_recorded }}
run: |
set -euo pipefail
dir="$RUNNER_TEMP/review-result"
expected=$(printf '%s\n' manifest.sha256 prepared.json review-document.md verdict.json | sort)
actual=$(find "$dir" -maxdepth 1 -type f -printf '%f\n' | sort)
test "$actual" = "$expected" || { echo "::error::неполный или неожиданный набор evidence"; printf 'ожидалось:\n%s\nполучено:\n%s\n' "$expected" "$actual"; exit 1; }
(cd "$dir" && sha256sum -c manifest.sha256)
test -s "$dir/review-document.md"
jq -e \
--arg run_id "$GITHUB_RUN_ID" --arg run_attempt "$GITHUB_RUN_ATTEMPT" \
--arg issue "$NUM" --arg stage "$STAGE" --arg cycle "$CYCLE" \
--arg branch "$BRANCH" --arg sha "$MATERIAL_SHA" --arg tree "$MATERIAL_TREE" \
--arg specs "$MATERIAL_SPECS" --arg body "$MATERIAL_ISSUE_BODY" \
--arg validate "$VALIDATE_RESULT" --arg validate_url "$VALIDATE_URL" \
--arg rebase_note "$REBASE_NOTE" --arg validated_note "$VALIDATED_NOTE" \
--arg spec_changed "$SPEC_BODY_CHANGED" --arg spec_doc "$SPEC_BODY_DOC" \
--arg spec_recorded "$SPEC_BODY_RECORDED" \
'.schema == 1 and .run_id == $run_id and .run_attempt == $run_attempt and .issue == $issue and .stage == $stage and .cycle == $cycle and .branch == $branch and .material_sha == $sha and .material_tree == $tree and .material_specs == $specs and .material_issue_body == $body and .validate_result == $validate and .validate_url == $validate_url and .rebase_note == $rebase_note and .validated_note == $validated_note and .spec_body_changed == $spec_changed and .spec_body_doc == $spec_doc and .spec_body_recorded == $spec_recorded' \
"$dir/prepared.json"
jq -e '
(.verdict == "green" or .verdict == "yellow" or .verdict == "red")
and (.high | type == "number") and (.medium | type == "number")
and (.summary | type == "string")' "$dir/verdict.json" >/dev/null
node scripts/review-result-gate.mjs --dir="$dir"
printf 'structured_output<<EOF_RESULT\n' >> "$GITHUB_OUTPUT"
cat "$dir/verdict.json" >> "$GITHUB_OUTPUT"
# structured_output не обязан оканчиваться LF: delimiter команды