Як побудований цикл
Цикл — це дві програми, вкладені одна в одну. Supervisor працює годинами; runner виконує один юніт і виходить.
| Шар | Живе | Робить |
|---|---|---|
| supervisor | весь запуск | бере лок, запускає батчі юнітів, читає PAUSE і STOP між юнітами, реагує на коди виходу, пересиджує ліміти |
| runner | один юніт | бере задачу, робить знімок, запускає координатора, перевіряє контракти, записує результат, виходить з кодом |
| координатор | один юніт | процес claude -p: читає задачу і викликає спеціалістів |
| спеціалісти | частину юніта | кожен робить одну вузьку роботу і лишає вердикт |
| контракти | кінець юніта | scope, task file, trace, gate, closing: код, який вирішує, чи зараховується юніт |
Пройди п'ять запусків, щоб побачити, який шар зайнятий, що лежить у теці трейсу і як відпрацьовують пауза, стоп, жорстка зупинка і ліміт.
Юніти
.harness/trace/
- supervisor.lock
Кожна команда нижче приймає --project: шлях або id чи назву, під якою проєкт зареєстровано. Без нього команда бере
поточну теку, і в ній має бути тека .harness/. Приклади використовують зразковий проєкт демо, створений і зареєстрований
командою harness example ~/code/bookmarks; назва проєкту за замовчуванням — назва теки.
Старт
У дашборді натисни Start. Він запускає harness run --project <path> як відв'язаний процес з оточенням самого дашборду, а його вивід іде в
.harness/trace/supervisor.out. Кнопки Pause, Resume і Stop у дашборді пишуть ті самі файли, що й CLI;
дашборд ніколи не шле сигналів і не вбиває процесів. Його док показує supervisor як Running, Paused, Stopping (STOP
записано, юніт доробляє) або Not running. Кнопка називає, кого запускає Start: Start · claude або Start · stand-in, коли задано
AGENT_CMD чи agent.command; перший рядок дашборду в терміналі каже те саме.
З термінала:
harness run --project bookmarks
За замовчуванням запуск не закінчується, коли черга порожня: він перевіряє нову роботу кожні
supervisor.idlePollSeconds (30 секунд за замовчуванням), доки ти його не зупиниш. Людська дія будить його одразу:
подання, відповідь чи схвалення — з CLI або з дашборду — потрапляє в журнал людських записів, а supervisor у простої
перевіряє цей файл кожні supervisor.pausePollSeconds (2 секунди). Ці опції це змінюють, а
harness unit запускає один юніт, щоб спробувати:
harness run --project bookmarks --deadline 120 # a time limit for the run, in minutes
harness run --project bookmarks --max-units 5 # at most five units
harness run --project bookmarks --once-through # exit as soon as nothing is left to take
harness unit --project bookmarks # exactly one unit; exits with the runner's code
Дві команди варто запустити спершу, і жодна не витрачає токенів:
harness agents --check --project bookmarks # the registry, standing-rules drift, the model split
harness preview T-002 --project bookmarks # the exact command and prompt of the next unit
Перед першим юнітом supervisor перевіряє дві речі й відмовляється стартувати, якщо хоч одна не виконана:
- На цьому checkout не працює інший цикл. Весь запуск він тримає лок-файл
.harness/trace/supervisor.lock. - Базова гілка чиста. Він відмовляється працювати на брудному дереві, яке забруднив не він, бо контракт scope повісив би твої зміни на агента.
Одразу після рядка старту він пише рядок supervisor · billing: … — як тарифікується запуск, за оточенням, у якому він
стартує:
| Рядок | Коли |
|---|---|
billing: subscription login — usage limits, not a per-token bill; costs shown are API-equivalent | claude залогінений через підписку |
billing: ANTHROPIC_API_KEY is set — claude -p bills the API per token | в оточенні є ANTHROPIC_API_KEY |
billing: a stand-in replaces claude — no tokens | задано AGENT_CMD або agent.command |
На підписці вартість у дашборді й у harness stats — це те, скільки ті самі токени коштували б через API. Це не
рахунок.
Пауза і відновлення
harness pause --project bookmarks
harness resume --project bookmarks
harness pause створює файл .harness/trace/PAUSE. harness resume видаляє його. Кнопки Pause і Resume у
дашборді пишуть той самий файл.
Пауза не перериває юніт, що зараз працює. Supervisor читає файл між юнітами, тож поточний юніт доробляє, його контракти проганяються, і лише тоді цикл чекає.
Стоп
harness stop --project bookmarks
harness stop створює .harness/trace/STOP. Supervisor доробляє поточний юніт, виходить і не повертається, доки ти
не запустиш його знову.
Якщо треба зупинити негайно, зупини процес supervisor, який ти запустив, за його PID. Оскільки стан живе на диску,
втратиш лише юніт, що працював. Ніколи не вбивай процеси за шаблоном (pkill -f, killall): шаблон зачіпає значно
більше, ніж здається, включно з іншими сесіями Claude.
Коди виходу
Кожен юніт завершується кодом виходу від runner. Supervisor читає його і вирішує, що робити далі.
| Код runner | Значення | Що далі |
|---|---|---|
0 | юніт готовий | supervisor бере наступний юніт |
1 | жорстка зупинка: scope, файл задачі, trace, gate або падіння агента | робота юніта йде в іменований stash, задачу запарковано, supervisor продовжує |
2 | конфігурація зламана: config.json, постійні правила, що розійшлися, або суддя на моделі implementer чи tester | supervisor зупиняється: чекання не допоможе |
3 | брати більше нічого | supervisor чекає нову роботу; з --once-through завершує запуск |
4 | агент не зміг запуститися: ліміт, мережа, логін | supervisor чекає й пробує знову |
Жорстка зупинка нічого не викидає. Runner переносить зміни юніта в git stash з назвою
harness quarantine: T-NNN unit N, відновлює дерево і записує причину в задачу. Можеш переглянути stash, перш ніж
перевідкрити задачу.
У supervisor є власні коди виходу — для моменту, коли закінчується весь запуск. Числа ті самі, а значення інші: код 3 від runner — порожня черга, а код 3 від supervisor — застряглий цикл.
| Код supervisor | Чому запуск закінчився |
|---|---|
0 | його зупинили, черга спорожніла під --once-through, або вичерпано дедлайн чи --max-units |
2 | конфігурація зламана, або увімкнено deliver.pr, а бракує remote, gh, входу в нього чи базової гілки на remote |
3 | безплідний: три юніти поспіль нічого не зрушили |
4 | він здався, чекаючи на агента |
Правило безплідності не дає застряглому циклу крутитися вхолосту: три юніти поспіль, які нічого не зрушили, зупиняють його. Прогрес — це нові коміти або нові handoff, а не щось одне.
Ліміти і збої
Код 4 означає, що агент так і не взявся до роботи, тож задачу не зачеплено. Supervisor чекає і пробує знову: перевіряє
мережу кожні 30 секунд, а коли мережа в порядку, але агенту все одно відмовляють, наприклад через ліміт, бере
30-хвилинну перерву перед наступною спробою. Коли відмова називає час скидання, наприклад «You've hit your session
limit · resets 9:10pm (Europe/Kiev)», він чекає до цього часу плюс одна хвилина і пише в лог agent could not run (…) — waiting until <time>. Ліміти сесії й тижневі ліміти теж рахуються як ліміти використання. Після шести годин очікування здається і зупиняється з кодом 4. Ці
інтервали — supervisor.usageProbeSeconds, supervisor.usageNapSeconds і supervisor.usageCeilingSeconds у
config.json.
Проєкт може додати власну команду probe: у config.json, наприклад перевірку, що dev-сервер відповідає. Коли probe
падає, юніт завершується з кодом 4 і нічого не чіпає.
Не редагуй, поки працює юніт
Контракт scope порівнює все робоче дерево до і після юніта. Він не відрізнить твою правку від правки агента. Якщо ти збережеш файл у проєкті, поки працює юніт, юніт впаде, а задачу запаркують за те, що зробив ти.
Безпечний порядок:
- Постав на паузу.
- Дочекайся, поки поточний юніт завершиться. Дашборд показує, коли нічого не в роботі.
- Зроби правку і закоміть її.
- Віднови.
Виняток — дії над задачами. Approve, answer, revise та інші, з дашборда чи з CLI, проходять через журнал: harness
записує кожен людський запис у файл задачі в .harness/trace/human-writes.jsonl, а контракт scope пропускає файл
задачі, вміст якого збігається з цим записом. Схвалення, зроблене, поки працює юніт іншої задачі, зберігається.
Усередині юніта людські команди CLI відмовляються працювати, тож агент не може видати себе за тебе. Журнал зупиняє
помилки, а не агента, який підробить сам журнал.
З тієї ж причини на один checkout дозволено лише один цикл. Щоб запускати задачі паралельно, потрібен окремий git worktree на кожну задачу — це заплановано після першої версії.
Як стежити за запуском
harness status --project bookmarks
harness status --project bookmarks --json # the same, for scripts
harness stats --project bookmarks # units, time, tokens and API-equivalent cost per stage and task
harness stats --project bookmarks --json # the same, for scripts
Вигляд Live у дашборді показує більше: стан supervisor, юніт у роботі, його агентів з роллю, часом і поточним інструментом, темп і лог supervisor. Знімок, з якого він будується, містить лише назви ролей, інструментів і файлів — ніколи аргументи інструментів чи текст повідомлень.
Сповіщення в Telegram
Harness може написати тобі в Telegram, коли задача чекає на тебе
(harness/notify.ts). Йому потрібні два ключі,
TELEGRAM_BOT_TOKEN і TELEGRAM_CHAT_ID, і він шукає їх у такому порядку. Вирішує перше місце, де задано токен:
- змінні оточення;
<project>/.env;$HARNESS_HOME/.env, за замовчуванням~/.harness/.env.
Порожній токен вимикає сповіщення. harness projects add додає .env у .gitignore проєкту, тож ключі не потрапляють
у git.
| Подія | Коли спрацьовує |
|---|---|
needs-input | задача чекає твоїх відповідей |
planned | план чекає твого схвалення (лише з autonomy.mode: gated) |
prototype | раунд прототипу чекає твого рев'ю (лише з gated) |
parked | задачу запарковано, з причиною |
done | задачу підтверджено |
delivered | pull request відкрито, з посиланням |
supervisor | запуск закінчився сам, або почалося перше очікування ліміту |
Усі події увімкнені за замовчуванням; notify.events у config.json звужує список. Повідомлення містить назву
проєкту, id і назву задачі, подію і посилання на pull request. Посилань на локальний дашборд у ньому немає. Сповіщення
односторонні: harness лише надсилає. Невдале надсилання ніколи не валить юніт — воно додає один рядок
notify · … not sent у progress.log, без токена.
Кожне повідомлення в Telegram надходить один раз. Повтор — той самий проєкт і текст, якщо не зважати на id і час —
затримується на шість годин, а весь чат отримує щонайбільше шість повідомлень за хвилину. Затримане повідомлення пише в
лог notify · <event> held back: <why>, а записи лежать у $HARNESS_HOME/notify-sent.json. Тека проєкту, якої вже
немає, нічого не надсилає, а про збій агента приходить одне повідомлення.
harness notify --project bookmarks # where the keys come from; never prints the token
harness notify --project bookmarks --test # send one test message
Що лежить у теці трейсу
Усе, що цикл знає про себе, — у .harness/trace/. .gitignore проєкту не пускає в git trace/, artifacts/, tasks/
і JOURNAL.md; версіонується лише config.json. Файли задач ігноруються навмисно: runner переписує їх, коли
перемикається між базовою гілкою і гілкою задачі, а версіонований файл задачі, що відрізняється між гілками, зламав би
checkout. Історія задачі живе в її ## Log і в handoffs.jsonl.
| Файл | Що це |
|---|---|
supervisor.lock | тримає запущений supervisor; другий цикл відмовляється стартувати |
PAUSE | є файл = пауза на наступній межі юнітів |
STOP | є файл = стоп на наступній межі юнітів |
running.json | задача, над якою зараз працює юніт |
progress.log | лог supervisor |
supervisor.out | вивід supervisor, коли запуск стартував дашборд |
handoffs.jsonl | що кожен юніт передав наступному: час старту, вердикт, токени, вартість і відмови в дозволах |
unit-seq | лічильник, що нумерує юніти |
unit-NNNN.jsonl | один файл на юніт: stream-json вивід агента, кожен рядок з позначкою harness_ts; тут контракт трейсу шукає вердикт |
human-writes.jsonl | кожен людський запис у файл задачі з його хешем; контракт scope пропускає файл, що збігається |
unit-NNNN.agents.json | спеціалісти, яких передали цьому юніту через --agents |
unit-NNNN.app.json | застосунок, який runner запустив для цього юніта: його pid, порт і власник; зникає, коли застосунок зупинено |
unit-NNNN.app.log | вивід цього застосунку |
unit-NNNN.mcp.json | MCP-конфіг, який отримав юніт із браузером: сервер Playwright і його прапорці |
preview.mcp.json | той самий конфіг, який пише harness preview для етапу із застосунком |