fix: не считать описание чужого раунда объявлением вердикта

User-Visible: no
Issue: #454
This commit is contained in:
Codex
2026-09-04 16:58:31 +00:00
committed by claude[bot]
parent dc76c5b2b8
commit 4b443d8cf5
3 changed files with 47 additions and 6 deletions
+16 -4
View File
@@ -98,8 +98,8 @@ jobs:
# Вердикты считаются ТОЛЬКО своего этапа: иначе вердикт по ТЗ съедал
# цикл из бюджета код-ревью (#89 получило r2/4). Этап опознаётся по
# имени документа в теле комментария; документа нет — вердикт не
# посчитается. Недосчёт даёт лишний заход, перерасчёт остановил бы
# работу досрочно: из двух ошибок выбрана обратимая.
# посчитается. Недосчёт считался обратимой ошибкой — «даёт лишний
# заход» — и в этой оценке была ошибка, см. ниже.
#
# Счёт по комментариям остаётся ровно тем же, но он БОЛЬШЕ НЕ
# ЕДИНСТВЕННЫЙ (#454). Маркер этапа попадает в тело комментария,
@@ -156,8 +156,20 @@ jobs:
counters=$(node scripts/review-doc-guard.mjs --counters \
--marker="$marker" --num="$NUM" --names="$names" --docs="$docs" \
--comment-attempt="$attempt" --comment-spent="$spent")
attempt=$(printf '%s\n' "$counters" | sed -n 's/^attempt=//p')
spent=$(printf '%s\n' "$counters" | sed -n 's/^spent=//p')
# Пустой ответ означает, что скрипт не отработал. Тогда действуют
# прежние значения: guard обязан продолжить работу, а не встать —
# худшее, что даёт откат к прозе, это сегодняшнее поведение.
new_attempt=$(printf '%s\n' "$counters" | sed -n 's/^attempt=//p')
new_spent=$(printf '%s\n' "$counters" | sed -n 's/^spent=//p')
new_blocking=$(printf '%s\n' "$counters" | sed -n 's/^blocking=//p')
case "$new_attempt" in ''|*[!0-9]*) echo "::warning::счёт по файлам не дал числа — остаётся счёт по комментариям" ;; *) attempt="$new_attempt" ;; esac
case "$new_spent" in ''|*[!0-9]*) : ;; *) spent="$new_spent" ;; esac
# Перечень учтённого обязан сходиться с числом: если цикл виден
# только документом, ссылка на комментарий его не объяснит.
if [ -n "$new_blocking" ]; then
spent_list="$spent_list
- документы: $new_blocking"
fi
echo "ветка материала: ${branch:-нет, читался dev}"
fi
+15 -2
View File
@@ -372,8 +372,18 @@ export function attemptFromRounds(rounds) {
return known.length ? Math.max(...known) + 1 : 1;
}
/** Строка, ОБЪЯВЛЯЮЩАЯ вердикт (а не упоминающая слово). */
const VERDICT_DECLARATION = /^[>\s]*(?:[-*+]\s*)?\*{0,2}Вердикт[:*\s]/;
/**
* Строка, ОБЪЯВЛЯЮЩАЯ вердикт (а не упоминающая слово).
*
* Двоеточие обязано стоять сразу за словом. Проверено на корпусе:
* `CODE-REVIEW-441-r1.md` (в нём, кстати, тоже лежит документ ВТОРОГО раунда —
* та же коллизия, что чинится) описывает предыдущий раунд строкой
* «- Вердикт r1: жёлтый, High 0…». Правило, допускавшее слово между «Вердикт»
* и двоеточием, засчитывало эту строку объявлением и добавляло зелёному
* раунду блокирующий цикл. Ошибка необратимая: лишний цикл останавливает
* работу по §4.
*/
const VERDICT_DECLARATION = /^[>\s]*(?:[-*+]\s*)?\*{0,2}Вердикт\*{0,2}\s*:/;
/** Заголовок раздела вердикта: «## Вердикт», «## 6. Вердикт». */
// `\b` здесь не работает: он ASCII-словесный, а «т» кириллическая, и граница
// после неё не находится. Хвост ограничивается явно.
@@ -521,6 +531,9 @@ if (invokedDirectly) {
+ ` циклы: файлы ${counters.spentFiles}, комментарии ${counters.spentComments}`);
console.log(`attempt=${counters.attempt}`);
console.log(`spent=${counters.spent}`);
// Перечень учтённого по файлам: без него владелец видит число, но не может
// сверить, из чего оно сложилось, когда комментарии цикла не показывают.
console.log(`blocking=${counters.blocking.join(', ')}`);
process.exit(0);
}
+16
View File
@@ -461,3 +461,19 @@ test('guard считает раунды скриптом, а не inline-shell (
// Скрипт лежит в репозитории, значит guard обязан его выкачать.
assert.match(workflow.slice(0, workflow.indexOf(' - id: decide')), /actions\/checkout/);
});
test('описание чужого раунда не объявляет вердикт (#454, находка на корпусе)', () => {
// CODE-REVIEW-441-r1.md: документ зелёного второго раунда описывает жёлтый
// ПЕРВЫЙ раунд строкой-буллитом. Правило, допускавшее слово между «Вердикт»
// и двоеточием, добавляло зелёному раунду блокирующий цикл.
assert.equal(
verdictDeclaration('- Вердикт r1: жёлтый, High 0, Medium 1 в скоупе — красный `check-docs.mjs`'),
null,
);
assert.equal(verdictDeclaration('- Вердикт предыдущего раунда: красный'), null);
// Штатные формы объявления при этом обязаны читаться по-прежнему.
assert.ok(verdictDeclaration('Вердикт: жёлтый · заход r1'));
assert.ok(verdictDeclaration('**Вердикт: красный** · заход r2'));
assert.ok(verdictDeclaration('- **Вердикт:** жёлтый'));
assert.ok(verdictDeclaration('- Вердикт: зелёный'));
});