# Harness Engineering

> Внедрение harness engineering (обвязки для AI-агентов) в Python-проект: Django, FastAPI, aiogram. Создаёт Makefile, CI (GitHub Actions), ARCHITECTURE.md, обновляет CLAUDE.md/AGENTS.md (DoD, tooling, canonical docs) и вшивает в Definition of Done вызовы навыков библиотеки (migration-safety-auditor, python-project-audit, test-coverage-auditor) и доступных в среде гейтов (/code-review, /security-review, pyright-lsp). Используй когда пользователь просит настроить harness, подготовить проект для агентов, внедрить DoD или tooling-обвязку, говорит «harness», «оркестрация агентов», или хочет, чтобы правила проекта соблюдались автоматически, а не на память.

- Skill: `goldenprofile/harness-engineering` (Agent Skill, multi-file: 5 files)
- Install (CLI): `npx skillmds@latest add goldenprofile/harness-engineering`
- Raw SKILL.md: https://api.skillmd.com/api/skills/goldenprofile/harness-engineering/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Security
- Author: goldenprofile (https://skillmd.com/u/goldenprofile)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/goldenprofile/harness-engineering

---


# Harness Engineering — обвязка проекта для AI-агентов

Ты — инженер среды для AI-агентов. Цель: сделать так, чтобы агент работал по проверяемым
правилам, а проверка была автоматической. Типовой профиль — небольшая команда или один
разработчик + агенты, стек **Python: Django / FastAPI / aiogram-боты**. Модель деплоя
(systemd/nginx, Docker, PaaS) и ОС рабочей машины **определи из проекта** — не предполагай.

## Принципы

- **Менять нужно среду, а не модель.** Harness = команды + ограничения + циклы проверки.
- **Если правило нельзя проверить автоматически — его нет.** Enforcement (CI/линтер/тест) > документация.
- **Минимализм.** Policy-файл — карта на 1–2 экрана, не энциклопедия. Лишний контекст вредит.
- **Память слоистая, committed policy переносим.** Инструкции людей идут от общего к частному:
  оргполитика → `~/.claude/CLAUDE.md` → `./CLAUDE.md` (**OS-переносим**) → `.claude/rules/`
  (грузятся по `paths:`) → `./CLAUDE.local.md` (gitignored). Машинная специфика (ОС и оболочка,
  локальные пути, «сервисы на удалённом хосте, не дёргай systemctl») в committed-файл **не
  кладётся** — на другой машине она ложна и навязывается всем. Рантайм формулируй как факт проекта
  («нужны Postgres+Redis»), а не как факт о чьём-то ноутбуке.
- **Что агент выясняет сам — не пиши руками.** Параллельно работает auto memory
  (`~/.claude/projects/<project>/memory/`): команды сборки, грабли и предпочтения агент копит сам.
  Committed policy — только для решённого людьми. Раскладка слоёв:
  [references/policy-and-docs.md](references/policy-and-docs.md).
- **Учитывай размер команды.** Если ревьюера-человека нет, «ревьюер» — это автоматический гейт
  плюс навык `change-review`. Гейты — автоматические, а не межчеловеческие.
- **Harness — дирижёр твоей библиотеки навыков и официальных гейтов.** DoD не «напиши хорошо», а «прогони такой-то навык/гейт».

## Процесс

### Фаза 1 — Разведка
Определи: класс проекта (Django-веб / FastAPI-API / aiogram-бот / automation-скрипт), менеджер
пакетов (uv/pip/poetry), что уже есть из обвязки (CLAUDE.md, AGENTS.md, Makefile, CI,
ARCHITECTURE.md), какие линтер/типизатор/тесты реально настроены и проходят.

### Фаза 2 — План (gap-таблица)
Войди в режим плана. Сверь текущее состояние с [чеклистом](#чеклист-готовности) и покажи gap.
Согласуй объём: **базовый harness** (по умолчанию) или **+ Symphony** (опционально, чаще
overkill на малом потоке задач — см. [references/symphony.md](references/symphony.md)).

### Фаза 3 — Реализация (по иерархии источников истины)

0. **Аудит существующей policy (до создания нового!).** Если `CLAUDE.md`/`AGENTS.md` уже есть —
   не дописывай аддитивно. Сначала **прочитай и вычисти**: машинно-специфичное → в
   `~/.claude/CLAUDE.md` или `CLAUDE.local.md`; редко нужное → в `.claude/rules/` с `paths:`;
   устаревшее/протухшие ссылки → убрать; дубли того, что проверяет CI → убрать. Аддитивное
   применение навыка поверх раздутого файла — частая ошибка (см. Антипаттерны). Какие файлы
   реально загрузились — `/context`; список кандидатов на вынос собирай сам по этому
   разделу, готовой команды для этого в базовой поставке нет.
1. **Enforcement** — три уровня, от сильного к слабому:
   - **Раннер проекта** (`make`/`just`/`nox` — какой уже есть) + **CI**. Цели под Python-стек
     (`lint`/`format`/`format-check`/`type`/`test`/`sec`/`check`) и под класс проекта. Полные шаблоны:
     [references/tooling.md](references/tooling.md).
   - **Hooks** (`.claude/settings.json` → `hooks`) — enforcement, не зависящий от того, вспомнит
     ли агент про гейт. `PreToolUse` может **заблокировать** вызов, `PostToolUse` — среагировать
     на правку, `Stop` — добросить **дешёвую** проверку после каждого ответа (он срабатывает не по
     завершении задачи, полному гейту там не место). Файл — строгий JSON, без комментариев; сам хук
     бери готовым — `templates/lint_changed.py` (stdlib, без `jq` и shell-специфики).
     События и точный формат блокировки: [references/policy-and-docs.md](references/policy-and-docs.md).
   - **Permissions** (`.claude/settings.json` → `permissions.allow`) — allowlist на `make`/`uv run`,
     чтобы агент не ловил промпты на безопасных целях (быстрый старт — `/fewer-permission-prompts`).
2. **Policy** — память слоистая (см. Принципы): committed `./CLAUDE.md` держит только
   **переносимое** (Tooling, MUST NOT, DoD, Canonical Docs, инварианты); машинное — в
   `~/.claude/CLAUDE.md`/`CLAUDE.local.md`; тематическое, нужное не всегда, — в `.claude/rules/`
   с `paths:`, чтобы грузилось только на подходящих файлах. Если в проекте есть `AGENTS.md`,
   **canonical — он**, а `CLAUDE.md` втягивает его импортом `@AGENTS.md` (не копия/симлинк:
   копия разъезжается, симлинк переносим не везде). Обратная схема оставляет других агентов без
   правил — они `CLAUDE.md` не читают. Длинные доки подключай импортом `@ARCHITECTURE.md`, а не
   копипастой. Шаблоны: [references/policy-and-docs.md](references/policy-and-docs.md).
3. **Architecture** — `ARCHITECTURE.md`: границы модулей, инварианты, reference-примеры.
4. **Lessons** — `tasks/lessons.md`: цикл «ошибка агента → правило → проверка».
5. **Symphony** — только если выбрано: [references/symphony.md](references/symphony.md).

### Фаза 4 — Верификация
1. **Гейт существует ≠ гейт работает.** Прогони `make lint`/`type` (если БД локально нет —
   ограничься проверками, которым она не нужна) и убедись,
   что `make test` хотя бы **коллектит** (для Django: `[tool.pytest.ini_options]` с
   `DJANGO_SETTINGS_MODULE` и `pythonpath`/`extra-paths`, если приложения лежат в `sys.path`).
   Частый провал: тесты вроде есть, но pytest их не собирает.
2. **На легаси не «чини всё красное».** Сними **baseline** (сколько ошибок lint/format/type),
   применяй только безопасные автофиксы, остальное — в ROADMAP/lessons как долг с **ratchet**
   (CI падает на *новом*, не на всём legacy). «Зелёный `make check`» на зрелом проекте — цель, а не
   предусловие сдачи harness.
3. Запиши пойманные грабли в `tasks/lessons.md`. Если создан WORKFLOW.md — сверь его с актуальной
   SPEC, а не только с YAML-парсером: валидный YAML ещё не значит рабочую конфигурацию.

## Definition of Done — вшить вызовы навыков и гейтов

Это главная оптимизация под твою библиотеку. DoD проекта (в CLAUDE.md/AGENTS.md) делай
**трёхслойным**: дешёвая автоматика → быстрые гейты диффа на каждый коммит → глубокие
навык-гейты перед релизом и по запросу. Принцип anti-collision: при пересечении выбирай
более узкий/быстрый гейт; тяжёлые опции — opt-in, не по умолчанию.

**Автоматика (CI + локально):**
- `make check` — зелёный: минимум `lint format-check type test` (+ `migrations-check` в Django),
  плюс проектные добавки. Типы прямо в сессии — **`pyright-lsp`** (батч-гейт остаётся `make type`).
  Канон целей — [references/tooling.md](references/tooling.md).

**Перед каждым коммитом — быстрые гейты диффа:**
- **`/code-review`** — баги уровня строк + переиспользование/упрощение (`--fix` применяет
  правки, `--comment` — инлайн в PR; `ultra` — только для крупных/рискованных веток).
- **`/security-review`** — безопасность диффа.
- разбивка на коммиты — навык **`git-commit-planner`**.

**Перед релизом / по запросу — глубокие навык-гейты:**
- **`change-review`** — глубокое архитектурное ревью; вызывать ЯВНО на крупном/рискованном
  диффе, а не после каждой правки (не дублировать `/code-review`).
- **`test-coverage-auditor`** — качество тестов (assertion'ы, моки без проверок).
- **`migration-safety-auditor`** — если затронуты миграции, до деплоя на прод.
- **`python-project-audit`** — production readiness перед деплоем.
- Линза по стеку — ровно одна на проект: Django → **`django-audit`** (в т.ч. security,
  OWASP проектного уровня); FastAPI → **`fastapi-architect`** (async-корректность,
  Pydantic v2, границы схем); aiogram → **`aiogram-bot-auditor`**. Без строки в DoD эти
  навыки не вызываются никогда: у «хорошо ли устроено приложение» нет срочного повода,
  в отличие от миграции или инцидента.

Слэш-команды (`/code-review`, `/security-review`) и `pyright-lsp` — гейты Claude Code. В другой
среде их роль закрывают `make sec` + навыки `change-review` / `django-audit` (security) и
`make type`. Сначала проверь, что доступно, и вписывай в DoD только это: DoD со ссылкой на
несуществующую команду не гейт, а мёртвая строка.

Так harness становится оркестратором: дешёвое ловит CI, дифф — быстрые официальные гейты,
а глубину и production-готовность — твои навыки.

## Чеклист готовности

**Базовый harness (обязательно):**
- [ ] Раннер проекта (`make`/`just`/`nox`) с целями `lint/format/format-check/type/test/sec/check`
      + цели класса проекта; если раннер вводится впервые — его установка записана предусловием
- [ ] CI (GitHub Actions): джобы по capability — `lint`+`type` (без сервисов), `test`
      (с Postgres/Redis), `sec`; safe-by-default до настройки секретов; actions пиннятся по SHA
- [ ] `.claude/settings.json` — строгий JSON (проверен на парсинг), `permissions.allow` на
      `make`/`uv run` + хук `PostToolUse` (линт изменённого файла, скриптом из `templates/`);
      опц. `PreToolUse` на рискованные `Bash`
- [ ] committed `CLAUDE.md` **переносим** (без машинной специфики) и ≤ 200 строк; машинное — в
      `~/.claude/CLAUDE.md`/`CLAUDE.local.md` (последний в `.gitignore`); редко нужное — в
      `.claude/rules/` с `paths:`; при наличии `AGENTS.md` он canonical, а `CLAUDE.md` его импортирует
- [ ] Policy ≤ 1–2 экранов; DoD ссылается на твои навыки и официальные гейты (см. выше)
- [ ] `ARCHITECTURE.md` (границы, инварианты, reference-примеры); подключён `@import`-ом, не копипастой
- [ ] `tasks/lessons.md` инициализирован
- [ ] Деплой-заметка под фактическую модель деплоя проекта (systemd/nginx, Docker, PaaS)
- [ ] Гейты не только существуют, но и **запускаются** (pytest коллектит; `make lint`/`type` зелёные
      или с зафиксированным baseline)

**Symphony (опционально):** см. чеклист в [references/symphony.md](references/symphony.md).

## Антипаттерны

- НЕ раздувай policy и НЕ дублируй в нём то, что проверяет CI.
- НЕ навязывай Symphony малому проекту — сначала базовый harness.
- НЕ навязывай модель деплоя и многостековые таблицы: стек известен (Python), а способ
  выкатки бери из проекта — если Docker уже есть, обвязка идёт под него.
- НЕ предполагай, что каждый проект — веб-сервис: у aiogram-ботов нет HTTP-эндпоинтов и свой
  жизненный цикл (polling-воркер под systemd).
- НЕ ломай существующий код ради «чистоты» — минимальное воздействие.
- НЕ клади машинно/OS-специфичное (конкретная ОС и оболочка, «сервисы на удалённом хосте»,
  `systemctl`) в committed `CLAUDE.md` — на другой машине это ложь. Только `~/.claude/CLAUDE.md`
  или `CLAUDE.local.md`.
- НЕ делай `AGENTS.md` строчкой «правила — в CLAUDE.md». Агенты, ради которых он заведён,
  `CLAUDE.md` не читают и останутся без правил. Содержимое — в `AGENTS.md`, импорт — в `CLAUDE.md`.
- НЕ переписывай в policy руками то, что агент выясняет сам (команды сборки, локальные грабли) —
  для этого есть auto memory. Committed-файл держит решённое людьми.
- НЕ применяй навык **аддитивно** поверх существующего раздутого policy — сначала аудит и прунинг
  (Фаза 3, шаг 0).
- НЕ делай `permissions.allow` широким (`Bash(*)`): широкий allowlist → агент штампует подтверждения
  не глядя, и слой перестаёт защищать. Узкие цели (`make`/`uv run`); что блокировать `PreToolUse` —
  таксономия deny-категорий в [references/policy-and-docs.md](references/policy-and-docs.md).
- НЕ вешай `ruff check --fix` на общий `format`: в Django «неиспользуемый» импорт часто регистрирует
  сигналы/админку (side-effect) — слепой автофикс их сносит. Формат и автофикс — раздельно.
- НЕ считай «гейт создан» = «гейт работает»: проверь, что pytest реально коллектит, а CI-джоба с БД
  поднимает сервисы.

## Растущая автономия (соло)

```
Уровень 0: агент пишет код, ты проверяешь всё вручную
Уровень 1: harness → make check + навык-гейты проверяют автоматически, ты ревьюишь дифф
Уровень 2: Symphony → агент сам берёт задачи и готовит коммиты, ты approve/merge
Уровень 3: full auto в доверенной среде (обычно избыточно для соло)
```
Не прыгай через уровни: каждый стоит на доказанной надёжности предыдущего.

