From 27044543a69d184f492dc72f78be82f7259a1209 Mon Sep 17 00:00:00 2001 From: Matysh Date: Wed, 2 Sep 2026 07:46:27 +0300 Subject: [PATCH 1/2] ci: mirror the review-material gate into main MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Конвейер исполняет process.yml из ветки по умолчанию, поэтому файл обязан совпадать в main и dev — это проверяет предполётный шаг «Процесс: process.yml идентичен в main и dev». Здесь ровно тот же файл, что уехал в dev коммитом 206732e9, байт в байт. Issue: #413 User-Visible: no --- .github/workflows/process.yml | 36 +++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/.github/workflows/process.yml b/.github/workflows/process.yml index cf99cfb2..deb7e897 100644 --- a/.github/workflows/process.yml +++ b/.github/workflows/process.yml @@ -745,6 +745,42 @@ jobs: fi echo "документ опубликован в $target: $doc" + # Материал раунда обязан быть достижим с origin (#413). + # + # SPEC-REVIEW-403-r2 объявил материал на `HEAD = 83005c3c`, и тот же SHA + # независимо назвал автор ТЗ в комментарии issue. Коммит существовал, но + # к моменту публикации был осиротевшим: ветку перебазировали за 15 минут + # ДО публикации документа, спец-коммит переехал в 94502d3d с тем же + # сообщением и тем же содержимым. Через раунд команда `git diff + # 83005c3c..HEAD` из §2.10 буквально не работала, и r3 восстанавливал + # реальный коммит по содержимому диффа руками. + # + # Проверка стоит ПОСЛЕ публикации намеренно. Артефакт ревью терялся здесь + # трижды (#171, #220), и «вердикт без документа» в этом репозитории + # дороже мёртвой ссылки: документ сначала спасается, потом судится. Шаг + # при этом идёт ДО «Переставить метку», поэтому инвариант «метка не + # сменилась = прогон упал» сохраняется. + # + # Достижимость считается от `refs/remotes/origin/*`, а не от локальных + # ссылок: осиротевший 83005c3c до сих пор лежит в клоне автора и + # достижим там из необновлённой локальной ветки. Читателю отчёта от этого + # пользы нет — он достанет только то, что есть на origin. + - name: "Материал раунда воспроизводим (#413)" + if: steps.rebase.outputs.conflict != 'true' + env: + NUM: ${{ github.event.issue.number }} + STAGE: ${{ needs.guard.outputs.stage }} + CYCLE: ${{ needs.guard.outputs.cycle }} + BRANCH: ${{ steps.branch.outputs.name }} + run: | + marker=CODE-REVIEW + if [ "$STAGE" = "spec" ]; then marker=SPEC-REVIEW; fi + doc="docs/reviews/${marker}-${NUM}-r${CYCLE}.md" + target="${BRANCH:-dev}" + git fetch -q origin "$target" + # Судится опубликованная версия, а не рабочая копия: именно её прочтёт + # следующий раунд. + git show "origin/$target:$doc" | node scripts/review-doc-guard.mjs --doc=- - name: Решение по вердикту id: decide if: steps.rebase.outputs.conflict != 'true' From 308aae1fc8d5d81d5f662a220335443303fd88c7 Mon Sep 17 00:00:00 2001 From: Matysh Date: Wed, 2 Sep 2026 08:01:41 +0300 Subject: [PATCH 2/2] ci: mirror the material anchors into main MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Конвейер исполняет process.yml из ветки по умолчанию, поэтому файл обязан совпадать в main и dev — это проверяет предполётный шаг. Здесь ровно тот же файл, что в dev, байт в байт. Issue: #416 User-Visible: no --- .github/workflows/process.yml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/.github/workflows/process.yml b/.github/workflows/process.yml index deb7e897..c317b76e 100644 --- a/.github/workflows/process.yml +++ b/.github/workflows/process.yml @@ -226,6 +226,20 @@ jobs: git checkout -q "origin/$branch" echo "материал ревью: ветка $branch, $(git rev-parse --short HEAD)" echo "name=$branch" >> "$GITHUB_OUTPUT" + # Якоря материала, устойчивые к ребейзу (#413, #414). SHA коммита + # ребейз меняет — содержимое нет: git адресует деревья и блобы их + # хешем. Снимаются здесь, где рабочая копия ЕЩЁ равна тому, что + # ревьюер прочтёт; в шаге публикации дерево уже сброшено на целевую + # ветку, и спрашивать его поздно. + echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT" + echo "tree=$(git rev-parse 'HEAD^{tree}')" >> "$GITHUB_OUTPUT" + # ТЗ задачи: блоб переживает и ребейз, и удаление ветки, пока текст + # где-нибудь достижим. Файлов может не быть (инфраструктурная + # задача) или быть несколько (разбитое ТЗ) — тогда список пуст либо + # длиннее одного. + specs=$(git ls-files -s -- "docs/specs/${NUM}-*.md" \ + | awk '{print $2" "$4}' | tr '\n' ';') + echo "specs=$specs" >> "$GITHUB_OUTPUT" else echo "::warning::ветка issue/${NUM}-* не найдена на origin — ревью пойдёт по dev" echo "МАТЕРИАЛ НЕ ЗАПУШЕН" >> "$GITHUB_STEP_SUMMARY" @@ -629,6 +643,9 @@ jobs: STAGE: ${{ needs.guard.outputs.stage }} CYCLE: ${{ needs.guard.outputs.cycle }} SOURCE: ${{ runner.temp }}/review-document.md + MATERIAL_SHA: ${{ steps.branch.outputs.sha }} + MATERIAL_TREE: ${{ steps.branch.outputs.tree }} + MATERIAL_SPECS: ${{ steps.branch.outputs.specs }} run: | # Ветки задачи может не быть: у задач, размеченных до появления # конвейера, ТЗ лежит прямо в dev. Раньше шаг в этом случае молча @@ -677,6 +694,14 @@ jobs: mkdir -p docs/reviews cp "$SOURCE" "$doc" echo "документ взят из $SOURCE ($(wc -c < "$doc") байт)" + # Якоря дописывает конвейер, а не ревьюер (#414). Дисциплина здесь + # уже подводила: на #403 SHA сняли до ребейза и не сверили перед + # выводом — через раунд команда из §2.10 не работала. Машина же + # снимает якоря в момент чтения материала и ошибиться в них не + # может; блок помечен как машинный, чтобы никто не правил его руками. + node scripts/review-doc-guard.mjs --anchor="$doc" \ + --sha="$MATERIAL_SHA" --tree="$MATERIAL_TREE" \ + --branch="${BRANCH:-dev}" --specs="$MATERIAL_SPECS" else echo "::warning::$SOURCE не найден — документа для публикации нет" fi