devcontour-harness 0.0.0-stage → 0.1.0-alpha.12

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 (466) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +268 -2
  3. package/docs/architecture.md +496 -0
  4. package/docs/compatibility-versions.md +60 -0
  5. package/docs/compatibility.md +192 -0
  6. package/docs/connectors.md +323 -0
  7. package/docs/contracts.md +630 -0
  8. package/docs/integrations/hindsight.md +148 -0
  9. package/docs/quickstart.md +185 -0
  10. package/docs/release-notes/0.1.0-alpha.10.md +50 -0
  11. package/docs/release-notes/0.1.0-alpha.11.md +44 -0
  12. package/docs/release-notes/0.1.0-alpha.12.md +66 -0
  13. package/docs/release-notes/0.1.0-alpha.6.md +238 -0
  14. package/docs/release-notes/0.1.0-alpha.7.md +71 -0
  15. package/docs/release-notes/0.1.0-alpha.8.md +73 -0
  16. package/docs/release-notes/0.1.0-alpha.9.md +55 -0
  17. package/docs/review-probes.md +199 -0
  18. package/docs/review-protocol.md +251 -0
  19. package/docs/standalone.md +271 -0
  20. package/docs/toolchains.md +167 -0
  21. package/docs/turbo-starter.md +269 -0
  22. package/docs/upgrade.md +219 -0
  23. package/examples/README.md +27 -0
  24. package/examples/external-connector/README.md +19 -0
  25. package/examples/external-connector/connector.mjs +55 -0
  26. package/examples/files-only/README.md +28 -0
  27. package/examples/files-only/context/norms/rules.md +4 -0
  28. package/examples/files-only/graph.json +23 -0
  29. package/examples/files-only/policy.json +30 -0
  30. package/examples/one-vendor/README.md +19 -0
  31. package/examples/one-vendor/policy.json +27 -0
  32. package/examples/single-task/README.md +18 -0
  33. package/examples/single-task/graph.json +27 -0
  34. package/examples/single-task/policy-same-vendor.json +55 -0
  35. package/examples/single-task/policy.json +52 -0
  36. package/examples/single-task/src/.gitkeep +0 -0
  37. package/examples/single-task/test/run.js +19 -0
  38. package/examples/toolchains/README.md +32 -0
  39. package/examples/toolchains/go/bad/label/label.go +9 -0
  40. package/examples/toolchains/go/good/label/label.go +9 -0
  41. package/examples/toolchains/go/policy.json +79 -0
  42. package/examples/toolchains/go/repo/REQUIREMENTS.md +9 -0
  43. package/examples/toolchains/go/repo/_gitignore +1 -0
  44. package/examples/toolchains/go/repo/go.mod +5 -0
  45. package/examples/toolchains/go/repo/go.sum +2 -0
  46. package/examples/toolchains/go/repo/label/label.go +9 -0
  47. package/examples/toolchains/go/repo/label/label_test.go +27 -0
  48. package/examples/toolchains/go/repo/label/version.go +9 -0
  49. package/examples/toolchains/go/task.json +25 -0
  50. package/examples/toolchains/mixed-go/bad/backend/label/label.go +9 -0
  51. package/examples/toolchains/mixed-go/bad/frontend/src/label.js +4 -0
  52. package/examples/toolchains/mixed-go/good/backend/label/label.go +9 -0
  53. package/examples/toolchains/mixed-go/good/frontend/src/label.js +4 -0
  54. package/examples/toolchains/mixed-go/policy.json +126 -0
  55. package/examples/toolchains/mixed-go/repo/REQUIREMENTS.md +9 -0
  56. package/examples/toolchains/mixed-go/repo/_gitignore +6 -0
  57. package/examples/toolchains/mixed-go/repo/backend/go.mod +3 -0
  58. package/examples/toolchains/mixed-go/repo/backend/label/label.go +9 -0
  59. package/examples/toolchains/mixed-go/repo/backend/label/label_test.go +30 -0
  60. package/examples/toolchains/mixed-go/repo/contract/check.mjs +17 -0
  61. package/examples/toolchains/mixed-go/repo/contract/label.json +9 -0
  62. package/examples/toolchains/mixed-go/repo/frontend/package.json +15 -0
  63. package/examples/toolchains/mixed-go/repo/frontend/pnpm-lock.yaml +22 -0
  64. package/examples/toolchains/mixed-go/repo/frontend/src/label.js +4 -0
  65. package/examples/toolchains/mixed-go/repo/frontend/test/label.test.js +11 -0
  66. package/examples/toolchains/mixed-go/task.json +25 -0
  67. package/examples/toolchains/mixed-python/bad/backend/src/fx_backend/__init__.py +6 -0
  68. package/examples/toolchains/mixed-python/bad/frontend/src/label.js +4 -0
  69. package/examples/toolchains/mixed-python/good/backend/src/fx_backend/__init__.py +6 -0
  70. package/examples/toolchains/mixed-python/good/frontend/src/label.js +4 -0
  71. package/examples/toolchains/mixed-python/policy.json +121 -0
  72. package/examples/toolchains/mixed-python/repo/REQUIREMENTS.md +9 -0
  73. package/examples/toolchains/mixed-python/repo/_gitignore +6 -0
  74. package/examples/toolchains/mixed-python/repo/backend/.python-version +1 -0
  75. package/examples/toolchains/mixed-python/repo/backend/pyproject.toml +19 -0
  76. package/examples/toolchains/mixed-python/repo/backend/src/fx_backend/__init__.py +6 -0
  77. package/examples/toolchains/mixed-python/repo/backend/tests/test_label.py +11 -0
  78. package/examples/toolchains/mixed-python/repo/backend/uv.lock +108 -0
  79. package/examples/toolchains/mixed-python/repo/contract/check.mjs +17 -0
  80. package/examples/toolchains/mixed-python/repo/contract/label.json +9 -0
  81. package/examples/toolchains/mixed-python/repo/frontend/package.json +15 -0
  82. package/examples/toolchains/mixed-python/repo/frontend/pnpm-lock.yaml +22 -0
  83. package/examples/toolchains/mixed-python/repo/frontend/src/label.js +4 -0
  84. package/examples/toolchains/mixed-python/repo/frontend/test/label.test.js +11 -0
  85. package/examples/toolchains/mixed-python/task.json +25 -0
  86. package/examples/toolchains/npm/bad/src/label.js +4 -0
  87. package/examples/toolchains/npm/good/src/label.js +4 -0
  88. package/examples/toolchains/npm/policy.json +82 -0
  89. package/examples/toolchains/npm/repo/REQUIREMENTS.md +9 -0
  90. package/examples/toolchains/npm/repo/_gitignore +4 -0
  91. package/examples/toolchains/npm/repo/package-lock.json +22 -0
  92. package/examples/toolchains/npm/repo/package.json +13 -0
  93. package/examples/toolchains/npm/repo/src/label.js +4 -0
  94. package/examples/toolchains/npm/repo/test/label.test.js +13 -0
  95. package/examples/toolchains/npm/task.json +25 -0
  96. package/examples/toolchains/pnpm/bad/packages/label/src/label.js +4 -0
  97. package/examples/toolchains/pnpm/good/packages/label/src/label.js +4 -0
  98. package/examples/toolchains/pnpm/policy.json +85 -0
  99. package/examples/toolchains/pnpm/repo/REQUIREMENTS.md +9 -0
  100. package/examples/toolchains/pnpm/repo/_gitignore +4 -0
  101. package/examples/toolchains/pnpm/repo/package.json +10 -0
  102. package/examples/toolchains/pnpm/repo/packages/checker/check.mjs +5 -0
  103. package/examples/toolchains/pnpm/repo/packages/checker/package.json +9 -0
  104. package/examples/toolchains/pnpm/repo/packages/label/package.json +14 -0
  105. package/examples/toolchains/pnpm/repo/packages/label/src/label.js +4 -0
  106. package/examples/toolchains/pnpm/repo/packages/label/test/label.test.js +13 -0
  107. package/examples/toolchains/pnpm/repo/pnpm-lock.yaml +29 -0
  108. package/examples/toolchains/pnpm/repo/pnpm-workspace.yaml +2 -0
  109. package/examples/toolchains/pnpm/task.json +25 -0
  110. package/examples/toolchains/python/bad/src/fx_label/__init__.py +6 -0
  111. package/examples/toolchains/python/good/src/fx_label/__init__.py +6 -0
  112. package/examples/toolchains/python/policy.json +80 -0
  113. package/examples/toolchains/python/repo/.python-version +1 -0
  114. package/examples/toolchains/python/repo/REQUIREMENTS.md +9 -0
  115. package/examples/toolchains/python/repo/_gitignore +5 -0
  116. package/examples/toolchains/python/repo/pyproject.toml +19 -0
  117. package/examples/toolchains/python/repo/src/fx_label/__init__.py +6 -0
  118. package/examples/toolchains/python/repo/tests/test_label.py +13 -0
  119. package/examples/toolchains/python/repo/uv.lock +108 -0
  120. package/examples/toolchains/python/task.json +25 -0
  121. package/examples/toolchains/turbo/bad/packages/label/src/label.js +4 -0
  122. package/examples/toolchains/turbo/good/packages/label/src/label.js +4 -0
  123. package/examples/toolchains/turbo/policy.json +110 -0
  124. package/examples/toolchains/turbo/repo/REQUIREMENTS.md +9 -0
  125. package/examples/toolchains/turbo/repo/_gitignore +4 -0
  126. package/examples/toolchains/turbo/repo/package.json +13 -0
  127. package/examples/toolchains/turbo/repo/packages/checker/check.mjs +5 -0
  128. package/examples/toolchains/turbo/repo/packages/checker/package.json +9 -0
  129. package/examples/toolchains/turbo/repo/packages/label/package.json +14 -0
  130. package/examples/toolchains/turbo/repo/packages/label/src/label.js +4 -0
  131. package/examples/toolchains/turbo/repo/packages/label/test/label.test.js +13 -0
  132. package/examples/toolchains/turbo/repo/pnpm-lock.yaml +94 -0
  133. package/examples/toolchains/turbo/repo/pnpm-workspace.yaml +2 -0
  134. package/examples/toolchains/turbo/repo/turbo.json +7 -0
  135. package/examples/toolchains/turbo/task.json +25 -0
  136. package/examples/turbo-starter/README.md +16 -0
  137. package/examples/turbo-starter/changes/issued-at-consumer-only.patch +12 -0
  138. package/examples/turbo-starter/changes/issued-at.patch +130 -0
  139. package/examples/turbo-starter/cleanup.mjs +58 -0
  140. package/examples/turbo-starter/graph.json +32 -0
  141. package/examples/turbo-starter/harness/acceptance/db-isolation.test.mjs +97 -0
  142. package/examples/turbo-starter/harness/acceptance/env-probe.test.mjs +75 -0
  143. package/examples/turbo-starter/harness/acceptance/generation.test.mjs +54 -0
  144. package/examples/turbo-starter/harness/acceptance/issued-at.test.mjs +43 -0
  145. package/examples/turbo-starter/harness/acceptance/lifecycle-probe.test.mjs +47 -0
  146. package/examples/turbo-starter/harness/compose-bridge.mjs +593 -0
  147. package/examples/turbo-starter/harness/compose.acceptance.yml +23 -0
  148. package/examples/turbo-starter/harness/env/backend.env.template +7 -0
  149. package/examples/turbo-starter/harness/env/compose.env.template +6 -0
  150. package/examples/turbo-starter/policy.json +109 -0
  151. package/examples/turbo-starter/policy.lifecycle.json +114 -0
  152. package/examples/turbo-starter/setup.mjs +352 -0
  153. package/examples/two-vendors/policy.json +30 -0
  154. package/lib/agents/cli-adapter.d.ts +48 -0
  155. package/lib/agents/cli-adapter.js +393 -0
  156. package/lib/agents/inspect.d.ts +60 -0
  157. package/lib/agents/inspect.js +296 -0
  158. package/lib/agents/probe-server.d.ts +2 -0
  159. package/lib/agents/probe-server.js +13 -0
  160. package/lib/agents/review-probe.d.ts +345 -0
  161. package/lib/agents/review-probe.js +1037 -0
  162. package/lib/agents/runtime-events.d.ts +11 -0
  163. package/lib/agents/runtime-events.js +21 -0
  164. package/lib/agents/schemas.d.ts +217 -0
  165. package/lib/agents/schemas.js +124 -0
  166. package/lib/agents/usage.d.ts +8 -0
  167. package/lib/agents/usage.js +83 -0
  168. package/lib/catalog/bytes.d.ts +9 -0
  169. package/lib/catalog/bytes.js +14 -0
  170. package/lib/catalog/harness.d.ts +3 -0
  171. package/lib/catalog/harness.js +11 -0
  172. package/lib/catalog/index.d.ts +5 -0
  173. package/lib/catalog/index.js +5 -0
  174. package/lib/catalog/operations.d.ts +24 -0
  175. package/lib/catalog/operations.js +74 -0
  176. package/lib/catalog/page.d.ts +46 -0
  177. package/lib/catalog/page.js +128 -0
  178. package/lib/catalog/shared.d.ts +37 -0
  179. package/lib/catalog/shared.js +67 -0
  180. package/lib/cli.d.ts +2 -0
  181. package/lib/cli.js +673 -0
  182. package/lib/connectors/files.d.ts +108 -0
  183. package/lib/connectors/files.js +133 -0
  184. package/lib/contracts/catalog.d.ts +31 -0
  185. package/lib/contracts/catalog.js +18 -0
  186. package/lib/contracts/checks.d.ts +307 -0
  187. package/lib/contracts/checks.js +181 -0
  188. package/lib/contracts/cli.d.ts +4665 -0
  189. package/lib/contracts/cli.js +840 -0
  190. package/lib/contracts/context.d.ts +135 -0
  191. package/lib/contracts/context.js +129 -0
  192. package/lib/contracts/diagnosis.d.ts +108 -0
  193. package/lib/contracts/diagnosis.js +69 -0
  194. package/lib/contracts/digest.d.ts +9 -0
  195. package/lib/contracts/digest.js +22 -0
  196. package/lib/contracts/errors.d.ts +89 -0
  197. package/lib/contracts/errors.js +74 -0
  198. package/lib/contracts/events.d.ts +827 -0
  199. package/lib/contracts/events.js +258 -0
  200. package/lib/contracts/execution.d.ts +3423 -0
  201. package/lib/contracts/execution.js +936 -0
  202. package/lib/contracts/failure.d.ts +38 -0
  203. package/lib/contracts/failure.js +53 -0
  204. package/lib/contracts/index.d.ts +22 -0
  205. package/lib/contracts/index.js +22 -0
  206. package/lib/contracts/planning.d.ts +433 -0
  207. package/lib/contracts/planning.js +159 -0
  208. package/lib/contracts/policy.d.ts +1075 -0
  209. package/lib/contracts/policy.js +544 -0
  210. package/lib/contracts/ports.d.ts +417 -0
  211. package/lib/contracts/ports.js +1 -0
  212. package/lib/contracts/primitives.d.ts +63 -0
  213. package/lib/contracts/primitives.js +73 -0
  214. package/lib/contracts/profiles.d.ts +340 -0
  215. package/lib/contracts/profiles.js +109 -0
  216. package/lib/contracts/project.d.ts +69 -0
  217. package/lib/contracts/project.js +47 -0
  218. package/lib/contracts/published.d.ts +3467 -0
  219. package/lib/contracts/published.js +40 -0
  220. package/lib/contracts/repair-policy.d.ts +31 -0
  221. package/lib/contracts/repair-policy.js +77 -0
  222. package/lib/contracts/result.d.ts +1284 -0
  223. package/lib/contracts/result.js +311 -0
  224. package/lib/contracts/review.d.ts +441 -0
  225. package/lib/contracts/review.js +477 -0
  226. package/lib/contracts/toolchain.d.ts +96 -0
  227. package/lib/contracts/toolchain.js +89 -0
  228. package/lib/contracts/version.d.ts +17 -0
  229. package/lib/contracts/version.js +17 -0
  230. package/lib/contracts/work.d.ts +171 -0
  231. package/lib/contracts/work.js +178 -0
  232. package/lib/control/base-move.d.ts +44 -0
  233. package/lib/control/base-move.js +90 -0
  234. package/lib/control/doctor.d.ts +50 -0
  235. package/lib/control/doctor.js +113 -0
  236. package/lib/control/harness.d.ts +1125 -0
  237. package/lib/control/harness.js +741 -0
  238. package/lib/control/provenance.d.ts +17 -0
  239. package/lib/control/provenance.js +25 -0
  240. package/lib/control/recovery.d.ts +237 -0
  241. package/lib/control/recovery.js +236 -0
  242. package/lib/control/review.d.ts +62 -0
  243. package/lib/control/review.js +182 -0
  244. package/lib/control/workflow.d.ts +256 -0
  245. package/lib/control/workflow.js +277 -0
  246. package/lib/engine/budget.d.ts +345 -0
  247. package/lib/engine/budget.js +278 -0
  248. package/lib/engine/classify.d.ts +8 -0
  249. package/lib/engine/classify.js +41 -0
  250. package/lib/engine/engine.d.ts +1564 -0
  251. package/lib/engine/engine.js +2283 -0
  252. package/lib/engine/failure.d.ts +1 -0
  253. package/lib/engine/failure.js +3 -0
  254. package/lib/engine/joint-runner.d.ts +257 -0
  255. package/lib/engine/joint-runner.js +254 -0
  256. package/lib/engine/measured.d.ts +15 -0
  257. package/lib/engine/measured.js +82 -0
  258. package/lib/engine/prompt.d.ts +66 -0
  259. package/lib/engine/prompt.js +131 -0
  260. package/lib/engine/property-recovery.d.ts +50 -0
  261. package/lib/engine/property-recovery.js +171 -0
  262. package/lib/engine/repair.d.ts +33 -0
  263. package/lib/engine/repair.js +82 -0
  264. package/lib/engine/result.d.ts +9 -0
  265. package/lib/engine/result.js +194 -0
  266. package/lib/engine/reuse.d.ts +80 -0
  267. package/lib/engine/reuse.js +102 -0
  268. package/lib/engine/runner.d.ts +360 -0
  269. package/lib/engine/runner.js +1844 -0
  270. package/lib/engine/standalone.d.ts +6 -0
  271. package/lib/engine/standalone.js +48 -0
  272. package/lib/engine/usage.d.ts +87 -0
  273. package/lib/engine/usage.js +165 -0
  274. package/lib/environment/isolation.d.ts +110 -0
  275. package/lib/environment/isolation.js +238 -0
  276. package/lib/environment/lifecycle.d.ts +40 -0
  277. package/lib/environment/lifecycle.js +197 -0
  278. package/lib/environment/live-calls.d.ts +14 -0
  279. package/lib/environment/live-calls.js +73 -0
  280. package/lib/environment/os-tools.d.ts +16 -0
  281. package/lib/environment/os-tools.js +35 -0
  282. package/lib/environment/process.d.ts +40 -0
  283. package/lib/environment/process.js +375 -0
  284. package/lib/environment/redaction.d.ts +22 -0
  285. package/lib/environment/redaction.js +102 -0
  286. package/lib/environment/resources.d.ts +31 -0
  287. package/lib/environment/resources.js +120 -0
  288. package/lib/explain/diagnose.d.ts +25 -0
  289. package/lib/explain/diagnose.js +60 -0
  290. package/lib/explain/state.d.ts +18 -0
  291. package/lib/explain/state.js +305 -0
  292. package/lib/index.d.ts +72 -0
  293. package/lib/index.js +74 -0
  294. package/lib/init/policy.d.ts +40 -0
  295. package/lib/init/policy.js +73 -0
  296. package/lib/init/project.d.ts +52 -0
  297. package/lib/init/project.js +412 -0
  298. package/lib/integrations/connector/commands.d.ts +358 -0
  299. package/lib/integrations/connector/commands.js +428 -0
  300. package/lib/integrations/connector/config.d.ts +215 -0
  301. package/lib/integrations/connector/config.js +214 -0
  302. package/lib/integrations/connector/delivery.d.ts +107 -0
  303. package/lib/integrations/connector/delivery.js +591 -0
  304. package/lib/integrations/connector/fake.d.ts +291 -0
  305. package/lib/integrations/connector/fake.js +786 -0
  306. package/lib/integrations/connector/history.d.ts +124 -0
  307. package/lib/integrations/connector/history.js +497 -0
  308. package/lib/integrations/connector/index.d.ts +16 -0
  309. package/lib/integrations/connector/index.js +16 -0
  310. package/lib/integrations/connector/redact.d.ts +19 -0
  311. package/lib/integrations/connector/redact.js +91 -0
  312. package/lib/integrations/connector/registry.d.ts +15 -0
  313. package/lib/integrations/connector/registry.js +43 -0
  314. package/lib/integrations/connector/snapshot.d.ts +131 -0
  315. package/lib/integrations/connector/snapshot.js +422 -0
  316. package/lib/integrations/connector/source.d.ts +37 -0
  317. package/lib/integrations/connector/source.js +73 -0
  318. package/lib/integrations/connector/steps.d.ts +71 -0
  319. package/lib/integrations/connector/steps.js +188 -0
  320. package/lib/integrations/connector/store.d.ts +140 -0
  321. package/lib/integrations/connector/store.js +277 -0
  322. package/lib/integrations/connector/summary.d.ts +38 -0
  323. package/lib/integrations/connector/summary.js +59 -0
  324. package/lib/integrations/connector/testing-worker.d.ts +1 -0
  325. package/lib/integrations/connector/testing-worker.js +81 -0
  326. package/lib/integrations/connector/testing.d.ts +205 -0
  327. package/lib/integrations/connector/testing.js +1615 -0
  328. package/lib/integrations/connector/transport.d.ts +240 -0
  329. package/lib/integrations/connector/transport.js +28 -0
  330. package/lib/integrations/memory/context.d.ts +120 -0
  331. package/lib/integrations/memory/context.js +239 -0
  332. package/lib/integrations/memory/index.d.ts +8 -0
  333. package/lib/integrations/memory/index.js +8 -0
  334. package/lib/integrations/memory/provider.d.ts +79 -0
  335. package/lib/integrations/memory/provider.js +22 -0
  336. package/lib/integrations/memory/sink.d.ts +56 -0
  337. package/lib/integrations/memory/sink.js +116 -0
  338. package/lib/integrations/memory/testing.d.ts +44 -0
  339. package/lib/integrations/memory/testing.js +61 -0
  340. package/lib/integrations/relay-v1/index.d.ts +122 -0
  341. package/lib/integrations/relay-v1/index.js +860 -0
  342. package/lib/integrations/relay-v2/index.d.ts +145 -0
  343. package/lib/integrations/relay-v2/index.js +752 -0
  344. package/lib/integrations/relay-v2/process.d.ts +32 -0
  345. package/lib/integrations/relay-v2/process.js +112 -0
  346. package/lib/integrations/relay-v2/protocol.d.ts +172 -0
  347. package/lib/integrations/relay-v2/protocol.js +142 -0
  348. package/lib/package.d.ts +2 -0
  349. package/lib/package.js +3 -0
  350. package/lib/planning/check.d.ts +26 -0
  351. package/lib/planning/check.js +174 -0
  352. package/lib/planning/facts.d.ts +13 -0
  353. package/lib/planning/facts.js +47 -0
  354. package/lib/planning/graph.d.ts +20 -0
  355. package/lib/planning/graph.js +114 -0
  356. package/lib/planning/index.d.ts +6 -0
  357. package/lib/planning/index.js +6 -0
  358. package/lib/planning/issues.d.ts +19 -0
  359. package/lib/planning/issues.js +210 -0
  360. package/lib/planning/request.d.ts +19 -0
  361. package/lib/planning/request.js +57 -0
  362. package/lib/planning/standalone.d.ts +16 -0
  363. package/lib/planning/standalone.js +19 -0
  364. package/lib/planning/stub.d.ts +2 -0
  365. package/lib/planning/stub.js +5 -0
  366. package/lib/profiles/resolve.d.ts +266 -0
  367. package/lib/profiles/resolve.js +322 -0
  368. package/lib/projections/chain.d.ts +35 -0
  369. package/lib/projections/chain.js +72 -0
  370. package/lib/projections/clip.d.ts +6 -0
  371. package/lib/projections/clip.js +23 -0
  372. package/lib/projections/enums.d.ts +7 -0
  373. package/lib/projections/enums.js +19 -0
  374. package/lib/projections/index.d.ts +8 -0
  375. package/lib/projections/index.js +9 -0
  376. package/lib/projections/next.d.ts +28 -0
  377. package/lib/projections/next.js +75 -0
  378. package/lib/projections/progress.d.ts +27 -0
  379. package/lib/projections/progress.js +37 -0
  380. package/lib/projections/queue.d.ts +21 -0
  381. package/lib/projections/queue.js +18 -0
  382. package/lib/projections/stop.d.ts +98 -0
  383. package/lib/projections/stop.js +532 -0
  384. package/lib/setup/files.d.ts +45 -0
  385. package/lib/setup/files.js +280 -0
  386. package/lib/storage/document.d.ts +1286 -0
  387. package/lib/storage/document.js +80 -0
  388. package/lib/storage/sqlite.d.ts +35 -0
  389. package/lib/storage/sqlite.js +260 -0
  390. package/lib/testing/index.d.ts +5 -0
  391. package/lib/testing/index.js +5 -0
  392. package/lib/testing/store-contract.d.ts +8 -0
  393. package/lib/testing/store-contract.js +167 -0
  394. package/lib/toolchains/index.d.ts +9 -0
  395. package/lib/toolchains/index.js +9 -0
  396. package/lib/toolchains/inspect.d.ts +105 -0
  397. package/lib/toolchains/inspect.js +831 -0
  398. package/lib/toolchains/manifests.d.ts +67 -0
  399. package/lib/toolchains/manifests.js +238 -0
  400. package/lib/toolchains/probe.d.ts +28 -0
  401. package/lib/toolchains/probe.js +54 -0
  402. package/lib/toolchains/recipes.d.ts +33 -0
  403. package/lib/toolchains/recipes.js +51 -0
  404. package/lib/validate.d.ts +37 -0
  405. package/lib/validate.js +182 -0
  406. package/lib/verification/acceptance.d.ts +284 -0
  407. package/lib/verification/acceptance.js +670 -0
  408. package/lib/verification/checks.d.ts +72 -0
  409. package/lib/verification/checks.js +190 -0
  410. package/lib/verification/confirm.d.ts +125 -0
  411. package/lib/verification/confirm.js +411 -0
  412. package/lib/verification/corpus.d.ts +1303 -0
  413. package/lib/verification/corpus.js +744 -0
  414. package/lib/verification/dependencies.d.ts +66 -0
  415. package/lib/verification/dependencies.js +144 -0
  416. package/lib/verification/gates.d.ts +182 -0
  417. package/lib/verification/gates.js +641 -0
  418. package/lib/verification/joint.d.ts +63 -0
  419. package/lib/verification/joint.js +121 -0
  420. package/lib/verification/proof.d.ts +54 -0
  421. package/lib/verification/proof.js +96 -0
  422. package/lib/verification/property.d.ts +86 -0
  423. package/lib/verification/property.js +214 -0
  424. package/lib/verification/requirements.d.ts +25 -0
  425. package/lib/verification/requirements.js +69 -0
  426. package/lib/verification/self-check.d.ts +29 -0
  427. package/lib/verification/self-check.js +99 -0
  428. package/lib/verification/spike.d.ts +79 -0
  429. package/lib/verification/spike.js +124 -0
  430. package/package.json +104 -4
  431. package/profiles/expo-mobile.json +15 -0
  432. package/profiles/go-api.json +15 -0
  433. package/profiles/go-gotestsum.json +21 -0
  434. package/profiles/mobile-maestro.json +29 -0
  435. package/profiles/nest-api.json +26 -0
  436. package/profiles/next-product.json +72 -0
  437. package/profiles/node-checks.json +77 -0
  438. package/profiles/npm-ci.json +22 -0
  439. package/profiles/pnpm-frozen.json +24 -0
  440. package/profiles/python-api.json +32 -0
  441. package/profiles/python-uv.json +26 -0
  442. package/profiles/react-native.json +50 -0
  443. package/profiles/react-vite-admin.json +71 -0
  444. package/schemas/context-snapshot.schema.json +231 -0
  445. package/schemas/contract-document.schema.json +385 -0
  446. package/schemas/execution-document.schema.json +3881 -0
  447. package/schemas/execution-policy.schema.json +2196 -0
  448. package/schemas/joint-verification.schema.json +617 -0
  449. package/schemas/plan-draft.schema.json +217 -0
  450. package/schemas/project-manifest.schema.json +167 -0
  451. package/schemas/result-envelope.schema.json +1654 -0
  452. package/schemas/review-record.schema.json +177 -0
  453. package/schemas/run-event.schema.json +2458 -0
  454. package/schemas/run-result.schema.json +1572 -0
  455. package/schemas/stack-profile.schema.json +535 -0
  456. package/schemas/stop-diagnosis.schema.json +169 -0
  457. package/schemas/work-graph.schema.json +381 -0
  458. package/schemas/work-order.schema.json +256 -0
  459. package/skills/harness/SKILL.md +247 -0
  460. package/skills/harness-setup/SKILL.md +356 -0
  461. package/templates/AGENTS.md +13 -0
  462. package/templates/controller-mode/AGENTS.controller.md +58 -0
  463. package/templates/controller-mode/README.md +44 -0
  464. package/templates/graph.json +22 -0
  465. package/templates/plan-draft.json +35 -0
  466. package/templates/policy.json +28 -0
@@ -0,0 +1,192 @@
1
+ # Совместимость форматов
2
+
3
+ Harness выделен из DevContour. Первая миграция DevContour на пакет **не
4
+ переносит данные**: состояние остаётся в прежнем хранилище, а адаптер
5
+ отображает существующие записи в нейтральные структуры в той же транзакции.
6
+ Ниже — как устроен адаптер, что сохраняется байт-в-байт и что меняется
7
+ намеренно.
8
+
9
+ ## Транзакции адаптера DevContour
10
+
11
+ DevContour хранит состояние одним JSON-документом в SQLite (`node:sqlite`) —
12
+ в центральной базе или распределённо по базам компонентов, присоединённым к
13
+ coordinator. `Store.change(type, fn)` открывает `BEGIN IMMEDIATE` (вложенный
14
+ `Store.atomic` — SAVEPOINT), читает документ, вызывает `fn`, записывает
15
+ документ целиком и вставляет событие журнала с `data` = возвращённым `fn`
16
+ значением (кроме `heartbeat`).
17
+
18
+ Адаптер `ExecutionStore`:
19
+
20
+ 1. **Внешняя транзакция** — `Store.atomic` с ровно одной `Store.change(kind, …)`
21
+ внутри. Документ, прочитанный ею, — текущий документ транзакции;
22
+ `ExecutionView` и `put*` читают и пишут его объекты (`Task`, `Run`, поля
23
+ управления), `putLocal` — `Store.saveLocal` той же транзакции.
24
+ 2. **Вложенная транзакция** не вызывает второй `Store.change`: она работает с
25
+ тем же документом внутри `Store.atomic` (SAVEPOINT для локальных записей).
26
+ Перед ней адаптер снимает копию документа и **всего журнального буфера**
27
+ (нейтральные события и legacy-данные); при исключении восстанавливает их на
28
+ месте. Изменения вложенной и внешней сохраняются обе — прямой вложенный
29
+ `Store.change` этого не обеспечивает: внешний снимок перезаписал бы
30
+ внутреннее изменение.
31
+ 3. **Журнал.** Возвращённое движком значение в журнал не идёт. Адаптер
32
+ записывает в `data` события прежнюю форму данных этого типа (для типов,
33
+ чьи данные читают хост и его экраны: `scheduler.error` → `{error}` и т. д.)
34
+ **без полей `token` на любой глубине** и добавляет ключ `harnessEvents` —
35
+ нейтральные события транзакции по порядку.
36
+ 4. **Событие без изменения документа.** `Store.change` не пишет событие, если
37
+ документ не изменился (транзакция только с `emit` или только с локальной
38
+ записью и `emit`). Для этого DevContour получает аддитивный метод
39
+ `Store.appendEvent(type, data)` — вставка события в открытой транзакции тем
40
+ же путём, что у `Store.change` (central: `main.events`; component:
41
+ `components.event`), с ростом ревизии данных. Адаптер вызывает его, когда
42
+ `Store.change` события не записал, а у транзакции есть нейтральные
43
+ события. Изменения остальных методов `Store` не требуется.
44
+ Транзакции только локальных записей (`workflow.updated`, `usage.recorded`,
45
+ `checks.recorded`) и `base.moved` (проверка владения и сдвиг ref)
46
+ событий не создают: адаптер выполняет их как `Store.atomic` без
47
+ `Store.change`, как DevContour сейчас пишет учёт вызовов и очередь ведущего.
48
+ 5. **Курсор.** `seq` нейтрального события = `id события журнала × 1024 + индекс` (не больше 1024 событий на транзакцию — с запасом для графа из 500 поручений; больше — отказ транзакции).
49
+ `ExecutionStore.events(after)` строит `RunEvent` из `harnessEvents` строго
50
+ по возрастанию `seq`; события прежних версий без `harnessEvents` в
51
+ нейтральный поток не попадают и читаются хостом как раньше.
52
+ 6. Возврат Promise из `fn` отклоняется до записи.
53
+ 7. `heartbeat` — `Store.change('heartbeat', …)`: растёт только счётчик сроков,
54
+ события нет.
55
+ 8. Приёмка: CAS `git update-ref <target> <integration> <base>` выполняется
56
+ внутри внешней транзакции до фиксации (как сейчас `finish(…, publish)`).
57
+
58
+ Свойства подтверждены пробой поверх baseline `Store` (с эмуляцией
59
+ `appendEvent`): вложенное и внешнее изменения сохраняются; исключение
60
+ вложенной откатывает её документ, события и legacy-данные; исключение
61
+ внешней не оставляет ни изменений, ни события; транзакция только с событием
62
+ и транзакция «локальная запись + событие» фиксируются и откатываются целиком;
63
+ токенов в журнале нет; курсор отдаёт события по возрастанию и каждое проходит
64
+ схему `RunEvent`; Promise отклоняется; `putLocal` различает владельцев.
65
+
66
+ ### Локальные записи
67
+
68
+ `local_workflow(namespace, owner, key, data)` — та же таблица. `owner` —
69
+ репозиторий-компонент или `@workspace`. У хранения по компонентам запись
70
+ владельца-компонента живёт в базе компонента (`componentN.local_workflow`),
71
+ `@workspace` — в coordinator. Адаптер не меняет правило размещения и
72
+ проверяет запись схемой пространства при чтении и записи.
73
+
74
+ | Пространство harness | `namespace` DevContour | Запись |
75
+ | ------------------------------------------------------ | ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
76
+ | `usage` | `usage` | `CallRecord` ↔ `UsageRecord` |
77
+ | `check-suites` | `check-suites` | `CheckSuite` (корпус: случаи, версии, решения, принятия, отложенные принятия, операции подтверждения под владением, CAS `revision`) |
78
+ | `workflow-jobs` | `lead` | `WorkflowJob` (техническая очередь ведущего: предмет, автор, digest входа, этап, попытки, ремонты, владение, история) |
79
+ | `context-snapshots`, `run-keys`, `delivery`, `reviews` | новые | Новые записи; прежних данных нет |
80
+ | `contract-documents` | — (`contracts` состояния) | Документ контракта по digest; адаптер отдаёт его из контрактов DevContour (`checks`, `artifacts`) и не пишет отдельно |
81
+ | `spikes` | `spikes` | `Spike` (эксперимент выполнимости: SHA, команда, seed, размеры, предел, исходы, пределы вывода, среда, digest) |
82
+ | `reproductions` | — (`reproductions` состояния) | `ReproductionRegistry` поручения ↔ `Reproduction[]` с `taskId`; адаптер группирует по задаче и сохраняет ограничение 500 |
83
+
84
+ Прочие пространства DevContour (`contract-change`, `signals`,
85
+ `projection`) — продуктовые; движок их не читает и не пишет.
86
+
87
+ ## Отображение записей
88
+
89
+ | Нейтральная запись | Запись DevContour | Правило |
90
+ | ------------------------ | --------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
91
+ | `WorkState` | `Task` (поля исполнения) | `status`, `attempt`, `activeRunId`, `resultSha`, `failure`, `failureKind`, `failureFingerprint`, `approvedAt`, `approvedDigest`, `approvedScope`, `approval`, `hold`, `budgetBlock`, `checkVersion`, `recovery`, `recoveryHistory` — те же имена и формы. `graphId` — доска, в активной ревизии которой задача |
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
+ | `AttemptState` | `Run` | Те же имена и формы, включая `context` (краткие pins), `consumers[].checked`, `partial`, `continuedFrom`, `dispatch`, `timings`; `taskId` ↔ `workId`; runtime `demo` ↔ `fixture` на границе (сохранённое значение не переписывается); `memory` остаётся полем `Run` хоста; `engineVersion`, `consumerVersion`, `contextSnapshot` — новые необязательные поля |
94
+ | `EvidenceRecord` | `Evidence` | Без изменений; аддитивные необязательные `reviewRound` и `findings[].id/status` ([review-protocol.md](review-protocol.md)) хранятся как есть, прежние записи читаются без них; строгие переходы находок — только для адаптера reviewer с необязательным `RuntimeCapabilities.reviewProtocol: 1` (`cliAdapter`), runtime-обёртки хоста без поля сохраняют приёмку alpha.5 |
95
+ | `ControlState` | `paused`, `pauseReason`, `pausedAt`, `pauseOperation`, `pauseFailures`, `waits`, `appliedConfig`, `reconfigure` | Те же формы; `appliedConfig` ↔ `appliedPolicy` |
96
+ | `ControllerLease` | `leader` | Без изменений |
97
+ | `JointVerificationState` | `Verification` | `manifest`, `manifestDigest`, `policyDigest`, `specDigest`, `impact`, `evidence`, `token`, `leaseUntil`, `status` — те же формы; `tasks` → `workIds` (снимок задач остаётся в `Verification`); `boards`, `productRelease` — поля хоста |
98
+ | `CallRecord` | `UsageRecord` (`local_workflow` `usage`) | Те же поля; `taskId` ↔ `workId`, `boardId` ↔ `groupId`, `taskIds` ↔ `workIds` |
99
+ | `ReviewRecord` | — (у DevContour раундов в форме `ReviewRecord` нет) | Прежние `contractAttempts` в раунды **не** превращаются: в них нет digest предмета и признака того же вендора, а выдумывать их нельзя. Согласование контракта DevContour — утверждённая редакция контракта (`contracts[]` с её `digest` и `approvedAt`); движок получает его через порт `ExecutionHost.contracts` (`approved({id, digest})`), а не по раундам `reviews`. История `contractAttempts` остаётся продуктовой записью и читается DevContour как прежде |
100
+
101
+ Адаптер записывает только поля нейтральной модели и сохраняет остальные поля
102
+ записи хоста как есть; неизвестные поля не удаляются.
103
+
104
+ ## Основания, которые не пересчитываются
105
+
106
+ Формулы digest записаны в принятой истории и receipts. Движок их не вычисляет —
107
+ их даёт хост через `BasisProvider`:
108
+
109
+ - digest постановки задачи (включает продуктовые поля), digest области
110
+ проверок, digest контракта по своей формуле закрепления;
111
+ - digest политики задачи, включая прежний вариант для старых receipts.
112
+
113
+ Формулы действующей версии проверок, основания реализации, seed свойства,
114
+ digest манифеста свойств и ключа ресурса переносятся в пакет без изменения.
115
+ Pins контекста DevContour сохраняют свой digest (`hostDigest` → `ContextPin.digest`);
116
+ `sourceDigest` снимка считается harness отдельно.
117
+
118
+ Standalone-хост использует `digestOf` (SHA-256 канонического JSON) от
119
+ нейтральных структур; эти digests с digests DevContour не сравниваются.
120
+
121
+ ## Бюджеты
122
+
123
+ | Область harness | DevContour |
124
+ | --------------- | --------------------------- |
125
+ | `work` | задача |
126
+ | `group` | доска со всеми её ревизиями |
127
+ | `total` | workspace |
128
+
129
+ `BudgetScopes` DevContour: `groupOf` — доска задачи в текущем графе,
130
+ `members` — задачи всех ревизий доски, `legacy` — прежнее правило атрибуции
131
+ (задача по попытке или предмету вызова, доска по текущему графу) для записей
132
+ без закреплённых `taskId`/`boardId`/`taskIds`; результат закрепляется один
133
+ раз. Расход остаётся за доской, на которой он сделан, при переносе задачи.
134
+ Тариф выбирается по модели, о которой сообщил runtime (`RuntimeResponse.model`
135
+ → `CallRecord.model`), а не по модели из политики.
136
+
137
+ ## Reviewer
138
+
139
+ Автор плана и контракта задаётся вызовом ревью (`--author`; у DevContour —
140
+ `authorRuntime` задания ведущего, закреплённый за входом: повтор с другим
141
+ автором отклоняется). Автор вне harness — `external`; его вендор не совпадает
142
+ ни с одним runtime.
143
+
144
+ Правило ревью кода роли собирается адаптером по прежней цепочке
145
+ `role.reviewer` компонента → `repository.reviewer` → `config.reviewer`;
146
+ одноимённые роли разных компонентов не схлопываются (политика строится на
147
+ компонент). Прежнее требование «writer ≠ reviewer» — `different-vendor`.
148
+
149
+ ## Сохраняемые форматы
150
+
151
+ | Что | Правило |
152
+ | --------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
153
+ | Состояние `version: 1` | Читается как есть; иная версия отклоняется до записи |
154
+ | Типы событий журнала | Прежние строки, см. `transactionKinds` |
155
+ | Ветки | Целевая `devcontour/accepted` (в политике `refs/heads/devcontour/accepted`), ветки попыток `devcontour/run-<runId>` |
156
+ | Каталоги попыток | `worktrees/<runId>`, `worktrees/<runId>-integration`, `artifacts/<runId>/…`, `dependencies/<runId>/manifest.json` |
157
+ | Receipt среды | `environment.json` `version: 1` |
158
+ | Отчёт свойств, договор проверки, корпус | `version: 1` |
159
+ | Владение репозиторием | `<git-common-dir>/devcontour-owner.json` |
160
+ | Ресурсы | Отдельная база блокировок ресурсов `resources.sqlite`: блокировки устройств и портов, а не состояние исполнения |
161
+ | Probe ревьюера | MCP-сервер `devcontour_probe`, инструмент `run_check`; протокол — построчный JSON-RPC (stdio), журнал `probes.jsonl` |
162
+
163
+ ## Переменные окружения gates
164
+
165
+ Gates, шаги подготовки и проверки потребителей получают переменные с
166
+ историческим префиксом `DEVCONTOUR_`: `DEVCONTOUR_REPOSITORY_ID`,
167
+ `DEVCONTOUR_RUN_ID`, `DEVCONTOUR_TASK_ID`, `DEVCONTOUR_PHASE`,
168
+ `DEVCONTOUR_RESOURCES_JSON`, `DEVCONTOUR_DEPENDENCIES_JSON`,
169
+ `DEVCONTOUR_COMPONENTS_JSON`, `DEVCONTOUR_REPORT_PATH`, `DEVCONTOUR_SEED`,
170
+ `DEVCONTOUR_PROPERTY_REPORT`, `DEVCONTOUR_PROPERTIES`, `DEVCONTOUR_CORPUS`,
171
+ `DEVCONTOUR_CASE`, `DEVCONTOUR_CASE_RESULT`, `DEVCONTOUR_MANIFEST_PATH`,
172
+ `DEVCONTOUR_CHANGESET_ID`, `DEVCONTOUR_PROCESS_MARK`. Имена — часть договора
173
+ проверок пользователей, а не связь с хостом; harness сохраняет их и задаёт
174
+ `HARNESS_*` с теми же значениями. Оба пространства резервирует controller:
175
+ политика не может задать переменные с этими префиксами.
176
+
177
+ ## Что меняется намеренно
178
+
179
+ | Изменение | Следствие |
180
+ | ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
181
+ | Версия движка в основании reuse | Попытка записывает `engineVersion` (и `consumerVersion`). Кандидат попытки другой версии движка повторно не используется: новая попытка заново проходит gates и ревью. Прежние попытки сохраняют своё основание; отсутствующая версия не выдумывается |
182
+ | Reviewer задаётся правилом | `different-vendor` по умолчанию; `same-vendor-allowed` включается явно |
183
+ | Токены вне журнала | Исправление утечки: прежде `run.claimed` и `run.recovered` записывали в журнал попытку целиком, с fencing-токеном. Новые события пишутся без токенов; при выдаче старых событий наружу (CLI, HTTP, RunEvent) токены удаляются. Историю базы физически не переписываем |
184
+ | История находок ревью | Reviewer называет состояние прежних находок; прежние попытки читаются без статусов; ревью кода ведёт историю находок поручения с ID controller, находки записей alpha.5 получают детерминированный ID (review-protocol.md) |
185
+
186
+ ## Состояние standalone
187
+
188
+ Standalone SQLite хранит документ `execution-document` (`stateVersion: 1`),
189
+ события и локальные записи в одной базе и запись о версии писателя. Процесс
190
+ другой версии с несовместимым форматом не открывает базу на запись. Матрица
191
+ поддерживаемых переходов и порядок upgrade/rollback — в
192
+ [docs/upgrade.md](upgrade.md).
@@ -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 -->