resolver-cow 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/README.md +71 -0
  2. package/assets/adapters/spec-box/instructions.md +44 -0
  3. package/assets/ci/Dockerfile +9 -0
  4. package/assets/ci/github-cow-run.yml +85 -0
  5. package/assets/ci/github-tests.yml +25 -0
  6. package/assets/project/architecture.md +29 -0
  7. package/assets/project/contracts.md +29 -0
  8. package/assets/project/conventions.md +29 -0
  9. package/assets/project/decisions.README.md +3 -0
  10. package/assets/project/glossary.md +13 -0
  11. package/assets/project/overview.md +29 -0
  12. package/assets/project/testing.md +33 -0
  13. package/assets/project/workflow.md +29 -0
  14. package/assets/roles/challenger.md +33 -0
  15. package/assets/roles/distiller.md +18 -0
  16. package/assets/roles/implementer.md +39 -0
  17. package/assets/roles/planner.md +57 -0
  18. package/assets/roles/researcher.md +40 -0
  19. package/assets/roles/reviewer.md +68 -0
  20. package/assets/roles/tester.md +36 -0
  21. package/assets/roles/verifier.md +56 -0
  22. package/assets/schema/cow-answer.schema.json +42 -0
  23. package/assets/schema/default.yaml +116 -0
  24. package/assets/templates/coverage.yaml +12 -0
  25. package/assets/templates/design.md +39 -0
  26. package/assets/templates/proposal.md +27 -0
  27. package/assets/templates/tasks.md +9 -0
  28. package/assets/templates/test-plan.md +7 -0
  29. package/bin/cow.js +5 -0
  30. package/dist/adapters/host/claude/index.js +94 -0
  31. package/dist/adapters/host/claude/index.js.map +1 -0
  32. package/dist/adapters/repo/git.js +81 -0
  33. package/dist/adapters/repo/git.js.map +1 -0
  34. package/dist/adapters/repo/github/index.js +136 -0
  35. package/dist/adapters/repo/github/index.js.map +1 -0
  36. package/dist/adapters/repo/index.js +4 -0
  37. package/dist/adapters/repo/index.js.map +1 -0
  38. package/dist/adapters/repo/local.js +47 -0
  39. package/dist/adapters/repo/local.js.map +1 -0
  40. package/dist/adapters/runner/claude.js +129 -0
  41. package/dist/adapters/runner/claude.js.map +1 -0
  42. package/dist/adapters/runner/codex.js +151 -0
  43. package/dist/adapters/runner/codex.js.map +1 -0
  44. package/dist/adapters/runner/index.js +4 -0
  45. package/dist/adapters/runner/index.js.map +1 -0
  46. package/dist/adapters/spec/index.js +3 -0
  47. package/dist/adapters/spec/index.js.map +1 -0
  48. package/dist/adapters/spec/spec-box/delta.js +198 -0
  49. package/dist/adapters/spec/spec-box/delta.js.map +1 -0
  50. package/dist/adapters/spec/spec-box/index.js +92 -0
  51. package/dist/adapters/spec/spec-box/index.js.map +1 -0
  52. package/dist/adapters/spec/spec-box/yaml.js +74 -0
  53. package/dist/adapters/spec/spec-box/yaml.js.map +1 -0
  54. package/dist/cli/commands/artifacts.js +86 -0
  55. package/dist/cli/commands/artifacts.js.map +1 -0
  56. package/dist/cli/commands/change.js +106 -0
  57. package/dist/cli/commands/change.js.map +1 -0
  58. package/dist/cli/commands/changeset.js +81 -0
  59. package/dist/cli/commands/changeset.js.map +1 -0
  60. package/dist/cli/commands/ci.js +45 -0
  61. package/dist/cli/commands/ci.js.map +1 -0
  62. package/dist/cli/commands/coverage.js +45 -0
  63. package/dist/cli/commands/coverage.js.map +1 -0
  64. package/dist/cli/commands/deliver.js +70 -0
  65. package/dist/cli/commands/deliver.js.map +1 -0
  66. package/dist/cli/commands/doctor.js +53 -0
  67. package/dist/cli/commands/doctor.js.map +1 -0
  68. package/dist/cli/commands/host.js +30 -0
  69. package/dist/cli/commands/host.js.map +1 -0
  70. package/dist/cli/commands/init.js +93 -0
  71. package/dist/cli/commands/init.js.map +1 -0
  72. package/dist/cli/commands/protocol.js +145 -0
  73. package/dist/cli/commands/protocol.js.map +1 -0
  74. package/dist/cli/commands/run.js +157 -0
  75. package/dist/cli/commands/run.js.map +1 -0
  76. package/dist/cli/commands/spec.js +84 -0
  77. package/dist/cli/commands/spec.js.map +1 -0
  78. package/dist/cli/context.js +18 -0
  79. package/dist/cli/context.js.map +1 -0
  80. package/dist/cli/main.js +57 -0
  81. package/dist/cli/main.js.map +1 -0
  82. package/dist/cli/output.js +31 -0
  83. package/dist/cli/output.js.map +1 -0
  84. package/dist/core/archive.js +39 -0
  85. package/dist/core/archive.js.map +1 -0
  86. package/dist/core/change.js +227 -0
  87. package/dist/core/change.js.map +1 -0
  88. package/dist/core/changeset.js +92 -0
  89. package/dist/core/changeset.js.map +1 -0
  90. package/dist/core/config.js +130 -0
  91. package/dist/core/config.js.map +1 -0
  92. package/dist/core/coverage.js +69 -0
  93. package/dist/core/coverage.js.map +1 -0
  94. package/dist/core/deliver.js +176 -0
  95. package/dist/core/deliver.js.map +1 -0
  96. package/dist/core/diagnostics.js +12 -0
  97. package/dist/core/diagnostics.js.map +1 -0
  98. package/dist/core/errors.js +12 -0
  99. package/dist/core/errors.js.map +1 -0
  100. package/dist/core/gates.js +76 -0
  101. package/dist/core/gates.js.map +1 -0
  102. package/dist/core/lock.js +57 -0
  103. package/dist/core/lock.js.map +1 -0
  104. package/dist/core/packet.js +162 -0
  105. package/dist/core/packet.js.map +1 -0
  106. package/dist/core/paths.js +63 -0
  107. package/dist/core/paths.js.map +1 -0
  108. package/dist/core/phases.js +195 -0
  109. package/dist/core/phases.js.map +1 -0
  110. package/dist/core/pr-body.js +58 -0
  111. package/dist/core/pr-body.js.map +1 -0
  112. package/dist/core/project-docs.js +246 -0
  113. package/dist/core/project-docs.js.map +1 -0
  114. package/dist/core/protect.js +24 -0
  115. package/dist/core/protect.js.map +1 -0
  116. package/dist/core/repo-host.js +11 -0
  117. package/dist/core/repo-host.js.map +1 -0
  118. package/dist/core/report.js +262 -0
  119. package/dist/core/report.js.map +1 -0
  120. package/dist/core/result.js +90 -0
  121. package/dist/core/result.js.map +1 -0
  122. package/dist/core/roles.js +18 -0
  123. package/dist/core/roles.js.map +1 -0
  124. package/dist/core/run.js +221 -0
  125. package/dist/core/run.js.map +1 -0
  126. package/dist/core/runner.js +34 -0
  127. package/dist/core/runner.js.map +1 -0
  128. package/dist/core/schema.js +104 -0
  129. package/dist/core/schema.js.map +1 -0
  130. package/dist/core/spec-adapter.js +12 -0
  131. package/dist/core/spec-adapter.js.map +1 -0
  132. package/dist/core/spec-model.js +8 -0
  133. package/dist/core/spec-model.js.map +1 -0
  134. package/dist/core/tasks.js +15 -0
  135. package/dist/core/tasks.js.map +1 -0
  136. package/dist/core/test-reports.js +60 -0
  137. package/dist/core/test-reports.js.map +1 -0
  138. package/docs/design.md +821 -0
  139. package/package.json +56 -0
package/docs/design.md ADDED
@@ -0,0 +1,821 @@
1
+ # cow: автономная реализация продуктовых фич ИИ-агентами
2
+
3
+ > Название инструмента и CLI-команды: `cow`.
4
+
5
+ ## Для кого этот документ
6
+
7
+ Документ для команды, которая будет строить инструмент и пилотировать его на двух проектах: spec-box и сервисе Magic в Аркадии. Читатель знает OpenSpec, spec-box, Claude Code и Codex, поэтому эти системы не объясняются, а только сравниваются.
8
+
9
+ Документ отвечает на три вопроса: что инструмент делает, как он устроен и что ещё не решено. Разделы 1–3 задают рамку. Разделы 4–6 описывают процесс, роли и хранение. Разделы 7–10 описывают знания проекта, дизайн-документ с правилами, дискавери и работу в нескольких репозиториях. Разделы 11–13 описывают адаптеры, CLI и автономный запуск. Раздел 14 собирает открытые вопросы, раздел 15 предлагает план.
10
+
11
+ Часть решений заимствована из Codex Tracker RCA + Arc Suite Дениса Платонова; разбор и список заимствований лежат в [research/codex-tracker-rca-arc-suite.md](research/codex-tracker-rca-arc-suite.md).
12
+
13
+ ## Коротко
14
+
15
+ - Инструмент получает описание фичи и доводит её до пул-реквеста, готового к влитию: код, тесты с отчётом, обновлённые спецификации, план ручного тестирования, если он включён.
16
+ - Решения принимаются в начале. Дискавери собирает границы, спецификации и ключевые решения до реализации, а дизайн-документ выносит оставшиеся вопросы с приоритетами. После утверждения процесс идёт до пул-реквеста без остановок.
17
+ - Ревью человеком сосредоточено в начале: на дискавери или на гейте плана. Тесты проверяет ИИ-ревьюер с автоисправлением, человек смотрит их только в профиле `supervised`.
18
+ - Тесты пишутся до кода по спецификации и дизайну. Реализация не имеет права их менять. Зелёные тесты это критерий готовности.
19
+ - Реализация запечатывается: после фазы implement CLI фиксирует дайджест дифа, верификатор и ревьюер работают с этим дайджестом, а доставка отказывает при любом изменении байт после ревью и идемпотентна к повторам.
20
+ - Всё состояние лежит в файлах репозитория продукта. Любой запуск на любой машине продолжает с того же места.
21
+ - Оркестратор написан на TypeScript и не принимает решений. Решения принимают агенты-роли с типизированным входом и выходом, а продуктовые развилки решает человек.
22
+ - Специфика проекта вынесена в адаптеры (формат спецификаций, хостинг репозитория, среда запуска агентов, хост разработчика, шина событий) и в проектную документацию заданной структуры, которую инструмент валидирует.
23
+ - Спецификация первична. Истина о поведении продукта хранится в `specs` в формате spec-box или OpenSpec, изменение описывается дельтой, а дельта вливается в истину внутри пул-реквеста.
24
+ - Два режима работы. В headless-режиме CLI сам запускает агентов и работает в CI или контейнере. В interactive-режиме разработчик ведёт процесс из Claude Code или Codex, а CLI хранит состояние и выдаёт инструкции.
25
+
26
+ ## 1. Что делает инструмент
27
+
28
+ ### Вход и выход
29
+
30
+ На вход инструмент получает описание фичи (текст, файл, ссылку на тикет или бриф после дискавери) и репозиторий продукта, в котором уже выполнен `cow init`. На выходе появляется пул-реквест, готовый к влитию. После мержа инструмент выгружает истину `specs` во внешние системы (например, в spec-box) и переносит журнал изменения в wiki проекта.
31
+
32
+ ### Готовность к влитию
33
+
34
+ Пул-реквест считается готовым к влитию, когда выполнены все условия ниже. Условия проверяет CLI на фазе deliver, а список публикуется в описании пул-реквеста с отметками.
35
+
36
+ 1. Все задачи из `tasks.md` отмечены выполненными.
37
+ 2. Все автотесты, написанные на фазе cover, проходят. Отчёт о прогоне приложен: модульные и компонентные тесты, а также e2e, если проект их использует.
38
+ 3. Тестовые файлы, зафиксированные на фазе cover, не изменены реализацией. Проверяется по дифу.
39
+ 4. Проверки проекта из `testing.md` (типы, линтеры, сборка) проходят, CI зелёный.
40
+ 5. Отчёт верификации без пунктов FAIL, ревью роли reviewer завершено вердиктом `готово`, и каждый не-PASS пункт верификатора и каждый пробел получили disposition ревьюера.
41
+ 6. Дельты спецификаций применены к истине `specs`, диф истины виден в пул-реквесте.
42
+ 7. План ручного тестирования приложен, если он включён в конфиге или тестировщик пометил сценарии, которые нельзя автоматизировать.
43
+ 8. Открытых вопросов уровня P0 и P1 нет; принятые допущения перечислены.
44
+ 9. Дайджест change-set, к которому привязан вердикт ревью, совпадает с текущим дифом ветки. Любая правка после ревью возвращает изменение в фазу verify.
45
+
46
+ ### Два режима работы
47
+
48
+ | | Headless | Interactive |
49
+ |---|---|---|
50
+ | Кто запускает агентов | CLI через адаптер среды (Claude Agent SDK, `codex exec`) | Сессия разработчика в Claude Code или Codex |
51
+ | Где работает | CI, контейнер, cron, локальная машина без участия человека | Локальная машина, человек смотрит и вмешивается |
52
+ | Как ждёт человека | Завершает запуск со статусом `waiting_approval`, следующий запуск продолжает | Задаёт вопрос в чате |
53
+ | Что общего | Один и тот же CLI, одни файлы состояния, одни роли и инструкции | |
54
+
55
+ Режимы совместимы в рамках одного изменения. Дискавери и утверждение можно провести интерактивно, а реализацию отдать headless-запуску в CI.
56
+
57
+ ### Универсальность и пилоты
58
+
59
+ Инструмент не привязан к проекту. Всё проектное живёт в трёх местах: в конфигурации `.cow/config.yaml`, в проектной документации `.cow/project/` и в адаптерах.
60
+
61
+ | Пилот | Хостинг | Формат спецификаций | Репозитории |
62
+ |---|---|---|---|
63
+ | spec-box | GitHub | spec-box | `sync` |
64
+ | Magic | Arcadia | spec-box | Один репозиторий в монорепозитории |
65
+
66
+ Оба пилота используют формат spec-box, поэтому адаптер spec-box делается первым, а адаптер OpenSpec после пилотов. Сам инструмент живёт на GitHub, написан на Node.js и TypeScript и распространяется как npm-пакет.
67
+
68
+ ## 2. Принципы
69
+
70
+ 1. **Спецификация первична.** Ревьюер и верификатор сверяют код со спецификацией, а не с описанием задачи. Если спецификация и код расходятся, чинят то, что неверно, осознанно и с записью причины.
71
+ 2. **Решения принимаются в начале.** Дискавери и дизайн-документ выявляют все развилки до написания тестов и кода. Вопросы приоритизированы, и только блокирующие останавливают процесс.
72
+ 3. **Тесты раньше кода.** Тесты пишутся по спецификации и дизайну, проходят ИИ-ревью и защищены от изменения реализацией. Реализация закончена, когда тесты зелёные.
73
+ 4. **Состояние хранится в файлах, а не в процессе.** Файл `change.yaml` в папке изменения содержит фазу, статус, гейты, блокер, точки ожидания и историю запусков. Процесс можно убить на любом шаге.
74
+ 5. **Детерминированное ядро, LLM только в ролях.** Оркестратор, валидация, применение дельт, защита тестов, сопоставление тестов со сценариями, генерация материалов для хостов написаны кодом и покрыты тестами.
75
+ 6. **Агент получает контекст от CLI, а не сканирует репозиторий.** Команда `cow next --json` выдаёт роли пакет: инструкцию, файлы для чтения, полномочия, правила проекта, ожидаемый формат ответа.
76
+ 7. **Роль отвечает только по контракту.** Каждый ответ агента заканчивается машиночитаемым блоком со статусом и категорией блокера.
77
+ 8. **Проектная специфика за слоем абстракции.** Формат спецификаций, хостинг репозитория, среда запуска агентов, хост разработчика, канал гейтов и шина событий подключаются адаптерами с одинаковыми интерфейсами.
78
+ 9. **Процесс пропорционален задаче.** Размер изменения определяет обязательный набор артефактов, профиль автономии определяет число остановок на человека.
79
+ 10. **Архитектурные решения закрепляются как правила.** Решение, принятое человеком в дизайн-документе, можно повысить до постоянного правила проекта, которое проверяется в каждом следующем изменении.
80
+
81
+ ## 3. Чего инструмент не делает
82
+
83
+ - Не хранит состояние вне репозитория продукта. Выгрузка в бэкенд spec-box производная и восстанавливается из файлов.
84
+ - Не запускает долгоживущий демон. Каждый запуск конечен и завершается на гейте, блокере, точке ожидания, лимите бюджета или завершении изменения.
85
+ - Не мержит пул-реквесты и не заменяет CI. Мерж делает человек или правила репозитория.
86
+ - Не меняет тесты на фазе реализации. Если тест неверен, изменение возвращается к тестировщику.
87
+ - Не принимает продуктовых решений и не расширяет объём задачи без явного согласия.
88
+ - Не нарушает принятые правила проекта. Правило меняется только новым правилом, утверждённым человеком.
89
+
90
+ ## 4. Процесс изменения
91
+
92
+ ### Фазы
93
+
94
+ | Фаза | Кто выполняет | Результат | Переход дальше |
95
+ |---|---|---|---|
96
+ | discover | Отдельная команда `cow discover`, см. раздел 9 | Бриф, дельты спецификаций и решения, утверждённые человеком | Необязательная фаза |
97
+ | intake | CLI | Папка `changes/<id>/`, `change.yaml`, `request.md` с исходным описанием; при создании из брифа сюда же копируются дельты и решения | Всегда |
98
+ | research | Роль researcher | `evidence/research.md`: факты о текущем поведении, границы, затронутые спецификации и код, пробелы. При создании из брифа проверяет, что бриф не устарел | Пробелов, меняющих решение, нет |
99
+ | propose | Роль planner | `proposal.md`, размер изменения, сложность реализации и ревью. При создании из брифа берётся из него | Гейт `proposal`, если включён |
100
+ | plan | Роль planner, для `large` роль challenger | Дельты `specs/` (при создании из брифа проверяются, а не пишутся), `design.md` с решениями и вопросами, `tasks.md`, `evidence/challenge.md` | `cow validate` без ошибок, нет вопросов P0, гейт `plan`, если включён |
101
+ | cover | Роль tester, затем роль reviewer в режиме ревью тестов | Автотесты по сценариям, `coverage.yaml`, `test-plan.md` при необходимости; тесты прошли ИИ-ревью с автоисправлением, запущены и падают по ожидаемой причине | Гейт `tests`, если включён |
102
+ | implement | Роль implementer, затем CLI | Код, отмеченные задачи в `tasks.md`, узкие проверки; CLI запечатывает change-set: список изменённых путей и дайджест дифа в `changeset.json` | Все задачи отмечены, все тесты из `coverage.yaml` зелёные, защищённые файлы не тронуты |
103
+ | verify | CLI и роль verifier | `evidence/verify-N.md`: отчёты тестов, покрытие сценариев, соответствие дизайну и правилам, проверки проекта; каждый пункт со статусом PASS, FAIL, PARTIAL или NOT_RUN и список пробелов (manual/CI gaps) | Нет пунктов FAIL, дайджест change-set не изменился |
104
+ | review | Роль reviewer | `evidence/review-N.md`: вердикт, disposition каждого не-PASS пункта и пробела верификатора, при вердикте `готово` `delivery_narrative`; вердикт привязан к дайджесту change-set | Вердикт `готово`, дайджест не изменился |
105
+ | deliver | CLI | Дельты применены к истине, папка изменения перенесена в архив, пул-реквест обновлён и переведён из черновика в готовый; текст пул-реквеста собран по шаблону из `delivery_narrative` ревьюера, отчёта верификатора и сводки дельт; перед коммитом и созданием пул-реквеста записан intent с ключом идемпотентности | Условия готовности к влитию выполнены |
106
+ | merge | Человек, внешняя система | Мерж в базовую ветку | Адаптер репозитория видит мерж |
107
+ | post-merge | CLI | Выгрузка истины во внешние системы, дистилляция журнала в wiki, событие `pr-merged` в шину | Всегда |
108
+
109
+ ### Размер изменения
110
+
111
+ Планировщик определяет размер на фазе propose, человек может переопределить его в `change.yaml`.
112
+
113
+ | Размер | Когда | Обязательные артефакты |
114
+ |---|---|---|
115
+ | `small` | Поведение продукта не меняется: рефакторинг, инфраструктура, документация | `proposal.md`, `tasks.md`; в `change.yaml` стоит `skip_specs: true`; фаза cover ограничена регрессионными тестами |
116
+ | `normal` | Меняется поведение в пределах одной-двух capability | `proposal.md`, дельты `specs/`, `design.md`, `tasks.md`, тесты |
117
+ | `large` | Несколько capability, новые контракты, миграции, безопасность | Все артефакты плюс аудит плана ролью challenger |
118
+
119
+ ### Гейты, профили и каналы
120
+
121
+ Гейт означает остановку до явного решения человека. Профиль автономии задаёт набор включённых гейтов. Профиль задаётся в конфиге проекта и переопределяется для конкретного изменения. Проект может задать свой набор гейтов списком.
122
+
123
+ | Профиль | Гейт `proposal` | Гейт `plan` | Гейт `tests` | Ревью пул-реквеста |
124
+ |---|---|---|---|---|
125
+ | `supervised` | Да | Да | Да | Да |
126
+ | `checkpoint` | Нет | Да | Нет | Да |
127
+ | `autonomous` | Нет | Нет | Нет | Да |
128
+
129
+ Гейт `plan` стоит до фазы cover: человек смотрит дизайн и спецификации раньше, чем тестировщик потратит запуск на отклонённый дизайн. ИИ-ревью тестов с автоисправлением выполняется при любом профиле. Для пилотов начинаем с `supervised`. Изменение, созданное из брифа дискавери, по умолчанию получает профиль `autonomous`, потому что спецификации и решения уже прошли ревью человека на дискавери.
130
+
131
+ Независимо от профиля инструмент останавливается на вопросе приоритета P0: без ответа нельзя писать спецификации и тесты. Вопросы P1 останавливают только на включённом гейте `plan`, иначе принимается рекомендованный вариант и записывается как допущение. Вопросы P2 не останавливают никогда.
132
+
133
+ Канал гейта настраивается в конфиге списком `gates.channels`. Реализованные каналы: `cli` (команда `cow approve <change> <gate> [--comment]`), `pr-comments` (комментарий `/cow approve plan` в пул-реквесте, читается адаптером репозитория), `tracker` (комментарий или статус в тикете через адаптер трекера), `chat` (вопрос в сессии в interactive-режиме). Черновой пул-реквест создаётся на фазе propose только если включён канал `pr-comments`.
134
+
135
+ ### Тесты как критерий готовности
136
+
137
+ Фаза cover переводит спецификацию в исполняемую форму до написания кода.
138
+
139
+ 1. Тестировщик читает дельты спецификаций, дизайн-документ (контракты, структура интерфейса, идентификаторы для тестов) и `testing.md`.
140
+ 2. Для каждого сценария из дельт он пишет тест на уровне, который задаёт `testing.md`: модульный, компонентный или e2e. Имя теста строится по правилу сопоставления из `testing.md`, чтобы адаптер отчётов связал тест со сценарием.
141
+ 3. Файл `coverage.yaml` фиксирует связь: сценарий, тесты (путь, полное имя, уровень) или пометка `manual` с причиной.
142
+ 4. Файл `test-plan.md` создаётся, если конфиг `testing.manual_plan` равен `always` или в `coverage.yaml` есть сценарии с пометкой `manual`. По умолчанию значение `when-needed`. План описывает ручную проверку таких сценариев и короткий чеклист для человека перед мержем.
143
+ 5. Тестировщик запускает тесты. Новые тесты должны компилироваться и падать по ожидаемой причине; регрессионные тесты неизменённого поведения должны проходить.
144
+ 6. Роль reviewer в режиме ревью тестов проверяет: у каждого сценария есть тест, тест проверяет именно то, что написано в сценарии, тест не дублирует продуктовую логику, соблюдены правило именования и соглашения проекта. Находки уходят тестировщику на автоисправление, цикл повторяется до чистого ревью или до лимита возвратов.
145
+ 7. Если гейт `tests` включён, человек утверждает тесты. Иначе фаза завершается по чистому ИИ-ревью.
146
+
147
+ По завершении фазы CLI записывает список тестовых файлов в `change.yaml` как защищённые. Реализатор не получает права записи в них, а после каждого запуска реализатора CLI сверяет диф с защищённым списком. Изменение защищённого файла откатывается, запуск считается неуспешным. Реализатор может добавлять в продуктовый код идентификаторы и хуки, которые предусмотрел дизайн.
148
+
149
+ Если реализатор считает тест неверным, он возвращает блокер категории `тесты` с доказательством. Тестировщик правит тест, ИИ-ревью повторяется, и при включённом гейте `tests` человек утверждает тесты заново, потому что критерий готовности поменялся.
150
+
151
+ ### Возвраты
152
+
153
+ Каждая роль возвращает статус `готово`, `утверждение` или `заблокировано`. Для блокера роль указывает категорию, а оркестратор маршрутизирует по таблице.
154
+
155
+ | Категория блокера | Кто выставил | Куда возвращается |
156
+ |---|---|---|
157
+ | `артефакт <id>` | tester, implementer, reviewer, verifier | В фазу plan к планировщику. Если изменились дельты, фаза cover повторяется |
158
+ | `тесты` | implementer, reviewer, verifier | В фазу cover к тестировщику |
159
+ | `реализация` | verifier, reviewer | В фазу implement к реализатору с текстом замечаний; после исправления фазы verify и review повторяются |
160
+ | `внешний` | Любая роль | Запуск завершается со статусом `blocked`, повтор по внешнему событию или вручную |
161
+ | `пользователь` | Любая роль | Запуск завершается со статусом `waiting_user`, вопрос публикуется в каналы гейтов |
162
+
163
+ Число возвратов на фазу ограничено конфигом (по умолчанию 3) и не растёт само. После лимита изменение переходит в `parked`: полезная работа сохранена, продолжить может только человек командой `cow change resume --returns N` с явно большим лимитом. Статус `blocked` остаётся за техническими и внешними препятствиями.
164
+
165
+ Отдельно считаются отклонённые отчёты: если ответ роли прошёл по контракту, но не прошёл детерминированную проверку (не создан артефакт, не отмечены задачи, тронуты защищённые файлы, дрейф change-set, нет disposition), CLI возвращает роли диагностику как фидбэк и запускает её снова. Лимит таких повторов на фазу задаёт `limits.rejectionsPerPhase` (по умолчанию 2), после него изменение паркуется.
166
+
167
+ Отдельно от возвратов существует один транспортный повтор запуска роли. Если среда не вернула пригодного по контракту ответа (обрыв соединения, нехватка мощности модели, отсутствие блока `cow-result`) и рабочая копия не изменилась (проверяется по отпечатку дифа), CLI запускает роль повторно один раз, не расходуя бюджет возвратов. Смена модели на запасную при нехватке мощности допустима при тех же условиях. Если ответ пригоден или рабочая копия изменилась, повтора нет: результат принимается или маршрутизируется как блокер.
168
+
169
+ ### Схема состояний
170
+
171
+ ```text
172
+ discover → intake → research → propose → [proposal] → plan → [plan] → cover → [tests] → implement → verify → review → deliver → (merge) → post-merge
173
+ ▲ ▲ ▲ ▲ │ │
174
+ │ пробел в фактах │ артефакт │ тесты │ реализация│ │
175
+ └─────────────────────────────┴─────────────────┴──────────────────┴───────────┴─────────┘
176
+ ```
177
+
178
+ Верификатор идёт до ревьюера: ревьюер принимает окончательное решение, имея на руках отчёт верификатора, и обязан распорядиться каждым его не-PASS пунктом.
179
+
180
+ Квадратные скобки обозначают гейты, стрелки снизу обозначают возвраты по категории блокера.
181
+
182
+ ### Вариант схемы для багфиксов
183
+
184
+ Для исправления дефектов, у которых поведение уже описано в истине `specs`, а код его нарушает, предусмотрена схема `bugfix` (переопределение `.cow/schema/`, этап расширений). Отличия от схемы по умолчанию:
185
+
186
+ - вместо `proposal.md` планировщик пишет причинный контракт `rca.md`: триггер и предусловия, наблюдаемое и ожидаемое, первое наблюдаемое расхождение, нарушенный инвариант и его владелец, цепочка причин со ссылками на доказательства, сильнейшая альтернатива и чем она отвергнута, границы правки, что сохранить, оракул в форме Given / When / RED / PASS;
187
+ - дельта спецификаций сводится к добавлению сценария, который воспроизводит дефект, либо к `skip_specs: true`, если сценарий уже есть;
188
+ - тест фазы cover это RED-оракул из контракта: он обязан падать на нарушенном инварианте до исправления и проходить после;
189
+ - верификатор сравнивает baseline и final: прогоняет оракул на базовой ревизии и на итоговой, а не доверяет отчёту реализатора;
190
+ - размер по умолчанию `small`, дизайн-документ создаётся только при смене владельца инварианта или контракта.
191
+
192
+ Структура контракта взята из RCA-планировщика Suite, где она показала себя фальсифицируемой: у каждого звена цепочки есть доказательство, а оракул отличает сломанное поведение от исправленного.
193
+
194
+ ## 5. Роли агентов
195
+
196
+ ### Состав
197
+
198
+ | Роль | Полномочия | Читает | Пишет | Уровни модели |
199
+ |---|---|---|---|---|
200
+ | researcher | Только чтение | Проектную документацию, спецификации, код | `evidence/research.md` | обычный |
201
+ | planner | Артефакты изменения | Исходный запрос или бриф, evidence, спецификации, правила проекта, код при необходимости | `proposal.md`, `specs/`, `design.md`, `tasks.md` | обычный, pro |
202
+ | challenger | Только чтение | Замороженный план или бриф, evidence, правила проекта; в режиме аудита документации всю `.cow/project/` и код | `evidence/challenge.md`, отчёт `doctor --deep` | pro |
203
+ | tester | Тестовые файлы и тестовая инфраструктура | Дельты, дизайн, `testing.md`, существующие тесты | Тесты, `coverage.yaml`, `test-plan.md` | обычный, pro |
204
+ | implementer | Продуктовый код и отметки в `tasks.md`; тестовые файлы защищены | Артефакты изменения, проектную документацию, правила, код | Код | обычный, pro |
205
+ | reviewer | Только чтение; вердикт привязан к дайджесту change-set | Артефакты, правила, диф, отчёт верификатора; в фазе cover тесты и `coverage.yaml` | `evidence/review-N.md` с disposition и `delivery_narrative`, `evidence/tests-review-N.md` | обычный, pro |
206
+ | verifier | Только чтение плюс запуск проверок | Артефакты, отчёты тестов, запечатанный диф | `evidence/verify-N.md`: пункты PASS, FAIL, PARTIAL, NOT_RUN и пробелы | обычный |
207
+ | distiller | Wiki проекта | `log.md` изменения, wiki | Страницы wiki | обычный |
208
+
209
+ Оркестратор ролью не является. Это код CLI, который по `change.yaml` выбирает следующую фазу, собирает пакет для роли, запускает её через адаптер среды и разбирает ответ.
210
+
211
+ ### Контракт роли
212
+
213
+ Определение роли лежит в пакете инструмента в виде Markdown с четырьмя разделами: правила, вход, этапы, выход. Проект может переопределить или дополнить роль файлом в `.cow/roles/<role>.md`. Раздел «правила» перечисляет, что роль делает и чего не делает. Раздел «вход» описывает пакет от CLI. Раздел «выход» задаёт шаблон ответа.
214
+
215
+ Пакет от CLI содержит девять полей: цель, вопрос или зона владения, допущенные файлы, ограничения, правила проекта, полномочия, способ проверки, форма результата, условие остановки. Восемь из них взяты из практики sarah, где такой пакет показал себя пригодным для делегирования без потери контекста; поле «правила проекта» добавлено для постоянных архитектурных правил.
216
+
217
+ Ответ роли состоит из человекочитаемого Markdown и завершающего машиночитаемого блока:
218
+
219
+ ```yaml
220
+ # cow-result
221
+ status: готово | утверждение | заблокировано
222
+ blocker:
223
+ category: артефакт | тесты | реализация | внешний | пользователь | нет
224
+ artifact: tasks # только для категории артефакт
225
+ message: ...
226
+ complexity: # только planner на фазе propose
227
+ implementation: обычная | высокая
228
+ review: обычная | высокая
229
+ size: small | normal | large # только planner на фазе propose
230
+ questions: # только planner на фазе plan, если остались вопросы
231
+ - { id: Q1, priority: P0, text: ... }
232
+ findings: # только reviewer: замечания с уровнем и файлом
233
+ - { level: blocking, file: ..., text: ... }
234
+ checks: # только verifier: по одному на проверку
235
+ - { id: V1, purpose: ..., result: PASS | FAIL | PARTIAL | NOT_RUN, evidence: ... }
236
+ gaps: # только verifier: недоступные проверки
237
+ - { id: G1, environment: ..., oracle: ..., risk: ... }
238
+ dispositions: # только reviewer в фазе review: по одному на каждый не-PASS пункт и пробел верификатора
239
+ - { item: V3, disposition: satisfied | manual_gap_accepted | change_required | blocked, reason: ... }
240
+ delivery_narrative: # только reviewer при статусе готово: единственный источник текста пул-реквеста
241
+ title: ...
242
+ delta: ... # что изменилось в поведении и коде после всех возвратов
243
+ why: ... # почему это работает
244
+ preserved: ... # что сохранено
245
+ rollout: ...
246
+ rollback: ...
247
+ ```
248
+
249
+ CLI разбирает блок, записывает результат в `change.yaml` и `runs/`, а Markdown сохраняет как артефакт evidence. Ответ без блока считается неуспешным запуском и повторяется один раз с напоминанием о контракте.
250
+
251
+ ### Правила ревьюера
252
+
253
+ Ревьюер единолично принимает решение о готовности к доставке и работает по трём правилам, взятым из Suite.
254
+
255
+ Допуск находки. Находка возвращается только если она вызвана этим изменением, называет нарушенный инвариант или требование спецификации, содержит конкретный сценарий отказа, цитирует точный путь и место в дифе, независима от других находок и полезна до мержа. Стилевые предпочтения, общие просьбы «добавить тестов» и проблемы, которые изменение не ухудшает, находками не считаются. Блокирующая находка обязана содержать наименьшую границу исправления и поведенческий оракул регрессии.
256
+
257
+ Disposition. Каждый пункт верификатора с результатом, отличным от PASS, и каждый пробел получают ровно одно решение: `satisfied` (закрыт другим доказательством), `manual_gap_accepted` (принят как ручная или CI-проверка с явной оценкой риска), `change_required` (возврат реализатору), `blocked` (без недоступной проверки решить нельзя). Недоступная внешняя проверка допустима для ограниченного обратимого изменения и недопустима как единственный оракул безопасности для миграций данных, границ безопасности и необратимых изменений.
258
+
259
+ Delivery narrative. При вердикте `готово` ревьюер пишет короткое описание итогового состояния после всех возвратов: заголовок, дельта поведения и кода, почему это работает, что сохранено, выкатка и откат. CLI собирает текст пул-реквеста только из него, отчёта верификатора и сводки дельт спецификаций; контроллер не сочиняет и не переводит поведенческие утверждения.
260
+
261
+ ### Уровни модели и продолжение сессий
262
+
263
+ Планировщик оценивает сложность реализации и ревью. По оценке CLI выбирает обычную или pro-модель для тестировщика, реализатора и ревьюера из таблицы в конфиге. Оценка может расти при возвратах, но не снижаться.
264
+
265
+ При возврате к роли CLI продолжает её прошлую сессию по идентификатору, если адаптер среды это умеет. Если нет, создаётся новая сессия, и в неё передаются исходный пакет и текст возврата вместе. Идентификаторы сессий хранятся в `runs/`.
266
+
267
+ ## 6. Хранение состояния
268
+
269
+ ### Раскладка в репозитории продукта
270
+
271
+ ```text
272
+ <repo>/
273
+ .cow/
274
+ config.yaml # адаптеры, модели, профиль автономии, каналы гейтов, шина событий, лимиты
275
+ project/ # проектная документация по категориям, см. раздел 7
276
+ decisions/ # постоянные правила проекта, см. раздел 8
277
+ roles/ # переопределения ролей, опционально
278
+ schema/ # переопределение графа артефактов, опционально
279
+ wiki/ # долговечные знания о коде, опционально
280
+ discovery/
281
+ <slug>/
282
+ brief.md # бриф дискавери
283
+ specs/ # дельты спецификаций, утверждённые на дискавери
284
+ decisions.md # принятые решения и допущения
285
+ changes/
286
+ <change-id>/
287
+ change.yaml # состояние: фаза, статус, гейты, блокер, защищённые файлы, точки ожидания, запуски
288
+ request.md # исходное описание фичи или бриф как есть
289
+ proposal.md
290
+ specs/ # дельты к истине в формате адаптера спецификаций
291
+ design.md # общая картина, контракты, решения, вопросы
292
+ tasks.md
293
+ coverage.yaml # сценарий → тесты или manual
294
+ test-plan.md # при необходимости
295
+ evidence/
296
+ research.md
297
+ challenge.md
298
+ tests-review-1.md
299
+ review-1.md
300
+ verify-1.md
301
+ changeset.json # запечатанный change-set после implement: пути и дайджест дифа
302
+ log.md # тегированный журнал: [CODE] [RULE] [TASK] [HUMAN]
303
+ runs/
304
+ r7/
305
+ packet.json # пакет роли
306
+ result.md # ответ роли с блоком cow-result
307
+ receipt.json # sha пакета, промпта и ответа, модель, сессия, время, стоимость, код выхода
308
+ .lock # держит работающий CLI; защита от двойного запуска
309
+ archive/
310
+ 2026-09-11-<change-id>/
311
+ **/*.spec-box.yml # истина для адаптера spec-box, пути из .tms.json
312
+ openspec/specs/ # истина для адаптера openspec
313
+ ```
314
+
315
+ Расположение `changes/` настраивается. Для проекта на OpenSpec его можно направить в `openspec/changes/`, тогда папка изменения остаётся совместимой с CLI и дашбордом OpenSpec, а файлы инструмента добавляются рядом.
316
+
317
+ ### Файл change.yaml
318
+
319
+ ```yaml
320
+ id: add-dark-mode
321
+ title: Тёмная тема в настройках
322
+ created: 2026-09-11
323
+ source: { kind: tracker, ref: PROJ-123, brief: .cow/discovery/dark-mode }
324
+ size: normal
325
+ autonomy: autonomous
326
+ revision: 42 # растёт при каждой записи; запись с устаревшей ревизией отклоняется
327
+ phase: implement
328
+ status: active # active | waiting_approval | waiting_user | waiting_peer | parked | blocked | stopped | delivery_unknown | failed | archived | done
329
+ settled: false # true, когда ни один процесс роли не жив
330
+ active_run: r7 # запуск, чей процесс выполняется
331
+ gates:
332
+ plan: { state: skipped, reason: autonomy }
333
+ tests: { state: skipped, reason: autonomy, ai_review: clean, ai_review_rounds: 2 }
334
+ complexity: { implementation: обычная, review: высокая }
335
+ assumptions: [ { question: Q3, priority: P1, accepted: "вариант B" } ]
336
+ protected: [ "src/theme/**/*.test.ts", "e2e/theme/**" ]
337
+ blocker: null
338
+ returns: { plan: 0, cover: 1, implement: 0 }
339
+ coordination: null # см. раздел 10
340
+ changeset: { sealed_after: r6, digest: "sha256:…", paths: 14 } # запечатано после implement
341
+ reviewed_digest: "sha256:…" # дайджест, к которому привязаны вердикты verify и review
342
+ delivery: { intent_key: "add-dark-mode:sha256:…", commit: null, pr_intent_at: null } # write-ahead intent доставки
343
+ branch: cow/add-dark-mode
344
+ pr: { number: 42, url: https://github.com/org/repo/pull/42, draft: true }
345
+ runs:
346
+ - { id: r7, role: implementer, runner: claude, model: claude-sonnet-5, session: s-abc, attempt: 1, started: ..., finished: ..., status: done, cost_usd: 0.84 }
347
+ ```
348
+
349
+ Запись файла подчиняется четырём правилам. Файл пишется атомарно: во временный файл рядом, затем переименование. Пишущий держит `.lock` в папке изменения, второй запуск получает ошибку `CHANGE_BUSY`. Каждая запись проверяет, что `revision` на диске совпадает с прочитанной, иначе отклоняется с `REVISION_CONFLICT`. Терминальные статусы `archived`, `done`, `stopped` и `delivery_unknown` не меняются обычными командами: только `change resume` и `change reopen` с явным намерением человека.
350
+
351
+ Словарь исходов запуска: `done` (пул-реквест готов к влитию), `parked` (нужно решение человека, работа сохранена), `blocked` (внешнее или техническое препятствие), `stopped` (остановлен командой `cow stop`, доставка запрещена), `delivery_unknown` (коммит или пул-реквест могли быть созданы, повтор запрещён до сверки), `failed` (ошибка инструмента). Признак `settled` означает, что процесс ни одной роли не жив; внешний вызывающий считает запуск завершённым только при терминальном статусе и `settled: true`.
352
+
353
+ ### Дельты спецификаций
354
+
355
+ В ходе изменения истина `specs` не правится. Изменение хранит дельты: добавленные, изменённые, удалённые и переименованные требования по каждой затронутой capability. Дельты остаются по трём причинам.
356
+
357
+ Во-первых, дельта это рабочая память изменения. Планировщик записывает не только новый текст требования, но и причину удаления, шаги миграции и связь с proposal. Диф истины этого не содержит.
358
+
359
+ Во-вторых, дельта не зависит от формата истины. Внутри инструмента дельта представлена одной моделью (см. раздел 11), а адаптер умеет её читать, валидировать и применять к spec-box-YAML или OpenSpec-Markdown. Тестировщик, ревьюер и верификатор работают с одной моделью.
360
+
361
+ В-третьих, дельта отделяет намерение от результата. Тесты пишутся по дельте до того, как истина изменилась, а человек на дискавери или гейте плана утверждает именно дельту.
362
+
363
+ Риск дельт в том, что раздел MODIFIED содержит копию требования и устаревает, если истина изменилась в базовой ветке. CLI закрывает риск двумя проверками: `cow validate` сравнивает копию с текущей истиной и сообщает расхождение, а `cow archive` отказывается применять дельту к изменённому требованию без явного `--force`.
364
+
365
+ Формат хранения дельты определяет адаптер спецификаций. Для spec-box дельта хранится в YAML-диалекте с секциями `added`, `modified`, `removed`, `renamed` поверх структуры `specs-unit`; диалект описан в адаптере, а инструкцию для планировщика выдаёт CLI. Для OpenSpec это родной синтаксис дельт, чтобы `openspec validate` и дашборд продолжали работать.
366
+
367
+ ### Архивация до мержа
368
+
369
+ Архивация происходит на фазе deliver, внутри пул-реквеста. CLI применяет дельты к истине `specs`, переносит папку изменения в `changes/archive/` с датой и коммитит всё в ветку изменения. Ревьюер пул-реквеста видит три дифа вместе: код, тесты и истину спецификаций.
370
+
371
+ Если ревью пул-реквеста требует правок, работают два пути. Правки только в коде идут через `cow change resume`: папка остаётся в архиве, реализатор получает замечания, фазы verify и review повторяются. Правки в спецификациях идут через `cow change reopen`: CLI восстанавливает затронутые файлы истины из базовой ветки, возвращает папку из архива и переводит изменение в фазу plan.
372
+
373
+ Если два изменения в разных ветках правят одну capability, второе при ребейзе получает конфликт в файле истины. Команда `cow archive --rebase` заново применяет дельту к обновлённой истине, а конфликт на уровне требований показывает человеку.
374
+
375
+ ### Журналы
376
+
377
+ Папка `runs/<run-id>/` хранит пакет роли, её ответ и `receipt.json`: sha пакета, промпта и ответа, адаптер, модель, идентификатор сессии, номер попытки, время, код выхода, стоимость. Так любой результат можно связать с точным входом модели. События среды (JSONL Codex или Claude) и stderr лежат вне репозитория в каталоге раннера `~/.cow/runs/<change>/<run>/`, а в receipt записаны их sha и размер. Транскрипты агентов в репозиторий не попадают.
378
+
379
+ Файл `changeset.json` после фазы implement содержит список изменённых путей относительно базовой ветки и дайджест дифа. Верификатор и ревьюер получают этот дайджест в пакете, а их вердикты записываются в `reviewed_digest`. Перед доставкой CLI пересчитывает дайджест и при расхождении возвращает изменение в фазу verify.
380
+
381
+ Файл `log.md` хранит тегированные однострочные записи, как в sarah: `[TASK]` для событий изменения, `[CODE]` для проверенных фактов о коде, `[RULE]` для принятых правил, `[HUMAN]` для предпочтений сессии. Записи `[CODE]` и `[RULE]` после мержа переносятся в wiki проекта ролью distiller.
382
+
383
+ ## 7. Знания проекта
384
+
385
+ ### Три горизонта
386
+
387
+ | Горизонт | Где | Живёт | Кто пишет |
388
+ |---|---|---|---|
389
+ | Долговечные | `specs`, `.cow/project/`, `.cow/project/decisions/`, `.cow/wiki/` | Пока живёт продукт | Архивация, повышение решений, distiller, люди |
390
+ | Изменение | `.cow/discovery/<slug>/`, `.cow/changes/<id>/` | До архивации | Роли через CLI |
391
+ | Запуск | `runs/`, сессия агента | До конца запуска | Адаптер среды |
392
+
393
+ ### Спецификации
394
+
395
+ Истина о поведении продукта хранится в формате проекта, а инструмент работает с ней через адаптер спецификаций. Внутренняя модель: capability содержит требования, требование содержит сценарии, сценарий содержит шаги GIVEN/WHEN/THEN и состояние автоматизации.
396
+
397
+ | Внутренняя модель | spec-box | OpenSpec |
398
+ |---|---|---|
399
+ | capability | YAML-файл, поля `feature`, `code` | `openspec/specs/<path>/spec.md` |
400
+ | requirement | Группа в `specs-unit` (только название) | `### Requirement:` с нормативным текстом |
401
+ | scenario | Элемент `assert`; шаги в `description` | `#### Scenario:` с шагами |
402
+ | атрибуты | `definitions` и `.spec-box-meta.yml` | Нет |
403
+ | purpose | `description` фичи | `## Purpose` |
404
+ | состояние автоматизации | `automationState`, `detailsUrl` из отчётов | Нет |
405
+
406
+ ### Формулировка требования в spec-box
407
+
408
+ В OpenSpec требование состоит из названия, нормативного текста и сценариев. Например: название «Истечение сессии», текст «Система SHALL завершать сессию после 30 минут бездействия», сценарии «Таймаут при бездействии» и «Активность продлевает сессию». Каждый сценарий это отдельный тест.
409
+
410
+ В spec-box три уровня тоже есть: фича, группа, утверждение `assert`. Но группа это только строка-заголовок в `specs-unit`, у неё нет текста. Утверждение `assert` это проверяемая фраза с необязательным описанием, и автотест сопоставляется именно с ним по ключам `featureTitle`, `groupTitle`, `assertionTitle`.
411
+
412
+ Отсюда два варианта сопоставления.
413
+
414
+ | Вариант | requirement | scenario | Что теряется |
415
+ |---|---|---|---|
416
+ | A: группа = требование, assert = сценарий | Группа | `assert` | Нормативный текст требования: у группы нет поля для него |
417
+ | B: assert = требование, сценарии в `description` | `assert` | Текст внутри `description` | Тесты сопоставляются только с требованием, а не с каждым сценарием; UI spec-box не показывает сценарии отдельно |
418
+
419
+ Вариант A сохраняет главное свойство spec-box: один `assert`, один автотест, видимое покрытие. Потеря нормативного текста лечится двумя способами. Первый: писать название группы полным нормативным предложением, например «Сессия завершается после 30 минут бездействия». Тогда в тестах `describe` получает то же предложение, а UI показывает его заголовком. Второй: расширить формат spec-box необязательным полем `description` у группы. Это правка парсера `sync` и модели `AssertionGroup` в бэкенде, формат принадлежит нашей команде.
420
+
421
+ Рекомендация: начать с варианта A и полных предложений в названиях групп, а расширение формата запланировать после первого пилота, когда станет видно, мешает ли отсутствие поля.
422
+
423
+ Обратная потеря тоже есть. В spec-box у фичи есть `type: Functional | Visual`, атрибуты и деревья группировки, у OpenSpec их нет. Это не мешает процессу, потому что каждый проект живёт в одном формате, а не конвертирует между двумя.
424
+
425
+ ### Категории информации о проекте
426
+
427
+ Для работы в конкретном проекте агентам нужна информация о его специфике. Инструмент задаёт категории явно: какие вопросы категория обязана закрывать, в каком файле она живёт, кто её читает. Проект может добавлять файлы и разделы, но не должен убирать обязательные.
428
+
429
+ | Категория | Файл | На какие вопросы отвечает | Кто читает |
430
+ |---|---|---|---|
431
+ | Продукт и границы | `overview.md` | Что за продукт, кто пользователи, какие системы рядом, где живёт код, где истина `specs` | researcher, planner, discover |
432
+ | Архитектура и карта кода | `architecture.md` | Какие пакеты и модули есть, точки входа, поток данных, где что лежит, границы модулей, генерируемый код | researcher, planner, implementer |
433
+ | Соглашения по коду | `conventions.md` | Язык кода и комментариев, стиль, запрещённые конструкции, именование, правила коммитов | implementer, reviewer, tester |
434
+ | Тестирование и проверки | `testing.md` | Уровни тестов и когда какой применять, команды узкого и полного прогона, пути отчётов, правило именования тестов по сценариям, среда e2e, что нельзя автоматизировать | tester, implementer, verifier |
435
+ | Рабочий процесс | `workflow.md` | Ветки, пул-реквесты, обязательные проверки CI, что запрещено без человека, разрешённые команды для агентов | CLI, implementer |
436
+ | Словарь домена | `glossary.md` | Термины в формулировках, которыми пишутся спецификации и тесты | Все роли |
437
+ | Архитектурные правила | `decisions/ADR-*.md` | Какие решения приняты навсегда, в какой области действуют, как проверяются | planner, challenger, implementer, reviewer |
438
+ | Интеграции и контракты | `contracts.md` | Внешние API, события, схемы, кто потребитель, где лежат машиночитаемые контракты, репозитории-партнёры | planner, tester; обязателен для межрепозиторных изменений |
439
+
440
+ Каждый файл начинается с фронтматтера: `id`, `summary`, `read_when`, `updated`, `review_on` (пути, изменение которых делает страницу подозрительной), `verification: verified | needs-review`. Команда `cow init` создаёт шаблоны с вопросами категории, а роль researcher при первом запуске в проекте предлагает заполнить их по коду. Заполнение утверждает человек.
441
+
442
+ ### Валидация документации
443
+
444
+ Команда `cow doctor` проверяет документацию на двух уровнях.
445
+
446
+ Структурный уровень детерминированный и быстрый. Он проверяет наличие всех обязательных файлов и разделов, корректность фронтматтера, отсутствие заглушек вроде TODO и TBD, существование путей и команд, на которые ссылаются страницы (команды сверяются со скриптами пакета, пути с деревом репозитория), согласованность конфига и документации (истина `specs` из конфига совпадает с описанной в `overview.md`, отчёты тестов из конфига описаны в `testing.md`), свежесть по `review_on` (если путь менялся после `updated`, страница помечается `needs-review`), формат и уникальность правил в `decisions/`.
447
+
448
+ Семантический уровень запускается командой `cow doctor --deep` и использует роль challenger в режиме аудита. Роль проверяет полноту: закрывает ли каждая категория свои вопросы; непротиворечивость: не расходятся ли страницы между собой и с кодом в проверяемых утверждениях; применимость: хватит ли документации роли implementer, чтобы внести типичное изменение без вопросов. Результат это отчёт с находками по файлам и предложениями правок. Правки вносит человек или отдельное изменение.
449
+
450
+ Оба уровня запускаются в CI проекта. Структурный уровень блокирует мерж, семантический публикует отчёт.
451
+
452
+ ### Wiki и дистилляция
453
+
454
+ Папка `.cow/wiki/` хранит долговечные знания о коде, которые не помещаются в категории выше: механизмы, инварианты, подводные камни по областям. Страницы имеют тот же фронтматтер, роутер `cow route --path | --query` возвращает план чтения из одной-двух страниц, линтер проверяет структуру. Роль distiller после мержа переносит записи `[CODE]` и `[RULE]` из `log.md` в wiki по правилам допуска: запись должна отвечать на повторяющийся вопрос и предотвращать класс ошибок, а не пересказывать один файл.
455
+
456
+ ### Как контекст попадает к агенту
457
+
458
+ CLI собирает пакет для роли из пяти источников: исходный запрос и артефакты изменения, категории документации по таблице ролей, действующие правила из `decisions/` по области изменения, спецификации затронутых capability, план чтения wiki по путям из evidence. В пакет входят пути и краткие выдержки, а не полные файлы. Роль читает файлы сама по допущенному списку. Размер пакета ограничен конфигом.
459
+
460
+ ## 8. Дизайн-документ и постоянные правила
461
+
462
+ ### Структура design.md
463
+
464
+ Дизайн-документ показывает человеку общую картину до реализации и собирает все решения в одном месте. Его читают на гейте `plan` первым, до спецификаций и задач.
465
+
466
+ | Раздел | Содержание |
467
+ |---|---|
468
+ | Общая картина | Как будет работать изменяемая функциональность целиком: сценарий пользователя от входа до результата, какие компоненты участвуют, что меняется в каждом. Одна схема потока данных |
469
+ | Контракты | Изменения интерфейсов: API, события, схемы данных, структура интерфейса и идентификаторы для тестов. Для межрепозиторных изменений это раздел-источник для партнёров |
470
+ | Решения | Каждое решение с обоснованием, рассмотренными альтернативами и последствиями. У решения есть идентификатор `D1`, `D2` и флаг `promote`, если планировщик предлагает сделать его постоянным правилом. Решения из брифа переносятся сюда как принятые |
471
+ | Вопросы, требующие решения | Таблица вопросов с приоритетом, вариантами, рекомендацией и влиянием, см. ниже |
472
+ | Соответствие правилам | Список действующих правил из `decisions/`, затронутых изменением, и как дизайн их соблюдает |
473
+ | Риски и компромиссы | Формат «риск → мера снижения» |
474
+ | Стратегия проверки | На каких уровнях проверяется каждый контракт, что уходит в ручной план |
475
+ | Миграция и откат | Шаги применения и возврата, если применимо |
476
+
477
+ ### Приоритизированные вопросы
478
+
479
+ Планировщик выносит в дизайн все вопросы, которые не может решить без домыслов, и приоритизирует их.
480
+
481
+ | Приоритет | Смысл | Поведение инструмента |
482
+ |---|---|---|
483
+ | P0 | Без ответа нельзя написать спецификацию или тест: меняется объём или контракт | Останавливает при любом профиле, вопрос уходит в каналы гейтов |
484
+ | P1 | Меняет дизайн или объём, но у планировщика есть обоснованная рекомендация | Останавливает на гейте `plan`, иначе рекомендация принимается и записывается в `assumptions` |
485
+ | P2 | Любой вариант допустим, разница во вкусе или в мелочах | Не останавливает, рекомендация принимается |
486
+
487
+ Каждый вопрос содержит варианты, рекомендованный вариант, влияние на спецификации, тесты и код. Человек отвечает на гейте командой `cow approve plan --answer Q1=B` или комментарием, и ответы попадают в дизайн как решения.
488
+
489
+ ### Постоянные правила
490
+
491
+ Решение, которое должно соблюдаться во всех будущих изменениях, повышается до правила проекта. Правило это файл в `.cow/project/decisions/`:
492
+
493
+ ```yaml
494
+ ---
495
+ id: ADR-0007
496
+ title: Сессии хранятся только на сервере
497
+ status: accepted # accepted | superseded
498
+ scope: [server/auth, client/features/session]
499
+ accepted_by: dima
500
+ accepted_at: 2026-09-11
501
+ origin: changes/add-2fa/design.md#D2
502
+ supersedes: null
503
+ ---
504
+ ## Правило
505
+ Клиент MUST NOT считать сессию завершённой без ответа сервера.
506
+
507
+ ## Контекст
508
+ ## Обоснование
509
+ ## Последствия
510
+ ## Как проверяется
511
+ Ревьюер проверяет обработчики выхода из системы; линтер `no-local-session-reset`.
512
+ ```
513
+
514
+ Повышение делает только человек: командой `cow decision promote <change> D2` или флагом `--promote D2` при утверждении плана или брифа. Планировщик может предложить повышение флагом `promote` в дизайне, но не выполнить его.
515
+
516
+ Правила соблюдаются четырьмя механизмами. Правила по области изменения входят в пакет планировщика, тестировщика, реализатора и ревьюера как отдельное поле «правила проекта». Планировщик обязан заполнить раздел «Соответствие правилам» в дизайне. Challenger и reviewer проверяют каждое применимое правило и оформляют нарушение как блокирующую находку со ссылкой на идентификатор. Если у правила есть детерминированная проверка (линтер, тест), `testing.md` включает её в обязательные проверки.
517
+
518
+ Правило нельзя нарушить, но можно заменить. Замена это новое правило со ссылкой `supersedes`, принятое человеком в рамках отдельного изменения. Старое правило получает статус `superseded` и остаётся в истории.
519
+
520
+ ## 9. Дискавери: решения в начале
521
+
522
+ ### Зачем и для кого
523
+
524
+ Возвраты и остановки после начала реализации стоят дороже всего: к моменту вопроса уже написаны спецификации, тесты и часть кода. Дискавери переносит продуктовые и архитектурные развилки и ревью спецификаций в отдельный этап до реализации. Его результат достаточен для того, чтобы изменение прошло профиль `autonomous` без единой остановки.
525
+
526
+ Основной пользователь дискавери это фиче-лид задачи, разработчик, который отвечает за реализацию. Он запускает дискавери сам, отвечает на вопросы и утверждает результат. По желанию фиче-лид передаёт инструмент продакт-менеджеру: тогда продакт проходит дискавери в чате хоста или через тикет, а фиче-лид получает готовый артефакт и подаёт его на вход конвейера. Для этого сценария есть команда `cow discover validate <slug>`, которая проверяет достаточность чужого брифа перед созданием изменения.
527
+
528
+ ### Команда cow discover
529
+
530
+ Дискавери запускается отдельно от изменения: `cow discover "<описание>"` или `cow discover --from-tracker PROJ-123`. Работает в обоих режимах. В interactive-режиме роли задают вопросы человеку в чате. В headless-режиме вопросы публикуются в канал гейтов пакетами по приоритету, а запуск завершается до ответов.
531
+
532
+ Процесс дискавери состоит из пяти шагов. Роль researcher собирает факты о текущем поведении, затронутых capability и контрактах. Роль planner в режиме дискавери формулирует границы, варианты решений и вопросы с приоритетами. Человек отвечает на вопросы P0 и P1 в порядке приоритета, на P2 принимаются рекомендации. Роль planner пишет дельты спецификаций по принятым решениям, и человек утверждает их вместе с брифом. Роль challenger проверяет результат на достаточность.
533
+
534
+ Результат лежит в `.cow/discovery/<slug>/`: `brief.md`, `specs/` с дельтами и `decisions.md`. Он передаётся в изменение командой `cow change new --from-brief <slug>`. Бриф становится `request.md`, дельты копируются в `specs/` изменения, решения предзаполняют дизайн, а профиль изменения по умолчанию становится `autonomous`. Планировщик на фазе plan проверяет дельты на актуальность против истины, а не пишет их заново.
535
+
536
+ ### Структура брифа
537
+
538
+ | Раздел | Содержание |
539
+ |---|---|
540
+ | Цель и ценность | Проблема, для кого, почему сейчас |
541
+ | Границы | Входит, не входит, что остаётся как есть |
542
+ | Затронутые capability | Существующие спецификации с идентификаторами, новые capability с назначением; ссылка на дельты в `specs/` |
543
+ | Решения | Принятые решения с вариантами и обоснованием; кандидаты в постоянные правила |
544
+ | Контракты | Изменения интерфейсов и интеграций, репозитории-партнёры |
545
+ | Стратегия тестирования | Уровни тестов, среда e2e, что проверяется вручную |
546
+ | Ограничения и риски | Технические и продуктовые |
547
+ | Допущения | Принятые рекомендации по P1 и P2 |
548
+ | Достаточность | Чеклист критериев ниже с отметками |
549
+
550
+ ### Критерии достаточности
551
+
552
+ Бриф достаточен для автономной реализации, когда выполнены все критерии. Детерминированные критерии проверяет CLI, остальные роль challenger.
553
+
554
+ 1. Нет открытых вопросов P0 и P1.
555
+ 2. Каждая затронутая capability существует в истине `specs` или помечена как новая с назначением.
556
+ 3. Дельты спецификаций написаны, проходят `cow validate` и утверждены человеком.
557
+ 4. У каждого меняемого контракта определена форма.
558
+ 5. Стратегия тестирования зафиксирована, среда e2e названа и доступна инструменту.
559
+ 6. Все термины брифа есть в словаре домена или определены в брифе.
560
+ 7. Границы не пересекаются с активными изменениями (по списку `changes/`).
561
+ 8. Аудит challenger вернул вердикт `достаточно`.
562
+
563
+ Если изменение, запущенное по брифу в профиле `autonomous`, всё же остановилось на блокере `пользователь`, это дефект дискавери. Вопрос записывается в `log.md` с тегом `[RULE]`, а после мержа distiller предлагает добавить его в чеклист вопросов роли planner для дискавери.
564
+
565
+ ## 10. Согласованное изменение в нескольких репозиториях
566
+
567
+ ### Когда нужно и когда делаем
568
+
569
+ Одна фича может требовать правок в нескольких репозиториях, например во фронтенде и бэкенде, которые живут раздельно. В монорепозитории (Arcadia) это одно изменение с несколькими областями, и протокол ниже не нужен. Протокол нужен, когда репозитории разные и у каждого свой цикл пул-реквестов.
570
+
571
+ Протокол не входит в первый этап и не привязан к пилотам: оба пилота работают в одном репозитории. Сначала инструмент проходит весь путь в одном репозитории, а межрепозиторные изменения появляются, когда возникнет проект с двумя репозиториями.
572
+
573
+ ### Равноправные изменения и точки ожидания
574
+
575
+ Каждый репозиторий ведёт своё изменение по обычному процессу: свой бриф или запрос, свои дельты, тесты, гейты и пул-реквест. Ведущего изменения нет. Изменения связаны общим идентификатором координации и знают друг о друге через поле `coordination` в `change.yaml`:
576
+
577
+ ```yaml
578
+ coordination:
579
+ id: dark-mode-2026-09
580
+ role: consumer # provider | consumer
581
+ peers:
582
+ - { repo: org/backend, change: add-theme-endpoint, role: provider }
583
+ waits:
584
+ - { phase: cover, for: contract-published, from: org/backend }
585
+ - { phase: deliver, for: pr-merged, from: org/backend }
586
+ events_sent: [ { type: contract-accepted, at: ..., ref: ... } ]
587
+ events_received: [ { type: contract-published, from: org/backend, at: ..., ref: "a1b2c3", path: contracts/theme.openapi.yaml } ]
588
+ ```
589
+
590
+ Точка ожидания привязана к фазе. Когда изменение доходит до фазы с ожиданием, а событие ещё не получено, запуск завершается со статусом `waiting_peer`. Следующий запуск проверяет события и продолжает. Так «сделал шаг на своей стороне, жду реакции другого репозитория» выражается состоянием, а не процессом.
591
+
592
+ ### События
593
+
594
+ | Событие | Кто публикует | Что несёт | Кто ждёт |
595
+ |---|---|---|---|
596
+ | `brief-shared` | Любая сторона после дискавери | Ссылка на бриф и общие решения | Партнёр перед фазой propose |
597
+ | `contract-published` | Поставщик после гейта `plan` | Коммит и путь машиночитаемого контракта в своём репозитории | Потребитель перед фазой cover |
598
+ | `contract-change-requested` | Потребитель | Причина и предложение | Поставщик, возврат в фазу plan |
599
+ | `contract-accepted` | Потребитель после фазы cover | Коммит контракта, против которого написаны тесты | Поставщик перед фазой deliver |
600
+ | `pr-ready` | Любая сторона после фазы deliver | Ссылка на пул-реквест | Человек, партнёр для порядка мержа |
601
+ | `pr-merged` | Любая сторона после мержа | Коммит в базовой ветке | Партнёр перед переводом своего пул-реквеста в готовый |
602
+
603
+ Контракт хранится в репозитории поставщика по пути из `contracts.md` в машиночитаемой форме: OpenAPI, JSON Schema, protobuf, схема событий. Потребитель читает его по коммиту из события через адаптер репозитория и генерирует моки для своих тестов. Так обе стороны проходят фазу cover независимо.
604
+
605
+ Порядок мержа задаётся ожиданиями: потребитель ждёт `pr-merged` поставщика перед переводом пул-реквеста в готовый. Для несовместимого изменения API поставщик выпускает новое поведение с сохранением старого, потребитель переключается, а удаление старого идёт отдельным изменением.
606
+
607
+ ### Доставка событий
608
+
609
+ Базовый механизм это опрос. В начале каждого запуска CLI читает `change.yaml` партнёров по их веткам через адаптер репозитория и выводит события из их состояния. Опрос работает в любой среде и не требует инфраструктуры.
610
+
611
+ Ускорение это шина событий через адаптер `EventBus`. Публикация события вызывает запуск партнёра, а не ждёт его расписания. Для GitHub реализация через `repository_dispatch`: рабочий процесс партнёра получает событие и запускает `cow run --change <id>`. Для Arcadia реализация появится вместе с адаптером. Шина не заменяет опрос: события в `change.yaml` остаются источником истины, а шина только сокращает ожидание.
612
+
613
+ Сквозная верификация требует среды, где развёрнуты ветки всех участников. Если у проекта такой среды нет, верификация опирается на контрактные тесты обеих сторон, а сквозные e2e запускаются после мержа на стенде и до этого момента попадают в план ручного тестирования.
614
+
615
+ ## 11. Адаптеры
616
+
617
+ Каждый адаптер реализует интерфейс из ядра и регистрируется в конфиге. Интерфейсы ниже даны в сокращённом виде.
618
+
619
+ ### Спецификации
620
+
621
+ ```ts
622
+ interface SpecAdapter {
623
+ readTruth(): Promise<Capability[]>;
624
+ readDelta(dir: string): Promise<SpecDelta[]>;
625
+ validate(truth: Capability[], deltas: SpecDelta[]): Diagnostic[];
626
+ apply(truth: Capability[], deltas: SpecDelta[]): Promise<void>; // архивация
627
+ instructions(artifact: 'specs'): string; // формат дельты для планировщика
628
+ sync?(): Promise<void>; // выгрузка во внешнюю систему
629
+ }
630
+ ```
631
+
632
+ Реализации: `spec-box` (YAML по глобам из `.tms.json`, `sync` вызывает `spec-box sync`), `openspec` (Markdown в `openspec/specs`, после пилотов).
633
+
634
+ ### Хостинг репозитория
635
+
636
+ ```ts
637
+ interface RepoHost {
638
+ createBranch(name: string, from: string): Promise<void>;
639
+ commit(message: string, paths: string[]): Promise<string>;
640
+ push(branch: string): Promise<void>;
641
+ openPullRequest(draft: boolean, title: string, body: string): Promise<PullRequest>;
642
+ updatePullRequest(pr: PullRequest, patch: { body?: string; draft?: boolean }): Promise<void>;
643
+ readComments(pr: PullRequest, since: string): Promise<Comment[]>;
644
+ isMerged(pr: PullRequest): Promise<boolean>;
645
+ checks(pr: PullRequest): Promise<CheckResult[]>;
646
+ artifacts(pr: PullRequest, name: string): Promise<Buffer>; // отчёты тестов из CI
647
+ readRemoteFile(repo: string, ref: string, path: string): Promise<string>; // состояние и контракты партнёров
648
+ prepareWorkspace(change: ChangeRef): Promise<Workspace>; // отдельная рабочая копия на изменение
649
+ verifyWorkspace(workspace: Workspace): Promise<WorkspaceIdentity>; // ветка, база, чистота перед фазой с записью
650
+ deliver(intent: DeliveryIntent): Promise<DeliveryReceipt>; // идемпотентный коммит и пул-реквест
651
+ }
652
+ ```
653
+
654
+ Реализации: `local` (только git: ветка, коммит с ключом, push при наличии remote; для interactive-режима и тестов), `github` (REST API через `fetch` с токеном из `GITHUB_TOKEN`, без зависимости от `gh`), `arcadia` (arc и Arcanum) для второго пилота. Локальные операции с git и arc внутри адаптера.
655
+
656
+ Рабочая копия. Каждое изменение работает в своей рабочей копии: `git worktree` для GitHub, отдельный `arc mount` для Arcadia. Исходный checkout разработчика никогда не служит рабочим местом ролей. Перед каждой фазой с правом записи адаптер проверяет identity копии: ветка изменения, базовая ревизия, отсутствие посторонних изменений. Несовпадение останавливает запуск с блокером категории `внешний`.
657
+
658
+ Доставка. Перед коммитом CLI записывает в `change.yaml` intent с ключом идемпотентности из идентификатора изменения и дайджеста change-set. Адаптер сначала сверяет ключ с существующими коммитами и пул-реквестами, и если доставка уже произошла, возвращает её receipt без повторных действий. Если результат внешней операции неизвестен (обрыв после команды), изменение получает статус `delivery_unknown`, и повтор запрещён до сверки командой `cow change resume`. Хуки коммита проекта выполняются один раз обычной командой коммита; инструмент не подставляет `--no-verify` и не интерпретирует их вывод.
659
+
660
+ ### Канал гейтов
661
+
662
+ ```ts
663
+ interface GateChannel {
664
+ post(change: ChangeRef, question: GateQuestion): Promise<void>;
665
+ read(change: ChangeRef, since: string): Promise<GateAnswer[]>;
666
+ }
667
+ ```
668
+
669
+ Реализации: `cli`, `pr-comments` (через `RepoHost.readComments`), `tracker` (Яндекс Трекер, GitHub Issues), `chat` (interactive-режим). Несколько каналов работают одновременно, первый полученный ответ считается решением.
670
+
671
+ Канал `tracker` дополнительно проецирует статус изменения на теги тикета: постоянный тег `cow` и ровно один тег состояния вида `cow_in_progress`, `cow_needs_input`, `cow_parked`, `cow_pr_ready`, `cow_stopped`. Проекция best-effort: ошибка обновления тега записывается и не меняет исход запуска, а истиной остаётся `change.yaml`. Статус тикета, исполнитель и остальные поля не трогаются.
672
+
673
+ ### Шина событий
674
+
675
+ ```ts
676
+ interface EventBus {
677
+ publish(event: CoordinationEvent, to: PeerRef): Promise<void>;
678
+ poll(change: ChangeRef): Promise<CoordinationEvent[]>; // опрос через RepoHost как реализация по умолчанию
679
+ }
680
+ ```
681
+
682
+ Реализации: `poll` (чтение `change.yaml` партнёров, всегда включён), `github-dispatch` (`repository_dispatch` для запуска партнёра), `arcadia` (позже).
683
+
684
+ ### Среда запуска агентов
685
+
686
+ ```ts
687
+ interface AgentRunner {
688
+ run(packet: RolePacket, opts: { model: string; resumeSession?: string; cwd: string; permissions: Permissions }): Promise<RoleRun>;
689
+ supportsResume: boolean;
690
+ }
691
+ ```
692
+
693
+ Реализации: `claude` через Claude Agent SDK (управляемые сессии, разрешения на инструменты, учёт стоимости), `codex` через `codex exec` в неинтерактивном режиме. Если Codex не умеет продолжать сессию, возврат к роли идёт через новую сессию с полным контекстом. Разрешения на команды и пути берутся из `workflow.md`, `testing.md` и списка защищённых файлов.
694
+
695
+ Рецепт вызова Codex повторяет проверенный в Suite:
696
+
697
+ ```text
698
+ codex exec --cd <workspace> --model <model> --skip-git-repo-check --json
699
+ --output-schema <cow-result.schema.json> --output-last-message <runs/rN/result.json>
700
+ -c model_reasoning_effort="<effort>" -c agents.enabled=false
701
+ --sandbox read-only | workspace-write -c approval_policy="on-request" -c approvals_reviewer="auto_review"
702
+ ```
703
+
704
+ Промпт (пакет и текст роли) подаётся на stdin, чтобы среда не читала файлы инструкций сама. Для ролей без права записи песочница `read-only`. Перед первым запуском адаптер проверяет возможности установленного Codex по `codex exec --help` и отказывается работать без `--output-schema` и `--output-last-message`. Для Claude Agent SDK эквивалент: структурированный вывод по схеме, разрешённые инструменты по роли, `cwd` в рабочей копии.
705
+
706
+ Надзор за процессом одинаков для обеих сред. Роль запускается в собственной process group; в каталоге запуска лежит файл запроса остановки, который проверяет надзиратель; `cow stop` записывает запрос, ждёт завершения группы и переводит изменение в `stopped`, после чего доставка запрещена. Заданы жёсткий таймаут запуска и таймаут бездействия по событиям среды. События JSONL разбираются ради прогресса, обнаружения запросов одобрения и классификации сбоя: транспорт, нехватка мощности модели, отсутствие результата, ошибка инструмента.
707
+
708
+ ### Аутентификация сред
709
+
710
+ Правила взяты из условий Anthropic для Claude Code и Agent SDK (страница legal-and-compliance, проверена 2026-09-12) и условий Codex CLI.
711
+
712
+ - Подписка Claude (вход через аккаунт) допустима только внутри немодифицированного Claude Code. cow не читает, не хранит и не передаёт токены аккаунта: вход выполняет сам пользователь командой `claude`, а адаптер `claude` запускает Claude Code через Agent SDK и наследует этот вход. Настроек токенов у адаптера нет намеренно.
713
+ - На своей машине разработчик может использовать свою подписку в обоих режимах, если запускает cow для собственной работы. Лимиты Pro и Max рассчитаны на индивидуальное использование, поэтому для длинных автономных прогонов предсказуемее ключ API.
714
+ - В CI и в любых запусках от имени команды подписочные учётные данные запрещены. Используется `ANTHROPIC_API_KEY` организации из Claude Console либо Bedrock, Vertex или Foundry. `doctor` в CI предупреждает об отсутствии ключа, шаблоны workflow передают только ключ из секретов.
715
+ - Прямые обращения к Messages API из cow не предусмотрены: cow оркестрирует Claude Code, а не заменяет его. Спорные сценарии уточняются у Anthropic по контакту на той же странице.
716
+ - Для Codex аутентификация остаётся в `codex` CLI и его каталоге `~/.codex`; cow ключи не хранит.
717
+
718
+ ### Материалы для хоста разработчика
719
+
720
+ ```ts
721
+ interface HostMaterials {
722
+ install(target: 'claude' | 'codex', roles: RoleDefinition[], skills: SkillDefinition[]): Promise<void>;
723
+ }
724
+ ```
725
+
726
+ Команда `cow host install --target claude` генерирует `.claude/skills/cow-*/SKILL.md` и `.claude/agents/cow-<role>.md`; для Codex генерируются `.codex/agents/*.toml` и раздел в `AGENTS.md`. Все сгенерированные файлы тонкие: они вызывают `cow next` и `cow report`, а определения ролей остаются в одном месте. Повторный запуск обновляет файлы, ручные правки в них не предполагаются.
727
+
728
+ ### Отчёты тестов
729
+
730
+ ```ts
731
+ interface TestReportAdapter {
732
+ read(path: string): Promise<TestCase[]>; // полное имя теста и статус
733
+ }
734
+ ```
735
+
736
+ Реализации: jest (он же vitest с репортёром json), playwright; testplane и storybook позже. Сопоставление сценариев с тестами по ключам (название capability, требования, сценария) повторяет алгоритм spec-box, поэтому состояние автоматизации совпадает с тем, что показывает бэкенд spec-box. В OpenSpec-проекте тот же алгоритм даёт покрытие сценариев без бэкенда.
737
+
738
+ ## 12. CLI и архитектура кода
739
+
740
+ ### Команды
741
+
742
+ | Группа | Команды | Назначение |
743
+ |---|---|---|
744
+ | Проект | `init`, `doctor [--deep]`, `host install` | Настройка проекта, проверка документации и конфига, материалы для хостов |
745
+ | Дискавери | `discover`, `discover validate` | Бриф, дельты и решения, достаточные для автономной реализации; проверка чужого брифа |
746
+ | Изменение | `change new [--from-brief]`, `change list`, `change show`, `change resume [--returns N]`, `change reopen`, `change link` | Жизненный цикл изменения; `resume` продолжает `parked`, `blocked` и `delivery_unknown` с явным намерением человека; `link` связывает изменения партнёров общим идентификатором координации |
747
+ | Протокол | `next`, `report`, `approve`, `reject` | Выдать пакет следующей роли, принять ответ, решить гейт и ответить на вопросы |
748
+ | Запуск | `run [--detach]`, `stop`, `watch` | Headless: выполнять фазы через адаптер среды до гейта, блокера, точки ожидания, лимита или конца; `stop` останавливает процесс роли и запрещает доставку; `watch` ждёт терминального статуса и `settled` |
749
+ | Артефакты | `instructions <artifact>`, `validate`, `status`, `coverage`, `changeset` | Инструкция для артефакта, проверка изменения, состояние в JSON (с `revision`, `settled`, `active_run` для внешних вызывающих), покрытие сценариев тестами, запечатанный change-set и сверка дайджеста |
750
+ | Тесты | `tests check` | Сверка дифа с защищённым списком, запуск тестов из `coverage.yaml` с чтением отчётов |
751
+ | Решения | `decision promote`, `decision list` | Повышение решения до правила, список действующих правил по области |
752
+ | Спецификации | `spec list`, `spec show`, `spec diff`, `spec sync`, `archive` | Чтение истины, сравнение дельты с истиной, выгрузка, применение дельт |
753
+ | События | `events poll`, `events publish` | Опрос и публикация событий координации |
754
+ | Доставка | `deliver [--check] [--keep-draft]`, `gates poll` | Чеклист готовности и доставка: intent, архив, коммит с ключом, push, пул-реквест; опрос гейтов в комментариях пул-реквеста |
755
+ | Среда | `ci install --target github \| docker` | Шаблоны workflows GitHub Actions (запуск cow и тесты с JSON-отчётом) и Dockerfile |
756
+ | Знания | `route`, `distill`, `wiki lint` | План чтения wiki, дистилляция журнала, проверка wiki |
757
+
758
+ Все команды поддерживают `--json` и выводят ровно один JSON-документ в stdout. Диагностика в едином конверте: `severity`, `code`, `message`, `target`, `fix`. Человекочитаемый вывод идёт в stderr. Контракт взят из OpenSpec, потому что агенты уже умеют с ним работать.
759
+
760
+ ### Модули
761
+
762
+ ```text
763
+ packages/
764
+ core/ доменная модель, схема артефактов, машина состояний, сборка пакетов, валидация, защита тестов, координация
765
+ adapters/
766
+ spec-spec-box/ spec-openspec/
767
+ repo-github/ repo-arcadia/
768
+ gate-cli/ gate-pr-comments/ gate-tracker/
769
+ events-poll/ events-github-dispatch/
770
+ runner-claude/ runner-codex/
771
+ host-claude/ host-codex/
772
+ tests-jest/ tests-playwright/ tests-testplane/ tests-storybook/
773
+ roles/ определения ролей и схема артефактов по умолчанию
774
+ cli/ команды, вывод, конфиг
775
+ ```
776
+
777
+ На первом этапе это один npm-пакет с такой раскладкой каталогов. Разделение на пакеты происходит, когда появляется второй потребитель ядра (например, GitHub Action или бот).
778
+
779
+ ### Технические решения
780
+
781
+ - Node.js 22 LTS, TypeScript в strict-режиме, ESM.
782
+ - Схема артефактов, роли и категории документации хранятся как данные (YAML и Markdown), а не как код, чтобы проект мог их переопределять без сборки.
783
+ - Парсер spec-box-YAML переиспользует модель из `@spec-box/sync`, парсер OpenSpec-Markdown свой, без зависимости от пакета `@fission-ai/openspec`: он меняет формат каждые несколько недель, а нам нужен стабильный внутренний контракт. Если `openspec` установлен в проекте, `cow validate` дополнительно вызывает `openspec validate` и показывает его диагностику.
784
+ - Тесты ядра на фикстурах: репозиторий-образец в каждом формате спецификаций, прогон машины состояний с записанными ответами ролей. Адаптеры сред тестируются контрактными тестами с заглушками.
785
+
786
+ ## 13. Автономная работа в нейтральной среде
787
+
788
+ Нейтральная среда это контейнер или CI-раннер без человека и без предыдущего состояния на диске. Инструмент работает в ней при выполнении восьми условий.
789
+
790
+ 1. **Состояние только в репозитории.** Запуск клонирует ветку изменения, читает `change.yaml` и продолжает. Кеши сессий агентов необязательны.
791
+ 2. **Конечный запуск.** `cow run` выполняет фазы, пока не встретит гейт, блокер, точку ожидания, лимит бюджета или конец. Затем коммитит артефакты, пушит ветку, обновляет пул-реквест и завершается с кодом статуса.
792
+ 3. **Асинхронные гейты и события.** Решения приходят через настроенные каналы, события партнёров через опрос или шину. Следующий запуск (по расписанию, по вебхуку, вручную) читает их и продолжает.
793
+ 4. **Секреты из окружения.** Ключи моделей и токен репозитория передаются переменными окружения, в файлы не пишутся. Подписочный вход Claude в нейтральной среде не используется, только ключ API или облачный провайдер.
794
+ 5. **Изоляция агентов.** Адаптер среды запускает роль с разрешениями из проектной документации: список команд, доступные каталоги, защищённые файлы, запрет сетевых вызовов вне списка.
795
+ 6. **Лимиты.** На запуск заданы предельная стоимость, время и число возвратов; бюджет возвратов монотонный и расширяется только человеком. Превышение переводит изменение в `parked` с записью причины.
796
+ 7. **Одно изменение на рабочую копию.** Параллельные изменения работают в разных ветках и разных рабочих копиях: `git worktree` или отдельный `arc mount`. Перед фазой с правом записи адаптер проверяет identity копии. Внутри папки изменения лежит lock-файл работающего CLI на случай двойного старта.
797
+ 9. **Остановка и завершённость.** `cow stop` записывает запрос остановки, надзиратель гасит process group роли, изменение получает статус `stopped`, доставка после остановки запрещена. Запуск считается завершённым только при терминальном статусе и `settled: true`.
798
+ 8. **Доступная среда тестов.** Модульные и компонентные тесты запускаются в контейнере. Для e2e проект в `testing.md` называет способ запуска: локально в контейнере, задачей CI с отчётом-артефактом или на стенде. Верификатор читает отчёт через адаптер репозитория, а не запускает e2e сам, если способ так задан. Для пилота `sync` CI на GitHub Actions настраивается в рамках пилота: прогон тестов и публикация отчётов как артефактов.
799
+
800
+ Готовый способ запуска: Docker-образ с Node.js, инструментом и CLI агентов, плюс GitHub Action, который вызывает `cow run --change <id>` по расписанию, по комментариям и по событиям `repository_dispatch`. Для Arcadia аналогичный раннер появится вместе с адаптером.
801
+
802
+ ## 14. Открытые вопросы
803
+
804
+ 1. **Формат хранения дельт.** Собственный YAML-диалект для spec-box и родной синтаксис для OpenSpec, либо единый диалект инструмента для обоих форматов. Пока оба пилота на spec-box, вопрос не блокирует.
805
+ 2. **Расширение формата spec-box.** Добавлять ли группе поле `description` для нормативного текста требования после первого пилота.
806
+ 3. **Шина событий в Arcadia.** Для GitHub есть `repository_dispatch`. Для Arcadia нужен аналог или только опрос.
807
+ 4. **Адаптер OpenSpec.** Оба пилота на spec-box, поэтому адаптер OpenSpec остаётся без проверки на реальном проекте до появления третьего пилота.
808
+ 5. **Снижение профиля.** После скольких изменений без замечаний на гейтах переводить пилот с `supervised` на `checkpoint`, а изменения из брифов запускать в `autonomous`.
809
+
810
+ ## 15. План
811
+
812
+ | Этап | Результат | Проверка |
813
+ |---|---|---|
814
+ | 1. Ядро и interactive-режим | Конфиг, `change.yaml`, машина состояний, `next` и `report`, адаптер spec-box, категории документации и структурный `doctor`, формат дизайна с вопросами и правилами, материалы для Claude Code | В репозитории `sync` разработчик проводит одно изменение до пул-реквеста из Claude Code, фазу cover выполняет вручную |
815
+ | 2. Тесты и headless на GitHub | Роль tester, ИИ-ревью тестов, защита тестов, `coverage.yaml`, верификатор с отчётами до ревьюера, контракт ревьюера с disposition и `delivery_narrative`, запечатывание change-set и привязка вердиктов к дайджесту, ревизия и lock `change.yaml`, receipt запусков, политика одного транспортного повтора, статусы `parked`, `stopped`, `delivery_unknown`, адаптеры сред Claude и Codex с надзором за процессами, `run`, `stop`, `watch`, адаптер GitHub с `git worktree` и идемпотентной доставкой, каналы `cli` и `pr-comments`, архивация в пул-реквесте, CI `sync` на GitHub Actions, Docker-образ и GitHub Action | Изменение в `sync` проходит от тикета до готового к влитию пул-реквеста, человек участвует только на гейтах |
816
+ | 3. Дискавери и правила | `discover` с дельтами и решениями, критерии достаточности, `discover validate`, повышение решений и проверка правил, `doctor --deep` | Изменение в `sync` по брифу проходит профиль `autonomous` без блокера `пользователь` |
817
+ | 4. Arcadia и Magic | Адаптер Arcadia с отдельным `arc mount` на изменение и идемпотентной доставкой в Arcanum, канал `tracker` с проекцией статуса тегами, материалы для Codex | Изменение в Magic проходит headless до готового к влитию пул-реквеста |
818
+ | 5. Несколько репозиториев | Координация, события, опрос и `github-dispatch` | Согласованное изменение в двух репозиториях доходит до двух пул-реквестов с правильным порядком мержа; проект для проверки выбирается, когда он появится |
819
+ | 6. Расширения | Адаптер OpenSpec, схема `bugfix` с причинным контрактом и RED-оракулом, wiki и дистилляция, шина событий для Arcadia | Порядок определяют потребности пилотов |
820
+
821
+ Каждый этап заканчивается изменением в реальном проекте, а не демонстрацией на образце.