From 23f48829c26deec968af6645301900880899139e Mon Sep 17 00:00:00 2001 From: Sergey Matyunin Date: Sun, 13 Sep 2026 14:11:42 +0300 Subject: [PATCH] =?UTF-8?q?fix:=20=D1=81=D0=BE=D1=85=D1=80=D0=B0=D0=BD?= =?UTF-8?q?=D0=B8=D1=82=D1=8C=20=D0=BE=D0=B1=D1=8A=D0=B5=D0=BA=D1=82=20?= =?UTF-8?q?=D0=B2=D0=B5=D1=80=D0=B4=D0=B8=D0=BA=D1=82=D0=B0=20=D0=BC=D0=B5?= =?UTF-8?q?=D0=B6=D0=B4=D1=83=20=D1=81=D1=82=D0=B0=D0=B4=D0=B8=D1=8F=D0=BC?= =?UTF-8?q?=D0=B8=20=D1=80=D0=B5=D0=B2=D1=8C=D1=8E=20(#551)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Issue: #551 User-Visible: no --- .github/workflows/process.yml | 5 +++-- scripts/mutation-gate.mjs | 11 +++++++++++ test/review-doc-guard.test.mjs | 11 +++++++++++ 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/.github/workflows/process.yml b/.github/workflows/process.yml index 37f550cc..3be57742 100644 --- a/.github/workflows/process.yml +++ b/.github/workflows/process.yml @@ -1082,10 +1082,11 @@ jobs: legacy="docs/reviews/${marker}-${NUM}-r${CYCLE}.md" if [ ! -f "$SOURCE" ] && [ -f "$legacy" ]; then cp "$legacy" "$SOURCE"; fi test -s "$SOURCE" || { echo "::error::модель не оставила документ ревью"; exit 1; } - printf '%s' "$OUT" | jq -e ' + printf '%s' "$OUT" > "$RUNNER_TEMP/verdict.json" + jq -e ' (.verdict == "green" or .verdict == "yellow" or .verdict == "red") and (.high | type == "number") and (.medium | type == "number") - and (.summary | type == "string")' > "$RUNNER_TEMP/verdict.json" + and (.summary | type == "string")' "$RUNNER_TEMP/verdict.json" >/dev/null dir="$RUNNER_TEMP/review-result" mkdir -p "$dir" cp "$RUNNER_TEMP/review-prepared/prepared.json" "$dir/prepared.json" diff --git a/scripts/mutation-gate.mjs b/scripts/mutation-gate.mjs index d397fb7d..d37126dd 100644 --- a/scripts/mutation-gate.mjs +++ b/scripts/mutation-gate.mjs @@ -7858,6 +7858,17 @@ const MUTANT_DEFINITIONS = [ replace: ' ref: dev # mutant: moving material', }], }, + { + id: 'review-model-seals-verdict-as-boolean', + guard: 'node --test --test-name-pattern="#551" test/review-doc-guard.test.mjs', + because: '#551: результат JSON-schema review должен дойти до интеграции объектом; ' + + 'перенаправление stdout предиката jq записывает boolean true и роняет публикацию', + patches: [{ + file: '.github/workflows/process.yml', + find: ' printf \'%s\' "$OUT" > "$RUNNER_TEMP/verdict.json"', + replace: ' printf \'%s\' "$OUT" | jq -e \'true\' > "$RUNNER_TEMP/verdict.json" # mutant: boolean payload', + }], + }, { id: 'review-integration-skips-evidence-checksum', guard: 'node --test --test-name-pattern="#551" test/review-doc-guard.test.mjs', diff --git a/test/review-doc-guard.test.mjs b/test/review-doc-guard.test.mjs index 94401309..231fc1d4 100644 --- a/test/review-doc-guard.test.mjs +++ b/test/review-doc-guard.test.mjs @@ -768,6 +768,17 @@ test('#551: gates, модель и интеграция имеют незави assert.doesNotMatch(model, /validate-gate\.mjs/, 'ожидания Validate нет в бюджете модели'); assert.match(model, /needs: \[guard, prepare\]/); assert.match(integrate, /needs: \[guard, prepare, model_review\]/); + const seal = model.slice( + model.indexOf('- name: Запечатать результат модели'), + model.indexOf('- name: Передать результат интеграции'), + ); + assert.match(seal, /printf '%s' "\$OUT" > "\$RUNNER_TEMP\/verdict\.json"/, + 'structured verdict сохраняется как объект до проверки'); + assert.match(seal, + /and \(\.summary \| type == "string"\)' "\$RUNNER_TEMP\/verdict\.json" >\/dev\/null/, + 'jq валидирует сохранённый объект, не записывает boolean предиката вместо него'); + assert.doesNotMatch(seal, /jq -e '[\s\S]*> "\$RUNNER_TEMP\/verdict\.json"/, + 'stdout предиката jq не становится payload интеграции'); assert.match(model, /ref: \$\{\{ needs\.prepare\.outputs\.material_sha \}\}/, 'модель получает exact material, а не подвижную ветку');