ревью: гейт, прогнанный до ревью, засчитывается по отпечатку дерева
Повтор той же команды на неизменившемся дереве снят: ступень автотестов засчитывает прогон, сделанный шагом opsx:apply или шагом гейта обслуживания. Признак — отпечаток рабочего дерева, снятый дважды; любое расхождение ведёт к честному прогону. Журнал — тема 73.
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: review-autotests
|
name: review-autotests
|
||||||
description: "Тема `autotests` — проверено ли машиной и хватает ли проверок. Запускает команду гейта проекта (сборка/vet/линт/формат/тесты/флаки/гонки/покрытие изменённых строк/миграции/секреты/уязвимости) и интерпретирует вывод. Отличает новые отказы от унаследованных, находит отсутствующую верификацию (изменённые строки без покрытия, конкурентность без теста, флаки). Пока гейт красный, проходы с мнением не запускаются. Первый проход ревью кода и источник его графа, обязателен при любой метке."
|
description: "Тема `autotests` — проверено ли машиной и хватает ли проверок. Гонит команду гейта проекта (сборка/vet/линт/формат/тесты/флаки/гонки/покрытие изменённых строк/миграции/секреты/уязвимости) и интерпретирует вывод; прогон, сделанный до ревью, засчитывает по отпечатку рабочего дерева вместо повтора. Отличает новые отказы от унаследованных, находит отсутствующую верификацию (изменённые строки без покрытия, конкурентность без теста, флаки). Пока гейт красный, проходы с мнением не запускаются. Первый проход ревью кода и источник его графа, обязателен при любой метке."
|
||||||
tools: Bash, Read, Grep, Glob
|
tools: Bash, Read, Grep, Glob
|
||||||
model: sonnet
|
model: sonnet
|
||||||
color: green
|
color: green
|
||||||
@@ -40,15 +40,42 @@ color: green
|
|||||||
`CLAUDE.md` не описана: состав шагов и их цена выведены из конфига, безусловные
|
`CLAUDE.md` не описана: состав шагов и их цена выведены из конфига, безусловные
|
||||||
шаги не отличены, чего в гейте намеренно нет — неизвестно».
|
шаги не отличены, чего в гейте намеренно нет — неизвестно».
|
||||||
|
|
||||||
|
## Прогнан ли гейт уже
|
||||||
|
|
||||||
|
**Задача приходит на ревью с зелёным гейтом:** сценарий, приведший её сюда,
|
||||||
|
довёл его до зелёного сам. Второй прогон на неизменившемся дереве вернёт тот же
|
||||||
|
вывод, а стоит он минут — правило и его причина в SKILL.md конвейера, ступень 1.
|
||||||
|
|
||||||
|
Задание несёт сводку прошлого прогона, путь к логам шагов и **отпечаток дерева**,
|
||||||
|
снятый сразу после него. Сними отпечаток сам и сверь:
|
||||||
|
|
||||||
|
<!-- копия: отпечаток-дерева из av-dev/skills/code-review/SKILL.md -->
|
||||||
|
|
||||||
|
```sh
|
||||||
|
{ git rev-parse HEAD; git status --porcelain -uall; git diff HEAD;
|
||||||
|
git ls-files -o --exclude-standard -z | xargs -0 -r git hash-object; } | sha1sum
|
||||||
|
```
|
||||||
|
|
||||||
|
<!-- /копия: отпечаток-дерева -->
|
||||||
|
|
||||||
|
**Совпал** — команду не запускай: читай готовую сводку и логи шагов, а тему
|
||||||
|
закрывай целиком, как обычно. **Разошёлся, отпечатка в задании нет, логи
|
||||||
|
недоступны** — гони гейт сам и ни у кого не спрашивай.
|
||||||
|
|
||||||
|
Переиспользованный прогон объявляется строкой сводки и строкой границ покрытия:
|
||||||
|
чем гейт прогнан, когда и на каком отпечатке.
|
||||||
|
|
||||||
## Что делаешь
|
## Что делаешь
|
||||||
|
|
||||||
1. Определи базу диффа: из задания, иначе `git merge-base HEAD <основная ветка>`
|
1. Определи базу диффа: из задания, иначе `git merge-base HEAD <основная ветка>`
|
||||||
(на основной ветке — `HEAD~1`).
|
(на основной ветке — `HEAD~1`).
|
||||||
2. Запусти команду гейта, передав ей базу. Она гонит все шаги до конца и печатает
|
2. Сверь отпечаток дерева — раздел «Прогнан ли гейт уже» выше. Совпал —
|
||||||
|
переходи к пункту 4 и работай по готовой сводке и логам.
|
||||||
|
3. Запусти команду гейта, передав ей базу. Она гонит все шаги до конца и печатает
|
||||||
сводку; подробности — в логах шагов.
|
сводку; подробности — в логах шагов.
|
||||||
3. По каждому отказу открой лог и прочитай **реальную** причину. Не пересказывай
|
4. По каждому отказу открой лог и прочитай **реальную** причину. Не пересказывай
|
||||||
строку «FAIL» — назови упавший тест, файл и утверждение.
|
строку «FAIL» — назови упавший тест, файл и утверждение.
|
||||||
4. **Отдели новое от унаследованного.** Если отказ выглядит не связанным с
|
5. **Отдели новое от унаследованного.** Если отказ выглядит не связанным с
|
||||||
диффом — переключись на базу в отдельном worktree
|
диффом — переключись на базу в отдельном worktree
|
||||||
(`git worktree add tmp/gate-base <база>`) и прогони там тот же шаг. Отказ,
|
(`git worktree add tmp/gate-base <база>`) и прогони там тот же шаг. Отказ,
|
||||||
воспроизводящийся на базе, — не блокер этого change: выводи его `minor` с
|
воспроизводящийся на базе, — не блокер этого change: выводи его `minor` с
|
||||||
@@ -117,10 +144,13 @@ color: green
|
|||||||
## Формат вывода
|
## Формат вывода
|
||||||
|
|
||||||
Сперва одной строкой: `ГЕЙТ: зелёный | красный` и таблица-сводка команды как
|
Сперва одной строкой: `ГЕЙТ: зелёный | красный` и таблица-сводка команды как
|
||||||
есть. Затем находки по контракту. В конце — обязательный блок:
|
есть. **Прогон переиспользован — скажи это той же строкой:** чем гейт прогнан,
|
||||||
|
когда и на каком отпечатке. Затем находки по контракту. В конце — обязательный
|
||||||
|
блок:
|
||||||
|
|
||||||
```
|
```
|
||||||
## Coverage of this pass
|
## Coverage of this pass
|
||||||
|
- гейт: <прогнан здесь | переиспользован: чем, когда, отпечаток>
|
||||||
- проверено: <перечисли выполненные команды>
|
- проверено: <перечисли выполненные команды>
|
||||||
- не проверялось и почему: <шаги SKIP с причинами; проверки вне гейта>
|
- не проверялось и почему: <шаги SKIP с причинами; проверки вне гейта>
|
||||||
- принципиально недоступно этому проходу: замысел, форма решения, архитектура
|
- принципиально недоступно этому проходу: замысел, форма решения, архитектура
|
||||||
|
|||||||
@@ -348,8 +348,12 @@ flowchart TD
|
|||||||
|
|
||||||
### Возврат — не длиннее экрана
|
### Возврат — не длиннее экрана
|
||||||
|
|
||||||
Агент возвращает: что сделано, **адресами** тронутого; исход гейта и чем он
|
Агент возвращает: что сделано, **адресами** тронутого; исход гейта, чем он
|
||||||
прогнан; что не удалось и почему; вопросы, если по заданию их не разрешить.
|
прогнан, где логи шагов и **отпечаток дерева сразу после прогона**; что не
|
||||||
|
удалось и почему; вопросы, если по заданию их не разрешить. Отпечаток нужен
|
||||||
|
ревью: по нему ступень автотестов засчитывает этот прогон вместо своего
|
||||||
|
(`av-dev:code-review`, ступень 1) — без него гейт гоняется дважды на том же
|
||||||
|
дереве.
|
||||||
Диффа, пересказа кода и логов в возврате нет — иначе экономия, ради которой шаг
|
Диффа, пересказа кода и логов в возврате нет — иначе экономия, ради которой шаг
|
||||||
и вынесен, отменяется в момент возврата.
|
и вынесен, отменяется в момент возврата.
|
||||||
|
|
||||||
|
|||||||
@@ -295,6 +295,10 @@ ADR: список источников канон закрыл двумя — а
|
|||||||
Прогони гейт и добейся зелёного — он же условие следующего шага: пока гейт
|
Прогони гейт и добейся зелёного — он же условие следующего шага: пока гейт
|
||||||
красный, проходы с мнением не запускаются.
|
красный, проходы с мнением не запускаются.
|
||||||
|
|
||||||
|
**Сразу после зелёного сними отпечаток дерева** (`av-dev:code-review`, ступень 1)
|
||||||
|
и сохрани его вместе со сводкой и путём к логам шагов. Шаг 4 передаёт их ревью, и
|
||||||
|
тогда ступень автотестов не гоняет тот же гейт второй раз.
|
||||||
|
|
||||||
**Поведенческая верификация здесь другая, чем в решении.** Проверяется не новое
|
**Поведенческая верификация здесь другая, чем в решении.** Проверяется не новое
|
||||||
поведение, а то, что прежнее не поехало: команда из `CLAUDE.md` поднимается, шаг
|
поведение, а то, что прежнее не поехало: команда из `CLAUDE.md` поднимается, шаг
|
||||||
сборки отрабатывает, хук ставится на чистом клоне. **У оснастки, заводимой
|
сборки отрабатывает, хук ставится на чистом клоне. **У оснастки, заводимой
|
||||||
@@ -304,8 +308,9 @@ ADR: список источников канон закрыл двумя — а
|
|||||||
|
|
||||||
### 4. Ревью — план фиксирован сценарием
|
### 4. Ревью — план фиксирован сценарием
|
||||||
|
|
||||||
Вызови Skill **`av-dev:code-review`**, дав базу диффа, режим и **план сценария**.
|
Вызови Skill **`av-dev:code-review`**, дав базу диффа, режим, **план сценария** и
|
||||||
Change ты не передаёшь — его нет.
|
**исход гейта с шага 3** — сводку, путь к логам шагов и отпечаток дерева. Change
|
||||||
|
ты не передаёшь — его нет.
|
||||||
|
|
||||||
**Разметчик здесь не зовётся, и это правило, а не пропуск.** Обе оси, по которым
|
**Разметчик здесь не зовётся, и это правило, а не пропуск.** Обе оси, по которым
|
||||||
он судит, у обслуживания не определены: размер он меряет по `proposal.md`,
|
он судит, у обслуживания не определены: размер он меряет по `proposal.md`,
|
||||||
|
|||||||
@@ -254,7 +254,9 @@ flowchart TD
|
|||||||
**Код пишет агент, и в его же задании лежит весь этот раздел** (SKILL.md, «Кто
|
**Код пишет агент, и в его же задании лежит весь этот раздел** (SKILL.md, «Кто
|
||||||
пишет»): вызов `opsx:apply` для реализации `tasks.md`, гейт до зелёного,
|
пишет»): вызов `opsx:apply` для реализации `tasks.md`, гейт до зелёного,
|
||||||
поведенческая верификация. Возврат — адреса тронутого, исход гейта и строка
|
поведенческая верификация. Возврат — адреса тронутого, исход гейта и строка
|
||||||
верификации; диффа в нём нет.
|
верификации; диффа в нём нет. **Исход гейта возвращается сводкой, путём к логам
|
||||||
|
шагов и отпечатком дерева** (SKILL.md, «Возврат — не длиннее экрана»): его
|
||||||
|
передача на шаг 7 избавляет ревью от второго прогона того же гейта.
|
||||||
|
|
||||||
Код — по конвенциям проекта
|
Код — по конвенциям проекта
|
||||||
(каталог `docs/conventions/`). Меняешь схему — обнови её описание в документации
|
(каталог `docs/conventions/`). Меняешь схему — обнови её описание в документации
|
||||||
@@ -273,7 +275,8 @@ flowchart TD
|
|||||||
### 7. Ревью кода — та же метка
|
### 7. Ревью кода — та же метка
|
||||||
|
|
||||||
Вызови Skill **`av-dev:code-review`**, дав ссылку на change `<id>`,
|
Вызови Skill **`av-dev:code-review`**, дав ссылку на change `<id>`,
|
||||||
базу диффа, **план разметки с шага 3** и режим запуска.
|
базу диффа, **план разметки с шага 3**, режим запуска и **исход гейта с шага 6** —
|
||||||
|
сводку, путь к логам шагов и отпечаток дерева.
|
||||||
|
|
||||||
**Метку ты не выбираешь, и это правило, а не упрощение.** Её назвал
|
**Метку ты не выбираешь, и это правило, а не упрощение.** Её назвал
|
||||||
`review-scope` ещё на шаге 3 — по размеру и сложности, с обоснованием по каждой
|
`review-scope` ещё на шаге 3 — по размеру и сложности, с обоснованием по каждой
|
||||||
|
|||||||
@@ -221,6 +221,10 @@ description: "Конвейер ревью изменения, устроенны
|
|||||||
- **метка, его глубина и режим** прогона — чтобы проход знал, что писать в
|
- **метка, его глубина и режим** прогона — чтобы проход знал, что писать в
|
||||||
границы покрытия.
|
границы покрытия.
|
||||||
|
|
||||||
|
**Ступень 1 получает сверх этого исход гейта, прогнанного до ревью** — сводку,
|
||||||
|
путь к логам шагов и отпечаток дерева, — если вызывающий скилл его дал. Зачем и
|
||||||
|
что происходит при расхождении — «Ступень 1 — Автотесты».
|
||||||
|
|
||||||
Чего проход **не** получает ни в каком режиме — выводов других проходов. См.
|
Чего проход **не** получает ни в каком режиме — выводов других проходов. См.
|
||||||
«Порядок прогона».
|
«Порядок прогона».
|
||||||
|
|
||||||
@@ -734,8 +738,9 @@ change**: у работы, не меняющей поведения, дельт
|
|||||||
|
|
||||||
## Ступень 1 — Автотесты (обязательна при любой метке)
|
## Ступень 1 — Автотесты (обязательна при любой метке)
|
||||||
|
|
||||||
Агент `review-autotests`, тема `autotests`. Запускает команду гейта из семантики
|
Агент `review-autotests`, тема `autotests`. Гонит команду гейта из семантики
|
||||||
гейта в `CLAUDE.md` и интерпретирует вывод.
|
гейта в `CLAUDE.md` — либо засчитывает прогон, сделанный до ревью, — и
|
||||||
|
интерпретирует вывод.
|
||||||
|
|
||||||
**Тема и проход названы одинаково намеренно, а «гейт» осталось именем команды.**
|
**Тема и проход названы одинаково намеренно, а «гейт» осталось именем команды.**
|
||||||
Раньше тема звалась `autotests`, а проход — `gate`: одна сущность под двумя
|
Раньше тема звалась `autotests`, а проход — `gate`: одна сущность под двумя
|
||||||
@@ -743,6 +748,42 @@ change**: у работы, не меняющей поведения, дельт
|
|||||||
Слово «гейт» теперь значит ровно одно — барьер, который проект запускает; тема
|
Слово «гейт» теперь значит ровно одно — барьер, который проект запускает; тема
|
||||||
шире него ровно на «чего в гейте намеренно нет».
|
шире него ровно на «чего в гейте намеренно нет».
|
||||||
|
|
||||||
|
**Гейт, прогнанный до ревью, второй раз не гоняется.** Задача приходит на ревью
|
||||||
|
с зелёным гейтом: сценарий решения доводит его до зелёного шагом `opsx:apply`,
|
||||||
|
сценарий обслуживания — своим шагом гейта. Повтор на неизменившемся дереве
|
||||||
|
вернёт тот же вывод, а стоит он минут — то есть платит ими ни за что.
|
||||||
|
|
||||||
|
**Признак один и проверяемый — отпечаток рабочего дерева.** Его снимают дважды:
|
||||||
|
тот, кто прогнал гейт, сразу после прогона, и проход перед началом работы.
|
||||||
|
|
||||||
|
<!-- дом: отпечаток-дерева -->
|
||||||
|
|
||||||
|
```sh
|
||||||
|
{ git rev-parse HEAD; git status --porcelain -uall; git diff HEAD;
|
||||||
|
git ls-files -o --exclude-standard -z | xargs -0 -r git hash-object; } | sha1sum
|
||||||
|
```
|
||||||
|
|
||||||
|
<!-- /дом: отпечаток-дерева -->
|
||||||
|
|
||||||
|
Сводку прошлого прогона, путь к логам шагов и отпечаток проход получает
|
||||||
|
**заданием** — их передаёт вызывающий скилл. Отпечатки совпали — проход читает
|
||||||
|
готовую сводку и логи, команду не запускает. Разошлись, отпечатка в задании нет,
|
||||||
|
логи недоступны — проход гонит гейт сам и ни у кого не спрашивает.
|
||||||
|
|
||||||
|
**Отказ здесь безопасен по построению.** Лишний прогон стоит минут, а
|
||||||
|
засчитанный чужой — красноты, которой никто не увидел. Временный каталог проекта
|
||||||
|
из отпечатка выпадает сам: `--exclude-standard` отбрасывает игнорируемое, а логи
|
||||||
|
шагов гейт пишет именно туда. У проекта, держащего временный каталог под git,
|
||||||
|
отпечатки не совпадут никогда — и он получит честный прогон вместо тихого
|
||||||
|
засчитывания.
|
||||||
|
|
||||||
|
**Переиспользуется команда, а не проход.** Тема `autotests` закрывается целиком:
|
||||||
|
логи проход читает сам, находки об отсутствующей верификации выдаёт как обычно.
|
||||||
|
Переиспользование он объявляет строкой сводки и строкой границ покрытия — чем
|
||||||
|
гейт прогнан, когда и на каком отпечатке. Молчащее переиспользование неотличимо
|
||||||
|
от собственного прогона, а разница между ними в том, кто видел вывод своими
|
||||||
|
глазами.
|
||||||
|
|
||||||
**Пока гейт красный — проходы с мнением не запускаются.** Оркестратор чинит и
|
**Пока гейт красный — проходы с мнением не запускаются.** Оркестратор чинит и
|
||||||
перезапускает гейт. Исключение одно: отказ, унаследованный от базовой ветки
|
перезапускает гейт. Исключение одно: отказ, унаследованный от базовой ветки
|
||||||
(гейт проверяет это прогоном на базе) — тогда он фиксируется находкой и не
|
(гейт проверяет это прогоном на базе) — тогда он фиксируется находкой и не
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
# 73. Гейт, прогнанный до ревью, не гоняется второй раз (2026-08-23)
|
||||||
|
|
||||||
|
## Что было
|
||||||
|
|
||||||
|
Гейт проекта прогонялся дважды на одном и том же дереве. Сценарий решения
|
||||||
|
доводит его до зелёного шагом `opsx:apply`, сценарий обслуживания — своим шагом
|
||||||
|
гейта; следом ступень автотестов конвейера запускала ту же команду заново.
|
||||||
|
|
||||||
|
Замер по прогонам в проекте transcriber 23 августа: ступень автотестов заняла 5
|
||||||
|
минут 51 секунду при ревью кода в 62 минуты. Дерево между двумя прогонами не
|
||||||
|
менялось ни разу — код писал агент, возвращал зелёный гейт, и до ревью его никто
|
||||||
|
не трогал.
|
||||||
|
|
||||||
|
Повтор держался не на доводе, а на умолчании: проход запускает команду, потому
|
||||||
|
что так написано в его уставе, и никто не спрашивал, откуда взялось дерево, на
|
||||||
|
котором он её запускает.
|
||||||
|
|
||||||
|
## Решено
|
||||||
|
|
||||||
|
**Р274. Гейт, прогнанный до ревью, второй раз не гоняется.** Переиспользуется
|
||||||
|
**команда, а не проход**: тема `autotests` закрывается целиком — логи шагов
|
||||||
|
проход читает сам, находки об отсутствующей верификации выдаёт как обычно. Дом
|
||||||
|
правила — ступень 1 конвейера, `av-dev:code-review`.
|
||||||
|
|
||||||
|
**Р275. Признак — отпечаток рабочего дерева, и он проверяемый.** Отпечаток
|
||||||
|
снимают дважды: тот, кто прогнал гейт, — сразу после прогона, проход — перед
|
||||||
|
началом работы. Совпали — прогон засчитан, разошлись — проход гонит гейт сам.
|
||||||
|
Команда живёт домом в ступени 1 и помеченной копией в уставе `review-autotests`;
|
||||||
|
дословность держит `copies.py`, а не обещание.
|
||||||
|
|
||||||
|
**Р276. Это не отмена правила «возврату на слово не верят»
|
||||||
|
([тема 72](72-writing-delegated-to-agents.md), Р270), и вот почему.** На слово
|
||||||
|
здесь не верят никому: гейт — вывод инструмента, а не проза агента, логи шагов
|
||||||
|
проход открывает сам, и отпечаток отвечает ровно на тот вопрос, который делает
|
||||||
|
чужой прогон доказательством, — **относится ли этот вывод к этому дереву**.
|
||||||
|
Правило Р270 запрещает верить пересказу; засчитывается же не пересказ, а
|
||||||
|
воспроизводимый артефакт с проверенной привязкой.
|
||||||
|
|
||||||
|
**Р277. Отказ безопасен по построению.** Любое расхождение — отпечатки не
|
||||||
|
совпали, отпечатка в задании нет, логи недоступны — ведёт к полному прогону, и
|
||||||
|
проход не спрашивает разрешения. Лишний прогон стоит минут, засчитанный чужой —
|
||||||
|
красноты, которой никто не увидел; асимметрия цены и задаёт направление отказа.
|
||||||
|
|
||||||
|
**Р278. Временный каталог из отпечатка выпадает сам.** `--exclude-standard`
|
||||||
|
отбрасывает игнорируемое, а логи шагов гейт пишет туда же. Проект, держащий
|
||||||
|
временный каталог под git, отпечатками не совпадёт никогда — и получит честный
|
||||||
|
прогон вместо тихого засчитывания. Настройки у этого нет намеренно: выключатель
|
||||||
|
здесь стал бы способом засчитать прогон, который засчитывать нельзя.
|
||||||
|
|
||||||
|
**Р279. Переиспользование объявляется строкой** — в сводке прохода и в границах
|
||||||
|
покрытия: чем гейт прогнан, когда и на каком отпечатке. Молчащее
|
||||||
|
переиспользование неотличимо от собственного прогона, а разница между ними в том,
|
||||||
|
кто видел вывод своими глазами.
|
||||||
|
|
||||||
|
## Следствия
|
||||||
|
|
||||||
|
**С254. Возврат агента письма пополнился отпечатком.** К исходу гейта и тому, чем
|
||||||
|
он прогнан ([тема 72](72-writing-delegated-to-agents.md), Р269), добавились путь к
|
||||||
|
логам шагов и отпечаток дерева. Возврат от этого не удлинился на экран: обе вещи
|
||||||
|
— строки, а не содержимое.
|
||||||
|
|
||||||
|
**С255. Оркестратор передаёт исход гейта в ревью.** Шаг 7 решения и шаг 4
|
||||||
|
обслуживания несут его вместе с планом, базой диффа и режимом. Не передал —
|
||||||
|
конвейер прогонит гейт сам, и это стоит минут, а не корректности.
|
||||||
|
|
||||||
|
**С256. Экономия названа замером, а не обещанием.** Снятый повтор — минуты одного
|
||||||
|
прогона из шестидесяти двух; остальное время ревью держат проход враждебных
|
||||||
|
постановок, цепочка за машину и триаж, и они разбираются отдельно.
|
||||||
@@ -124,3 +124,4 @@
|
|||||||
| 70 | [«Провенанс» снят из словаря: одного синонима мало для проверки](70-provenance-word-removed.md) | 2026-08-13 |
|
| 70 | [«Провенанс» снят из словаря: одного синонима мало для проверки](70-provenance-word-removed.md) | 2026-08-13 |
|
||||||
| 71 | [Образец языка назван прямо; «интейк» снят вслед за «провенансом»](71-language-model-popular-science.md) | 2026-08-13 |
|
| 71 | [Образец языка назван прямо; «интейк» снят вслед за «провенансом»](71-language-model-popular-science.md) | 2026-08-13 |
|
||||||
| 72 | [Письмо уходит агентам: оркестратор ставит задание и читает возврат](72-writing-delegated-to-agents.md) | 2026-08-22 |
|
| 72 | [Письмо уходит агентам: оркестратор ставит задание и читает возврат](72-writing-delegated-to-agents.md) | 2026-08-22 |
|
||||||
|
| 73 | [Гейт, прогнанный до ревью, не гоняется второй раз](73-gate-run-reused-by-fingerprint.md) | 2026-08-23 |
|
||||||
|
|||||||
Reference in New Issue
Block a user