devcontour-harness 0.1.0-alpha.5 → 0.1.0-alpha.7

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 (263) hide show
  1. package/README.md +42 -2
  2. package/docs/compatibility-versions.md +56 -0
  3. package/docs/compatibility.md +2 -2
  4. package/docs/connectors.md +323 -0
  5. package/docs/contracts.md +33 -5
  6. package/docs/integrations/hindsight.md +148 -0
  7. package/docs/quickstart.md +1 -1
  8. package/docs/release-notes/0.1.0-alpha.6.md +238 -0
  9. package/docs/release-notes/0.1.0-alpha.7.md +71 -0
  10. package/docs/review-probes.md +199 -0
  11. package/docs/review-protocol.md +251 -0
  12. package/docs/toolchains.md +167 -0
  13. package/docs/turbo-starter.md +269 -0
  14. package/docs/upgrade.md +77 -24
  15. package/examples/README.md +1 -0
  16. package/examples/toolchains/README.md +32 -0
  17. package/examples/toolchains/go/bad/label/label.go +9 -0
  18. package/examples/toolchains/go/good/label/label.go +9 -0
  19. package/examples/toolchains/go/policy.json +79 -0
  20. package/examples/toolchains/go/repo/REQUIREMENTS.md +9 -0
  21. package/examples/toolchains/go/repo/_gitignore +1 -0
  22. package/examples/toolchains/go/repo/go.mod +5 -0
  23. package/examples/toolchains/go/repo/go.sum +2 -0
  24. package/examples/toolchains/go/repo/label/label.go +9 -0
  25. package/examples/toolchains/go/repo/label/label_test.go +27 -0
  26. package/examples/toolchains/go/repo/label/version.go +9 -0
  27. package/examples/toolchains/go/task.json +25 -0
  28. package/examples/toolchains/mixed-go/bad/backend/label/label.go +9 -0
  29. package/examples/toolchains/mixed-go/bad/frontend/src/label.js +4 -0
  30. package/examples/toolchains/mixed-go/good/backend/label/label.go +9 -0
  31. package/examples/toolchains/mixed-go/good/frontend/src/label.js +4 -0
  32. package/examples/toolchains/mixed-go/policy.json +126 -0
  33. package/examples/toolchains/mixed-go/repo/REQUIREMENTS.md +9 -0
  34. package/examples/toolchains/mixed-go/repo/_gitignore +6 -0
  35. package/examples/toolchains/mixed-go/repo/backend/go.mod +3 -0
  36. package/examples/toolchains/mixed-go/repo/backend/label/label.go +9 -0
  37. package/examples/toolchains/mixed-go/repo/backend/label/label_test.go +30 -0
  38. package/examples/toolchains/mixed-go/repo/contract/check.mjs +17 -0
  39. package/examples/toolchains/mixed-go/repo/contract/label.json +9 -0
  40. package/examples/toolchains/mixed-go/repo/frontend/package.json +15 -0
  41. package/examples/toolchains/mixed-go/repo/frontend/pnpm-lock.yaml +22 -0
  42. package/examples/toolchains/mixed-go/repo/frontend/src/label.js +4 -0
  43. package/examples/toolchains/mixed-go/repo/frontend/test/label.test.js +11 -0
  44. package/examples/toolchains/mixed-go/task.json +25 -0
  45. package/examples/toolchains/mixed-python/bad/backend/src/fx_backend/__init__.py +6 -0
  46. package/examples/toolchains/mixed-python/bad/frontend/src/label.js +4 -0
  47. package/examples/toolchains/mixed-python/good/backend/src/fx_backend/__init__.py +6 -0
  48. package/examples/toolchains/mixed-python/good/frontend/src/label.js +4 -0
  49. package/examples/toolchains/mixed-python/policy.json +121 -0
  50. package/examples/toolchains/mixed-python/repo/REQUIREMENTS.md +9 -0
  51. package/examples/toolchains/mixed-python/repo/_gitignore +6 -0
  52. package/examples/toolchains/mixed-python/repo/backend/.python-version +1 -0
  53. package/examples/toolchains/mixed-python/repo/backend/pyproject.toml +19 -0
  54. package/examples/toolchains/mixed-python/repo/backend/src/fx_backend/__init__.py +6 -0
  55. package/examples/toolchains/mixed-python/repo/backend/tests/test_label.py +11 -0
  56. package/examples/toolchains/mixed-python/repo/backend/uv.lock +108 -0
  57. package/examples/toolchains/mixed-python/repo/contract/check.mjs +17 -0
  58. package/examples/toolchains/mixed-python/repo/contract/label.json +9 -0
  59. package/examples/toolchains/mixed-python/repo/frontend/package.json +15 -0
  60. package/examples/toolchains/mixed-python/repo/frontend/pnpm-lock.yaml +22 -0
  61. package/examples/toolchains/mixed-python/repo/frontend/src/label.js +4 -0
  62. package/examples/toolchains/mixed-python/repo/frontend/test/label.test.js +11 -0
  63. package/examples/toolchains/mixed-python/task.json +25 -0
  64. package/examples/toolchains/npm/bad/src/label.js +4 -0
  65. package/examples/toolchains/npm/good/src/label.js +4 -0
  66. package/examples/toolchains/npm/policy.json +82 -0
  67. package/examples/toolchains/npm/repo/REQUIREMENTS.md +9 -0
  68. package/examples/toolchains/npm/repo/_gitignore +4 -0
  69. package/examples/toolchains/npm/repo/package-lock.json +22 -0
  70. package/examples/toolchains/npm/repo/package.json +13 -0
  71. package/examples/toolchains/npm/repo/src/label.js +4 -0
  72. package/examples/toolchains/npm/repo/test/label.test.js +13 -0
  73. package/examples/toolchains/npm/task.json +25 -0
  74. package/examples/toolchains/pnpm/bad/packages/label/src/label.js +4 -0
  75. package/examples/toolchains/pnpm/good/packages/label/src/label.js +4 -0
  76. package/examples/toolchains/pnpm/policy.json +85 -0
  77. package/examples/toolchains/pnpm/repo/REQUIREMENTS.md +9 -0
  78. package/examples/toolchains/pnpm/repo/_gitignore +4 -0
  79. package/examples/toolchains/pnpm/repo/package.json +10 -0
  80. package/examples/toolchains/pnpm/repo/packages/checker/check.mjs +5 -0
  81. package/examples/toolchains/pnpm/repo/packages/checker/package.json +9 -0
  82. package/examples/toolchains/pnpm/repo/packages/label/package.json +14 -0
  83. package/examples/toolchains/pnpm/repo/packages/label/src/label.js +4 -0
  84. package/examples/toolchains/pnpm/repo/packages/label/test/label.test.js +13 -0
  85. package/examples/toolchains/pnpm/repo/pnpm-lock.yaml +29 -0
  86. package/examples/toolchains/pnpm/repo/pnpm-workspace.yaml +2 -0
  87. package/examples/toolchains/pnpm/task.json +25 -0
  88. package/examples/toolchains/python/bad/src/fx_label/__init__.py +6 -0
  89. package/examples/toolchains/python/good/src/fx_label/__init__.py +6 -0
  90. package/examples/toolchains/python/policy.json +80 -0
  91. package/examples/toolchains/python/repo/.python-version +1 -0
  92. package/examples/toolchains/python/repo/REQUIREMENTS.md +9 -0
  93. package/examples/toolchains/python/repo/_gitignore +5 -0
  94. package/examples/toolchains/python/repo/pyproject.toml +19 -0
  95. package/examples/toolchains/python/repo/src/fx_label/__init__.py +6 -0
  96. package/examples/toolchains/python/repo/tests/test_label.py +13 -0
  97. package/examples/toolchains/python/repo/uv.lock +108 -0
  98. package/examples/toolchains/python/task.json +25 -0
  99. package/examples/toolchains/turbo/bad/packages/label/src/label.js +4 -0
  100. package/examples/toolchains/turbo/good/packages/label/src/label.js +4 -0
  101. package/examples/toolchains/turbo/policy.json +110 -0
  102. package/examples/toolchains/turbo/repo/REQUIREMENTS.md +9 -0
  103. package/examples/toolchains/turbo/repo/_gitignore +4 -0
  104. package/examples/toolchains/turbo/repo/package.json +13 -0
  105. package/examples/toolchains/turbo/repo/packages/checker/check.mjs +5 -0
  106. package/examples/toolchains/turbo/repo/packages/checker/package.json +9 -0
  107. package/examples/toolchains/turbo/repo/packages/label/package.json +14 -0
  108. package/examples/toolchains/turbo/repo/packages/label/src/label.js +4 -0
  109. package/examples/toolchains/turbo/repo/packages/label/test/label.test.js +13 -0
  110. package/examples/toolchains/turbo/repo/pnpm-lock.yaml +94 -0
  111. package/examples/toolchains/turbo/repo/pnpm-workspace.yaml +2 -0
  112. package/examples/toolchains/turbo/repo/turbo.json +7 -0
  113. package/examples/toolchains/turbo/task.json +25 -0
  114. package/examples/turbo-starter/README.md +16 -0
  115. package/examples/turbo-starter/changes/issued-at-consumer-only.patch +12 -0
  116. package/examples/turbo-starter/changes/issued-at.patch +130 -0
  117. package/examples/turbo-starter/cleanup.mjs +58 -0
  118. package/examples/turbo-starter/graph.json +32 -0
  119. package/examples/turbo-starter/harness/acceptance/db-isolation.test.mjs +97 -0
  120. package/examples/turbo-starter/harness/acceptance/env-probe.test.mjs +75 -0
  121. package/examples/turbo-starter/harness/acceptance/generation.test.mjs +54 -0
  122. package/examples/turbo-starter/harness/acceptance/issued-at.test.mjs +43 -0
  123. package/examples/turbo-starter/harness/acceptance/lifecycle-probe.test.mjs +47 -0
  124. package/examples/turbo-starter/harness/compose-bridge.mjs +593 -0
  125. package/examples/turbo-starter/harness/compose.acceptance.yml +23 -0
  126. package/examples/turbo-starter/harness/env/backend.env.template +7 -0
  127. package/examples/turbo-starter/harness/env/compose.env.template +6 -0
  128. package/examples/turbo-starter/policy.json +109 -0
  129. package/examples/turbo-starter/policy.lifecycle.json +114 -0
  130. package/examples/turbo-starter/setup.mjs +352 -0
  131. package/lib/agents/cli-adapter.js +10 -1
  132. package/lib/agents/inspect.d.ts +56 -2
  133. package/lib/agents/inspect.js +264 -14
  134. package/lib/agents/review-probe.d.ts +229 -7
  135. package/lib/agents/review-probe.js +830 -72
  136. package/lib/agents/schemas.d.ts +3 -1
  137. package/lib/agents/schemas.js +7 -3
  138. package/lib/cli.js +167 -7
  139. package/lib/contracts/cli.d.ts +820 -11
  140. package/lib/contracts/cli.js +243 -4
  141. package/lib/contracts/errors.d.ts +3 -1
  142. package/lib/contracts/errors.js +2 -0
  143. package/lib/contracts/events.d.ts +37 -5
  144. package/lib/contracts/events.js +9 -0
  145. package/lib/contracts/execution.d.ts +389 -15
  146. package/lib/contracts/execution.js +76 -4
  147. package/lib/contracts/failure.js +5 -0
  148. package/lib/contracts/index.d.ts +1 -0
  149. package/lib/contracts/index.js +1 -0
  150. package/lib/contracts/policy.d.ts +15 -0
  151. package/lib/contracts/policy.js +10 -0
  152. package/lib/contracts/ports.d.ts +50 -0
  153. package/lib/contracts/profiles.d.ts +14 -0
  154. package/lib/contracts/profiles.js +4 -0
  155. package/lib/contracts/published.d.ts +279 -8
  156. package/lib/contracts/result.d.ts +171 -0
  157. package/lib/contracts/result.js +39 -1
  158. package/lib/contracts/review.d.ts +231 -0
  159. package/lib/contracts/review.js +326 -2
  160. package/lib/contracts/toolchain.d.ts +96 -0
  161. package/lib/contracts/toolchain.js +89 -0
  162. package/lib/control/doctor.d.ts +16 -1
  163. package/lib/control/doctor.js +45 -3
  164. package/lib/control/harness.d.ts +52 -6
  165. package/lib/control/harness.js +28 -1
  166. package/lib/control/recovery.d.ts +3 -1
  167. package/lib/control/review.js +23 -11
  168. package/lib/engine/budget.d.ts +3 -3
  169. package/lib/engine/classify.js +1 -0
  170. package/lib/engine/engine.d.ts +67 -17
  171. package/lib/engine/prompt.d.ts +29 -1
  172. package/lib/engine/prompt.js +57 -0
  173. package/lib/engine/property-recovery.js +2 -0
  174. package/lib/engine/result.js +52 -0
  175. package/lib/engine/reuse.js +6 -1
  176. package/lib/engine/runner.d.ts +25 -0
  177. package/lib/engine/runner.js +500 -123
  178. package/lib/environment/lifecycle.js +6 -0
  179. package/lib/environment/live-calls.js +3 -2
  180. package/lib/environment/os-tools.d.ts +16 -0
  181. package/lib/environment/os-tools.js +35 -0
  182. package/lib/environment/process.d.ts +8 -0
  183. package/lib/environment/process.js +13 -6
  184. package/lib/integrations/connector/commands.d.ts +358 -0
  185. package/lib/integrations/connector/commands.js +428 -0
  186. package/lib/integrations/connector/config.d.ts +215 -0
  187. package/lib/integrations/connector/config.js +214 -0
  188. package/lib/integrations/connector/delivery.d.ts +107 -0
  189. package/lib/integrations/connector/delivery.js +591 -0
  190. package/lib/integrations/connector/fake.d.ts +291 -0
  191. package/lib/integrations/connector/fake.js +786 -0
  192. package/lib/integrations/connector/history.d.ts +124 -0
  193. package/lib/integrations/connector/history.js +497 -0
  194. package/lib/integrations/connector/index.d.ts +16 -0
  195. package/lib/integrations/connector/index.js +16 -0
  196. package/lib/integrations/connector/redact.d.ts +19 -0
  197. package/lib/integrations/connector/redact.js +91 -0
  198. package/lib/integrations/connector/registry.d.ts +15 -0
  199. package/lib/integrations/connector/registry.js +43 -0
  200. package/lib/integrations/connector/snapshot.d.ts +131 -0
  201. package/lib/integrations/connector/snapshot.js +422 -0
  202. package/lib/integrations/connector/source.d.ts +37 -0
  203. package/lib/integrations/connector/source.js +73 -0
  204. package/lib/integrations/connector/steps.d.ts +71 -0
  205. package/lib/integrations/connector/steps.js +188 -0
  206. package/lib/integrations/connector/store.d.ts +140 -0
  207. package/lib/integrations/connector/store.js +277 -0
  208. package/lib/integrations/connector/summary.d.ts +38 -0
  209. package/lib/integrations/connector/summary.js +59 -0
  210. package/lib/integrations/connector/testing-worker.d.ts +1 -0
  211. package/lib/integrations/connector/testing-worker.js +81 -0
  212. package/lib/integrations/connector/testing.d.ts +205 -0
  213. package/lib/integrations/connector/testing.js +1615 -0
  214. package/lib/integrations/connector/transport.d.ts +240 -0
  215. package/lib/integrations/connector/transport.js +28 -0
  216. package/lib/integrations/memory/context.d.ts +120 -0
  217. package/lib/integrations/memory/context.js +239 -0
  218. package/lib/integrations/memory/index.d.ts +8 -0
  219. package/lib/integrations/memory/index.js +8 -0
  220. package/lib/integrations/memory/provider.d.ts +79 -0
  221. package/lib/integrations/memory/provider.js +22 -0
  222. package/lib/integrations/memory/sink.d.ts +56 -0
  223. package/lib/integrations/memory/sink.js +116 -0
  224. package/lib/integrations/memory/testing.d.ts +44 -0
  225. package/lib/integrations/memory/testing.js +61 -0
  226. package/lib/integrations/relay-v1/index.d.ts +122 -0
  227. package/lib/integrations/relay-v1/index.js +860 -0
  228. package/lib/integrations/relay-v2/index.d.ts +145 -0
  229. package/lib/integrations/relay-v2/index.js +752 -0
  230. package/lib/integrations/relay-v2/process.d.ts +32 -0
  231. package/lib/integrations/relay-v2/process.js +112 -0
  232. package/lib/integrations/relay-v2/protocol.d.ts +172 -0
  233. package/lib/integrations/relay-v2/protocol.js +142 -0
  234. package/lib/profiles/resolve.d.ts +5 -1
  235. package/lib/profiles/resolve.js +31 -0
  236. package/lib/storage/document.d.ts +116 -16
  237. package/lib/toolchains/index.d.ts +9 -0
  238. package/lib/toolchains/index.js +9 -0
  239. package/lib/toolchains/inspect.d.ts +105 -0
  240. package/lib/toolchains/inspect.js +831 -0
  241. package/lib/toolchains/manifests.d.ts +67 -0
  242. package/lib/toolchains/manifests.js +238 -0
  243. package/lib/toolchains/probe.d.ts +28 -0
  244. package/lib/toolchains/probe.js +54 -0
  245. package/lib/toolchains/recipes.d.ts +33 -0
  246. package/lib/toolchains/recipes.js +51 -0
  247. package/lib/verification/acceptance.d.ts +16 -0
  248. package/lib/verification/acceptance.js +16 -0
  249. package/lib/verification/gates.d.ts +38 -0
  250. package/lib/verification/gates.js +108 -26
  251. package/package.json +22 -1
  252. package/profiles/go-gotestsum.json +21 -0
  253. package/profiles/npm-ci.json +22 -0
  254. package/profiles/pnpm-frozen.json +24 -0
  255. package/profiles/python-uv.json +26 -0
  256. package/schemas/execution-document.schema.json +345 -4
  257. package/schemas/execution-policy.schema.json +48 -0
  258. package/schemas/result-envelope.schema.json +213 -1
  259. package/schemas/run-event.schema.json +93 -0
  260. package/schemas/run-result.schema.json +207 -0
  261. package/schemas/stack-profile.schema.json +43 -0
  262. package/templates/controller-mode/AGENTS.controller.md +58 -0
  263. package/templates/controller-mode/README.md +44 -0
package/README.md CHANGED
@@ -5,7 +5,7 @@ Headless-движок исполнения инженерных поручени
5
5
  интеграция с принятой веткой через compare-and-swap и машинный результат с SHA
6
6
  и доказательствами.
7
7
 
8
- > Статус: `0.1.0-alpha.5` — предварительный выпуск (alpha). Договоры
8
+ > Статус: `0.1.0-alpha.7` — предварительный выпуск (alpha). Договоры
9
9
  > `v1alpha1` могут измениться между alpha-версиями; закрепляйте точную версию.
10
10
  > Файл → ревью плана → кандидат → интеграция → принятие → результат JSON, с
11
11
  > ограниченным восстановлением, повторным использованием кандидата, бюджетами,
@@ -27,7 +27,7 @@ Headless-движок исполнения инженерных поручени
27
27
  Из npm — точная версия (воспроизводимо) или последняя alpha по тегу:
28
28
 
29
29
  ```sh
30
- npm install --save-exact devcontour-harness@0.1.0-alpha.5 # точная версия
30
+ npm install --save-exact devcontour-harness@0.1.0-alpha.7 # точная версия
31
31
  # или: npm install --save-exact devcontour-harness@alpha # текущая alpha
32
32
  npx harness --version
33
33
  ```
@@ -117,6 +117,46 @@ PASS» нет. Адаптеры хоста реализуют порты `Execut
117
117
  `devcontour-harness/schemas/<имя>.schema.json`. Декларации используют типы
118
118
  Node.js: TypeScript-потребителю нужен `@types/node` (или `skipLibCheck`).
119
119
 
120
+ ## Выпуск 0.1.0-alpha.7
121
+
122
+ Исправление учёта команд reviewer в `inspection` ревью кода. Подробно —
123
+ [заметки выпуска](docs/release-notes/0.1.0-alpha.7.md).
124
+
125
+ - `inspection` собирается controller из событий runtime и журнала проверок
126
+ `run_check` одного вызова reviewer: запуски probe теперь засчитываются, а
127
+ команда с ненулевым кодом — выполнение, а не `diff-only`
128
+ ([review-probes](docs/review-probes.md#итог-inspection-что-reviewer-действительно-выполнил)).
129
+ - Старт probe подтверждается на границе исполнения; отказ до старта, отменённый
130
+ до запуска вызов и резерв бюджета выполнением не считаются. Уже отменённый
131
+ вызов больше не запускает команду.
132
+ - Gates, бюджеты, правила находок и принятия не менялись: `inspection` по-прежнему
133
+ не влияет на решение.
134
+
135
+ Записи alpha.6 читаются без пересчёта; состояние с новыми записями `inspection`
136
+ alpha.6 не читает — откат только через копию ([upgrade](docs/upgrade.md)).
137
+
138
+ ## Выпуск 0.1.0-alpha.6
139
+
140
+ Ядро работает как прежде; всё новое подключается явно. Подробно —
141
+ [заметки выпуска](docs/release-notes/0.1.0-alpha.6.md).
142
+
143
+ - Инструменты целевого проекта: npm, pnpm, Turborepo, Go/gotestsum,
144
+ Python/uv — явными шагами `prepare`, gates и `repositories[].toolchain`;
145
+ `doctor` и обязательный preflight `run` ([toolchains](docs/toolchains.md)).
146
+ - Рецепт Turbo Starter с lifecycle-мостом Docker
147
+ ([turbo-starter](docs/turbo-starter.md)).
148
+ - Коннектор задач с транспортами Relay v1 и Relay-2: команды `source-*`,
149
+ `run --source`, доставка с намерением, read-back и явным разбором
150
+ неизвестного исхода ([connectors](docs/connectors.md)).
151
+ - Протокол ревью кода: ID и переходы находок, ограниченный повтор reviewer,
152
+ `REVIEW_PROTOCOL_INVALID` ([review-protocol](docs/review-protocol.md));
153
+ объявленные gates в проверках reviewer ([review-probes](docs/review-probes.md)).
154
+ - Точка подключения внешней памяти — договор без готового адаптера
155
+ ([hindsight](docs/integrations/hindsight.md)).
156
+
157
+ Состояние alpha.5 читается без преобразования. Standalone-состояние с новыми записями
158
+ alpha.5 не читает; прежний DevContour его открывает без отказа — откат только через копию ([upgrade](docs/upgrade.md)).
159
+
120
160
  ## Выпуск 0.1.0-alpha.5
121
161
 
122
162
  Первая публикация в npm. Движок, форматы состояния, политики и договоры — те
@@ -0,0 +1,56 @@
1
+ # Проверенные версии
2
+
3
+ Набор версий, на котором выпуск фактически проверен. Это не список
4
+ обязательных требований: другие версии могут работать, но не проверялись.
5
+ Строки с «TODO» заполняются по итогам приёмки выпуска.
6
+
7
+ ## Пакет и потребители
8
+
9
+ | Что | Версия | Где проверено |
10
+ | ---------------------- | --------------------------------------------------- | -------------------------------------- |
11
+ | ОС / архитектура | macOS 15 (Darwin 24.6), arm64 | все проверки выпуска |
12
+ | Node.js | 24.3.0 (`engines: >=24.3.0`) | все проверки выпуска |
13
+ | npm | 11.4.2 | C01 (установка архива), A01, A02 |
14
+ | pnpm | 10.12.3 | C01 (`pnpm add <архив> --prod`) |
15
+ | TypeScript потребителя | 6.0.3 + `@types/node` 24.13.5 | A01, C01 (ставится отдельно от пакета) |
16
+ | Прежний потребитель | API 0.1.0-alpha.5 | A01 |
17
+ | DevContour | 0.4.0-alpha.7 (`e26f8f4`), vendored alpha.4 → архив | C02 |
18
+ | Git | 2.33.0 | все сценарии |
19
+
20
+ ## Инструменты целевых проектов
21
+
22
+ | Что | Версия | Где проверено |
23
+ | -------------- | ------ | -------------- |
24
+ | Turbo | TODO | T01/turbo, S01 |
25
+ | Go / gotestsum | TODO | T01/go |
26
+ | Python / uv | TODO | T01/python |
27
+ | Docker | TODO | S01–S06 |
28
+
29
+ ## Транспорты
30
+
31
+ | Что | Версия / commit | Где проверено |
32
+ | ------------------- | --------------- | ------------- |
33
+ | Relay v1 (server) | TODO | R01, W01 |
34
+ | Relay-2 (CLI) / Bun | TODO | R02, R03, W01 |
35
+
36
+ ## Как повторить
37
+
38
+ <!-- repo-only -->
39
+
40
+ Проверки выпуска — инструменты разработки harness: они лежат в checkout
41
+ репозитория (`scripts/acceptance/`, `tests/fixtures/consumer-alpha5`) и в
42
+ npm-архив не входят.
43
+
44
+ ```sh
45
+ node scripts/acceptance/package-pack.mjs --out <dir>
46
+ node scripts/acceptance/package-check.mjs <dir>/devcontour-harness-<ver>.tgz
47
+ node scripts/acceptance/consumer-check.mjs <dir>/devcontour-harness-<ver>.tgz
48
+ node scripts/acceptance/core-minimal-env.mjs <dir>/devcontour-harness-<ver>.tgz
49
+ node scripts/acceptance/devcontour-check.mjs <dir>/devcontour-harness-<ver>.tgz \
50
+ --devcontour <путь или URL DevContour> --baseline
51
+ ```
52
+
53
+ Отчёты скриптов (`report.json`) содержат фактические версии `node`, `npm`,
54
+ `pnpm` и компилятора, hash архива и argv каждой команды.
55
+
56
+ <!-- /repo-only -->
@@ -91,7 +91,7 @@ coordinator. `Store.change(type, fn)` открывает `BEGIN IMMEDIATE` (вл
91
91
  | `WorkState` | `Task` (поля исполнения) | `status`, `attempt`, `activeRunId`, `resultSha`, `failure`, `failureKind`, `failureFingerprint`, `approvedAt`, `approvedDigest`, `approvedScope`, `approval`, `hold`, `budgetBlock`, `checkVersion`, `recovery`, `recoveryHistory` — те же имена и формы. `graphId` — доска, в активной ревизии которой задача |
92
92
  | `WorkOrder` | `Task` (поля постановки) | `description` → `goal`; `writePaths` (или область роли) → `writeScope`; `contracts` + `contractDigests` → `contracts[{id, digest}]`; `contextPacks` → `context`; `source` = `{connector: 'devcontour', ref: <taskId>, revision: <approvedDigest>}`. Продуктовые поля (`preparation`, `featureId`, `assignee`, `supersedes`, `finding`, `sharedCompletion`, `scope`) в поручение не входят и остаются в `Task` |
93
93
  | `AttemptState` | `Run` | Те же имена и формы, включая `context` (краткие pins), `consumers[].checked`, `partial`, `continuedFrom`, `dispatch`, `timings`; `taskId` ↔ `workId`; runtime `demo` ↔ `fixture` на границе (сохранённое значение не переписывается); `memory` остаётся полем `Run` хоста; `engineVersion`, `consumerVersion`, `contextSnapshot` — новые необязательные поля |
94
- | `EvidenceRecord` | `Evidence` | Без изменений |
94
+ | `EvidenceRecord` | `Evidence` | Без изменений; аддитивные необязательные `reviewRound` и `findings[].id/status` ([review-protocol.md](review-protocol.md)) хранятся как есть, прежние записи читаются без них; строгие переходы находок — только для адаптера reviewer с необязательным `RuntimeCapabilities.reviewProtocol: 1` (`cliAdapter`), runtime-обёртки хоста без поля сохраняют приёмку alpha.5 |
95
95
  | `ControlState` | `paused`, `pauseReason`, `pausedAt`, `pauseOperation`, `pauseFailures`, `waits`, `appliedConfig`, `reconfigure` | Те же формы; `appliedConfig` ↔ `appliedPolicy` |
96
96
  | `ControllerLease` | `leader` | Без изменений |
97
97
  | `JointVerificationState` | `Verification` | `manifest`, `manifestDigest`, `policyDigest`, `specDigest`, `impact`, `evidence`, `token`, `leaseUntil`, `status` — те же формы; `tasks` → `workIds` (снимок задач остаётся в `Verification`); `boards`, `productRelease` — поля хоста |
@@ -181,7 +181,7 @@ Gates, шаги подготовки и проверки потребителе
181
181
  | Версия движка в основании reuse | Попытка записывает `engineVersion` (и `consumerVersion`). Кандидат попытки другой версии движка повторно не используется: новая попытка заново проходит gates и ревью. Прежние попытки сохраняют своё основание; отсутствующая версия не выдумывается |
182
182
  | Reviewer задаётся правилом | `different-vendor` по умолчанию; `same-vendor-allowed` включается явно |
183
183
  | Токены вне журнала | Исправление утечки: прежде `run.claimed` и `run.recovered` записывали в журнал попытку целиком, с fencing-токеном. Новые события пишутся без токенов; при выдаче старых событий наружу (CLI, HTTP, RunEvent) токены удаляются. Историю базы физически не переписываем |
184
- | История находок ревью | Reviewer называет состояние прежних находок; прежние попытки читаются без статусов |
184
+ | История находок ревью | Reviewer называет состояние прежних находок; прежние попытки читаются без статусов; ревью кода ведёт историю находок поручения с ID controller, находки записей alpha.5 получают детерминированный ID (review-protocol.md) |
185
185
 
186
186
  ## Состояние standalone
187
187
 
@@ -0,0 +1,323 @@
1
+ # Коннектор задач: Relay v1 и Relay-2
2
+
3
+ Коннектор подключает к harness внешнее хранилище задач: читает задачу,
4
+ закрепляет её постановку, исполняет её обычным путём (ревью плана →
5
+ согласование → изолированная попытка → gates → независимое ревью → приёмка в
6
+ Git) и доставляет подтверждённый результат обратно в задачу. Это opt-in
7
+ модуль `devcontour-harness/integrations/connector`: корень пакета и
8
+ `./contracts` его не импортируют, адаптер backend загружается только для
9
+ явно выбранного provider.
10
+
11
+ Поддерживаются два независимых транспорта:
12
+
13
+ | provider | Backend | Транспорт |
14
+ | --------------- | ------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
15
+ | `relay-v1-http` | Relay v1 (`/api/v1`, project-scoped routes) | HTTP, `src/integrations/relay-v1` |
16
+ | `relay-v2-cli` | Relay-2 | subprocess CLI (`--format json`, явные `--config`/`--project`/`--actor`), `src/integrations/relay-v2` |
17
+
18
+ Provider выбирается **только явно** в конфигурации — никогда по имени
19
+ бинарника `relay`. Базы v1 и v2 не взаимозаменяемы, миграции истории нет.
20
+
21
+ ## Пользовательский путь
22
+
23
+ Все шаги — команды CLI; TypeScript писать не нужно.
24
+
25
+ ```sh
26
+ # 1. Настроить: connector.json (см. «Конфигурация») и policy.json harness.
27
+
28
+ # 2. Проверить подключение, проект, capabilities и mapping — только чтение:
29
+ # ни мутаций, ни записи истории, ни создания состояния.
30
+ npx harness source-check --source connector.json --policy policy.json --state ../state
31
+
32
+ # 3. Прочитать и закрепить снимок задач: graph.json, context.json, manifest.json.
33
+ npx harness source-snapshot --source connector.json --policy policy.json --state ../state --out ../snapshot
34
+
35
+ # 4. Обычное согласование (approvalMode: operator) — по файлам снимка.
36
+ npx harness review-plan --state ../state --policy policy.json --source connector.json \
37
+ --graph ../snapshot/graph.json --context ../snapshot/context.json --author external
38
+ npx harness approve --state ../state --subject <graphId> --digest <graphDigest>
39
+ # С --source внешние предпосылки (зависимости, выполненные через этот
40
+ # коннектор) допускаются по решению источника (trusts) и доступности SHA в
41
+ # target ref — те же правила, что у run; без --source доверенных receipt нет.
42
+ # В approvalMode: agent ревью плана выполняется в шаге 5 автоматически.
43
+
44
+ # 5. Запуск: задачи перечитываются, semantic digest сверяется с закреплённым
45
+ # снимком, eligibility проверяется — всё до любого вызова runtime.
46
+ # Принятый результат доставляется в исходную задачу.
47
+ npx harness run --source connector.json --policy policy.json --state ../state
48
+
49
+ # 6. Локальный и внешний статус: принято / доставлено / конфликт / неизвестный
50
+ # исход и независимое чтение получателя.
51
+ npx harness source-status --source connector.json --state ../state
52
+
53
+ # 7. Повтор или продолжение доставки — с тем же ключом операции; writer,
54
+ # reviewer, gates и Git не вызываются.
55
+ npx harness source-deliver --source connector.json --state ../state [--work <workId>]
56
+
57
+ # 8. Разбор неизвестного исхода или конфликта (см. «Восстановление»).
58
+ npx harness source-reconcile --source connector.json --state ../state \
59
+ --operation <key> [--decision resend|adopt:<id>|recheck]
60
+ ```
61
+
62
+ `<graphId>`, `<graphDigest>`, ключи операций и ID поручений выводят
63
+ `source-snapshot` и `source-status` (JSON в stdout). Exit codes как у всего
64
+ CLI: 0 — успех, 2 — неверный вход, 3 — отказ/блокировка (в том числе
65
+ `uncertain`/`conflict` доставки и непройденная проверка), 4 — инфраструктура
66
+ (backend недоступен).
67
+
68
+ ## Конфигурация
69
+
70
+ ```json
71
+ {
72
+ "version": 1,
73
+ "provider": "relay-v1-http",
74
+ "relay": { "endpoint": "http://127.0.0.1:4700" },
75
+ "projectId": "AbCd1234",
76
+ "actor": "harness-controller",
77
+ "repository": { "id": "app" },
78
+ "tasks": [
79
+ {
80
+ "ref": "PRODUCT-3",
81
+ "role": "developer",
82
+ "writeScope": ["apps/api/src", "packages/sdk/src"],
83
+ "gates": ["unit"],
84
+ "criteria": [
85
+ { "title": "API returns displayName", "gate": "unit", "testId": "api.user.displayName" },
86
+ { "id": "Ab3dE6gH", "gate": "typecheck", "evidence": "gate" }
87
+ ],
88
+ "requiredDocuments": ["Spec"]
89
+ }
90
+ ],
91
+ "completion": { "mode": "complete", "historicalComment": false },
92
+ "eligibility": { "columns": ["ready", "in-progress"] },
93
+ "limits": {
94
+ "documents": 20,
95
+ "documentBytes": 262144,
96
+ "totalBytes": 4194304,
97
+ "taskBytes": 1048576,
98
+ "rereads": 3,
99
+ "leaseMs": 120000
100
+ },
101
+ "history": { "enabled": true, "client": "devcontour-harness" }
102
+ }
103
+ ```
104
+
105
+ | Поле | Смысл |
106
+ | --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
107
+ | `provider` | `relay-v1-http` или `relay-v2-cli`; только явно |
108
+ | `relay` | Настройки адаптера. v1: `endpoint` (+ `timeoutMs`, `maxResponseBytes`). v2: `binary`, `config` (относительно файла конфигурации), `project?` (workspace), `env?` (например `RELAY_HOME`, на macOS `DYLD_LIBRARY_PATH`), `timeoutMs`, `maxOutputBytes`, `build?`. `projectId`/`actor` берутся из верхнего уровня |
109
+ | `projectId` | Ожидаемая идентичность проекта backend; несовпадение блокирует чтение снимка и любые мутации |
110
+ | `actor` | Автор всех записей коннектора |
111
+ | `stateDir` | Каталог состояния коннектора (относительно файла); без него — `--state` |
112
+ | `repository` | ID репозитория политики (и путь для сверки) |
113
+ | `tasks[]` | Задачи: `ref` (постоянный ID или ключ), роль, `writeScope`, `gates`, сопоставление критериев, обязательные документы |
114
+ | `tasks[].criteria[]` | Критерий (`id` или точный `title`) → `gate` и `testId` JUnit; `evidence: "gate"` — достаточно прошедшего gate (явный выбор) |
115
+ | `completion` | `complete` — закрыть по правилам backend (по умолчанию в `done`); `review-only` — перевести в `review`/`column` и оставить человеку; `historicalComment` — при конфликте постановки записать явно исторический комментарий |
116
+ | `eligibility.columns` | Колонки, из которых задача допускается к запуску (`done`/`cancelled` — никогда) |
117
+ | `limits` | Пределы документов и постановки, число повторных чтений, срок владения доставкой |
118
+ | `history` | Запись истории работы (корневая и дочерние сессии) |
119
+
120
+ ### Идентичность и снимок
121
+
122
+ - Идентичность задачи — `<provider>:<projectId>:<постоянный ID>`; ID поручения
123
+ и графа — хэш от неё. Одинаковые номера задач разных проектов/backend не
124
+ совпадают; смена cwd на worktree идентичность не меняет.
125
+ - `SourceRef.revision` — **semantic digest** постановки: текст, определения
126
+ критериев (без отметок выполнения), зависимости, подзадачи, значимые
127
+ документы и digest mapping. Колонка, свой in-progress, комментарии, история
128
+ и отметки критериев в него не входят. CAS-ревизии backend, токены
129
+ согласованности чтения и идентичность сборки хранятся только в manifest
130
+ снимка (никогда в поле Git SHA).
131
+ - Чтение — раундами по всем задачам; снимок принимается, когда два раунда
132
+ подряд совпали токенами и содержимым. Изменения — не больше `rereads`
133
+ повторов, затем явный отказ `NOT_READY`; ошибка сети/CLI — отказ, а не
134
+ пустая доска.
135
+ - Обязательное содержимое (текст постановки, критерии, `requiredDocuments`)
136
+ не усекается: превышение предела блокирует запуск с причиной и пределом.
137
+ Дополнительные документы усекаются с явной отметкой в начале файла и в
138
+ manifest (`truncated`) либо опускаются с записью в `missing` снимка.
139
+ - Eligibility проверяется отдельно от digest: `done`/`cancelled`, колонка вне
140
+ `eligibility.columns`, незавершённая зависимость, зависимость в `done` без
141
+ receipt этого коннектора. Колонка, в которую задачу перевела наша
142
+ подтверждённая доставка той же постановки, повтор не блокирует.
143
+ - Статус `done` зависимости — не доказательство: предпосылка зависимой
144
+ задачи появляется только из receipt доставки этого коннектора (издатель —
145
+ ID коннектора, digest сверяется с его состоянием), и движок дополнительно
146
+ проверяет доступность result SHA в target ref.
147
+
148
+ ## Доставка: протокол и гарантии
149
+
150
+ Ключ операции — `sha256(provider, projectId, workId, runId, resultDigest)`.
151
+
152
+ 1. **Intent до вызова.** Операция записывается в состояние коннектора до
153
+ первого внешнего вызова. Не удалось записать — мутация не отправляется.
154
+ 2. **Сверка перед мутацией.** Задача перечитывается: изменился semantic
155
+ digest или задача отменена/закрыта другим — `conflict`; результат остаётся
156
+ привязан к своему снимку и SHA, текущая задача не закрывается (по
157
+ `historicalComment` — один явно исторический комментарий).
158
+ 3. **Шаги**, каждый записан до и после вызова: комментарий с маркером
159
+ `<!-- devcontour-harness:op=<key>:step=<n> -->`; отметки сопоставленных
160
+ критериев с CAS; перемещение с CAS; закрытие истории. Критерий отмечается
161
+ только по mapping на прошедший в integration gate/testcase принятого
162
+ результата; ручные критерии, непрошедший testcase, незавершённые
163
+ подзадачи и зависимости оставляют задачу незавершённой.
164
+ Перед **каждым** неподтверждённым шагом (в том числе при продолжении
165
+ после обрыва или перезапуска) задача перечитывается: постановка обязана
166
+ совпасть со снимком результата, задача — не быть отменённой или закрытой
167
+ другим, а для нового CAS-шага текущая ревизия — равняться ревизии,
168
+ записанной после **нашего** предыдущего шага. Иначе — `conflict`, и
169
+ изменённая задача старым результатом не закрывается.
170
+ 4. **Неизвестный исход** (таймаут, обрыв ответа, падение процесса между
171
+ вызовом и записью итога) разбирается фактами получателя: комментарий — по
172
+ маркеру (ровно один — подтверждено); CAS-шаг — по состоянию и ревизии
173
+ (цель достигнута, ревизия ровно на 1 больше нашей и постановка та же —
174
+ подтверждено; цель достигнута, но ревизия или постановка ушли дальше —
175
+ `conflict`; ревизия прежняя — не применено; ревизия
176
+ сменилась без нашей записи — `conflict`). Ответ `conflict` получателя
177
+ никогда не лечится повтором старой команды по новой ревизии.
178
+ 5. **Подтверждённый отказ ≠ неизвестный исход.** Структурный отказ
179
+ получателя (`refused`; `conflict` для записи без CAS — комментария;
180
+ `unavailable` до отправки) означает, что мутации не было: шаг остаётся
181
+ `planned`, доставка — `failed` с причиной «подтверждённый отказ
182
+ получателя», повтор `source-deliver` безопасен (тот же маркер). Класс
183
+ ответа задаёт адаптер: 5xx/ненулевой exit без доказательства отката для
184
+ мутации адаптер сообщает как `uncertain`, и тогда действует п. 6.
185
+ 6. **`uncertain`.** Если исход доказать нельзя (0 маркеров после возможной
186
+ отправки, больше одного, read-back недоступен), операция получает
187
+ `uncertain` (событие `delivery.uncertain` с ключом операции), повторная
188
+ доставка ничего не отправляет до `source-reconcile`.
189
+ 7. **Receipt.** После подтверждения всех шагов сохраняется receipt (ID
190
+ комментария, критерии, колонка, ревизия, сессии истории); для принятого
191
+ результата — доверенный receipt для зависимых задач.
192
+
193
+ Повторная доставка принятого результата не вызывает writer/reviewer, не
194
+ запускает gates и не меняет Git. Частично выполненная доставка продолжается
195
+ с первого неподтверждённого шага без повтора append-only записей.
196
+
197
+ ### Один владелец доставки
198
+
199
+ Владение операцией — lease с токеном, сроком и fencing в SQLite состояния
200
+ коннектора (`BEGIN IMMEDIATE`). Живой владелец продлевает lease и во время
201
+ долгого вызова; второй процесс над тем же каталогом получает
202
+ `CONTROLLER_BUSY` и ничего не отправляет. Упавшего владельца можно
203
+ перехватить только после истечения срока, intent при этом не теряется;
204
+ каждая запись проверяет lease в той же транзакции, поэтому процесс,
205
+ потерявший владение, больше ничего не записывает. Срок `limits.leaseMs`
206
+ должен превышать таймаут одного вызова транспорта.
207
+
208
+ **Граница гарантии:** она действует только для процессов, разделяющих один
209
+ каталог состояния коннектора. Независимые установки с разными каталогами
210
+ состояния вне гарантии и могут доставить одну операцию дважды —
211
+ `source-check` и `source-status` показывают это в `ownership`. Distributed
212
+ lock в этой версии не реализован.
213
+
214
+ ### Что гарантируется, а что нет
215
+
216
+ Доказывается тестами (`tests/connector-*.test.ts`, общий набор
217
+ `connectorSuite`):
218
+
219
+ - одна подтверждённая доставка — один комментарий с маркером, без повторных
220
+ эффектов критериев и истории, в том числе после перезапуска процесса;
221
+ - обрыв до отправки — безопасный повтор; запись получателем без ответа —
222
+ read-back без дубля; ответ без сохранённого receipt — без новых мутаций;
223
+ отказ записи intent — без мутации;
224
+ - неизвестный исход не объявляется успехом и не повторяется автоматически;
225
+ - старая постановка не закрывает новую; чужое изменение между сверкой и CAS —
226
+ конфликт без повтора;
227
+ - два процесса не отправляют одну операцию одновременно.
228
+
229
+ **Не гарантируется:** distributed exactly-once — у получателя нет
230
+ идемпотентности по `requestId` (Relay v1 явно её не обещает, у Relay-2 её
231
+ нет), а Git и Relay не образуют общей транзакции. Неоднозначные случаи
232
+ решает оператор.
233
+
234
+ ### Восстановление (`source-reconcile`)
235
+
236
+ Сначала проверяются факты получателя; решение оператора нужно только там,
237
+ где факты неоднозначны.
238
+
239
+ | Ситуация | Что видно | Действие |
240
+ | ----------------------------------------------------------------------------------- | ------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
241
+ | Запись прошла, ответ потерян | маркер найден ровно один | `source-reconcile --operation <key>` — подтверждение по факту, доставка продолжается |
242
+ | Read-back был недоступен | `uncertain`, причина «read-back недоступен» | повторить `source-reconcile` после восстановления доступа |
243
+ | Таймаут, маркера нет | 0 маркеров | проверить получателя; если записи нет — `--decision resend` (тот же маркер) |
244
+ | Несколько записей с маркером | N > 1 | выбрать одну: `--decision adopt:<commentId>`; первая найденная не выбирается автоматически |
245
+ | CAS-конфликт без изменения постановки (например, сдвиг ранга соседней задачей в v1) | `conflict` | `--decision recheck`: постановка перечитывается, при совпадении digest шаг продолжается от новой ревизии |
246
+ | Постановка изменилась | `conflict`, `matchesSnapshot: false` | результат остаётся историческим; новый `source-snapshot` → согласование → `run --source` |
247
+
248
+ ## История работы
249
+
250
+ При включённой истории (`history.enabled`):
251
+
252
+ - до исполнения попытки (до вызова writer) — корневая сессия поручения с
253
+ intent, затем fork дочерней сессии попытки, attach и её intent. Ошибка
254
+ записи intent — отказ окружения: попытка не начинается;
255
+ - отказ попытки — отмена дочерней сессии; доставка результата — checkpoint с
256
+ SHA и сводкой gates, завершение и приём дочерней, завершение корня (или
257
+ отмена при непринятом результате);
258
+ - каждая запись несёт маркер; после перезапуска controller шаги разбираются
259
+ read-back (`findSessions`, `checkpoints`, ревизия сессии) — без дублей;
260
+ записи прерванной попытки только подтверждаются фактами и отменяются;
261
+ - ревизия сессии и run backend — fencing: если сессию перехватил другой
262
+ владелец, устаревший run её не завершает (`conflict`);
263
+ - ID сессии/run backend, sessionId runtime и lease token попытки — разные
264
+ значения; lease token и полный runtime log в историю не пишутся.
265
+
266
+ ## В Relay уходит
267
+
268
+ Краткий итог: исход, проверяемые SHA (candidate, integration/result), target
269
+ ref, сводка gates (без журналов), таблица критериев → gate/testcase →
270
+ результат, semantic digest и CAS-ревизия снимка. Локальные пути заменяются
271
+ пометкой (они не ссылки для другого компьютера), значения секретов политики
272
+ маскируются, полный runtime log и вызовы инструментов не копируются.
273
+
274
+ ## Маскирование секретов
275
+
276
+ Ошибки транспорта (тело ответа HTTP, stderr CLI) маскируются до того, как
277
+ попадут в отчёт `source-check`, `source-status`, ошибки снимка, причины
278
+ шагов и операций в состоянии коннектора, `lastError` доставки, отказ записи
279
+ истории и вывод CLI. Маскируются значения секретов политики на любом уровне
280
+ (`environment.secrets` политики, репозиториев и tool profiles — по
281
+ окружению controller; для `source-snapshot` укажите `--policy`, иначе
282
+ используется политика каталога состояния) и значения настроек provider с
283
+ именем вида `*TOKEN*`, `*SECRET*`, `*PASSWORD*`, `*KEY*`.
284
+
285
+ ## Состояние коннектора
286
+
287
+ `<stateDir>/connectors/<provider>-<hash(projectId)>.sqlite` — отдельная от
288
+ `state.sqlite` база: снимки (manifest и digest), операции доставки (intent,
289
+ шаги, receipt), lease, связи с историей. Это не состояние исполнения: оно
290
+ остаётся единственным — в `state.sqlite` harness.
291
+
292
+ ## Библиотека и договор адаптера
293
+
294
+ ```ts
295
+ import { openConnector } from 'devcontour-harness/integrations/connector';
296
+
297
+ const connector = await openConnector('connector.json', { stateDir: '../state' });
298
+ const h = Harness.open({ stateDir: '../state', policy, baseDir, hooks: connector.hooks() });
299
+ await connector.snapshot('../snapshot');
300
+ await connector.run(h, { author: 'external' });
301
+ await connector.status(h);
302
+ ```
303
+
304
+ Модуль адаптера (`src/integrations/<adapter>/index.ts`) экспортирует
305
+ `createTransport(settings, options?)` и возвращает `RelayTransport`
306
+ (`src/integrations/connector/transport.ts`). `settings` — объект `relay`
307
+ конфигурации, дополненный `projectId` и `actor` верхнего уровня; для
308
+ `relay-v2-cli` пути `binary` и `config` уже абсолютные (разрешены от каталога
309
+ файла конфигурации). Неверные настройки — `HarnessError('INVALID_INPUT')`.
310
+
311
+ ### Общий набор проверки адаптера
312
+
313
+ `src/integrations/connector/testing.ts` содержит подделку backend
314
+ (`FakeRelayTransport`, fixture), обёртку точек отказа `faultyTransport` для
315
+ любого транспорта, барьеры (`LocalBarrier`, `FileBarrier`) и общий набор
316
+ `connectorSuite(backend)` — W02–W05, D01–D06, R04. Backend набора
317
+ (`SuiteBackend`) создаёт изолированный проект (`project()`), даёт транспорт,
318
+ действия «другого участника» (`admin`) и способ воссоздать транспорт в новом
319
+ процессе (`worker: { kind: 'config' }` — через `createTransportFor`).
320
+ Crash/restart и гонки выполняются в отдельных процессах
321
+ (`testing-worker.ts`) над тем же каталогом состояния. <!-- repo-only -->Набор на подделке —
322
+ `tests/connector-suite.test.ts`; на настоящих Relay v1/Relay-2 —
323
+ `tests/integration/*.test.ts` (репозиторий harness).<!-- /repo-only -->
package/docs/contracts.md CHANGED
@@ -103,6 +103,7 @@ result SHA доступен в target ref.
103
103
  | `toolProfiles{}`, `environment`, `lifecycle`, `resources[]` | Инструменты (у профиля — встроенные инструменты claude, shell и сеть codex, MCP-серверы `mcp{}`: stdio-команда или http-адрес, только перечисленные `tools`, учётные данные — именами переменных `env`/`bearerTokenEnv`, передаются CLI по имени), среда, ресурсы (порты 1024–65535) |
104
104
  | `concurrency`, `leaseMs`, `runTimeoutMs`, `reviewProbes` | Пределы исполнения |
105
105
  | `maxAttempts`, `repairBudget`, `reuseImplementation` | Повторы и повторное использование кандидата |
106
+ | `reviewProtocolRetries` | Необязательное: дополнительные вызовы reviewer кода при ошибке протокола ответа (0–2; нет поля — 1, умолчание не подставляется и digest политики не меняет), [review-protocol.md](review-protocol.md) |
106
107
  | `budgets{work, group, total, onUnknown}`, `prices[]` | Пределы USD и/или времени; тарифы |
107
108
 
108
109
  Переменные `HARNESS_*` и `DEVCONTOUR_*` политика задать не может.
@@ -211,7 +212,17 @@ Runtime возвращает сырой ответ; движок разбира
211
212
  а не самооценка writer. Reviewer называет состояние каждой прежней открытой
212
213
  находки (`previous[]`); `reviewIssues` отклоняет `approved` при блокирующей
213
214
  находке, в том числе прежней открытой, неназванную прежнюю находку и ссылку на
214
- неизвестную. Идентичность находки — `findingId(subject, {rule, message})`.
215
+ неизвестную. Идентичность находки плана/контракта — `findingId(subject, {rule, message})`;
216
+ невалидный ответ раунд не применяет (переходы и новые находки не записываются).
217
+
218
+ Ревью кода ведёт историю находок поручения с ID, выданными controller
219
+ (`codeReviewHistory`, `checkCodeReview`): reviewer называет ровно один переход
220
+ каждой открытой находки, ошибка протокола (`REVIEW_PROTOCOL_INVALID`,
221
+ причина и путь поля, например `previous[0].id`) отделена от содержательного
222
+ отказа (`REVIEW_REJECTED`) и даёт ограниченный повтор только reviewer
223
+ (`reviewProtocolRetries`, 0–2, по умолчанию 1). Evidence ревью несёт
224
+ аддитивное поле `reviewRound`, находки — `id`/`status`, ссылки результата —
225
+ `review`. Правила — [docs/review-protocol.md](review-protocol.md).
215
226
 
216
227
  ## Выходы
217
228
 
@@ -282,7 +293,7 @@ Runtime возвращает сырой ответ; движок разбира
282
293
 
283
294
  Подлинность SHA, деревьев, artifacts и записей (их наличие в Git и в
284
295
  хранилище исполнителя) получатель не удостоверяет — это транспорт хоста.
285
- Таблица — проверяемое утверждение: `tests/receipt-trust.test.ts` по JSON
296
+ Таблица — проверяемое утверждение: <!-- repo-only -->`tests/receipt-trust.test.ts`<!-- /repo-only --> по JSON
286
297
  Schema строит подделку каждого поля (другое значение, удаление
287
298
  необязательного поля, пустой массив, повтор и удаление элемента) на двух
288
299
  фикстурах и требует отказа для первых двух категорий и того же вердикта для
@@ -386,6 +397,20 @@ Schema строит подделку каждого поля (другое зн
386
397
  | `evidence[].reviewer.runtime` | ожидание получателя | reviewer роли в политике получателя |
387
398
  | `evidence[].reviewer.model` | сведение | модель reviewer |
388
399
  | `evidence[].reviewer.sameVendor` | согласованность | вендоры writer и reviewer |
400
+ | `evidence[].review` | сведение | раунд протокола ревью кода |
401
+ | `evidence[].review.round` | сведение | раунд протокола ревью кода |
402
+ | `evidence[].review.call` | сведение | раунд протокола ревью кода |
403
+ | `evidence[].review.protocol` | сведение | раунд протокола ревью кода |
404
+ | `evidence[].review.issues` | сведение | раунд протокола ревью кода |
405
+ | `evidence[].review.issues[]` | сведение | раунд протокола ревью кода |
406
+ | `evidence[].review.issues[].path` | сведение | раунд протокола ревью кода |
407
+ | `evidence[].review.issues[].reason` | сведение | раунд протокола ревью кода |
408
+ | `evidence[].review.issues[].message` | сведение | раунд протокола ревью кода |
409
+ | `evidence[].review.transitions` | сведение | раунд протокола ревью кода |
410
+ | `evidence[].review.transitions[]` | сведение | раунд протокола ревью кода |
411
+ | `evidence[].review.transitions[].id` | сведение | раунд протокола ревью кода |
412
+ | `evidence[].review.transitions[].from` | сведение | раунд протокола ревью кода |
413
+ | `evidence[].review.transitions[].to` | сведение | раунд протокола ревью кода |
389
414
  | `findings` | сведение | сводка находок может относиться к отклонённым раундам |
390
415
  | `findings[]` | сведение | сводка находок |
391
416
  | `findings[].severity` | сведение | сводка находок |
@@ -393,6 +418,8 @@ Schema строит подделку каждого поля (другое зн
393
418
  | `findings[].path` | сведение | сводка находок |
394
419
  | `findings[].line` | сведение | сводка находок |
395
420
  | `findings[].rule` | сведение | сводка находок |
421
+ | `findings[].id` | сведение | сводка находок |
422
+ | `findings[].status` | сведение | сводка находок |
396
423
  | `limitations` | сведение | чего результат не доказывает |
397
424
  | `limitations[]` | сведение | чего результат не доказывает |
398
425
  | `finishedAt` | сведение | время окончания |
@@ -479,8 +506,9 @@ manifestDigest, impact?, evidence[], error?}`. Смена любого SHA де
479
506
  (`implementation`, `<фаза>-test:<gate>`, `<фаза>-consumer:<репозиторий>:<gate>`,
480
507
  `<фаза>-review`, `<фаза>-environment:<шаг>`, `integration-wait`) и сохраняет
481
508
  снимок контекста артефактом `implementation/context.json`. Отказ приёмки с
482
- кодом `GATE_FAILED`, `REVIEW_REJECTED`, `SCOPE_VIOLATION` или
483
- `BUDGET_EXHAUSTED` записывается классом отказа этого правила.
509
+ кодом `GATE_FAILED`, `REVIEW_REJECTED`, `REVIEW_PROTOCOL_INVALID`,
510
+ `SCOPE_VIOLATION` или `BUDGET_EXHAUSTED` записывается классом отказа этого
511
+ правила.
484
512
 
485
513
  Хост, ведущий движок поверх своего хранилища, собирает исполнение из
486
514
  экспортов корня пакета: `Engine`, `Runner`, `WorkflowQueue`, `JointRunner`,
@@ -514,7 +542,7 @@ exit code CLI.
514
542
  | 0 | успех | — |
515
543
  | 2 | `input` | `INVALID_INPUT`, `UNSUPPORTED_COMMAND`, `UNSUPPORTED_VERSION`, `GRAPH_*`, `GATE_*`, `REVIEWER_*` |
516
544
  | 3 | `refused` | `APPROVAL_STALE`, `APPROVAL_REQUIRED`, `LEASE_STALE`, `CONTROLLER_BUSY`, `PAUSED`, `NOT_READY`, `SCOPE_VIOLATION`, `GATE_FAILED`, `REVIEW_REJECTED`, `BUDGET_EXHAUSTED`, `BUDGET_UNKNOWN`, `ATTEMPTS_EXHAUSTED`, `REVISION_CONFLICT`, `CANCELLED`, `NOT_FOUND` |
517
- | 4 | `infrastructure` | `STATE_VERSION_UNSUPPORTED`, `RUNTIME_UNAVAILABLE`, `SANDBOX_UNAVAILABLE`, `GIT_FAILED`, `STORAGE_FAILED`, `DELIVERY_FAILED`, `INTERNAL` |
545
+ | 4 | `infrastructure` | `STATE_VERSION_UNSUPPORTED`, `RUNTIME_UNAVAILABLE`, `REVIEW_PROTOCOL_INVALID`, `SANDBOX_UNAVAILABLE`, `GIT_FAILED`, `STORAGE_FAILED`, `DELIVERY_FAILED`, `INTERNAL` |
518
546
 
519
547
  Exit 1 не используется: им Node завершает процесс при необработанном
520
548
  исключении.