feat(ci): контракт описывает правила своей эволюции - #1395
Merged
Conversation
Правило 113 каталога: перечислить поля мало — потребитель обязан знать, что в контракте стабильно, что расширяемо и как в него добавляют новое. Написано это было ровно в одном контракте из четырёх («Ожидания стабильности» в result-contract.md), поэтому остальные разошлись бы с ним незаметно: раздела нет — и претензии предъявить не к чему. Разделы дописаны в `api.md`, `web-contracts.md` и `usage-export.md`, а держит их `scripts/check_contract_evolution.py`: список контрактов закрыт, а раздел обязан отвечать на все три вопроса — заголовок без текста считается невыполнением, потому что по форме он выглядит выполнением. Заодно `gh_rest.py edit-pr`: тело PR веток `agent/**` берётся из сообщения коммита один раз, при открытии, и `Closes #N`, ставший известным позже, до сих пор оставалось только советовать проставить руками — операция же ровно на один REST-запрос (правило 111: инструмент, который может сделать сам, делает). Гейт печатает в узкой консоли Windows (issue #1095), и тест это проверяет запуском под `PYTHONIOENCODING=cp1252`. Охват самого приёма держится ручным списком и потому дырявый — вынесено в #1394, потому что чинится в 42 файлах сразу. Часть #1393 — правило 113 и инструмент для связи PR с задачей. Без issue: закрывающего PR у зонтичной задачи нет — #1393 остаётся открытой, пока метрика «не обеспечено ничем» уменьшается. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Co-Authored-By: Artem Markitanov <86671904+ArtVsMark@users.noreply.github.com>
ArtVsMark
enabled auto-merge (squash)
August 28, 2026 13:26
ArtVsMark
added a commit
that referenced
this pull request
Aug 28, 2026
…1397) * fix(ci): охват защиты кодировки задаётся признаком, а не списком Приём против `UnicodeEncodeError` в консоли cp1251/cp866 существует с #1095, и охват его сторожил guard — но список охраняемых файлов вёлся руками и не рос вместе с каталогом. Новый гейт в него никто не вписал, и он покраснел сразу на трёх джобах Windows, показав вместо результата проверки её падение. Это четвёртый повтор одного дефекта (#64, #1095, #1108, теперь #1392). Поэтому охват больше не хранится, а вычисляется: скрипт, который запускается процессом и несёт не-ASCII в строковых литералах, обязан иметь защиту. Ручные списки остались объявлением автономности, а не источником охвата. По этому признаку нашлись 41 скрипт без защиты — все починены тем же приёмом. Второй тест держит сам признак: правило, которое никого не находит, зелено по недосмотру. Closes #1394 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Co-Authored-By: Artem Markitanov <86671904+ArtVsMark@users.noreply.github.com> * feat(ci): у отмены по префиксу ветки появился адресат Правило 147 каталога. PR открывает `agent-pr.yml` и только для веток `agent/**` — ветка с другим именем не получает PR **вовсе**, и это не отказ: прогона нет, значит нет ни красного, ни лога, ни кода возврата. Успешный `push` выглядит одинаково в обоих случаях, потому что он и есть одинаковый: толкнувший видит успех своей команды и уходит, считая работу сданной. Лекарство поэтому не может жить внутри переключателя — он не запускался. Заметить обязан третий, и им работает ночной обход: `check_orphan_branches.py` называет ветки без открытого PR старше шести часов. Отсрочка обязательна — конвейер ходит по расписанию, и только что запушенная ветка законно ещё без PR; граница закреплена тестом с обеих сторон. Правильный префикс от проверки не освобождает: workflow мог не сработать и на `agent/**`, а наблюдаемый исход у обоих случаев один — ничего. Часть #1393 — правило 147. Без issue: закрывающего PR у зонтичной задачи нет — #1393 остаётся открытой, пока метрика «не обеспечено ничем» уменьшается. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Co-Authored-By: Artem Markitanov <86671904+ArtVsMark@users.noreply.github.com> * fix: разрешить конфликт реестра исполнителей гардов Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Co-Authored-By: Artem Markitanov <86671904+ArtVsMark@users.noreply.github.com> * docs(rules): ответ каталогу перепроверен по следам — 31 правило держится решением, а не ничем Метрика «не обеспечено ничем» показывала 37 правил и была неверна: почти все эти правила **родились из этого проекта**, и решение по ним записано — просто ответ каталогу об этом не знал. Перепроверка шла по следу каждого правила, а не по памяти: у каждой строки ниже названо место, где решение живёт. Что нашлось: срок жизни окна и метка окружения в имени — CLAUDE.md § Два окна; размер волны, разбор после неё, запреты в промпте, лимиты в схеме ответа и рестарт дельты — docs/agent/multiagent.md; критерий приёмки роли, обязанность роли запустить продукт и вход критика метода — docs/agent/roles.md; фазы аудита и статус гипотезы у находки с фикстуры — docs/agent/environments.md; маршрут при исчерпании квоты — docs/agent/preflight.md; сбор отдельно от разбора и внешний судья вердикта — docs/agent/course-walkthrough.md; связки карточек и разрешение конфликта разделов по последствию — docs/dev/glossary.md; полу-откат как доказательство красноты — CONTRIBUTING.md; решение не давать огласку до настоящего прогона курса — HISTORY.md. Правило 063 (автовмешательство включается по всем условиям сразу) переведено в gate: конъюнкцию трёх предохранителей `rerun_flaky_checks.py` держат тесты. Осталось шесть, и это честный остаток: 111, 137, 146, 150, 152 — механизма нет вовсе, 113 закрывается отдельным PR. Часть #1393 — сверка ответа каталогу. Без issue: закрывающего PR у зонтичной задачи нет — #1393 остаётся открытой, пока метрика «не обеспечено ничем» уменьшается. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Co-Authored-By: Artem Markitanov <86671904+ArtVsMark@users.noreply.github.com> * feat(ci): последние правила на словах получили опору Остаток после сверки ответа каталогу — пять правил, и они разошлись на две части по одному признаку: ловится ли нарушение механизмом. Механизмом закрыты два: - **152** — закрепление вызываемого согласовано с событием прогона. На `pull_request` файл прогона берётся из самого изменения, и закреплять вызываемый скрипт на общей ветке бессмысленно: заперта дверь в открытой стене. На `workflow_run` и `pull_request_target` рассуждение переворачивается, и чекаут кода из изменения отдаёт правящему PR и токен, и исполнение. Обе стороны проверяет `check_workflow_guardrails.py`. - **137** — сторож смотрит на сырое значение. `f"{value}"` от `None` это `"None"`, то есть форматирование уничтожает ровно тот признак, по которому сторож узнаёт молчание источника. Условие целиком над форматированием — находка; форматирование внутри более сложного выражения (`if str(x) in known`) — нет: там проверяют принадлежность. Живёт рядом с правилом 122, потому что предмет один — сырое значение. Записаны решением три: 111 (может сделать сам — делает, а не советует; прецедент — `gh_rest.py edit-pr` вместо совета проставить `Closes` руками), 146 (зелёный гейт подтверждает себя, а не утверждение; утверждение проверяется замером на живом предмете, и замер пишется рядом с механизмом) и 150 (тест проверки спрашивает её вердикт, а не пересказывает условие). Последние два намеренно без гейта, и это проверено, а не предположено: признак «приватная константа механизма в утверждении» даёт 66 попаданий по набору при одном настоящем инциденте — там, где предметом теста и является сама константа, он законен. Механизм, который не отличает нарушение от нормы, — не механизм. Метрика «не обеспечено ничем»: 1 из 152, и это правило 113, закрываемое PR #1395. Часть #1393 — остаток правил без механизма. Без issue: закрывающего PR у зонтичной задачи нет — #1393 остаётся открытой, пока метрика «не обеспечено ничем» уменьшается. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Co-Authored-By: Artem Markitanov <86671904+ArtVsMark@users.noreply.github.com> * feat(ci): правило без механизма обязано быть записано документом Метрика «не обеспечено ничем» до сих пор только печаталась: файл говорил, что она должна уменьшаться, но ничто этого не требовало — «принято на словах» и «закреплено» выглядели в ответе каталогу одинаково, и разошлись бы молча. Теперь остаток держит храповик `UNHELD_BUDGET`: правило либо закрыто гейтом, либо записано решением в документ (`process-step` с указанием места). `none` означает, что правило действует ровно до тех пор, пока о нём помнит окно, — и такого состояния быть не должно. Бюджет опускается починкой, а не правкой числа; отдельный тест сверяет число с реальностью, иначе храповик тихо разойдётся с ответом. Сейчас бюджет 1: правило 113, закрываемое PR #1395. Часть #1393 — остаток правил без механизма. Без issue: закрывающего PR у зонтичной задачи нет — #1393 остаётся открытой, пока метрика «не обеспечено ничем» уменьшается. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Co-Authored-By: Artem Markitanov <86671904+ArtVsMark@users.noreply.github.com> * docs(rules): 38 правил числились чужими, хотя описывают наш конвейер Проверка началась с вопроса «а конвейер закрывает правила про конвейер?» — и вскрыла системную ошибку прошлой волны: 45 правил были отклонены **одной типовой причиной** «след правила не ведёт в этот проект». След вёл сюда почти у всех, и в первую очередь как раз у конвейерных. Что нашлось на самом деле: - 049 очередь вычисляется из живых артефактов — `gh_rest.py queue`, реестра нет намеренно; 052 из общей ветки обновляется только голова — `move_merge_queue.py` (N против N(N−1)/2); 064 метки — вход механизма, `check_pr_ready.py` отказывает без area/типа/связи; 123 атрибуция проверяется на итоговой истории — `check_attribution.py --check-branch`, потому что squash пересобирает сообщение; - 105 внешний аудит ведёт не то окно, что писало код — обязательная проверка `claude-review`; 107 «у автора работает» — матрица трёх ОС, руками вторая и третья не проверяются нигде; - дальше по каталогу тем же порядком: 005/127 числа только бейджами, 009 счёт по уникальным номерам PR, 021/023/025/108 бюджеты и раскладка docs, 028 чек-лист находок, 030 changelog.d, 035 версия из тегов, 041 два бейджа покрытия вместо одного усреднённого, 045 отказ вместо тихого отката песочницы, 050 храповики бюджетов, 065 двуязычные good first issue, 085 недоверенный вход в промпт, 090 общий хелпер в top-level leaf (ADR-0011), 095 умолчание в пользу пользователя, 110 компиляция до подмены глобального состояния, 112 что создал — умеет удалить, 114 миграции от `user_version`, 125 производное не хранилище, 130 правило приходит с кандидатами, 134 окно читает репозиторий. Неприменимыми остались три, и у каждой причина теперь названа **фактом**, а не следом: 008 (`<details>` нет ни в одном действующем документе), 048 (остаток квоты приходит числом в заголовках — калибровать по светофору не по чему), 079 (история вытесняется по числу прогонов, а не по сроку). Итог: 96 правил держатся гейтом, 52 — записанным решением, 3 неприменимы, 1 ждёт PR #1395. Часть #1393 — сверка ответа каталогу. Без issue: закрывающего PR у зонтичной задачи нет — #1393 остаётся открытой, пока метрика «не обеспечено ничем» уменьшается. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Co-Authored-By: Artem Markitanov <86671904+ArtVsMark@users.noreply.github.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…ion-gate Конфликты — «обе стороны»: шаги CI, хуки pre-commit и реестр исполнителей гардов дополняют друг друга, а не спорят. Производные (DIGEST.md, указатель правил) пересобраны генератором, а не выбором стороны. Заодно бюджет `UNHELD_BUDGET` опущен до нуля: правило 113 закрывается этой же веткой, и правил «на словах» не осталось ни одного. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Co-Authored-By: Artem Markitanov <86671904+ArtVsMark@users.noreply.github.com>
2 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Правило 113 каталога: перечислить поля мало — потребитель обязан знать, что в
контракте стабильно, что расширяемо и как в него добавляют новое. Написано это
было ровно в одном контракте из четырёх («Ожидания стабильности» в
result-contract.md), поэтому остальные разошлись бы с ним незаметно: раздела
нет — и претензии предъявить не к чему.
Разделы дописаны в
api.md,web-contracts.mdиusage-export.md, а держитих
scripts/check_contract_evolution.py: список контрактов закрыт, а разделобязан отвечать на все три вопроса — заголовок без текста считается
невыполнением, потому что по форме он выглядит выполнением.
Заодно
gh_rest.py edit-pr: тело PR ветокagent/**берётся из сообщениякоммита один раз, при открытии, и
Closes #N, ставший известным позже, до сихпор оставалось только советовать проставить руками — операция же ровно на один
REST-запрос (правило 111: инструмент, который может сделать сам, делает).
Гейт печатает в узкой консоли Windows (issue #1095), и тест это проверяет
запуском под
PYTHONIOENCODING=cp1252. Охват самого приёма держится ручнымсписком и потому дырявый — вынесено в #1394, потому что чинится в 42 файлах
сразу.
Часть #1393 — правило 113 и инструмент для связи PR с задачей.
Без issue: закрывающего PR у зонтичной задачи нет — #1393 остаётся открытой,
пока метрика «не обеспечено ничем» уменьшается.
Работа сделана вместе: @ArtVsMark — постановка, решения и приёмка; Claude Code — реализация.