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
@@ -0,0 +1,148 @@
1
+ # Внешняя память (Hindsight): точка подключения
2
+
3
+ Статус: **контракт и проверка поддельным провайдером**. Готового
4
+ Hindsight-коннектора в harness нет: нет HTTP-клиента, SDK, MCP-сервера,
5
+ развёртывания сервера и платных обращений. Harness работает без памяти;
6
+ память подключается явно через opt-in entrypoint
7
+ `devcontour-harness/integrations/memory` (исходники — `src/integrations/memory/`).
8
+ Корень пакета и `devcontour-harness/contracts` этот модуль не импортируют
9
+ <!-- repo-only -->(проверяется `tests/memory-availability.test.ts`, `M02/boundary`)<!-- /repo-only -->. Глобально
10
+
11
+ установленный MCP или Hindsight SDK для пользователей harness не требуется.
12
+
13
+ ## Что есть
14
+
15
+ | Часть | API | Назначение |
16
+ | ------------------ | ----------------------------------------------------------- | ------------------------------------------------------------- |
17
+ | Договор провайдера | `MemoryRecallProvider`, `MemoryRetainProvider` | два метода, которые реализует будущий адаптер |
18
+ | Источник контекста | `memoryContextSource(provider, options)` | recall → источник `ContextSnapshot` (`kind: memory`) |
19
+ | Композиция | `composeContext(parts, at)`, `mergeSnapshots(...)` | файлы + память в одном снимке; повтор ID — отказ |
20
+ | Получатель итога | `memoryResultSink(provider, options)` | обычный `ResultSink` с собственным ключом доставки |
21
+ | Тестовый провайдер | `src/integrations/memory/testing.ts` (`FakeMemoryProvider`) | детерминированные ответы, счётчики, отказы; только для тестов |
22
+
23
+ Изменений ядра нет: используются существующие `ContextSource`, `ResultSink`,
24
+ закрепление снимка при согласовании (`context-snapshots` + `approval.context`)
25
+ и записи `delivery` у `Harness.deliver`.
26
+
27
+ ## Recall до согласования
28
+
29
+ ```ts
30
+ import { FileContextSource } from 'devcontour-harness';
31
+ import { composeContext, memoryContextSource } from 'devcontour-harness/integrations/memory';
32
+
33
+ const memory = memoryContextSource(provider, {
34
+ id: 'memory', // ID источника в снимке
35
+ mode: 'optional', // 'required' | 'optional' | 'disabled'
36
+ bank: 'project-app', // стабильный bank проекта
37
+ project: 'app',
38
+ query: 'conventions for greeting module',
39
+ limits: { maxBytes: 16384, maxItems: 20, maxCostUnits: 5 },
40
+ });
41
+ const context = await composeContext(
42
+ [{ source: new FileContextSource('context'), ids: ['norms'] }, memory.part()],
43
+ { repositoryId: 'app' },
44
+ );
45
+ // review-plan → approve → run с этим же снимком
46
+ ```
47
+
48
+ - Recall выполняется в `snapshot`, т.е. до ревью плана и согласования.
49
+ Исполнение берёт только снимок, связанный с согласованием
50
+ (`Harness.contextOf`): runner к провайдеру не обращается. Новый ответ
51
+ памяти после согласования в попытку не попадает; новый снимок при том же
52
+ графе — `APPROVAL_REQUIRED` (нужно новое ревью плана).
53
+ - Источник памяти закрепляет три файла (все входят в digest источника):
54
+ - `recall.md` — вкладывается в запрос writer и reviewer; заголовок говорит,
55
+ что это непроверенные данные, которые не меняют поручение, область,
56
+ политику и ничего не разрешают;
57
+ - `provenance.json` (в поле `references`: закреплён, но не вкладывается):
58
+ провайдер и его версия, mode, bank, project, query, `recalledAt`,
59
+ digest/размер/число элементов/стоимость фактического ответа, пределы,
60
+ применённый объём и признаки обрезки (`truncated.items`, `truncated.bytes`,
61
+ `truncated.omitted`);
62
+ - `response.json` (тоже в `references`) — фактические байты тела ответа провайдера (если не
63
+ больше предела файла снимка; иначе остаётся только digest в provenance).
64
+ - Пределы: элементы берутся по порядку провайдера целиком. Превышение
65
+ `maxItems`/`maxBytes`: у `required` по умолчанию — отказ подготовки
66
+ (`truncation: 'forbid'`), у `optional` — отбрасывание с явной строкой
67
+ `[TRUNCATED by harness: …]` в `recall.md` и в provenance. Элемент с
68
+ `mandatory: true` не отбрасывается никогда: его обрезка — отказ
69
+ (`required`) или отсутствие источника (`optional`). Стоимость больше
70
+ `maxCostUnits` — так же.
71
+ - Режимы недоступности (ошибка, таймаут, ответ не JSON или вне схемы):
72
+ - `required` — `HarnessError NOT_READY` с причиной до ревью/согласования;
73
+ пустой «успешный» ответ не подставляется. Если ID памяти указан в
74
+ `order.context`, снимок без неё не пройдёт ни `run`, ни `review-plan`;
75
+ - `optional` — запись в `snapshot.missing` с причиной; снимок
76
+ согласуется и исполняется без памяти;
77
+ - `disabled` — провайдер не вызывается и может не передаваться вовсе;
78
+ снимок совпадает со снимком без памяти.
79
+ - Память — контекст, а не постановка: она не меняет `WorkOrder`, write
80
+ scope, gates, политику и approval digest. Непроверенный дневник writer
81
+ (его `summary`) reviewer не передаётся: reviewer получает diff, evidence
82
+ controller и тот же закреплённый контекст.
83
+
84
+ ## Запись подтверждённого итога
85
+
86
+ ```ts
87
+ const sink = memoryResultSink(provider, { bank: 'project-app', project: 'app' });
88
+ await harness.deliver(workId, sink); // отдельно от Relay/файлового получателя
89
+ ```
90
+
91
+ - `sink.id` по умолчанию `memory-<provider.id>`: ключ доставки
92
+ `<sink>:<work>:<run>` отдельный, состояние видно в `result().delivery` и
93
+ событиях `delivery.*` с этим `sink`. Сбой памяти не откатывает Git
94
+ accepted и не меняет receipt других получателей. Повтор
95
+ `Harness.deliver(work, sink)` отправляет только память; writer, reviewer и
96
+ другие получатели не вызываются. Общей распределённой транзакции нет.
97
+ - Сохраняются только `accepted` результаты; остальные исходы получают
98
+ receipt `skipped: …` без обращения к провайдеру.
99
+ - Содержимое — итог controller (исход, SHA, gates, находки), не пересказ writer.
100
+
101
+ ### Identity: bank, document_id, operation_id
102
+
103
+ | Идентичность | Как получается | Гарантия |
104
+ | -------------- | ------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
105
+ | bank | стабильный по проекту (`options.bank`), задаётся оператором | память одного проекта не смешивается с другими |
106
+ | `document_id` | `dch-` + digest(provider, bank, project, work, run, digest результата) — `memoryDocumentId` | повтор того же результата — **upsert** того же документа (в Hindsight `update_mode: replace` по умолчанию удаляет прежний документ и его память) |
107
+ | `operation_id` | UUID (форма v5) из digest(ключ доставки, document_id) — `uuidOf` | повтор **асинхронного** retain с тем же `operation_id` возвращает прежнюю операцию и не создаёт новой работы |
108
+
109
+ Это разные гарантии: upsert документа не мешает серверу повторно выполнить
110
+ асинхронную обработку (извлечение фактов, расход LLM), а `operation_id` не
111
+ задаёт идентичность документа. Адаптер должен передавать оба.
112
+
113
+ ## Что проверить при создании реального адаптера
114
+
115
+ Сверено с публичной документацией https://hindsight.vectorize.io
116
+ (`/developer/api/retain`, `/developer/api/recall`, на 2026-10-22 указана
117
+ версия документации 1.1); **на конкретном сервере не проверялось**:
118
+
119
+ - retain принимает `content`, `document_id`, `async`, `operation_id` (UUID),
120
+ `context`, `timestamp`, `metadata`, `tags`, `update_mode`; операции
121
+ async — `GET /v1/default/banks/{bank_id}/operations`; ответ async не
122
+ содержит usage;
123
+ - recall принимает `query` (до 500 токенов), `budget`, `max_tokens`, `types`,
124
+ `tags`; ответ `results[]` с `id`, `text`, `document_id`, `scores`; бюджет
125
+ ответа задаётся токенами, а не числом элементов.
126
+
127
+ Обязательные проверки адаптера (не реализованы, статус «to verify»):
128
+
129
+ 1. Версия API и capabilities сервера: поддержка `operation_id` для
130
+ идемпотентного async retain и `document_id` upsert в используемой
131
+ версии; без поддержки `operation_id` повтор async retain не считается
132
+ идемпотентным — адаптер должен ждать операцию синхронно или сообщать
133
+ неизвестный исход.
134
+ 2. Отображение `MemoryLimits` на `max_tokens`/`budget` и перевод `results[]`
135
+ в `memoryRecallBodySchema` (тело ответа закрепляется как получено).
136
+ 3. Учёт: стоимость recall/retain (`costUnits`, `usage` источника и
137
+ получателя) ведётся отдельно от usage coding runtime и бюджетов попыток.
138
+ 4. Ключи и адрес сервера — только у выбранного адаптера; без них core,
139
+ файловый сценарий и `mode: 'disabled'` работают.
140
+
141
+ ## Проверка
142
+
143
+ <!-- repo-only -->В репозитории harness: `node --import tsx --test tests/memory-*.test.ts`<!-- /repo-only --> — M01 (одинаковые
144
+
145
+ закреплённые байты у writer и reviewer, смена ответа после согласования,
146
+ пределы), M02 (required/optional/disabled с одним недоступным провайдером),
147
+ M03 (независимая доставка памяти и повтор только её). Используется обычный
148
+ путь `review-plan → approve → run` с fixture runtime.
@@ -13,7 +13,7 @@
13
13
  ```sh
14
14
  mkdir harness-demo && cd harness-demo
15
15
  npm init -y >/dev/null
16
- npm install --save-exact devcontour-harness@0.1.0-alpha.5
16
+ npm install --save-exact devcontour-harness@0.1.0-alpha.7
17
17
  npx harness --version
18
18
  npx harness doctor
19
19
  ```
@@ -0,0 +1,238 @@
1
+ # devcontour-harness 0.1.0-alpha.6
2
+
3
+ Этот выпуск добавляет:
4
+
5
+ - переносимые инструменты целевого проекта: npm, pnpm, Turborepo, Go и
6
+ Python/uv;
7
+ - рецепт Turbo Starter;
8
+ - общий контракт внешнего источника заданий и доставки с двумя opt-in
9
+ транспортами Relay;
10
+ - протокол истории находок ревью кода;
11
+ - нейтральную точку подключения внешней памяти.
12
+
13
+ Ядро — Engine, Runner, файловые источник и получатель, standalone CLI —
14
+ работает как раньше. Интеграции подключаются только явно, через отдельные
15
+ входы пакета. Пакет не публикуется автоматически.
16
+
17
+ ## Новые входы пакета
18
+
19
+ | Вход | Назначение |
20
+ | ------------------------------------------- | -------------------------------------------------------------- |
21
+ | `devcontour-harness/toolchains` | рецепты установки, пробы версий, диагностика (`inspectPolicy`) |
22
+ | `devcontour-harness/integrations/connector` | общий контракт источника и доставки, состояние коннектора |
23
+ | `devcontour-harness/integrations/relay-v1` | транспорт Relay v1 (HTTP) |
24
+ | `devcontour-harness/integrations/relay-v2` | транспорт Relay-2 (subprocess CLI) |
25
+ | `devcontour-harness/integrations/memory` | дополнительный `ContextSource` памяти и получатель итога |
26
+
27
+ Корень пакета и `./contracts` эти модули не импортируют. CLI загружает
28
+ коннектор и адаптер транспорта только для явно выбранного provider.
29
+
30
+ ## Изменения API и поведения
31
+
32
+ ### CLI
33
+
34
+ - `source-check` — проверка подключения, проекта, capabilities и mapping;
35
+ только чтение.
36
+ - `source-snapshot --out <dir> [--policy]` — закреплённый снимок задач:
37
+ `graph.json`, `context.json`, `manifest.json`.
38
+ - `source-status` — локальный и внешний статус доставки.
39
+ - `source-deliver [--work]` — повтор или продолжение доставки с тем же ключом
40
+ операции.
41
+ - `source-reconcile --operation <key> [--decision resend|adopt:<id>|recheck]` —
42
+ явный разбор неизвестного исхода или конфликта.
43
+ - `run --source <connector.json>`: задачи перечитываются, их semantic digest
44
+ сверяется с закреплённым снимком, eligibility проверяется. Всё это —
45
+ до любого вызова runtime.
46
+ - `review-plan --source`: внешние предпосылки допускаются по решению
47
+ источника (`trusts`) и доступности SHA в target ref — те же правила, что у
48
+ `run`. Без `--source` поведение прежнее.
49
+ - `cleanup --state --run` — довести незавершённую очистку попытки.
50
+ - `run` выполняет обязательный preflight toolchain: при любой проблеме
51
+ диагностики попытка не начинается.
52
+
53
+ Прежние команды и их коды выхода не изменились.
54
+
55
+ ### Доставка
56
+
57
+ - `DeliveryOutcome` получил необязательный вариант
58
+ `{ status: 'uncertain', operation, reason }`. Прежние получатели
59
+ (`delivered` / `conflict`) компилируются и работают без изменений.
60
+ - `DeliveryState`: значение статуса `uncertain` и необязательное поле
61
+ `operation`.
62
+ - Новое событие журнала `delivery.uncertain`:
63
+ `{ sink, key, revision, attempts, operation, reason }`.
64
+ - Повтор доставки не отправляет мутацию, пока неизвестный исход не разобран
65
+ (`source-reconcile`).
66
+ - Протокол коннектора: намерение сохраняется до внешнего вызова, затем
67
+ read-back по маркеру, затем receipt.
68
+ - Подтверждённый отказ получателя — безопасный повтор.
69
+ - Неизвестный исход — `uncertain`, без автоматического повтора.
70
+ - Перед каждым неподтверждённым шагом сверяется snapshot: старая постановка
71
+ не закрывает новую; чужое изменение между сверкой и CAS даёт конфликт.
72
+ - Состояние коннектора — отдельный SQLite
73
+ `<state>/connectors/<provider>-<hash>.sqlite`. Это не вторая база
74
+ исполнения.
75
+ - Секреты маскируются в ошибках транспорта, проблемах `source-check` и
76
+ причинах шагов.
77
+
78
+ ### Протокол ревью кода
79
+
80
+ Подробно — [review-protocol](../review-protocol.md).
81
+
82
+ **История находок.**
83
+
84
+ - Находки ведутся на поручение (область = поручение + репозиторий), а не на
85
+ попытку или SHA.
86
+ - ID находки выдаёт controller; reviewer называет состояние каждой открытой
87
+ находки (`previous`).
88
+ - Записи ревью получили необязательные поля: `EvidenceRecord.reviewRound` и
89
+ `findings[].id/status`. В `RunResult.evidence[]` — `findings[].id/status` и
90
+ `review`.
91
+ - На одном SHA и фазе бывает несколько записей ревью: невалидная, затем
92
+ валидная. Это не отказ задачи.
93
+
94
+ **Ошибка протокола.**
95
+
96
+ - Ошибка формата ответа reviewer отделена от содержательного отказа.
97
+ - Reviewer вызывается повторно ограниченное число раз, writer при этом не
98
+ вызывается. Затем — код `REVIEW_PROTOCOL_INVALID` (класс infrastructure,
99
+ exit 4) и вид отказа попытки `review-protocol`. Автоматического повтора
100
+ попытки нет.
101
+ - Политика: необязательное поле `reviewProtocolRetries` (0–2, по умолчанию 1).
102
+ Значение по умолчанию в политику не подставляется.
103
+
104
+ **Совместимость адаптеров.**
105
+
106
+ - `RuntimeCapabilities.reviewProtocol?: 1` — строгий протокол для адаптера,
107
+ который его объявляет. `cliAdapter` (codex, claude, fixture executables) его
108
+ объявляет.
109
+ - Адаптер хоста без этого поля получает семантику alpha.5: раунд помечается
110
+ `legacy`, переходы не требуются. Поэтому прежние runtime-обёртки хоста
111
+ работают без изменений.
112
+ - `RuntimeRequest.findings` — открытые и закрытые находки для адаптера с
113
+ протоколом.
114
+
115
+ **Проверки reviewer.** `run_check` принимает `{"gate": "<id>"}`: controller
116
+ исполняет объявленный gate (его argv, cwd, окружение, путь отчёта во
117
+ временном каталоге) без записи в исходники. Другой вид — исследовательский
118
+ `{"argv": [...]}`. Подробно — [review-probes](../review-probes.md).
119
+
120
+ ### Gates и отчёты
121
+
122
+ - Каждый gate `kind: test` получает `TURBO_FORCE=true` и `-count=1` в конце
123
+ `GOFLAGS`. Дочерние процессы наследуют оба значения, поэтому они действуют
124
+ через любую обёртку, в библиотеке и в CLI.
125
+ - Это только заставляет тесты выполниться заново и не меняет смысл
126
+ результата. Значения записываются в заголовок журнала gate (`fresh=…`).
127
+ - Gates `kind: check` не изменились.
128
+ - JUnit: атрибуты `name`/`classname` декодируются один раз, включая двойное
129
+ экранирование `"` репортером Node.js 24. Testcase с кавычками в имени теперь
130
+ сопоставляется с `testId` критерия.
131
+
132
+ ### Инструменты целевого проекта (toolchains)
133
+
134
+ - `repositories[].toolchain`: `tools` — проверенные версии, `packageManagers` —
135
+ явный выбор менеджера по каталогу. Поле необязательное, аддитивное.
136
+ - Новые встроенные профили: `npm-ci`, `pnpm-frozen`, `go-gotestsum`,
137
+ `python-uv`. Прежние профили побайтно те же, их закрепления не изменились.
138
+ - `doctor --policy` добавляет `toolchains[]`: план шагов и gates, инструменты
139
+ и их версии, lockfiles, отчёты, неоднозначности, проблемы с кодом и полем.
140
+ - Диагностика только читает файлы и запускает ограниченные пробы версий
141
+ (`--version`).
142
+ - Путь инструмента разрешается до канонического. Инструмент внутри
143
+ репозитория или каталога состояния не запускается (`TOOL_IN_PROJECT`).
144
+ - Диапазон версии в `package.json` допустим, если lockfile закрепляет точную
145
+ версию.
146
+ - Подробно — [toolchains](../toolchains.md).
147
+
148
+ ### Turbo Starter
149
+
150
+ - Рецепт для Turbo Starter на закреплённом commit.
151
+ - В архиве:
152
+ - `examples/turbo-starter/`: политика, граф, приёмочные тесты с JUnit,
153
+ шаблоны env, подготовленные изменения;
154
+ - lifecycle-мост `compose-bridge`: PostgreSQL на попытку и фазу, с порядком
155
+ принятия и очистки;
156
+ - оверлей controller mode в `templates/controller-mode/`.
157
+ - Upstream не патчится.
158
+ - Подробно — [turbo-starter](../turbo-starter.md).
159
+
160
+ ### Точка подключения памяти
161
+
162
+ - Договор провайдера: `MemoryRecallProvider` и `MemoryRetainProvider`.
163
+ - `memoryContextSource` — recall как источник `ContextSnapshot`.
164
+ - `composeContext` / `mergeSnapshots` — файлы и память в одном закреплённом
165
+ снимке.
166
+ - `memoryResultSink` — запись подтверждённого итога с собственным ключом.
167
+ - Изменений ядра нет; без памяти harness работает как прежде.
168
+ - Подробно — [hindsight](../integrations/hindsight.md).
169
+
170
+ ## Совместимость
171
+
172
+ - **Прежние потребители.**
173
+ - Реализации портов в форме alpha.5 проходят проверку типов против `.d.ts`
174
+ архива: `WorkSource`, `ContextSource`, `ResultSink` без `uncertain`,
175
+ `ExecutionHost` без необязательных портов, `ExecutionStore` из четырёх
176
+ методов.
177
+ - С ними проходит файловый сценарий через CLI, `Harness.run` и
178
+ `Engine`/`Runner`.
179
+ - Регистрация Relay или памяти и новый обязательный метод хоста не нужны.
180
+ - **Основания согласований.**
181
+ - Digest политик, графов, поручений и контекста прежних примеров совпадают
182
+ со значениями alpha.5.
183
+ - Политика без новых полей (`toolchain`, `reviewProtocolRetries`) сохраняет
184
+ `policyDigest`, `executionDigest` и закрепления. Политика с ними получает
185
+ новое основание явно (новое согласование).
186
+ - **Состояние alpha.5.** alpha.6 читает его без преобразования:
187
+ - результат, журнал и записи доставки читаются так же, строки базы не
188
+ меняются;
189
+ - прежние записи ревью — legacy-раунды;
190
+ - receipts и история не переписываются.
191
+ - **Откат.**
192
+ - Standalone-состояние с новыми полями — записи ревью с `reviewRound`,
193
+ доставка `uncertain` — alpha.5 и alpha.4 не читают: отказ
194
+ `STATE_VERSION_UNSUPPORTED` до любой записи.
195
+ - Хост DevContour с адаптером alpha.4 такое состояние открывает без отказа
196
+ и пишет в него роль controller и события. Новые поля при этом
197
+ сохраняются, потери данных нет, но строгой гарантии нет.
198
+ - Откат с alpha.6 в обоих случаях — только восстановлением копии состояния,
199
+ сделанной до обновления. Матрица — [upgrade](../upgrade.md).
200
+ - **Версия движка.** Номер версии — это `engineVersion`. Правила
201
+ alpha.2–alpha.5 действуют и между alpha.5 и alpha.6: `CONTROLLER_BUSY`, а
202
+ `LEASE_STALE` — до истечения срока и recovery.
203
+ - **DevContour.**
204
+ - Прежняя встройка (vendored архив, `vendor-harness.mjs`) работает с новым
205
+ архивом без изменений DevContour: собственные runtime-обёртки DevContour
206
+ не объявляют `reviewProtocol` и сохраняют семантику ревью alpha.5.
207
+ - Переход DevContour на протокол ревью — отдельное изменение его адаптеров.
208
+ - Проверенные версии инструментов —
209
+ [compatibility-versions](../compatibility-versions.md).
210
+
211
+ ## Известные ограничения
212
+
213
+ - Ядро наблюдает процессы системными `ps`, `lsof` и `sysctl`. С alpha.6 они
214
+ ищутся по штатным путям ОС (`/bin/ps`, `/usr/sbin/lsof`, `/usr/sbin/sysctl`
215
+ и т. п.), а не через PATH: PATH из одних `node` и `git` достаточен. Только
216
+ если ни одного штатного пути нет (нестандартная ОС), используется PATH. В
217
+ песочнице, запрещающей запуск `ps`, containment невозможен — это отказ
218
+ окружения.
219
+ - **Relay-2 на macOS** требует SQLite не ниже 3.44. Системная библиотека
220
+ старше: путь к новой задаётся в `relay.env` коннектора (`DYLD_LIBRARY_PATH`).
221
+ - Доставка не обещает distributed exactly-once. У получателя нет
222
+ идемпотентности по запросу, Git и Relay не образуют общей транзакции.
223
+ Неоднозначные случаи решает оператор (`source-reconcile`).
224
+ - Гарантия одного владельца доставки действует только для процессов с общим
225
+ каталогом состояния коннектора. Независимые установки с разными каталогами
226
+ состояния вне гарантии и могут доставить одну операцию дважды; это видно в
227
+ `ownership` у `source-check`/`source-status`.
228
+ - Готового Hindsight-адаптера нет: только договор, композиция и проверка
229
+ поддельным провайдером.
230
+ - Встроенные профили `npm-ci`, `pnpm-frozen`, `go-gotestsum` и `python-uv`
231
+ задают путь отчёта жёстко. В проверке reviewer `{"gate": …}` такой gate
232
+ возвращает диагностику окружения с рецептом; gates движка работают как
233
+ обычно.
234
+ - На macOS отмена может не найти отсоединённого не-node потомка, запущенного
235
+ менее чем за 250 мс до отмены (опрос `ps -E`).
236
+ - **Ограничения Turbo Starter upstream.** `pnpm env:check` падает на
237
+ Node 24.3.0. `check-types` admin-панели скачивает пакет через `npx` (нужна
238
+ сеть). Изоляция gates в примере выключена.
@@ -0,0 +1,71 @@
1
+ # devcontour-harness 0.1.0-alpha.7
2
+
3
+ Исправление учёта фактически выполненных команд reviewer в `inspection` ревью
4
+ кода. Gates, `maxAttempts`, бюджеты попыток и повторов, правила находок, защиты
5
+ worktree и принятие target ref не менялись: `inspection` по-прежнему описывает
6
+ evidence и не влияет на решение.
7
+
8
+ ## Что исправлено
9
+
10
+ - **Проверки reviewer не учитывались.** На пилоте alpha.6 reviewer запускал
11
+ проверки через `run_check` (в том числе объявленные gates), а `inspection`
12
+ оставался `diff-only` с пустым списком команд: учитывались только shell-события
13
+ Codex. Теперь итог собирает controller из событий runtime и журнала
14
+ `probes.jsonl` одного вызова reviewer; самоотчёт модели по-прежнему ничего не
15
+ подтверждает.
16
+ - **Ненулевой код выхода превращал выполнение в `diff-only`.** Режим зависел от
17
+ наличия команды с кодом 0. Теперь `mode: commands` — любая подтверждённая
18
+ выполненная команда; провал теста — исход, а не отсутствие выполнения.
19
+ - **Уже отменённый вызов проверки запускал команду.** Слушатель отмены на уже
20
+ отменённом сигнале не срабатывал, и команда шла до таймаута. Теперь такой
21
+ вызов — `cancelled` без запуска.
22
+ - **Код и вывод терялись при сбое после исхода** (уборка временных каталогов,
23
+ сохранение evidence gate): исход сохраняется как `error`/`environment` с
24
+ кодом и выводом.
25
+ - **Короткий секрет портил разбор событий Codex** и служебные значения
26
+ записи: события разбираются из немаскированного потока только в памяти,
27
+ маскируются поля текста до обрезки; сохраняемые журналы маскированы, как и
28
+ раньше.
29
+
30
+ ## Граница старта
31
+
32
+ Запуск probe засчитывается по признаку `started`, полученному на границе
33
+ исполнения `runCheck`: без песочницы — порождение процесса, в песочнице — метка,
34
+ которую обёртка создаёт перед `exec`. Отказ до старта, исчерпанный бюджет,
35
+ неизвестный gate, отменённый до запуска вызов, резерв `start` и `abandoned`
36
+ выполнением не считаются; они перечислены в `inspection.excluded`.
37
+
38
+ ## Изменения договора
39
+
40
+ Только добавления и nullable — подробно в
41
+ [review-probes](../review-probes.md#итог-inspection-что-reviewer-действительно-выполнил):
42
+
43
+ - `inspection.source`: новые значения `controller-probes`,
44
+ `runtime-and-controller`;
45
+ - `inspection.commands[].exitCode` может быть `null`; новые необязательные
46
+ `origins`, `status`, `failure`, `gate`, `note`;
47
+ - новые необязательные `inspection.context`, `total`, `excluded`,
48
+ `excludedTotal`, `invalid`;
49
+ - строки исхода `probes.jsonl` и `review.json.probes`: `started`, `scope`,
50
+ `exitCode`, `note`;
51
+ - `runCheck` возвращает `started` и принимает `onStart`; `command()` —
52
+ `onSpawn`, `onRawStdout`.
53
+
54
+ Полный `inspection` в `run-result` не добавлен; публичные схемы, кроме
55
+ `execution-document`, не менялись.
56
+
57
+ ## Совместимость
58
+
59
+ alpha.7 читает состояние alpha.6 без преобразования и без пересчёта прежних
60
+ `inspection`, digest и решений. Направление одностороннее: строгая схема alpha.6
61
+ не принимает новые значения `source`, `exitCode: null` и новые поля, поэтому
62
+ откат с alpha.7 — только восстановлением копии состояния, сделанной до
63
+ обновления ([upgrade](../upgrade.md)). Потребителю, разбирающему `inspection`,
64
+ нужно принять новые значения enum и nullable `exitCode`; признак выполнения —
65
+ `mode`.
66
+
67
+ ## Проверка выпуска
68
+
69
+ `npm run check` (lint, typecheck, schemas, build, весь набор тестов, package
70
+ smoke) на macOS arm64, Node.js 24.3.0. Матрица приёмки IN01–IN20; независимая
71
+ приёмка другой моделью воспроизвела IN01–IN19 собственными прогонами.