Promote tdd-criteria → claude-skills wiki + 4 tasks; archive buffer
Promoted .brainstorm/tdd-criteria.md to claude-skills/.wiki/concepts/tdd-criteria-design.md (commits a03d2804+2ad6f4ce+5fb648d4 in claude-skills repo). Created 4 tasks in claude-skills/.tasks/STATUS.md: - tdd-criteria-skill-write (ready) — write skills/tdd-criteria/SKILL.md - tdd-criteria-hermes-mapping (blocked by skill-write) — mapping.yaml entry - tdd-criteria-build-install (blocked by above two) — build.sh + install.sh - tdd-criteria-review (blocked-umbrella per §5) — code-review checkpoint Lead rationale (anti-vandalism / contract-vs-artefact) and 4+4 bright-line structure (Ironclad + Permissive accepted-risk zones) in design doc; runtime SKILL.md is implementation task.
This commit is contained in:
197
.archive/2026-05-07-tdd-criteria.md
Normal file
197
.archive/2026-05-07-tdd-criteria.md
Normal file
@@ -0,0 +1,197 @@
|
||||
# TDD-критерии: когда можно без
|
||||
|
||||
**Открыто:** 2026-05-07
|
||||
**Status:** brainstorm in progress
|
||||
|
||||
---
|
||||
|
||||
## Постановка (от user)
|
||||
|
||||
Разработка через тесты показывает свою эффективность. Готов внедрить её **почти во все задачи**. Но нужно определить критерии, **когда всё-таки можно без неё** — иначе TDD превращается из инструмента в догму, и каждая задача начинает оплачивать налог TDD даже там, где он не окупается.
|
||||
|
||||
## Ключевая опасность
|
||||
|
||||
«Carve-out для категории» легко превращается в «удобную лазейку». Если правило формулировать как «без TDD можно для exploratory» — каждая задача задним числом оказывается «exploratory». Критерий должен:
|
||||
|
||||
1. Быть **bright-line** (а не «по ощущениям»)
|
||||
2. **Принуждать к моменту осознанного выбора** (не «по умолчанию проскочило»)
|
||||
3. Делать пропуск **видимым** (commit, код-ревью могут это проверить)
|
||||
|
||||
## Открытые вопросы
|
||||
|
||||
- Какой реальный опыт стоит за «TDD показывает эффективность»? — нужен якорь, чтобы критерии калибровались на фактах, а не на мнениях
|
||||
- Где живёт правило: global `~/.claude/CLAUDE.md` (override `superpowers:test-driven-development`) или проектные CLAUDE.md?
|
||||
- Как обеспечить аудит: явная маркировка в commit (`[skip-tdd: <reason>]`), или достаточно ревью diff?
|
||||
|
||||
## Рабочая гипотеза (моя стартовая позиция, до обсуждения)
|
||||
|
||||
**TDD = default.** Carve-outs допустимы **по типу артефакта**, не «по ощущению задачи».
|
||||
|
||||
**Permissive (можно без TDD):**
|
||||
- Untestable surface: CSS/visual polish, layout, config-файлы, документация, prompt-инжиниринг
|
||||
- Throwaway: явный spike/POC с обещанием выкинуть (или переписать с TDD при принятии)
|
||||
- One-shot: миграции данных, ETL backfill, ad-hoc скрипты-однодневки
|
||||
|
||||
**Ironclad (TDD обязателен):**
|
||||
- Bug fixes — всегда начинать с red-теста, воспроизводящего баг (никаких исключений: иначе не убедимся, что починили именно его)
|
||||
- Pure logic, бизнес-правила, branching
|
||||
- Публичные API / контракты
|
||||
- Security, auth/authz, money/dates/identifiers
|
||||
|
||||
**Anti-loophole:**
|
||||
- При пропуске TDD — явная маркировка в commit (`[skip-tdd: visual]`, `[skip-tdd: spike]`)
|
||||
- Spike → если код пошёл в продакшен, заводится тех-долг «backfill tests»
|
||||
|
||||
## Trade-off
|
||||
|
||||
Маркировка `[skip-tdd: ...]` создаёт friction в каждом commit. **Это и есть смысл** — friction блокирует превращение carve-out в норму.
|
||||
|
||||
---
|
||||
|
||||
## Обсуждение
|
||||
|
||||
### Раунд 1 (2026-05-07 утро) — якорный вопрос обратно к user
|
||||
|
||||
Agent не пошёл сразу в формальное multi-persona meeting (нет `scenarios/tdd-criteria.md`, буфер ещё незрел) и не стал перечислять carve-outs в вакууме. Recommend-don't-menu: **остаёмся в single-thread брейнсторме**, потому что буфер сам залочен на якорном вопросе. Personas без factual anchor сгенерят гипотетику, потратят раунды.
|
||||
|
||||
Запрошено у user: какой проект / тип артефакта; что было до TDD vs после; контр-кейс, где TDD замедлил.
|
||||
|
||||
### Раунд 2 (2026-05-07) — user указал на наблюдаемые симптомы + контекст «не все используют Claude»
|
||||
|
||||
User: «modules-db и pilonuxt буксуют, полны багов, не могут уйти в прод. Посмотри, в каких проектах TDD использовали в общих тасках» + «у нас есть claude-skills. Не все используют Клода».
|
||||
|
||||
Из второго: **правило не должно быть Claude-specific.** Локация в `~/.claude/CLAUDE.md` (как было предложено в раунде 4) — ошибка. Правило — методология, не агент-инструкция.
|
||||
|
||||
### Раунд 3 (2026-05-07) — cross-project research через projects-meta
|
||||
|
||||
Agent выполнил Step 0 freshness gate (`meta_status` → 5.4h cache → `node sync.js` → 13 проектов, 0 errors, freshly synced 2026-05-07T03:27Z), затем `tasks_search "tdd"` / `"test"` + `tasks_get` по `modules-db`, `pilonuxt`, `books`.
|
||||
|
||||
**Поиск «tdd»**: 0 хитов в активных задачах ни одного проекта. **Поиск «test»**: 1 хит (`claude-skills/hermes-mvp-coverage` — про smoke-test, не TDD).
|
||||
|
||||
**books** (контрастный кейс — фичи систематически закрываются, активные двигаются):
|
||||
- `books-ops-mcp` Phase 2 (CLOSED, прод): «**3 unit-tested helpers (TDD)** + integration tests via docker-compose + smoke 7/7». Helpers — `enforceLimit`, `checkSql`, `capPayload` (defence-in-depth для read-only SQL).
|
||||
- `books-analyst-mcp` Phase 1.1: после real-API smoke вылез баг handler-обёртки → **сначала написан `handler-body.test.js` 10 кейсов через `Configuration.fetchApi`-перехват, потом fix** (`de3360c`). Классический red-test для bug-fix.
|
||||
- `analyst-report-formats` (CLOSED): `_test/` 11 unit-тестов для 5 pluggable formatters.
|
||||
- Закрытые крупные фичи: `pluggable-scheduler` Phase 1-9, `agenda-admin-ui`, `registry-gc`, `books-analyst-mcp`. Активный затык — `ozon-knowledge-base-ingest` (anti-bot, не баги кода).
|
||||
|
||||
**modules-db** (буксует):
|
||||
- `ai-fill`: «❌ Phase 7 (Testing) — unit tests, smoke test» — тесты отложены ПОСЛЕ Phases 1-6. Test-after.
|
||||
- `fill-fields-ai-sdk-migration`: SDK v5→v6 signature change, **endpoint висит несколько дней без contract-теста**.
|
||||
- `entity-drawer-refactor`: vitest упомянут в shell-setup, но в Progress нет тестовых артефактов; Playwright smoke в Phase 5 (после refactor).
|
||||
- `hydration-mismatch` шумит на всех страницах после миграции.
|
||||
|
||||
**pilonuxt** (буксует):
|
||||
- 2 задачи в кэше. `design-system-cleanup`: битые hex'ы, font preload — нет тестов и нечем их писать. `snolla-contract-refresh` (CLOSED): пересборка клиента к внешнему контракту, без тестов; открытый item — «`pnpm build` зависает».
|
||||
|
||||
### Раунд 4 (2026-05-07) — user сообщил n=1 + увольнение автора + «реши сам»
|
||||
|
||||
User: «Это программист выбирал, я не настаивал. Теперь уже не узнаем, он уволился. Давай ты сам порешаешь».
|
||||
|
||||
Что это меняет:
|
||||
- Нельзя говорить «коллективный опыт books validates TDD» — n=1, автор недоступен, мотивация неизвестна.
|
||||
- Опираться на: (а) **observable evidence** — где TDD применялся, фичи закрылись; где не применялся (modules-db/pilonuxt), фичи буксуют. (б) **First principles** — где TDD физически дешевле/дороже исходя из природы артефакта.
|
||||
- Урок «программист уволился — его конвенции исчезли»: **правило должно быть кодифицировано вне головы конкретного человека**. Поэтому промоушен в общий artefakt — не опционально.
|
||||
|
||||
Agent в этом раунде предложил `~/.claude/CLAUDE.md` как location → корректно отвергнуто на следующем раунде user-ом (Claude-only, не для всех агентов). Промежуточная попытка fallback'а на «shared agent-agnostic wiki via `projects-meta knowledge_ingest`» тоже устарела — финальная локация в раунде 5.
|
||||
|
||||
### Раунд 5 (2026-05-07) — user поправил локацию: claude-skills, не правила
|
||||
|
||||
User: «У нас есть проект `claude-skills`. Изучи принципы. Туда его, а не в правила».
|
||||
|
||||
Agent изучил `~/projects/claude-skills/`: `README.md`, `.wiki/concepts/repo-layout.md`, `skills/project-discipline/SKILL.md`, `skills/recommend-dont-menu/SKILL.md`, `hermes/mapping.yaml`. Принципы:
|
||||
|
||||
- `skills/<name>/` — source of truth. `SKILL.md` с frontmatter (`name`, `version`, `description`).
|
||||
- `dist/<name>.skill` — committed архивы (для Claude). `dist-hermes/<category>/<name>/` — committed pre-converted Hermes-flavour tree.
|
||||
- `hermes/mapping.yaml` — required entry per skill. Modes: `auto` (apply replace-rules), `manual` (Hermes-flavour rewrite), `skip`, `pending`.
|
||||
- Конкретный прецедент `recommend-dont-menu`: rule переехал из `~/.claude/CLAUDE.md` в skill ровно по причине «Moving to a skill makes it portable: cross-agent compatibility is explicit».
|
||||
|
||||
→ TDD-criteria — это **policy skill**, его место в `claude-skills`. Локация раунда 4 (`~/.claude/CLAUDE.md`) была повторением старой ошибки; правка применена в секции «Локация правила» ниже.
|
||||
|
||||
### Раунд 6 (2026-05-07) — user сформулировал самый сильный аргумент: TDD как защита от vandalism
|
||||
|
||||
User: «возмущает, что несколько раз наблюдал, как эти уроды стирали целые простыни чужого кода и рапортовали "смотри, как я пиздато сделал, тут не получалось, но я почистил и стало пиздато". А потом по гиту собирали назад. Был бы код покрыт тестами — хуй бы они так сделали».
|
||||
|
||||
**Это аргумент про существование поведения, не про корректность.** Все мои четыре Ironclad-аргумента (bug fix, pure logic, third-party, security/money) — про защиту от *неправильного* поведения. Этот — про защиту от *удалённого* поведения.
|
||||
|
||||
Механизм: без теста контракт кода = «лежит в repo» (артефакт, не инвариант). Агент видит «грязно» → удаляет → коммитит «стало чище» → success-отчёт. Что код реализовывал реальное поведение — **нигде не записано кроме самого кода**, которого теперь нет. С тестом контракт = «X(Y)=Z» (инвариант). Удалить X = test fails = success невозможен. **Тест — адвокат поведения, когда поведения уже нет.**
|
||||
|
||||
Для контекста разработки с участием агентов (Claude, ChatGPT, будущих незнакомых) это означает: TDD-default — не «good practice», это **единственная защита от well-intentioned destruction**.
|
||||
|
||||
**Что меняется в Final decision:**
|
||||
|
||||
- Структура 4 Ironclad + 4 Permissive **сохраняется** (bright-line требует binary properties; этот аргумент — rationale, не критерий).
|
||||
- Permissive переформулируется **честнее**: не «зоны где TDD не нужен», а **«зоны принятого риска агентской дезорганизации»**. Ты явно соглашаешься: здесь агент может прибрать-удалить без signal-а, и ты accept-ишь recovery cost (eyeball-проверка для visual, throwaway по контракту для spike, one-shot после run-а уже неактуален, wrapper легко реконструировать).
|
||||
- В Ironclad: recovery cost > defending cost, поэтому signal обязателен.
|
||||
- В Final decision добавляется секция «Почему TDD-default — контракт, а не качество» как ведущий rationale.
|
||||
- В будущем `claude-skills/skills/tdd-criteria/SKILL.md` это становится ключевым «Why this exists».
|
||||
|
||||
---
|
||||
|
||||
## Final decision (2026-05-07)
|
||||
|
||||
**TDD = default. Bright-line carve-outs — четыре, по природе артефакта.**
|
||||
|
||||
### Почему TDD-default — это контракт, а не качество (ведущий rationale)
|
||||
|
||||
В контексте разработки с участием агентов (Claude, ChatGPT, нанятые программисты приходящие и уходящие) тест выполняет **функцию, которой нет ни у документации, ни у код-ревью**: он делает поведение **инвариантом**, а не артефактом.
|
||||
|
||||
- Без теста контракт = «код лежит в repo». Агент видит «грязно» → удаляет → коммитит «стало чище» → success-отчёт. Поведение **существовало только в самом коде**, который теперь удалён. Восстановление — `git revert` после того как заметили; до тех пор silent regression.
|
||||
- С тестом контракт = «X(Y)=Z». Удалить X → test fails → pipeline red → success невозможен. **Тест — адвокат поведения в момент, когда поведения уже нет.**
|
||||
|
||||
Этот аргумент сильнее остальных четырёх (bug fix / pure logic / contract / security) потому что они про *корректность* поведения, а этот — про *существование*. Без него классические аргументы решают локальную задачу, но оставляют уязвимость к well-intentioned destruction. Он — основа TDD-default; всё остальное — частные случаи.
|
||||
|
||||
### Permissive (зоны принятого риска агентской дезорганизации, skip + маркер)
|
||||
|
||||
Не «зоны где TDD не нужен», а **зоны где принимаешь риск silent deletion и accept-ишь recovery cost**. Вход в зону — явный, маркируется в commit subject.
|
||||
|
||||
| Категория | Триггер | Маркер | Recovery cost |
|
||||
|---|---|---|---|
|
||||
| Visual / config | CSS, layout, design tokens, `.env.example`, prompt-тексты, wiki, README | `[skip-tdd: visual]` | Eyeball на следующем render-е |
|
||||
| Spike | Explicit POC с обещанием выкинуть | `[skip-tdd: spike]` | Throwaway по контракту, deletion = no problem |
|
||||
| One-shot | Миграции данных, ETL backfill, скрипты-однодневки | `[skip-tdd: oneshot]` | После run-а уже неактуален |
|
||||
| Wrapper | Транзитный код без логики (re-export, glue ≤10 строк) | `[skip-tdd: wrapper]` | Reconstruct cost ≈ delete cost |
|
||||
|
||||
### Ironclad (без исключений, без маркера — TDD обязателен)
|
||||
|
||||
1. **Bug fixes** — red-test, воспроизводящий баг, **до** fix-а. Подтверждено `books-analyst-mcp` `handler-body.test.js` 10 кейсов через `Configuration.fetchApi`-перехват **до** `de3360c`.
|
||||
2. **Pure logic с bounded inputs** — helpers, parsers, validators, formatters, любая `(known input) → (known output)` без I/O. Подтверждено `books-ops-mcp` Phase 2: `enforceLimit/checkSql/capPayload`.
|
||||
3. **Third-party contract consumption** — SDK clients, API-обёртки. **Контр-кейс прямо сейчас**: `modules-db/fill-fields-ai-sdk-migration` висит после AI SDK v5→v6 без contract-теста.
|
||||
4. **Security / auth / money / identifiers** — без изменений vs стартовой гипотезы.
|
||||
|
||||
### Anti-loophole
|
||||
|
||||
- **Skip без категории не существует.** Категория — одна из четырёх явных, не «других причин». Без маркера — нарушение.
|
||||
- **Spike survivor**: если spike-код пережил merge в master → тем же merge-commit создаётся `[backfill-tests-<slug>]` task в `.tasks/STATUS.md`. Иначе категория «spike» становится loophole.
|
||||
- Friction чисто социальная (видна в `git log --oneline`); pre-commit hook опционален и обсуждается отдельно.
|
||||
|
||||
### Локация правила
|
||||
|
||||
**Корректировка после раунда 5 (user поправил).**
|
||||
|
||||
Правило живёт **как skill** в `~/projects/claude-skills/skills/tdd-criteria/SKILL.md`, не в `~/.claude/CLAUDE.md` (Claude-only) и не в `projects-wiki/concepts/` (пассивный artefact).
|
||||
|
||||
Обоснование:
|
||||
- `claude-skills` — единственный механизм с встроенным cross-agent rollout: `install.sh` → `~/.claude/skills/`, `build-hermes.py` + `mapping.yaml` → `dist-hermes/<category>/<name>/`. Любой будущий агент получает свой target.
|
||||
- Прямой прецедент — `recommend-dont-menu`. Цитата из его SKILL.md: «Original rule lived in `~/.claude/CLAUDE.md` as a per-machine instruction. Moving to a skill makes it portable: cross-agent compatibility is explicit.» Ровно тот же refactoring, который user уже один раз делал.
|
||||
- Skill — активный trigger-driven artefact: description-frontmatter + триггер-фразы → pull-механизм. Concept-страница в wiki — push-механизм («помни читать»), который не работает для cross-cutting policy.
|
||||
|
||||
Структура skill:
|
||||
- `name: tdd-criteria`, `version: 0.1.0`, multi-line `description:` с явными триггер-фразами
|
||||
- секции: «When this runs» (trigger phrases) → «Default mode (TDD-by-default)» → «Permissive carve-outs (4 категории + маркеры)» → «Ironclad (4 правила)» → «Anti-loophole» → «Out of scope» → «Why this exists»
|
||||
- mapping.yaml entry: вероятно `mode: auto` без replace-rules (правило агент-агностично — pure policy, не упоминает Claude-tool-refs); category `software-development`. Пометить `pending` если хочется отдельный Hermes-аудит.
|
||||
|
||||
Проектные CLAUDE.md могут **расширять** Permissive (например `karu` — проект чистого CSS) или **расширять** Ironclad через локальный override. **Не могут сужать Ironclad.** Триггер-line в шаблоне `project-bootstrap` **не добавляем автоматически** — skill активируется по description при релевантных запросах, форсированный pull в каждый проект — отдельное решение.
|
||||
|
||||
### Trade-off (зафиксирован честно)
|
||||
|
||||
- **Supportive evidence — n=1**. books мог быть продуктивнее по причинам не связанным с TDD. Но направление эффекта (где помогало vs где не применялось) согласуется с first principles, инверсия маловероятна.
|
||||
- **Friction в UI-итерациях**. `[skip-tdd: visual]` 50 раз подряд при `web-design-system` — раздражает. Это и есть замысел: friction — fence, не bug. Снимать не раньше 2 недель usage.
|
||||
- **Альтернатива** «не кодифицировать вовсе» отвергнута: именно «не кодифицировано» привело к ситуации, что после ухода автора в books невозможно реконструировать его критерий. Уволенный человек — argument *за* кодификацию, не *против*.
|
||||
|
||||
---
|
||||
|
||||
## Где остановились (2026-05-07)
|
||||
|
||||
- Final decision выше — готово к промоушену.
|
||||
- Status: ready-for-promotion.
|
||||
- **Resume:** запустить `meeting-room-promote-brainstorm` → routing: `domain` → target project: **`claude-skills`** (не `projects-wiki` через knowledge_ingest, не `~/.claude/CLAUDE.md`). Действие промоушена: (а) написать `~/projects/claude-skills/skills/tdd-criteria/SKILL.md` напрямую файлом + (б) добавить entry в `hermes/mapping.yaml` + (в) `bash scripts/install.sh tdd-criteria` + `bash scripts/build.sh tdd-criteria` + `python scripts/build-hermes.py` + (г) commit (push по grant-у). Импл-таски в `claude-skills/.tasks/`: (1) написать `SKILL.md` с разделами When/Default/Permissive/Ironclad/Anti-loophole/Why; (2) `mapping.yaml` entry (`mode: auto`, `category: software-development`, без replace-rules — правило агент-агностично); (3) install + build + build-hermes; (4) опционально pre-commit hook на `[skip-tdd: <one-of-four>]` (отдельная таска, не блокер). Review-таска `tdd-criteria-review` создаётся автоматически скилом промоушена.
|
||||
@@ -28,3 +28,6 @@ Events: `started`, `promoted`, `registered`, `archived`.
|
||||
2026-05-06 designed hermes-skills-rollout (.brainstorm/hermes-skills-rollout.md — domain spec, target promote: claude-skills)
|
||||
2026-05-06 promoted hermes-skills-rollout → claude-skills/.wiki/concepts/hermes-skills-rollout-design.md (commits b3dc1251+5ae14fd6+0f65e057); tasks created: claude-skills#hermes-converter-mvp (ready), claude-skills#hermes-flavour-mcp-setups (blocked), claude-skills#hermes-installer-skill (blocked), claude-skills#hermes-mvp-coverage (blocked), claude-skills#hermes-converter-ci (blocked, deferred), common#tasks-close-normalize-body (ready, discipline pre-req), claude-skills#using-tasks-close-coverage-gate (ready, discipline pre-req)
|
||||
2026-05-06 archived hermes-skills-rollout → .archive/2026-05-06-hermes-skills-rollout.md
|
||||
2026-05-07 designed tdd-criteria (.brainstorm/tdd-criteria.md — domain spec, target promote: claude-skills; 6-round arc with cross-project research via projects-meta)
|
||||
2026-05-07 promoted tdd-criteria → claude-skills/.wiki/concepts/tdd-criteria-design.md (commits a03d2804+2ad6f4ce+5fb648d4); tasks created: claude-skills#tdd-criteria-skill-write (ready), claude-skills#tdd-criteria-hermes-mapping (blocked), claude-skills#tdd-criteria-build-install (blocked), claude-skills#tdd-criteria-review (blocked, umbrella per §5)
|
||||
2026-05-07 archived tdd-criteria → .archive/2026-05-07-tdd-criteria.md
|
||||
|
||||
Reference in New Issue
Block a user