Фаза 5.5 серии Harness Engineering: твоё «неправильно» превращается не в забытый чат, а в код. Команда /wrong ловит коррекцию, normalize разводит развилку: computational-урок (ловится машиной) уходит правкой сенсора в рельсы, inferential-урок (нужно суждение) — markdown-файлом в lessons/<boundary>/, который агент читает перед задачей (retrieve-правило). maintain дедуплицирует и промоутит созревшие inferential-уроки в сенсоры. Метрика здоровья — сколько уроков выпущено в сенсоры, а не размер тетрадки. Стек-агностично, с копируемыми промптами и интерактивом.
ПродвинутыйDevOps с AI25 минClaude Code, .claude/commands, AGENTS.md, git, boundary-linter
1
Леджер — это staging, а не склад
Ты не учишь модель знать проект — её знание стохастично и не версионируется. Вместо этого ты превращаешь каждую свою поправку в две вещи: текст, который агент читает перед задачей, и правило, которое нельзя проигнорить. Это и есть lessons-ledger — слой между context-инжинирингом (что агент попробует) и архитектурными барьерами (что ему разрешено зашипить). По месту в стопке: ADR объясняет «почему так решили» (для людей), рельсы (lint / boundaries / sensors) держат «как нельзя» (для машины), а леджер ловит «как именно агент ошибся» и постепенно отдаёт зрелые уроки вниз, в рельсы.
Главное — режим работы. Леджер это staging, а не склад. Его здоровье измеряется не размером («сколько уроков накопилось»), а пропускной способностью («сколько уроков уехало в сенсоры»). Растущая тетрадка, из которой ничего не выпускается в барьеры, — это write-only кладбище: туда пишут, оттуда не читают, агент всё равно повторяет ошибки. Об этом нужно помнить с первого дня.
🟥 Склад (write-only)
Метрика — размер тетрадки растёт
Уроки копятся, в барьеры не уходят
Агент всё равно повторяет ошибки
🟩 Staging (стекает в рельсы)
Метрика — % уроков, выпущенных в сенсоры
Зрелые inferential → computational барьеры
Знание затвердевает в проверки
Если за месяц из леджера не выпустилось ни одного урока в сенсор — это не повод писать больше уроков, а сигнал, что развилка normalize настроена неверно: всё, что детерминируемо, должно уходить в рельсы сразу, а не оседать текстом.
2
Жизнь урока: 5 станций и две развилки
У урока есть пайплайн из пяти станций: capture (поймать «неправильно» в моменте) → normalize (LLM-проход структурирует и разводит развилку) → store (markdown в git) → retrieve (агент сам подтягивает перед задачей) → maintain (фоновая уборка и промоушн). Не каждый урок проходит все пять: на развилке normalize он либо уезжает вниз, в рельсы (computational), либо вверх, в леджер (inferential), и только inferential-уроки доходят до store/retrieve/maintain.
Эксплорер ниже показывает это в двух режимах. Кликайте по станциям, чтобы открыть деталь и характерный «подводный камень» каждой. Затем прогоните два примера: computational (UI ходит в БД напрямую — выражается import-границей, уезжает в рельсы сразу) и inferential (как назвать / как грузить данные — суждение, остаётся уроком и зреет). Разница между ними — сердце всей фазы.
Lessons-ledger — коррекции как код
STAGING → RAILS
Ты не учишь модель знать проект. Ты превращаешь свои поправки в две вещи: текст, который агент читает перед задачей, и правила, которые нельзя проигнорить. Леджер — это staging, а не склад: его здоровье измеряется тем, сколько уроков уехало в сенсоры, а не сколько накопилось.
Говоришь «неправильно» прямо там, где увидел — slash-командой агента или строкой в CLI. Майнить коррекции из git-диффов можно потом, но шумно: в диффе и суть правки, и стиль, и постороннее. Начинай с явного захвата.
/wrong (Claude Code)lessons add (CLI)Cursor rule
Высокое трение убивает всё. Если захват дольше пары секунд — делать не будет никто, включая тебя.
Прогон одного урока
шаг 1/4Ловишь в ревью, говоришь «неправильно». Фиксируешь через /wrong — одной строкой, не отрываясь.
Команда и гейт
Команда (напр. 12 девов)→
PR на lessons / куратор→
Владелец архитектуры · арбитр→
Общий леджер в git
Без гейта леджер быстро забивается конфликтами и личным вкусом под видом правил. Поэтому новые уроки проходят через гейт: PR на lessons/ или один куратор как арбитр — он держит леджер связным и не даёт правилам противоречить друг другу. Generic-уроки можно делегировать, а промоушн в сенсоры и _core держать у куратора; гейт стоит оставлять лёгким, иначе арбитр станет узким местом.
Метрика здоровья
✕Размер леджера растёт — тревога: это write-only кладбище
✓% уроков, выпущенных в сенсоры — вот что должно расти
Что тут реально новое. «Capture → rule» в зачатке уже щупают (Cursor auto-rules, память моделей), но per-tool и заперто в вендоре. Незанятая ниша:
Дисциплина «леджер как staging, а не помойка» — то, что хайповые продукты пропускают, и то, что делает из «ещё одной memory» харнес-инструмент.
Прогоняйте оба примера подряд: видно, что computational-урок вообще НЕ заходит в леджер (это важно — иначе тетрадка раздувается дубликатами того, что уже зарельсено), а inferential-урок живёт как нудж и помечается кандидатом на промоушн только когда стабилизировался.
3
Развилка normalize: что в рельсы, что в леджер
Normalize — сердце системы. LLM-проход берёт сырое «так неправильно» и делает две вещи: структурирует (scope, блоки «Неверно»/«Верно», короткое «Почему») и разводит развилку computational vs inferential sensor. Это ровно то различие, на котором стоит весь харнес: контекст решает, что агент попробует; детерминированные ограничения — что ему разрешено зашипить.
Computational — правило выразимо статической проверкой (import-граница, запрещённый вызов, структурный тест). Тогда его место НЕ в леджере, а сразу в рельсах: правка boundary-линтера, а в тетрадке остаётся лишь запись status: promoted — «почему правило появилось». Inferential — суждение, вкус, контекстный выбор, который ловится только LLM-judge или человеком. Тогда это урок-файл в lessons/<boundary>/. Сомневаешься — ставь inferential + promotable: true и опиши, при каком условии дозреет. И жёсткое табу: никогда не формулируй правило-погоню-за-метрикой («поднять покрытие до N%») — это Goodhart-давление, агент ослабит ассерты ради цифры. Правильный аналог — политика («не править существующие тесты»), а не число.
⚙️ Computational → рельсы
Ловится статической проверкой
Место — boundary-линтер, НЕ урок-файл
В тетрадке — лишь запись status: promoted
Барьер: агент физически не повторит
🧠 Inferential → леджер
Суждение / вкус / контекстный выбор
Ловится LLM-judge или человеком
Урок lessons/<boundary>/, читается перед задачей
Нудж: можно проигнорить, потому зреет к промоушну
# prompts/normalize.md — превращает сырую коррекцию в урок и разводит развилку
# turns a raw correction into a lesson and splits the fork
You are the lesson normalizer for the project's agent harness.
The architecture and its boundaries live in AGENTS.md / ADR. Rely on those, not on
generic best practices. Input: a raw correction (what the dev marked "wrong") + an
optional file path / diff from context.
1. SCOPE — where the rule applies. A glob over THIS project's structure, or a named
boundary / module, or a task type. Narrower is better. Never widen beyond the example.
2. RULE — two blocks on a CONCRETE example: "Wrong" (what the agent did) and
"Right" (what it should be), plus a short "Why".
3. SENSOR — the fork:
computational — expressible as a static check (import boundary, forbidden call,
structural test, fitness function) -> belongs in the architectural constraints
(the boundary-linter), NOT the ledger. Give the concrete rule/config.
inferential — judgement / taste / context, catchable only by an LLM-judge or human
-> belongs in the ledger as a lesson.
Unsure -> inferential, promotable: true, and state the condition under which it matures.
NEVER write a rule that chases a metric (e.g. "raise coverage to N%"): that creates
Goodhart pressure. The right analog is a policy ("don't edit existing tests"), not a number.
4. Return EXACTLY one lesson markdown file (frontmatter + blocks). File only — no preamble.
5. Last line — the verdict:
VERDICT: computational -> <concrete place in the constraints>
VERDICT: inferential -> lessons/<boundary>/
One rule = one file. Several different mistakes in the input -> split into several files.
Тест на развилку в одну фразу: «можно ли это зашить в линтер?». Да → рельсы, и в леджере ему делать нечего. Нет → урок. Если на развилке всё время выходит «да» — это хорошо: значит твоя архитектура легибельна и почти всё детерминируемо.
4
Capture без трения: /wrong и шаблон урока
Захват — самое хрупкое звено: высокое трение убивает всё. Если зафиксировать коррекцию дольше пары секунд, не будет делать никто, включая тебя. Поэтому захват — одна команда в моменте, а не «потом оформлю». В Claude Code это макрос в .claude/commands/wrong.md (агент подхватывает markdown из этой папки), вызов — /wrong <в чём ошибка>. Для другого агента это просто alias или сохранённый промпт с тем же телом. Команда не пишет файл молча — она показывает урок и строку VERDICT и ждёт твоего «ок».
Сам урок — markdown-файл с frontmatter, организованный по границам/модулям проекта (lessons/<boundary>/<id>.md). Ключевые поля: scope (glob под структуру именно твоего проекта), sensor (computational | inferential), promotable (дозреет ли до барьера), status (active | promoted | archived | conflicting), seen (сколько раз встречалось — растёт). Чтобы не стартовать с пустого леджера, прогони bootstrap по последним N PR / ревью-тредам и вытащи коррекции, которые реально повторяются (seen ≥ 2) — ничего не выдумывая.
Шаблон урока — frontmatter
scope — glob/граница под структуру ТВОЕГО проекта (уже — лучше)
sensor — computational | inferential
promotable — дозреет ли до computational барьера
status — active | promoted | archived | conflicting
seen — счётчик повторов (растёт, сигнал к промоушну)
Захват дольше пары секунд — никто не делает
Команда пишет файл без твоего «ок»
# .claude/commands/wrong.md — Claude Code подхватывает команды из этой папки
# other agents: same body as a saved prompt / alias. Invoke: /wrong <what's wrong>
---
description: Capture an agent correction as a lesson (lessons-ledger)
---
I marked something as done wrong. The correction: $ARGUMENTS
If the current context has a file/diff, note its path and which boundary it touches.
Run normalize (prompts/normalize.md):
1) decide scope 2) write the "Wrong"/"Right" blocks 3) decide sensor: computational vs inferential.
Show me the lesson file and the VERDICT line. AFTER my "ok":
- inferential -> write the file to lessons/<boundary>/<id>.md (status: active)
- computational -> propose a boundary-linter rule AND log a lesson with status: promoted
(the record of "why this rule appeared")
Do not write any file before my confirmation.
Майнинг коррекций из git-диффов выглядит заманчиво, но шумно: в диффе и суть правки, и стиль, и постороннее. Начинай с явного /wrong; авто-майнинг диффов и шеринг на всю команду — не на первой неделе.
5
Retrieve: агент сам читает уроки границы
Записанный урок бесполезен, если агент не увидит его в нужный момент. Retrieve — это как урок попадает агенту перед похожей задачей. Дешёвый и надёжный механизм — не семантический поиск, а фильтр по затронутым границам проекта: трогаешь persistence/* → грузишь lessons/_global.md, lessons/_core/ и уроки этой границы. Границы модулей дают почти бесплатный сигнал релевантности. «Агент сам заглядывает» — это строка-правило в файле, который он читает на старте: AGENTS.md (универсально) и/или CLAUDE.md.
Правило простое: перед работой определи затронутые границы, прочитай их active-уроки и считай их жёсткими ограничениями — нарушает решение урок, остановись и сообщи, не делай молча. Уроки со status: promoted | archived в retrieve НЕ подаются (promoted уже в сенсорах — дублировать незачем). Самое сложное здесь — тайминг: перельёшь контекст (200 уроков) — агент их игнорит и цена растёт; недольёшь — нужный не сработал. Поэтому scope по границам, а не «всё сразу».
Задача затрагивает границу
Грузим _global + _core + <boundary>
Уроки = жёсткие ограничения
нарушает
Конфликт → стоп и доклад
не нарушает
Нет конфликта → работа
## Lessons-ledger (rules learned from mistakes) ← put in AGENTS.md / CLAUDE.md
Before working on a task:
1. Identify which boundaries / modules you touch.
2. Read lessons/_global.md, lessons/_core/ and lessons/<touched-boundary>/*.md.
3. Treat these lessons as hard constraints. If a solution violates a lesson —
stop and report, do not do it silently.
4. If I say "wrong" during the work, that is the signal to capture a lesson: /wrong.
Ignore lessons with status: archived | promoted (promoted are already in the sensors).
Не клади весь леджер в контекст «на всякий случай». Тот же принцип, что у GC-проверок: сигнал должен быть узким и релевантным. Граница модуля — это и есть готовый дешёвый фильтр; семантику подключай, только если границ не хватает.
6
Maintain: дедуп, промоушн в сенсоры, метрика
Maintain — это «сборщик мусора» леджера, маленькие непрерывные платежи вместо разовой чистки. Фоновый job того же класса, что GC-проверки фазы 5 (на том же планировщике — CI / cron / n8n), раз в неделю или руками. Он только ПРЕДЛАГАЕТ диффы, ничего не применяет молча. Четыре функции: дедуп/мерж близких уроков (с суммой seen); ловля противоречий (два конфликтующих урока на один scope → пометить оба conflicting и вынести человеку, победителя сам не выбирает); промоушн (стабильные inferential-уроки с promotable: true, которые не менялись N дней и набрали seen ≥ M → предложить конкретное правило для boundary-линтера, исходный урок пометить status: promoted — архив, не удаление); гигиена (promoted/archived в retrieve не подаются).
И главное, что выдаёт maintain в отчёте, — не размер леджера, а сколько уроков выпущено в computational sensors за период. Это и есть метрика здоровья. Леджер должен стекать в рельсы: inferential-урок — нудж, его можно проигнорить, поэтому ценность в том, что зрелое знание затвердевает в барьер, а не лежит вечно текстом. На этом замыкается ядро автономности: коррекции, которые GC не ловит (недетерминируемые ошибки вкуса), проходят через леджер и часть из них становится теми самыми барьерами фазы 3. Дальше серия продолжается фазами 6–8 (skills, workflow, тиражирование); полная карта — в хабе.
# prompts/maintain.md — gardener of the ledger; PROPOSES diffs, applies nothing silently
You are the ledger keeper. Input: all files in lessons/**.
1. DEDUP / MERGE: lessons saying the same thing -> merge into one, keep the clearest
example, sum their seen.
2. CONFLICTS: pairs that contradict for the same scope -> mark both status: conflicting
and surface to the human. Do NOT pick a winner yourself.
3. PROMOTION (machine over text): inferential lessons with promotable: true that are
stable (unchanged N days AND seen >= M) -> propose a concrete boundary-linter rule /
structural test, mark the source lesson status: promoted (archive, not delete).
4. HYGIENE: lessons with status: promoted | archived are not served on retrieve.
Output: the diffs; a health summary (active / promoted / conflicting counts);
and ABOVE ALL — how many lessons graduated into computational sensors this period.
That is the metric, not the ledger's size.
Дедуп считай по scope, а не по тексту: два урока про одну границу, сказанные разными словами, — это один урок с суммой seen. Иначе «промоушн стабильных» никогда не сработает, потому что seen размазан по дубликатам и порог M не достигается.
Результат
Фаза 5.5 собрана: твои коррекции перестали теряться в чате и стали кодом. Ты понимаешь пайплайн урока (capture → normalize → store → retrieve → maintain), главную развилку normalize (computational уходит в рельсы сразу, inferential живёт уроком и зреет к промоушну), как сделать захват без трения через /wrong, как агент сам подтягивает уроки границы по retrieve-правилу и как maintain дедуплицирует и промоутит созревшее в сенсоры. И главное — метрика здоровья: сколько уроков выпущено в барьеры, а не размер тетрадки, плюс анти-Goodhart дисциплина (никаких правил-погонь-за-процентом). Это ядро автономности серии; дальше — фазы 6–8 (skills, workflow, тиражирование), полная карта в хабе.