fix(process): prepare and the usage step run pipeline scripts from one pinned dev snapshot (#765)

The body of _process.yml is read from dev (@dev, #623). After "Перейти на
ветку задачи" the working copy of job prepare is the task branch, and a
show/ship branch with a clean merge is not rebased before review: its
scripts/ may lag dev or be replaced. #749 fixed job integrate; prepare
still ran four control scripts from the material — the issue-body digest
for the anchor, --reuse of a green verdict (#499), validate-gate (#510)
and the spec-change check (#517). The material decided its own admission:
a branch whose review-doc-guard.mjs prints reuse=true merges without the
model. model_review took model-usage.mjs from the material too: a lagging
branch has none, and the publication silently wrote reason=missing.

Now prepare extracts one snapshot right after setup-node, before the
branch switch: `git rev-parse origin/dev` once and `git archive <sha>
scripts .github/workflows/validate.yml`, so the git fetch of the track and
rebase steps cannot mix versions. Every repo script of the job runs from
it via TOOLS — the track step and the rebase guard lose their own
extractions. The SHA goes out as job output tools_sha; the usage step of
model_review archives the same commit inside itself, so a snapshot failure
is a failure of the reporting step (continue-on-error), not of the stage.
The model session runs on that runner, so the usage line stays untrusted
input parsed strictly (#556, #737).

withMaterialAnchors is idempotent: a repeated call drops the separator the
previous call wrote instead of piling up `---` lines.

Tests: test/process-prepare-tools.test.mjs — the job contract (no step
calls scripts/ from the working copy, one pinned archive before the branch
switch, tools_sha reaches model_review) and the steps as they are, on real
bash and git: a branch behind dev without model-usage.mjs and with a
substituted review-doc-guard.mjs; the anchor digest, reuse and the spec
check come from dev, the usage line is data even after dev moved. Red on
the old workflow: all four. Harnesses of process-track, rebase-generated,
review-doc-guard and model-usage take the prepare snapshot. Three registry
mutants (reuse from the material, usage from moving dev, separators).

Canon: PROCESS.md §10.4 «Скрипты конвейера — из dev» covers prepare and
the usage step; «Расход модели» names it a pipeline step, not the reviewer's.

Issue: #765
User-Visible: no
This commit is contained in:
claude[bot]
2026-10-02 10:18:29 +03:00
parent ceee249210
commit 23f822475b
10 changed files with 462 additions and 49 deletions
+18 -6
View File
@@ -1327,16 +1327,27 @@ Validate запускает `ship-review.yml` dispatch'ем с `-f tag=nightly`
origin/dev scripts .github/workflows/validate.yml` и зовёт из него каждый
repo-скрипт: `review-result-gate`, `review-doc-guard`, `reviews-index`,
`merge-candidate`, `process-track route`, `status-label`. Снимок один на job —
одна версия на весь заход, а не смесь по шагам. Так же уже работают шаг трека
(#707), страж ребейза (#698) и разбор отказа push (#723). Рабочая копия ветки
одна версия на весь заход, а не смесь по шагам. Job `prepare` (#765) делает
то же до перехода на ветку задачи, но архивом **закреплённого** SHA
(`git rev-parse origin/dev` один раз — `git fetch` трека и ребейза его не
сдвигают): из снимка идут шаг трека (#707), страж ребейза (#698) с разбором
отказа push (#723), хеш тела issue для якоря, reuse зелёного
вердикта (#499), Validate-гейт (#510) и проверка изменившегося ТЗ (#517).
Прежде четыре последних исполняли скрипты ветки — материал сам решал, допущен
ли он к ревью: подменённый `review-doc-guard.mjs` выдавал себе `reuse=true`.
SHA уходит выходом `tools_sha` в `model_review`. Рабочая копия ветки
задачи — только материал: git-команды, документ и пути судятся в ней. Ветка
`show`/`ship` с чистым слиянием до ревью не ребейзится и может нести
`scripts/`, отставшие от `dev` на дни: её `review-doc-guard.mjs` молча
проглотил бы неизвестный флаг, её `merge-candidate.mjs` слил бы по-старому.
`validate.yml` в снимке обязателен: `workflow-jobs.mjs` читает его по пути от
себя, без него доказательство Validate кандидата — `failed (#622)`. Исключение
— job `model_review`: ревьюер исполняет тесты и скрипты материала, это его
работа, а не конвейера. Следствие для задач, меняющих сам конвейер: их
себя, без него доказательство Validate кандидата — `failed (#622)`. В job
`model_review` скрипты материала исполняет ревьюер — это его работа, а не
конвейера; детерминированный шаг «Снять расход модели» (#737) — работа
конвейера, и `model-usage.mjs` он берёт архивом `tools_sha` (#765): на
отставшей ветке скрипта нет, и публикация молча писала `reason=missing`. От
сессии модели на том же раннере снимок не защищает, поэтому строка расхода
по-прежнему недоверенный ввод и разбирается строго. Следствие для задач, меняющих сам конвейер: их
слияние судит версия `dev`, новая начинает действовать со следующей задачи.
Правка контракта доказательства Validate (имена потребляемых job
`validate.yml`, политики `ci-proof.mjs`, #622) пишется совместимой с
@@ -1470,7 +1481,8 @@ merge. Timeout/cancel/failure называет конкретную стадию
**Расход модели** (#737). Шаг `Review` отдаёт `execution_file` — все сообщения
сессии SDK, а журнал Actions расход намеренно прячет. Шаг «Снять расход модели»
сразу после `Review` (`if: always()`, `continue-on-error`) берёт из файла только
сразу после `Review` (`if: always()`, `continue-on-error`) — шаг конвейера, а не
ревьюера, со скриптом из снимка `dev` (#765) — берёт из файла только
последнее сообщение `result` (`scripts/model-usage.mjs`) и отдаёт одну строку
выходом job `usage`: `<!-- hp:usage input_tokens=N output_tokens=N
cache_creation_input_tokens=N cache_read_input_tokens=N num_turns=N -->` — суммы