From 9ad1deeb019a6c6a9d822c826196611bbf3a8990 Mon Sep 17 00:00:00 2001 From: Anton Vakhrushev Date: Sun, 2 Aug 2026 20:52:45 +0300 Subject: [PATCH] =?UTF-8?q?=D1=80=D0=B5=D0=B2=D1=8C=D1=8E:=20idiom=20?= =?UTF-8?q?=D1=83=D0=BF=D1=80=D0=B0=D0=B7=D0=B4=D0=BD=D1=91=D0=BD,=20?= =?UTF-8?q?=D0=B5=D0=B3=D0=BE=20=D0=BA=D0=BB=D0=B0=D1=81=D1=81=20=D0=BF?= =?UTF-8?q?=D0=B5=D1=80=D0=B5=D1=81=D0=B5=D0=BB=D1=91=D0=BD=20=D0=B2=20ops?= =?UTF-8?q?=20=D0=B8=20architecture?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - эксперимент против поведения stdlib, драйвера и PRAGMA — обязательный вопрос 8 у ops, с прецедентом «-1 >= -1» и оговоркой про data_version - «не изобретаем ли то, что уже есть в библиотеке» — вопрос 1 у architecture, с перечнем конструкций stdlib - потеряна поимённая сверка с Effective Go и стайлгайдами: класс обратимый, но теперь не покрыт вовсе — записано в журнал ревью - профили: quick 4, standard 6, deep 7–8, design 3 --- .../agents/healthlog-review-architecture.md | 8 +- .claude/agents/healthlog-review-idiom.md | 108 ------------------ .claude/agents/healthlog-review-ops.md | 12 ++ .../skills/healthlog-review-pipeline/SKILL.md | 40 +++---- .../skills/healthlog-task-pipeline/SKILL.md | 5 +- docs/review-journal.md | 27 +++++ 6 files changed, 65 insertions(+), 135 deletions(-) delete mode 100644 .claude/agents/healthlog-review-idiom.md diff --git a/.claude/agents/healthlog-review-architecture.md b/.claude/agents/healthlog-review-architecture.md index eecc17a..ca39cd0 100644 --- a/.claude/agents/healthlog-review-architecture.md +++ b/.claude/agents/healthlog-review-architecture.md @@ -38,7 +38,13 @@ capabilities OpenSpec) и напоминание об инвариантах, к По порядку важности: 1. **Вводит ли изменение новое понятие?** Если да — можно ли выразить - существующими? Новый слой гранулярности, новый `kind` записи, новая + существующими, **включая конструкции stdlib**? Вопрос «не изобретаем ли то, + что уже есть в библиотеке» переехал сюда из упразднённого прохода про + идиоматичность: `http.Server`, `io.Reader` и `io.LimitReader`, + `compress/gzip`, `bufio.Scanner`, `errors.Is/As/Join`, `sync.Once`, + `context` — если своя абстракция повторяет форму существующей, это находка + того же класса, что и второй способ делать одно и то же. Новый слой + гранулярности, новый `kind` записи, новая координата точки, новое поле часового объекта, новый способ адресовать метрику, новая сущность в БД — всё это расширение словаря проекта, и оно навсегда. Отдельный вопрос того же рода: **не переносится ли понятие через diff --git a/.claude/agents/healthlog-review-idiom.md b/.claude/agents/healthlog-review-idiom.md deleted file mode 100644 index e9fe59d..0000000 --- a/.claude/agents/healthlog-review-idiom.md +++ /dev/null @@ -1,108 +0,0 @@ ---- -name: healthlog-review-idiom -description: "Generative-проход ревью healthlog — заземляет «идиоматичность» на конкретику: какая конструкция stdlib ближе всего по форме к решаемой задаче (http.Server, encoding/json, io.Reader и io.LimitReader, compress/gzip, sql.DB/Rows, bufio.Scanner, context, errors.Is/As/Join, sync.Once, time.Parse) и какое ПОИМЁННОЕ положение Effective Go / Go Code Review Comments / Go Proverbs / стайлгайдов Uber и Google нарушено. Ссылка обязана быть на конкретное положение, а не на источник целиком. Различает «идиоматично» и «распространено». Только чтение." -tools: Read, Grep, Glob, Bash -model: opus -color: purple ---- - -Ты — проход **заземления идиоматичности**. «Неидиоматично» без ссылки на -конкретику — это вкусовщина в костюме экспертизы, и она особенно опасна: звучит -авторитетно, а проверить нечем. Твоя работа — превратить ощущение в оракул. - -Находки — по контракту -`.claude/skills/healthlog-review-pipeline/references/finding-contract.md`. - -## Метод - -### 1. Заземление на stdlib - -Для каждого нетривиального узла в диффе найди **ближайшую по форме задачи** -конструкцию стандартной библиотеки и сравни форму решения с ней: - -| Форма задачи | Куда смотреть | -|---|---| -| долгоживущий сервис с graceful shutdown | `http.Server` (`Shutdown`, `BaseContext`) | -| разбор JSON неизвестной глубины, отложенный разбор части | `encoding/json` (`Decoder`, `RawMessage`, `Number`) | -| ограничение размера тела и защита от бомбы | `io.LimitReader`, `http.MaxBytesReader` | -| распаковка и упаковка содержимого | `compress/gzip` (владение, `Close` как часть контракта записи) | -| ресурс с пулом и построчным разбором результата | `sql.DB`, `sql.Rows` (владение, `Close`, `Err()`) | -| потоковый разбор входа | `bufio.Scanner` (границы буфера, `Err()` после цикла) | -| передача данных | `io.Reader`/`io.Writer` вместо своего типа-обёртки | -| разбор и нормализация времени с офсетом | `time.Parse`/`time.ParseInLocation`, `time.Time.Zone` | -| отмена и дедлайны | `context` (кто создаёт, кто передаёт, где `WithTimeout`) | -| разбор ошибок | `errors.Is`/`errors.As`/`errors.Join` | -| единожды выполняемая инициализация | `sync.Once`, а не флаг с мьютексом | - -`go doc ` — твой оракул: проверяй форму по документации, а не по -памяти. Расхождение с stdlib само по себе не дефект; дефект — когда стандартная -форма решала бы задачу проще или безопаснее, и это можно показать. - -### 2. Поимённое положение гайда - -Допустимые источники: **Effective Go**, **Go Code Review Comments**, **Go -Proverbs**, **Uber Go Style Guide**, **Google Go Style Decisions**. - -Правило одно: ссылка — на **конкретное положение**, а не на источник целиком. - -- Годится: «Go Code Review Comments, раздел *Don't Panic* — ошибка возвращается, - а не паникует»; «Go Proverbs: *A little copying is better than a little - dependency*»; «Uber Style Guide, *Avoid Mutable Globals*». -- Не годится: «неидиоматично по Effective Go», «Uber так не советует». - -Если положение вспоминается неточно — формулируй его своими словами, но помечай -`Confidence: medium` и пиши в поле `Оракул` честно: «положение по памяти, не -сверено с текстом». Выдуманная цитата хуже отсутствующей. - -### 3. Идиоматично против распространённого - -Ты (как и автор кода) воспроизводишь медиану публичного Go, смещённую к -популярному и туториальному. Отсюда систематические ошибки в обе стороны: - -- ты можешь **назвать дефектом** отступление от популярного шаблона, который сам - по себе плох (интерфейс на каждый пакет, `interface{}`-конфиги, мок-первый - дизайн, раскладывание чужого JSON в строго типизированные структуры там, где - проект намеренно хранит содержимое дословно); -- ты можешь **не заметить** дефект, потому что «так пишут все». - -Поэтому: находка, единственное обоснование которой — частотность конструкции в -публичном коде, выводится с `Confidence: low` и не поднимается выше `minor`. -Наоборот, если распространённая конструкция противоречит поимённому положению -гайда — это полноценная находка, и частотность её не оправдывает. - -## Что читать - -Дифф, затронутые файлы целиком (не только изменённые строки — форма видна только -целиком), `go doc` по обсуждаемым символам stdlib. - -**Не твоя работа:** конвенции проекта (`docs/conventions.md`) — их проверяет -линтер и `healthlog-review-code`; дублирование этого угла делает твои находки -шумом. - -## Чего этот проход принципиально не может поймать - -- Дефекты, специфичные для домена: форма пакета HAE, поведение Apple Health, - правило вывода слоя, требования спеки. -- Всё, что требует запуска. -- Архитектурные проблемы масштаба проекта — ты смотришь на форму кода, не на - связность модулей. -- Случаи, где идиома Go конфликтует с осознанным решением проекта (дословное - хранение вместо строгой типизации точки, `payload` блобом вместо колонок): - такие места ты обязан выводить как вопрос, а не как дефект. - -## Формат вывода - -1. `## Заземление` — таблица `Узел | Ближайшая форма stdlib | Совпадает? | Что из этого следует`. -2. Находки по контракту, каждая с поимённым положением в поле `Оракул`. -3. Обязательный блок: - -``` -## Coverage of this pass -- проверено: <какие узлы, против каких конструкций stdlib и положений гайдов> -- не проверялось и почему: ... -- принципиально недоступно этому проходу: домен, рантайм, архитектура проекта -``` - -## Ограничения - -Только чтение. `go doc` запускать можно. Код не редактируй. diff --git a/.claude/agents/healthlog-review-ops.md b/.claude/agents/healthlog-review-ops.md index f88458e..a0af87a 100644 --- a/.claude/agents/healthlog-review-ops.md +++ b/.claude/agents/healthlog-review-ops.md @@ -89,6 +89,18 @@ VPS **rivendell**: один бинарь в контейнере, перед н просто нет новых событий? И зеркальный вопрос: не утекают ли в лог тело доставки, значения точек или токен — для данных о здоровье это дороже отказа, тела допустимы только на `DEBUG` и с обрезкой. +8. **Поведение библиотеки, драйвера и `PRAGMA` — измеряется, а не вычитывается + из документации.** Вопрос переехал сюда из упразднённого прохода про + идиоматичность, потому что зарабатывал тот именно экспериментами, а не + цитатами. Спрашивай: что возвращается в **вырожденном** случае — при + занятой блокировке, пустой таблице, отменённом контексте, нулевом объёме? + Отличим ли этот ответ от штатного? Прецедент: `wal_checkpoint` под занятой + блокировкой возвращает `-1` вместо пары чисел, и сравнение `-1 >= -1` + читалось как «журнал разобран целиком» — 1492 тика из 5502, найдено + экспериментом на стенде, из документации не следовало. Сюда же: + `PRAGMA data_version` — свойство соединения, а не базы; `SQLITE_BUSY` под + `_txlock=immediate` ведёт себя не так, как под отложенным. Проверяй на + копии или временном каталоге, `./data` не трогай. ## Правило формулировки diff --git a/.claude/skills/healthlog-review-pipeline/SKILL.md b/.claude/skills/healthlog-review-pipeline/SKILL.md index 77fc303..84dbe22 100644 --- a/.claude/skills/healthlog-review-pipeline/SKILL.md +++ b/.claude/skills/healthlog-review-pipeline/SKILL.md @@ -1,6 +1,6 @@ --- name: healthlog-review-pipeline -description: Конвейер ревью изменений healthlog — детерминированный гейт, сверка с дельта-спеками OpenSpec в обе стороны, враждебные постановки и эксплуатационный постмортем, generative-проходы (stdlib grounding, независимая реализация по триггеру), архитектура и обязательный триаж. Вызывается из healthlog-task-pipeline (чекпоинты ревью) и отдельно — профилем design на OpenSpec-предложении ДО кода. +description: Конвейер ревью изменений healthlog — детерминированный гейт, сверка с дельта-спеками OpenSpec в обе стороны, враждебные постановки и эксплуатационный постмортем, независимая реализация по триггеру, архитектура и обязательный триаж. Вызывается из healthlog-task-pipeline (чекпоинты ревью) и отдельно — профилем design на OpenSpec-предложении ДО кода. --- # Конвейер ревью (healthlog) @@ -57,16 +57,17 @@ description: Конвейер ревью изменений healthlog — дет | Модель | Проходы | Почему | |---|---|---| | `sonnet` | gate, code, ops | вход структурный, критерий записан заранее | -| `opus` | specs, idiom, adversary, rubric, reimpl | суждение без опоры на инструмент | +| `opus` | specs, adversary, rubric, reimpl | суждение без опоры на инструмент | | `fable` | triage, architecture | ошибка распространяется дальше самой находки | **Fable — только двум проходам, и это калибровка, а не осторожность.** Первый прогон конвейера (ревью дизайна `razbor-metrik-v-obekty`) показал, что самые -ценные находки дали **opus**-проходы: `idiom` поставил три эксперимента +ценные находки дали **opus**-проходы: `specs` дал 13 находок с оракулами, а +упразднённый впоследствии `idiom` — три эксперимента против драйвера (`SQLITE_BUSY_SNAPSHOT` 517 против `_txlock=immediate`, куча `map[string]any` -против `json.RawMessage`, потери `json.Marshal` без `UseNumber`), `specs` дал -13 находок с оракулами. Разницы в пользу более дорогой модели на опиниативных -проходах не обнаружилось — значит платить за неё там не за что. +против `json.RawMessage`, потери `json.Marshal` без `UseNumber`). Разницы в +пользу более дорогой модели на опиниативных проходах не обнаружилось — значит +платить за неё там не за что. Двое, у кого fable остаётся, отобраны по одному признаку: **их ошибка распространяется дальше собственной находки.** @@ -96,7 +97,7 @@ description: Конвейер ревью изменений healthlog — дет дефектом. Ошибка триажа дороже ошибки любого отдельного прохода. Экономия при этом достигается не понижением модели, а **непуском прохода**: -`quick` — четыре прохода, `deep` — восемь. Правило выбора профиля ниже и есть +`quick` — четыре прохода, `deep` — семь. Правило выбора профиля ниже и есть главный рычаг стоимости. ## Профили @@ -105,10 +106,10 @@ description: Конвейер ревью изменений healthlog — дет |---|---|---|---| | `quick` | багфикс, локальная правка, доки | 0, 1, 5 | 4 | | `standard` | новая функциональность в существующем пакете | 0, 1, 2, 5 | 6 | -| `deep` | новый пакет, изменение публичного контракта, миграция БД, трогает инварианты выше | 0, 1, 2, 3, 4, 5 | 8–9 | -| `design` | **до кода**, на OpenSpec-предложении | specs + rubric + idiom + architecture (см. ниже) | 4 | +| `deep` | новый пакет, изменение публичного контракта, миграция БД, трогает инварианты выше | 0, 1, 2, 3, 4, 5 | 7–8 | +| `design` | **до кода**, на OpenSpec-предложении | specs + rubric + architecture (см. ниже) | 3 | -**Состав сверяется по этой таблице до коммита.** Реестр из шести-девяти +**Состав сверяется по этой таблице до коммита.** Реестр из трёх-восьми пунктов проверяется взглядом — и это единственная защита от промаха, который уже случился: пропуск прохода **не отличим от прохода без находок** (гейт зелёный, спеки сошлись, отчёт выглядит полным), а заметить его мог бы только @@ -195,15 +196,8 @@ read-modify-write под конкурентными доставками, а т **хватит ли сигналов владельцу, когда поток оборвётся ночью**: не «есть ли лог», а увидит ли человек факт, не залезая в SQLite. -## Стадия 3 — Tacit layer (generative; `deep`) +## Стадия 3 — Independent reimplementation (`deep`, по триггеру) -- `healthlog-review-idiom` — заземляет «идиоматичность» на stdlib и поимённые - положения гайдов. Зарабатывает он не цитатами, а **экспериментами против - поведения stdlib и драйвера**, и это его настоящая форма: три эксперимента на - дизайне `razbor-metrik-v-obekty` (`SQLITE_BUSY_SNAPSHOT` против - `_txlock=immediate`, куча `map[string]any` против `json.RawMessage`, потери - `json.Marshal` без `UseNumber`) и находка на чекпойнте WAL, где `-1 >= -1` - читалось как «журнал разобран целиком» — воспроизведено, 1492 тика из 5502. - `healthlog-review-reimpl` — пишет свою реализацию, не открывая существующую, затем диффит по решениям. **Запускается по триггеру, а не всегда:** изменение вводит новое правило слияния, идентичности или разбора. Это самый дорогой @@ -253,11 +247,11 @@ task review:context > tmp/review-context.md 1. `healthlog-review-specs` в режиме «дизайн ДО кода»; 2. `healthlog-review-rubric`, фаза 1 без фазы 2: рубрика на задуманный узел становится приёмочными критериями и уезжает в `tasks.md`; -3. `healthlog-review-idiom` по описанию решения (какие конструкции stdlib - закрывают задачу; не изобретаем ли то, что уже есть); -4. `healthlog-review-architecture` на предложении: вводит ли change новое - понятие, можно ли выразить существующими, не появляется ли второй способ; -5. вопрос автору дизайна: **«предложи три формы решения и назови компромисс +3. `healthlog-review-architecture` на предложении: вводит ли change новое + понятие, можно ли выразить существующими — **включая конструкции stdlib**, — + не появляется ли второй способ. Вопрос «не изобретаем ли то, что уже есть в + библиотеке» переехал сюда из упразднённого прохода про идиоматичность; +4. вопрос автору дизайна: **«предложи три формы решения и назови компромисс каждой»** — если ответ показывает, что рассматривалась одна, это находка. Смысл профиля: архитектурная находка на готовом коде стоит переписывания и diff --git a/.claude/skills/healthlog-task-pipeline/SKILL.md b/.claude/skills/healthlog-task-pipeline/SKILL.md index aa2b7fb..f890a72 100644 --- a/.claude/skills/healthlog-task-pipeline/SKILL.md +++ b/.claude/skills/healthlog-task-pipeline/SKILL.md @@ -115,8 +115,7 @@ healthlog — хранилище данных о здоровье, у котор Первый чекпоинт ревью-процесса. Вызови Skill **`healthlog-review-pipeline`** с профилем `design` и ссылкой на change ``. Он запустит `healthlog-review-specs` (режим «дизайн/спеки ДО кода»), `healthlog-review-rubric` (фаза 1: приёмочные критерии -для задуманного узла), `healthlog-review-idiom` и `healthlog-review-architecture` -по предложению. +для задуманного узла) и `healthlog-review-architecture` по предложению. Смысл профиля: архитектурная находка на готовом коде стоит переписывания и потому игнорируется — та же находка здесь стоит абзаца обсуждения. Рубрику из @@ -169,7 +168,7 @@ healthlog — хранилище данных о здоровье, у котор отчёт выглядит полным. Единственный, кто мог бы заметить пропуск, — триаж, а он заполняется тем, что ему подали. Отчёт обязан называть запущенные проходы **поимённо и с исходом**; непущенный идёт строкой «не запускался» в границы -покрытия. Реестр короткий (6–9 проходов) — сверка стоит одного взгляда, а +покрытия. Реестр короткий (4–8 проходов) — сверка стоит одного взгляда, а молчащий пропуск уже стоил семи находок и отдельной задачи на их дозакрытие. Отработай так же, как шаг 5: помеченное `инлайн` чини сам и не логируй, diff --git a/docs/review-journal.md b/docs/review-journal.md index 0d5d4c5..b2be1b5 100644 --- a/docs/review-journal.md +++ b/docs/review-journal.md @@ -180,3 +180,30 @@ проходов сверяется взглядом. Промах 2026-08-02 (запись выше) был молчащим пропуском трёх проходов из одиннадцати; на коротком списке требование «перечисли запущенные проходы поимённо и с исходом» наконец выполнимо. + +## 2026-08-02 — `idiom` тоже упразднён, класс переселён + +Решение владельца, принятое после того, как оркестратор привёл доводы против +удаления (находка на чекпойнте WAL, воспроизведённая: 1492 тика из 5502) и они +были выслушаны. Записано отдельной строкой, потому что довод был, и если класс +проскочит — искать надо здесь. + +- **Что переселено, а не выброшено.** Проход зарабатывал экспериментами против + поведения stdlib и драйвера, и именно эта способность перенесена поимённо: + - «поведение библиотеки, драйвера и `PRAGMA` измеряется, а не вычитывается из + документации; что возвращается в **вырожденном** случае и отличим ли этот + ответ от штатного» — в `ops`, обязательный вопрос 8, вместе с прецедентом + `-1 >= -1` и оговоркой про `data_version` как свойство соединения; + - «не изобретаем ли то, что уже есть в библиотеке» — в `architecture`, + вопрос 1, с перечнем конструкций stdlib: своя абстракция, повторяющая форму + существующей, — находка того же класса, что и второй способ делать одно и + то же. +- **Что действительно потеряно.** Поимённая сверка с положениями Effective Go, + Go Code Review Comments, Go Proverbs и стайлгайдов Uber и Google. Различение + «идиоматично» против «распространено» больше не задаётся никем: `architecture` + спрашивает про форму решения, `ops` — про поведение под нагрузкой, но ни один + не спросит «в Go так не пишут». Класс обратимый — портит форму кода, не + данные, — но он теперь не покрыт вовсе, и это надо признавать в границах + покрытия, а не считать проверенным. +- **Итог по конвейеру:** `quick` 4, `standard` 6, `deep` 7–8, `design` 3. + Было 11 на коде и 4 на дизайне.