From 5bf599a767dff30c9edfb9b3e002a4cb0bbecd37 Mon Sep 17 00:00:00 2001 From: Anton Vakhrushev Date: Tue, 4 Aug 2026 17:12:40 +0300 Subject: [PATCH] =?UTF-8?q?=D0=B2=D0=B5=D1=80=D1=85=D0=BD=D1=8F=D1=8F=20?= =?UTF-8?q?=D1=81=D1=82=D1=83=D0=BF=D0=B5=D0=BD=D1=8C=20=D1=80=D0=B5=D0=B2?= =?UTF-8?q?=D1=8C=D1=8E=20=D0=B7=D0=B0=D0=B4=D0=B0=D0=BD=D0=B0=20=D1=82?= =?UTF-8?q?=D0=B5=D1=81=D1=82=D0=BE=D0=BC,=20=D0=B0=20=D0=BD=D0=B5=20?= =?UTF-8?q?=D1=81=D0=BF=D0=B8=D1=81=D0=BA=D0=BE=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit «Идентичность, слияние, разбор» пришли из одного проекта, и в общем виде формулировка не читалась: вопрос «как применить это к моему проекту» не имел ответа в тексте. Теперь класс задан тремя условиями, не зависящими ни от домена, ни от языка: вариантов несколько и оба защитимы; спека между ними не выбирает; неверный выбор не падает, а даёт правдоподобный результат и молча меняет смысл данных. Отрицательный тест сильнее трёх положительных: то, что красит гейт, роняет запрос или ломает тест, в класс не входит — это ловят проходы дешевле. Отсюда же и причина, по которой класс достался самому дорогому проходу: независимая реализация выберет другой вариант, и дифф между вариантами и есть находка; там, где вариант один, она совпадёт с существующей. Три слова остались как три места, где такие правила водятся — граница, где данные входят или встречаются: состав ключа и нормализация перед сравнением; победитель конфликта и тай-брейк при равенстве; границы токенов и неоднозначный вход. Проект перечисляет свои места в docs/review.md, и перечень производен от теста, а не заменяет его. Две оговорки, без которых правило вырождается: - триггер — новое или изменённое по существу правило, а не код рядом с ним; иначе проект, чей домен и состоит из таких правил, всегда в deep; - проект, где такого класса нет вовсе, deep не запускает никогда, и это законное состояние, а не недонастройка. review-reimpl получил тот же тест и право сказать первой строкой, что позвали не на его класс, — строкой в границы покрытия, а не отказом работать. DECISIONS 18, XXX и следствия 76–77. Co-Authored-By: Claude Opus 5 (1M context) --- DECISIONS.md | 25 ++++++ av-dev-pipeline/agents/review-reimpl.md | 31 ++++++-- .../skills/review-pipeline/SKILL.md | 77 +++++++++++++++++-- av-dev-pm/skills/canon/references/canon.md | 9 ++- .../skills/canon/references/skeletons.md | 14 +++- 5 files changed, 137 insertions(+), 19 deletions(-) diff --git a/DECISIONS.md b/DECISIONS.md index 6ffce7e..360cfdf 100644 --- a/DECISIONS.md +++ b/DECISIONS.md @@ -1310,6 +1310,22 @@ SSS: рубрика на узел без нового понятия порож ступени, делает ревью **дороже**: тот же объём тем же составом, но костяк оплачен дважды. Резать — когда разрез снимает дорогой проход с большей части диффа. +**XXX. Верхняя ступень задана тестом, а не списком.** «Идентичность, слияние, +разбор» — формулировка, пришедшая из одного проекта, и в общем виде она не +читалась: вопрос «как это применить к моему проекту» не имел ответа в тексте. +Теперь класс задан тремя условиями, независимыми от домена и языка: вариантов +несколько и оба защитимы; спека между ними не выбирает; неверный выбор не падает, +а молча меняет смысл данных. Отрицательный тест сильнее положительных — то, что +красит гейт или роняет запрос, в класс не входит. Три слова остались как **три +места**, где такие правила водятся (граница входа данных и место их встречи), а +проект перечисляет свои места в `docs/review.md` — перечень производен от теста и +не расширяет класс. + +Оговорка, без которой правило вырождается: триггер — **новое или изменённое по +существу правило**, а не код рядом с ним. Проект, чей домен и состоит из таких +правил, иначе оказывался бы в `deep` всегда — та же болезнь, от которой лечилась +ступень `wide`. + ### Что из этого следует 72. **Порога в числе границ не заводится.** Тот же принцип, что в теме 16 (CCC): @@ -1323,3 +1339,12 @@ SSS: рубрика на узел без нового понятия порож 75. **Замер остаётся за обкаткой.** Правило выведено из состава проходов, а не из статистики прогонов: считать, какая доля задач попадает в каждую ступень, можно только на спринтах нового процесса (TODO шаг 4). +76. **Отсутствие верхней ступени — законное состояние проекта.** Бывают проекты, + где данные приходят нормализованными, ничего ни с чем не сливается, а внешних + форматов нет: `deep` там не срабатывает никогда, и придумывать ему повод не + надо. Раньше это читалось как недонастройка. +77. **Ступень определяет класс правила, а не вид работы.** Миграция схемы — + `standard`, но миграция, переносящая данные по правилу («сложить дубли», + «привести к одному виду перед сравнением»), несёт правило идентичности и + потому `deep`. Одно слово в описании задачи попадает в разные ступени — это не + противоречие, смотрят не на слово. diff --git a/av-dev-pipeline/agents/review-reimpl.md b/av-dev-pipeline/agents/review-reimpl.md index 612e3f2..7bcfb04 100644 --- a/av-dev-pipeline/agents/review-reimpl.md +++ b/av-dev-pipeline/agents/review-reimpl.md @@ -38,11 +38,32 @@ color: yellow твоя версия проще, потому что не знает, чего проект боится. **Тебя запускают только в верхнем профиле, `deep`, а не всегда.** Он выбирается -ровно тогда, когда изменение вводит **новое правило идентичности, слияния или -разбора** (проектная формулировка — в разделе -`docs/review.md`, если он там записан); ты — единственное, чем `deep` отличается -от соседней ступени `wide`. Вне этого случая твой счёт — самый большой в -конвейере (он +ровно тогда, когда вводится или меняется по существу **правило идентичности, +слияния или разбора**; ты — единственное, чем `deep` отличается от соседней +ступени `wide`. + +Класс задан тестом, а не списком, и тест не зависит ни от домена, ни от языка. +Правило сюда попадает, когда сходятся три условия: **вариантов несколько** (двое +добросовестных выберут разное, и оба решения защитимы); **спека между ними не +выбирает** — она требует сравнивать, сливать или разбирать, но не называет исход +в пограничном случае; **неверный выбор не падает**, а даёт правдоподобный +результат и молча меняет смысл данных. Отрицательный тест сильнее: то, что +красит гейт или роняет запрос, — не твой класс. Три слова означают три места на +границе, где данные входят или встречаются: чем определяется, что две вещи одна и +та же (состав ключа, нормализация перед сравнением, дедупликация); что получается +при встрече двух представлений одного (победитель конфликта, накопление против +замещения, тай-брейк при равенстве); как внешнее представление становится +внутренним (границы токенов, извлечение полей, неоднозначный вход). Проектный +перечень мест — в `docs/review.md`, если он там записан; он производен от теста, +а не расширяет его. + +**Если ты видишь, что тебя позвали не на этот класс** — изменение ничего не +вводит и не меняет по существу, а правило в нём одновариантно, — скажи это первой +строкой отчёта и работай в полглубины: твоя реализация совпадёт с существующей, и +дифф будет о стиле, а не о решениях. Это строка в границы покрытия, а не отказ +работать. + +Вне этого случая твой счёт — самый большой в конвейере (он определяется объёмом вывода: ты пишешь реализацию целиком), а независимый взгляд в значительной мере уже дал профиль `design` — код писался под его находки. Если тебя позвали, значит случай тот самый: работай в полную глубину и не экономь на diff --git a/av-dev-pipeline/skills/review-pipeline/SKILL.md b/av-dev-pipeline/skills/review-pipeline/SKILL.md index be0b78e..126f167 100644 --- a/av-dev-pipeline/skills/review-pipeline/SKILL.md +++ b/av-dev-pipeline/skills/review-pipeline/SKILL.md @@ -177,8 +177,8 @@ charter'а, а модель потом двигает калибровка, и Правило выбора профиля — **по факту изменения, не по ощущению важности**: -- трогается правило, определяющее **идентичность, слияние или разбор** данных → - `deep`; +- вводится или меняется по существу правило, определяющее **идентичность, слияние + или разбор** данных (тест — ниже) → `deep`; - иначе изменение вводит **новое понятие или структурную единицу**: новый пакет или слой, новая точка входа, второй способ делать то, что уже делается, перенос ответственности между узлами → `wide`; @@ -215,6 +215,63 @@ charter'а, а модель потом двигает калибровка, и правда архитектурное (публичный SDK, чужие потребители), там же поднимает его до `wide` — и это уточнение, а не возврат прежнего умолчания. +### Идентичность, слияние, разбор — тест, а не список + +Три слова названы затем, чтобы верхнюю ступень нельзя было выбрать по ощущению. +Читаются они **тестом**, применимым к любому проекту на любом языке; домен, стек +и имена узлов в тест не входят. + +Правило принадлежит этому классу, если сходятся **три условия**: + +1. **вариантов несколько** — два добросовестных исполнителя выберут разное, и оба + решения защитимы; +2. **спека между ними не выбирает** — она требует, чтобы вещи сравнивались, + сливались или разбирались, но не называет исход в пограничном случае; +3. **неверный выбор не падает** — он даёт правдоподобный результат и меняет смысл + данных молча. + +**Отрицательный тест, и он важнее трёх положительных:** если неверная реализация +красит гейт, роняет запрос или ломает тест — это **не** сюда. Такое ловят проходы +дешевле, и платить за него верхней ступенью не за что. + +Отсюда же и причина, по которой класс достался самому дорогому проходу: +независимая реализация **выберет другой вариант**, и дифф между двумя вариантами +и есть находка. Там, где вариант один, она совпадёт с существующей — и верхняя +ступень оплатит подтверждение того, что и так известно. + +Три слова — это **три места**, где такие правила водятся, и все три стоят на +границе, где данные входят или встречаются: + +| Слово | Вопрос, на который правило отвечает | Что в нём выбирается | +|---|---|---| +| **идентичность** | когда две вещи считаются одной и той же | состав ключа и что в него намеренно не входит; нормализация перед сравнением — регистр, пробелы, кодировка, время, единицы, округление; дедупликация | +| **слияние** | что получается, когда два представления одного встретились | кто побеждает при конфликте; накопительное против замещающего; что делать с отсутствующим полем; тай-брейк при равенстве | +| **разбор** | как внешнее представление становится внутренним | границы токенов; извлечение полей; сопоставление с известным набором; поведение на неоднозначном входе | + +**Триггер — новое или изменённое правило, а не код рядом с ним.** Правка +сообщения об ошибке в узле, который разбирает вход, ступень не поднимает. +Поднимают: заводится ключ или меняется его состав; в слияние добавляется источник +или меняется победитель при конфликте; у разбора появляется новый вид входа или +новая ветка неоднозначности. Без этой оговорки проект, чей домен и **состоит** из +таких правил, оказывался бы в `deep` всегда — та же болезнь, от которой лечилась +ступень `wide`. + +**Ступень определяет класс правила, а не вид работы.** Миграция схемы сама по +себе `standard` — но миграция, которая **переносит данные** по правилу («сложить +дубли», «привести к одному виду перед сравнением»), несёт правило идентичности и +потому `deep`. Одно и то же слово в описании задачи попадает в разные ступени, и +это не противоречие: смотрят не на слово, а на то, есть ли выбор, которого спека +не сделала. + +**Проект, у которого таких правил нет вовсе, `deep` не запускает никогда.** Это +законное состояние, а не признак недонастройки: бывают проекты, где данные +приходят уже нормализованными, ничего ни с чем не сливается, а внешних форматов +нет. Верхняя ступень там просто не срабатывает, и придумывать ей повод не надо. + +Свои места проект перечисляет в `docs/review.md`, подраздел «Триггеры профиля» — +поимённо, узлами или capability. Перечень **производен от теста**: он не расширяет +класс, а называет, где этот класс живёт именно здесь. + ### Профиль — максимум по поверхности, и отсюда размер задачи Условия читаются сверху вниз, и **первое подошедшее отвечает за весь дифф**. @@ -458,14 +515,20 @@ Recall обоих равен длине их источника — это и е - `review-reimpl` — пишет свою реализацию, не открывая существующую, затем диффит по решениям. **Профиль и есть его условие:** `deep` выбирается ровно - тогда, когда изменение вводит новое правило идентичности, слияния или разбора - (проектная формулировка — в `docs/review.md`, если записана). Это самый дорогой - проход конвейера (его счёт определяется объёмом вывода — он пишет реализацию - целиком), а вне этого случая независимый взгляд в значительной мере уже дал - профиль `design`: код писался под его находки. Условие выбрано по факту: + тогда, когда вводится или меняется по существу правило идентичности, слияния + или разбора — по тесту из раздела «Идентичность, слияние, разбор»; проектный + перечень мест, где такие правила живут, — в `docs/review.md`, если записан. Это + самый дорогой проход конвейера (его счёт определяется объёмом вывода — он пишет + реализацию целиком), а вне этого случая независимый взгляд в значительной мере + уже дал профиль `design`: код писался под его находки. Условие выбрано по факту: единственный раз, когда триаж назвал отсутствие `reimpl` дырой покрытия, — это была задача с новым правилом слияния сущностей. +Такие правила обычно занимают десятки строк, но определяют смысл **всех** данных +проекта. Отсюда особенность верхней ступени, из-за которой её легко выбрать +неверно: самый дорогой проход тратится на самый **маленький** дифф. `deep` не про +размер изменения и не про его опасность — он про класс правила. + Раньше это условие стояло **внутри** профиля, и `deep` означал то семь проходов, то восемь. Реестр состава, который «проверяется взглядом», проверять было нечем: у профиля не было одного правильного ответа. Теперь ступеней две — `wide` и diff --git a/av-dev-pm/skills/canon/references/canon.md b/av-dev-pm/skills/canon/references/canon.md index 3cdb655..a881cfe 100644 --- a/av-dev-pm/skills/canon/references/canon.md +++ b/av-dev-pm/skills/canon/references/canon.md @@ -184,10 +184,11 @@ kebab-case. (<провенанс>)`; - **Триггеры профиля** — проектная конкретизация правила выбора профиля ревью: что в этом проекте считается **новым понятием или структурной единицей** (это - поднимает прогон до `wide`) и что — правилом идентичности, слияния или разбора - (до `deep`). Уточняет умолчания конвейера, а не отменяет их. Рабочее умолчание - — `standard`: миграция схемы и публичный контракт ступень **не** поднимают, - их проверяют проходы, которые в `standard` и так есть; + поднимает прогон до `wide`) и **где живут правила идентичности, слияния и + разбора** (до `deep`) — перечнем мест, производным от теста конвейера, а не + вторым определением класса. Уточняет умолчания, а не отменяет их. Рабочее + умолчание — `standard`: миграция схемы и публичный контракт ступень **не** + поднимают, их проверяют проходы, которые в `standard` и так есть; - **Недоступно проверке** — два подраздела: «не проверит ни один проход» (принципиальная граница, по факту промаха не пересматривается) и «перестали проверять сознательно» (пересматривается первым). diff --git a/av-dev-pm/skills/canon/references/skeletons.md b/av-dev-pm/skills/canon/references/skeletons.md index f1bb26c..8d318fd 100644 --- a/av-dev-pm/skills/canon/references/skeletons.md +++ b/av-dev-pm/skills/canon/references/skeletons.md @@ -284,9 +284,17 @@ Проектная конкретизация правила выбора профиля: что здесь считается **новым понятием или структурной единицей** (поднимает прогон до `wide` и запускает -архитектурный проход) и что — правилом идентичности, слияния или разбора -(до `deep`, запускает независимую реализацию). Уточняет умолчания конвейера, не -отменяет их; рабочее умолчание — `standard`. +архитектурный проход) и **где живут правила идентичности, слияния и разбора** +(до `deep`, запускает независимую реализацию) — перечнем узлов или capability, +поимённо. Уточняет умолчания конвейера, не отменяет их; рабочее умолчание — +`standard`. + +Перечень для `deep` **производен от теста конвейера**, а не заменяет его: +правило попадает в класс, когда вариантов несколько, спека между ними не +выбирает, а неверный выбор не падает, а молча меняет смысл данных. Перечисляй +места, где этот класс здесь живёт, а не переписывай определение. Таких мест нет +вовсе — так и напиши: `deep` тогда не запускается никогда, и это законное +состояние. ### Недоступно проверке