Підключи свій репозиторій
harness projects add ~/code/my-app --name my-app
harness projects list
Add a project на сторінці Projects у дашборді робить те саме. projects add реєструє репозиторій у
$HARNESS_HOME/projects.json, за замовчуванням ~/.harness/projects.json. У твоєму репозиторії з'являється:
| Шлях | Що це | У git |
|---|---|---|
.harness/config.json | налаштування проєкту | так, версіонується |
.harness/tasks/ | один Markdown-файл на задачу | ні |
.harness/trace/ | докази, які лишає кожен юніт | ні |
.harness/JOURNAL.md | нотатки, які агенти лишають наступному юніту | ні |
Ці шляхи, а також .harness/artifacts/ і .env, дописуються в .gitignore проєкту. Тека вважається проєктом
harness, лише коли в ній є .harness/config.json. harness projects remove <id|name> знімає проєкт з реєстру і ніколи
не видаляє файлів.
Далі створи першу задачу: harness new --title "…" --project my-app або New task у дашборді.
config.json
projects add пише лише ключі, які ти, найімовірніше, зміниш:
{
"baseBranch": "main",
"autonomy": {
"mode": "gated"
},
"limits": {
"bounces": 2,
"prototypeRounds": 3,
"unitTimeoutMinutes": 60
},
"gate": null,
"probe": null,
"deliver": {
"pr": false,
"remote": "origin"
}
}
Решта ключів лишається стандартною в коді, тож змінене значення за замовчуванням доходить до кожного проєкту.
| Ключ | За замовчуванням | Що робить | Детальніше |
|---|---|---|---|
baseBranch | main | гілка, від якої стартує кожна гілка задачі | Життя задачі |
autonomy.mode | gated | gated: план і прототип чекають на тебе; full: рухаються далі самі | Життя задачі |
limits.bounces | 2 | скільки відмов паркує задачу для тебе | Контракти |
limits.prototypeRounds | 3 | найбільша кількість раундів прототипу на задачу | Життя задачі |
limits.unitTimeoutMinutes | 60 | юніт, що триває довше, — жорстка зупинка | Контракти |
gate | null | команда перевірки проєкту; власний gate: задачі має пріоритет | Контракти |
probe | null | команда, яка має завершитися з кодом 0 до старту юніта | Контракти |
deliver.pr, deliver.remote | false, origin | пушити гілку і відкривати pull request для готової задачі | Життя задачі |
Ключі, які можна додати, коли знадобляться:
| Ключ | За замовчуванням | Що робить | Детальніше |
|---|---|---|---|
roles.models | координатори sonnet, суддя opus | модель для кожної ролі; суддя має відрізнятися від implementer і tester | Ролі |
roles.mergeQaJudge | false | один юніт рев'ю на задачу замість двох | Ролі |
notify.events | усі сім | які події надсилати в Telegram | Керування циклом |
agent.command | null | команда, що підміняє claude, як AGENT_CMD | |
agent.permissionMode | dontAsk | claude --permission-mode: відмовляти в недозволеному замість чекати | |
agent.allowedTools | список | claude --allowedTools; read-only grep, cat, head, tail і wc, а також sleep, там є; комітів і пушів немає навмисно | |
agent.settingSources | project,local | claude --setting-sources: твої особисті хуки не спрацьовують у циклі | |
supervisor.* | у коді | інтервали опитування й очікування, а також barrenUnits: 3 | Керування циклом |
app | null | запускати застосунок проєкту і браузер для юнітів qa і prototype, описано нижче |
Твій застосунок для qa і prototype
Задай app, коли юніт має бачити твій проєкт запущеним. Застосунок запускає runner, а не агент:
"app": { "start": "pnpm exec next dev -p {port}" }
| Ключ | За замовчуванням | Що робить |
|---|---|---|
app.start | обов'язковий | команда, що запускає застосунок; без {port} конфіг відхиляється (код 2) |
app.ready | "/" | шлях, який runner опитує, доки застосунок не відповість |
app.timeoutSeconds | 60 | скільки runner чекає на цю відповідь |
app.host | "localhost" | хост в адресі, яку отримують юніти; dev-сервер Next 16 віддає свій сокет живого перезавантаження лише на localhost, тож сторінка на 127.0.0.1 ніколи не гідрується |
app.stages | ["qa", "prototype"] | етапи юнітів, які отримують застосунок і браузер |
app.browser | "@playwright/mcp@0.0.83" | пакет Playwright MCP, через який браузер працює з npx, закріплена версія |
Проєкт-приклад задає "app": { "start": "pnpm exec next dev -p {port}", "ready": "/" }, тож його юніти qa і prototype
отримують застосунок на http://localhost:<port>.
Перед кожним юнітом цих етапів runner бере вільний порт, запускає команду в окремій групі процесів і чекає на
застосунок. Юніт отримує адресу в HARNESS_APP_URL і рядком у ## This run, який також каже агенту не запускати й не
зупиняти сервер. Після юніта, хоч би чим він закінчився, runner зупиняє саме цю групу процесів. Якщо застосунок не
стартував, Log задачі пише чому, і юніт працює без нього. Запуск також зупиняє застосунок, чий runner помер, і пише
про це в лог.
Runner запускає застосунок до того, як зафіксує дерево для контракту scope, тож файли, які сервер пише на старті, —
не юнітові. Ще він прибирає з оточення застосунку маркери AI-агента: Next.js 16.4 пише AGENTS.md у проєкт, коли бачить агента,
і такий зайвий файл запаркував би read-only юніт. Так само зникають NODE_ENV і внутрішні змінні Next (__NEXT_*,
NEXT_PRIVATE_*, NEXT_RUNTIME, NEXT_DEPLOYMENT_ID, TURBOPACK*), тож застосунок, запущений supervisor-ом із
дашборду, працює як звичайний next dev. Відстежуваний файл, який застосунок переписав за роботи, після зупинки
повертається з git, і Log задачі його називає.
Браузер
Юніт етапу з app.stages отримує ще й браузер, але лише коли його застосунок стартував: нема застосунку — нема
браузера. Жоден інший юніт не отримує MCP-сервера взагалі. Runner пише .harness/trace/unit-NNNN.mcp.json і передає
його через --mcp-config … --strict-mcp-config, додає в allowlist mcp__playwright, що дозволяє всі інструменти цього
одного сервера, і задає HARNESS_MCP_CONFIG. Сервер запускається як
npx -y <app.browser> --headless --isolated --browser chrome, тож npx має бути в PATH: harness agents --check
попереджає, коли його немає, а код виходу не змінюється. harness preview показує ці прапорці для етапу із
застосунком.
Координатор етапу передає адресу застосунку своєму спеціалісту, бо спеціаліст бачить лише свій промпт виклику.
Спеціаліст, який перелічує свої інструменти, отримує mcp__playwright у JSON --agents; суддя — ніколи.
Playwright MCP зберігає названий скриншот у корені проєкту, а не в теці артефактів. Після юніта з браузером runner
переносить нові невідстежувані файли png, jpg і webp з кореня проєкту в .harness/artifacts/T-NNN/ і записує кожен у
Log задачі. Юніт без браузера все одно паркується через такий файл як через stray file.
Свій спеціаліст
Спеціаліст — це один Markdown-файл. Поклади його в <project>/.claude/agents/. Harness зливає цю теку зі своїми
базовими агентами, а файл з іменем базового агента підміняє його.
---
name: a11y-reviewer
description: Checks every changed screen for keyboard access and contrast, and reports what fails.
model: sonnet
tools: Read, Grep, Glob
stages: [qa]
when: in the qa stage of a `kind: ui` task
---
You review accessibility. You change nothing.
| Поле | Що означає |
|---|---|
name | ім'я агента; якщо його немає, береться ім'я файлу |
description | обов'язкове: один рядок, за яким координатор вирішує, чи викликати агента |
model | його модель; roles.models.<name> її перевизначає |
tools | інструменти, якими він може користуватися |
stages | етапи, що його пропонують; без stages його пропонує кожен етап |
when | підказка координатору: в яких задачах його викликати |
Потім додай у файл постійні правила і перевір реєстр:
harness agents --sync --project my-app
harness agents --check --project my-app
Координатор кожного етапу читає запропонованих спеціалістів і викликає тих, які потрібні задачі.
Змінні оточення
| Змінна | Що змінює |
|---|---|
HARNESS_HOME | де лежать реєстр проєктів і запасний .env; за замовчуванням ~/.harness |
HARNESS_AGENTS_DIR | тека базових агентів; за замовчуванням agents/ у harness-demo |
AGENT_CMD | команда, що підміняє claude для кожного юніта; нею користується fake agent |
HARNESS_GH | команда, що підміняє gh для доставки |
TELEGRAM_BOT_TOKEN, TELEGRAM_CHAT_ID | сповіщення в Telegram, описані в Керуванні циклом |
Перед першим справжнім запуском
- Базова гілка чиста й актуальна. Supervisor відмовляється працювати на брудному дереві, яке забруднив не він.
- Гейт проходить, коли ти запускаєш його команду вручну.
harness agents --checkзакінчується наok.harness preview T-NNNпоказує той промпт, якого ти чекаєш.- Перший запуск короткий: з
--once-throughабо--max-units <n>, і з відкритим дашбордом.