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

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 (390) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +183 -2
  3. package/docs/architecture.md +473 -0
  4. package/docs/compatibility-versions.md +56 -0
  5. package/docs/compatibility.md +192 -0
  6. package/docs/connectors.md +323 -0
  7. package/docs/contracts.md +603 -0
  8. package/docs/integrations/hindsight.md +148 -0
  9. package/docs/quickstart.md +182 -0
  10. package/docs/release-notes/0.1.0-alpha.6.md +238 -0
  11. package/docs/review-probes.md +120 -0
  12. package/docs/review-protocol.md +251 -0
  13. package/docs/toolchains.md +167 -0
  14. package/docs/turbo-starter.md +269 -0
  15. package/docs/upgrade.md +202 -0
  16. package/examples/README.md +27 -0
  17. package/examples/external-connector/README.md +19 -0
  18. package/examples/external-connector/connector.mjs +55 -0
  19. package/examples/files-only/README.md +28 -0
  20. package/examples/files-only/context/norms/rules.md +4 -0
  21. package/examples/files-only/graph.json +23 -0
  22. package/examples/files-only/policy.json +30 -0
  23. package/examples/one-vendor/README.md +19 -0
  24. package/examples/one-vendor/policy.json +27 -0
  25. package/examples/single-task/README.md +18 -0
  26. package/examples/single-task/graph.json +27 -0
  27. package/examples/single-task/policy-same-vendor.json +55 -0
  28. package/examples/single-task/policy.json +52 -0
  29. package/examples/single-task/src/.gitkeep +0 -0
  30. package/examples/single-task/test/run.js +19 -0
  31. package/examples/toolchains/README.md +32 -0
  32. package/examples/toolchains/go/bad/label/label.go +9 -0
  33. package/examples/toolchains/go/good/label/label.go +9 -0
  34. package/examples/toolchains/go/policy.json +79 -0
  35. package/examples/toolchains/go/repo/REQUIREMENTS.md +9 -0
  36. package/examples/toolchains/go/repo/_gitignore +1 -0
  37. package/examples/toolchains/go/repo/go.mod +5 -0
  38. package/examples/toolchains/go/repo/go.sum +2 -0
  39. package/examples/toolchains/go/repo/label/label.go +9 -0
  40. package/examples/toolchains/go/repo/label/label_test.go +27 -0
  41. package/examples/toolchains/go/repo/label/version.go +9 -0
  42. package/examples/toolchains/go/task.json +25 -0
  43. package/examples/toolchains/mixed-go/bad/backend/label/label.go +9 -0
  44. package/examples/toolchains/mixed-go/bad/frontend/src/label.js +4 -0
  45. package/examples/toolchains/mixed-go/good/backend/label/label.go +9 -0
  46. package/examples/toolchains/mixed-go/good/frontend/src/label.js +4 -0
  47. package/examples/toolchains/mixed-go/policy.json +126 -0
  48. package/examples/toolchains/mixed-go/repo/REQUIREMENTS.md +9 -0
  49. package/examples/toolchains/mixed-go/repo/_gitignore +6 -0
  50. package/examples/toolchains/mixed-go/repo/backend/go.mod +3 -0
  51. package/examples/toolchains/mixed-go/repo/backend/label/label.go +9 -0
  52. package/examples/toolchains/mixed-go/repo/backend/label/label_test.go +30 -0
  53. package/examples/toolchains/mixed-go/repo/contract/check.mjs +17 -0
  54. package/examples/toolchains/mixed-go/repo/contract/label.json +9 -0
  55. package/examples/toolchains/mixed-go/repo/frontend/package.json +15 -0
  56. package/examples/toolchains/mixed-go/repo/frontend/pnpm-lock.yaml +22 -0
  57. package/examples/toolchains/mixed-go/repo/frontend/src/label.js +4 -0
  58. package/examples/toolchains/mixed-go/repo/frontend/test/label.test.js +11 -0
  59. package/examples/toolchains/mixed-go/task.json +25 -0
  60. package/examples/toolchains/mixed-python/bad/backend/src/fx_backend/__init__.py +6 -0
  61. package/examples/toolchains/mixed-python/bad/frontend/src/label.js +4 -0
  62. package/examples/toolchains/mixed-python/good/backend/src/fx_backend/__init__.py +6 -0
  63. package/examples/toolchains/mixed-python/good/frontend/src/label.js +4 -0
  64. package/examples/toolchains/mixed-python/policy.json +121 -0
  65. package/examples/toolchains/mixed-python/repo/REQUIREMENTS.md +9 -0
  66. package/examples/toolchains/mixed-python/repo/_gitignore +6 -0
  67. package/examples/toolchains/mixed-python/repo/backend/.python-version +1 -0
  68. package/examples/toolchains/mixed-python/repo/backend/pyproject.toml +19 -0
  69. package/examples/toolchains/mixed-python/repo/backend/src/fx_backend/__init__.py +6 -0
  70. package/examples/toolchains/mixed-python/repo/backend/tests/test_label.py +11 -0
  71. package/examples/toolchains/mixed-python/repo/backend/uv.lock +108 -0
  72. package/examples/toolchains/mixed-python/repo/contract/check.mjs +17 -0
  73. package/examples/toolchains/mixed-python/repo/contract/label.json +9 -0
  74. package/examples/toolchains/mixed-python/repo/frontend/package.json +15 -0
  75. package/examples/toolchains/mixed-python/repo/frontend/pnpm-lock.yaml +22 -0
  76. package/examples/toolchains/mixed-python/repo/frontend/src/label.js +4 -0
  77. package/examples/toolchains/mixed-python/repo/frontend/test/label.test.js +11 -0
  78. package/examples/toolchains/mixed-python/task.json +25 -0
  79. package/examples/toolchains/npm/bad/src/label.js +4 -0
  80. package/examples/toolchains/npm/good/src/label.js +4 -0
  81. package/examples/toolchains/npm/policy.json +82 -0
  82. package/examples/toolchains/npm/repo/REQUIREMENTS.md +9 -0
  83. package/examples/toolchains/npm/repo/_gitignore +4 -0
  84. package/examples/toolchains/npm/repo/package-lock.json +22 -0
  85. package/examples/toolchains/npm/repo/package.json +13 -0
  86. package/examples/toolchains/npm/repo/src/label.js +4 -0
  87. package/examples/toolchains/npm/repo/test/label.test.js +13 -0
  88. package/examples/toolchains/npm/task.json +25 -0
  89. package/examples/toolchains/pnpm/bad/packages/label/src/label.js +4 -0
  90. package/examples/toolchains/pnpm/good/packages/label/src/label.js +4 -0
  91. package/examples/toolchains/pnpm/policy.json +85 -0
  92. package/examples/toolchains/pnpm/repo/REQUIREMENTS.md +9 -0
  93. package/examples/toolchains/pnpm/repo/_gitignore +4 -0
  94. package/examples/toolchains/pnpm/repo/package.json +10 -0
  95. package/examples/toolchains/pnpm/repo/packages/checker/check.mjs +5 -0
  96. package/examples/toolchains/pnpm/repo/packages/checker/package.json +9 -0
  97. package/examples/toolchains/pnpm/repo/packages/label/package.json +14 -0
  98. package/examples/toolchains/pnpm/repo/packages/label/src/label.js +4 -0
  99. package/examples/toolchains/pnpm/repo/packages/label/test/label.test.js +13 -0
  100. package/examples/toolchains/pnpm/repo/pnpm-lock.yaml +29 -0
  101. package/examples/toolchains/pnpm/repo/pnpm-workspace.yaml +2 -0
  102. package/examples/toolchains/pnpm/task.json +25 -0
  103. package/examples/toolchains/python/bad/src/fx_label/__init__.py +6 -0
  104. package/examples/toolchains/python/good/src/fx_label/__init__.py +6 -0
  105. package/examples/toolchains/python/policy.json +80 -0
  106. package/examples/toolchains/python/repo/.python-version +1 -0
  107. package/examples/toolchains/python/repo/REQUIREMENTS.md +9 -0
  108. package/examples/toolchains/python/repo/_gitignore +5 -0
  109. package/examples/toolchains/python/repo/pyproject.toml +19 -0
  110. package/examples/toolchains/python/repo/src/fx_label/__init__.py +6 -0
  111. package/examples/toolchains/python/repo/tests/test_label.py +13 -0
  112. package/examples/toolchains/python/repo/uv.lock +108 -0
  113. package/examples/toolchains/python/task.json +25 -0
  114. package/examples/toolchains/turbo/bad/packages/label/src/label.js +4 -0
  115. package/examples/toolchains/turbo/good/packages/label/src/label.js +4 -0
  116. package/examples/toolchains/turbo/policy.json +110 -0
  117. package/examples/toolchains/turbo/repo/REQUIREMENTS.md +9 -0
  118. package/examples/toolchains/turbo/repo/_gitignore +4 -0
  119. package/examples/toolchains/turbo/repo/package.json +13 -0
  120. package/examples/toolchains/turbo/repo/packages/checker/check.mjs +5 -0
  121. package/examples/toolchains/turbo/repo/packages/checker/package.json +9 -0
  122. package/examples/toolchains/turbo/repo/packages/label/package.json +14 -0
  123. package/examples/toolchains/turbo/repo/packages/label/src/label.js +4 -0
  124. package/examples/toolchains/turbo/repo/packages/label/test/label.test.js +13 -0
  125. package/examples/toolchains/turbo/repo/pnpm-lock.yaml +94 -0
  126. package/examples/toolchains/turbo/repo/pnpm-workspace.yaml +2 -0
  127. package/examples/toolchains/turbo/repo/turbo.json +7 -0
  128. package/examples/toolchains/turbo/task.json +25 -0
  129. package/examples/turbo-starter/README.md +16 -0
  130. package/examples/turbo-starter/changes/issued-at-consumer-only.patch +12 -0
  131. package/examples/turbo-starter/changes/issued-at.patch +130 -0
  132. package/examples/turbo-starter/cleanup.mjs +58 -0
  133. package/examples/turbo-starter/graph.json +32 -0
  134. package/examples/turbo-starter/harness/acceptance/db-isolation.test.mjs +97 -0
  135. package/examples/turbo-starter/harness/acceptance/env-probe.test.mjs +75 -0
  136. package/examples/turbo-starter/harness/acceptance/generation.test.mjs +54 -0
  137. package/examples/turbo-starter/harness/acceptance/issued-at.test.mjs +43 -0
  138. package/examples/turbo-starter/harness/acceptance/lifecycle-probe.test.mjs +47 -0
  139. package/examples/turbo-starter/harness/compose-bridge.mjs +593 -0
  140. package/examples/turbo-starter/harness/compose.acceptance.yml +23 -0
  141. package/examples/turbo-starter/harness/env/backend.env.template +7 -0
  142. package/examples/turbo-starter/harness/env/compose.env.template +6 -0
  143. package/examples/turbo-starter/policy.json +109 -0
  144. package/examples/turbo-starter/policy.lifecycle.json +114 -0
  145. package/examples/turbo-starter/setup.mjs +352 -0
  146. package/examples/two-vendors/policy.json +30 -0
  147. package/lib/agents/cli-adapter.d.ts +48 -0
  148. package/lib/agents/cli-adapter.js +386 -0
  149. package/lib/agents/inspect.d.ts +6 -0
  150. package/lib/agents/inspect.js +46 -0
  151. package/lib/agents/probe-server.d.ts +2 -0
  152. package/lib/agents/probe-server.js +13 -0
  153. package/lib/agents/review-probe.d.ts +295 -0
  154. package/lib/agents/review-probe.js +872 -0
  155. package/lib/agents/runtime-events.d.ts +11 -0
  156. package/lib/agents/runtime-events.js +21 -0
  157. package/lib/agents/schemas.d.ts +217 -0
  158. package/lib/agents/schemas.js +124 -0
  159. package/lib/agents/usage.d.ts +8 -0
  160. package/lib/agents/usage.js +83 -0
  161. package/lib/cli.d.ts +2 -0
  162. package/lib/cli.js +491 -0
  163. package/lib/connectors/files.d.ts +108 -0
  164. package/lib/connectors/files.js +133 -0
  165. package/lib/contracts/checks.d.ts +307 -0
  166. package/lib/contracts/checks.js +181 -0
  167. package/lib/contracts/cli.d.ts +3895 -0
  168. package/lib/contracts/cli.js +664 -0
  169. package/lib/contracts/context.d.ts +135 -0
  170. package/lib/contracts/context.js +129 -0
  171. package/lib/contracts/digest.d.ts +9 -0
  172. package/lib/contracts/digest.js +22 -0
  173. package/lib/contracts/errors.d.ts +81 -0
  174. package/lib/contracts/errors.js +70 -0
  175. package/lib/contracts/events.d.ts +827 -0
  176. package/lib/contracts/events.js +258 -0
  177. package/lib/contracts/execution.d.ts +3216 -0
  178. package/lib/contracts/execution.js +872 -0
  179. package/lib/contracts/failure.d.ts +67 -0
  180. package/lib/contracts/failure.js +127 -0
  181. package/lib/contracts/index.d.ts +18 -0
  182. package/lib/contracts/index.js +18 -0
  183. package/lib/contracts/policy.d.ts +1080 -0
  184. package/lib/contracts/policy.js +544 -0
  185. package/lib/contracts/ports.d.ts +417 -0
  186. package/lib/contracts/ports.js +1 -0
  187. package/lib/contracts/primitives.d.ts +63 -0
  188. package/lib/contracts/primitives.js +73 -0
  189. package/lib/contracts/profiles.d.ts +336 -0
  190. package/lib/contracts/profiles.js +109 -0
  191. package/lib/contracts/published.d.ts +3237 -0
  192. package/lib/contracts/published.js +34 -0
  193. package/lib/contracts/result.d.ts +1280 -0
  194. package/lib/contracts/result.js +311 -0
  195. package/lib/contracts/review.d.ts +441 -0
  196. package/lib/contracts/review.js +477 -0
  197. package/lib/contracts/toolchain.d.ts +96 -0
  198. package/lib/contracts/toolchain.js +89 -0
  199. package/lib/contracts/version.d.ts +17 -0
  200. package/lib/contracts/version.js +17 -0
  201. package/lib/contracts/work.d.ts +173 -0
  202. package/lib/contracts/work.js +178 -0
  203. package/lib/control/base-move.d.ts +44 -0
  204. package/lib/control/base-move.js +90 -0
  205. package/lib/control/doctor.d.ts +50 -0
  206. package/lib/control/doctor.js +113 -0
  207. package/lib/control/harness.d.ts +1125 -0
  208. package/lib/control/harness.js +741 -0
  209. package/lib/control/provenance.d.ts +17 -0
  210. package/lib/control/provenance.js +25 -0
  211. package/lib/control/recovery.d.ts +237 -0
  212. package/lib/control/recovery.js +236 -0
  213. package/lib/control/review.d.ts +62 -0
  214. package/lib/control/review.js +178 -0
  215. package/lib/control/workflow.d.ts +247 -0
  216. package/lib/control/workflow.js +259 -0
  217. package/lib/engine/budget.d.ts +345 -0
  218. package/lib/engine/budget.js +278 -0
  219. package/lib/engine/classify.d.ts +8 -0
  220. package/lib/engine/classify.js +32 -0
  221. package/lib/engine/engine.d.ts +1527 -0
  222. package/lib/engine/engine.js +2259 -0
  223. package/lib/engine/failure.d.ts +1 -0
  224. package/lib/engine/failure.js +3 -0
  225. package/lib/engine/joint-runner.d.ts +257 -0
  226. package/lib/engine/joint-runner.js +254 -0
  227. package/lib/engine/measured.d.ts +15 -0
  228. package/lib/engine/measured.js +82 -0
  229. package/lib/engine/prompt.d.ts +66 -0
  230. package/lib/engine/prompt.js +131 -0
  231. package/lib/engine/property-recovery.d.ts +50 -0
  232. package/lib/engine/property-recovery.js +171 -0
  233. package/lib/engine/repair.d.ts +33 -0
  234. package/lib/engine/repair.js +82 -0
  235. package/lib/engine/result.d.ts +9 -0
  236. package/lib/engine/result.js +218 -0
  237. package/lib/engine/reuse.d.ts +80 -0
  238. package/lib/engine/reuse.js +102 -0
  239. package/lib/engine/runner.d.ts +360 -0
  240. package/lib/engine/runner.js +1836 -0
  241. package/lib/engine/standalone.d.ts +6 -0
  242. package/lib/engine/standalone.js +48 -0
  243. package/lib/engine/usage.d.ts +87 -0
  244. package/lib/engine/usage.js +165 -0
  245. package/lib/environment/isolation.d.ts +110 -0
  246. package/lib/environment/isolation.js +238 -0
  247. package/lib/environment/lifecycle.d.ts +40 -0
  248. package/lib/environment/lifecycle.js +197 -0
  249. package/lib/environment/live-calls.d.ts +14 -0
  250. package/lib/environment/live-calls.js +73 -0
  251. package/lib/environment/os-tools.d.ts +16 -0
  252. package/lib/environment/os-tools.js +35 -0
  253. package/lib/environment/process.d.ts +32 -0
  254. package/lib/environment/process.js +369 -0
  255. package/lib/environment/redaction.d.ts +22 -0
  256. package/lib/environment/redaction.js +102 -0
  257. package/lib/environment/resources.d.ts +31 -0
  258. package/lib/environment/resources.js +120 -0
  259. package/lib/index.d.ts +65 -0
  260. package/lib/index.js +67 -0
  261. package/lib/integrations/connector/commands.d.ts +358 -0
  262. package/lib/integrations/connector/commands.js +428 -0
  263. package/lib/integrations/connector/config.d.ts +215 -0
  264. package/lib/integrations/connector/config.js +214 -0
  265. package/lib/integrations/connector/delivery.d.ts +107 -0
  266. package/lib/integrations/connector/delivery.js +591 -0
  267. package/lib/integrations/connector/fake.d.ts +291 -0
  268. package/lib/integrations/connector/fake.js +786 -0
  269. package/lib/integrations/connector/history.d.ts +124 -0
  270. package/lib/integrations/connector/history.js +497 -0
  271. package/lib/integrations/connector/index.d.ts +16 -0
  272. package/lib/integrations/connector/index.js +16 -0
  273. package/lib/integrations/connector/redact.d.ts +19 -0
  274. package/lib/integrations/connector/redact.js +91 -0
  275. package/lib/integrations/connector/registry.d.ts +15 -0
  276. package/lib/integrations/connector/registry.js +43 -0
  277. package/lib/integrations/connector/snapshot.d.ts +131 -0
  278. package/lib/integrations/connector/snapshot.js +422 -0
  279. package/lib/integrations/connector/source.d.ts +37 -0
  280. package/lib/integrations/connector/source.js +73 -0
  281. package/lib/integrations/connector/steps.d.ts +71 -0
  282. package/lib/integrations/connector/steps.js +188 -0
  283. package/lib/integrations/connector/store.d.ts +140 -0
  284. package/lib/integrations/connector/store.js +277 -0
  285. package/lib/integrations/connector/summary.d.ts +38 -0
  286. package/lib/integrations/connector/summary.js +59 -0
  287. package/lib/integrations/connector/testing-worker.d.ts +1 -0
  288. package/lib/integrations/connector/testing-worker.js +81 -0
  289. package/lib/integrations/connector/testing.d.ts +205 -0
  290. package/lib/integrations/connector/testing.js +1615 -0
  291. package/lib/integrations/connector/transport.d.ts +240 -0
  292. package/lib/integrations/connector/transport.js +28 -0
  293. package/lib/integrations/memory/context.d.ts +120 -0
  294. package/lib/integrations/memory/context.js +239 -0
  295. package/lib/integrations/memory/index.d.ts +8 -0
  296. package/lib/integrations/memory/index.js +8 -0
  297. package/lib/integrations/memory/provider.d.ts +79 -0
  298. package/lib/integrations/memory/provider.js +22 -0
  299. package/lib/integrations/memory/sink.d.ts +56 -0
  300. package/lib/integrations/memory/sink.js +116 -0
  301. package/lib/integrations/memory/testing.d.ts +44 -0
  302. package/lib/integrations/memory/testing.js +61 -0
  303. package/lib/integrations/relay-v1/index.d.ts +122 -0
  304. package/lib/integrations/relay-v1/index.js +860 -0
  305. package/lib/integrations/relay-v2/index.d.ts +145 -0
  306. package/lib/integrations/relay-v2/index.js +752 -0
  307. package/lib/integrations/relay-v2/process.d.ts +32 -0
  308. package/lib/integrations/relay-v2/process.js +112 -0
  309. package/lib/integrations/relay-v2/protocol.d.ts +172 -0
  310. package/lib/integrations/relay-v2/protocol.js +142 -0
  311. package/lib/package.d.ts +2 -0
  312. package/lib/package.js +3 -0
  313. package/lib/profiles/resolve.d.ts +190 -0
  314. package/lib/profiles/resolve.js +273 -0
  315. package/lib/storage/document.d.ts +1234 -0
  316. package/lib/storage/document.js +80 -0
  317. package/lib/storage/sqlite.d.ts +35 -0
  318. package/lib/storage/sqlite.js +260 -0
  319. package/lib/testing/index.d.ts +5 -0
  320. package/lib/testing/index.js +5 -0
  321. package/lib/testing/store-contract.d.ts +8 -0
  322. package/lib/testing/store-contract.js +167 -0
  323. package/lib/toolchains/index.d.ts +9 -0
  324. package/lib/toolchains/index.js +9 -0
  325. package/lib/toolchains/inspect.d.ts +105 -0
  326. package/lib/toolchains/inspect.js +831 -0
  327. package/lib/toolchains/manifests.d.ts +67 -0
  328. package/lib/toolchains/manifests.js +238 -0
  329. package/lib/toolchains/probe.d.ts +28 -0
  330. package/lib/toolchains/probe.js +54 -0
  331. package/lib/toolchains/recipes.d.ts +33 -0
  332. package/lib/toolchains/recipes.js +51 -0
  333. package/lib/validate.d.ts +36 -0
  334. package/lib/validate.js +175 -0
  335. package/lib/verification/acceptance.d.ts +284 -0
  336. package/lib/verification/acceptance.js +670 -0
  337. package/lib/verification/checks.d.ts +72 -0
  338. package/lib/verification/checks.js +190 -0
  339. package/lib/verification/confirm.d.ts +125 -0
  340. package/lib/verification/confirm.js +411 -0
  341. package/lib/verification/corpus.d.ts +1303 -0
  342. package/lib/verification/corpus.js +744 -0
  343. package/lib/verification/dependencies.d.ts +66 -0
  344. package/lib/verification/dependencies.js +144 -0
  345. package/lib/verification/gates.d.ts +160 -0
  346. package/lib/verification/gates.js +600 -0
  347. package/lib/verification/joint.d.ts +63 -0
  348. package/lib/verification/joint.js +121 -0
  349. package/lib/verification/proof.d.ts +54 -0
  350. package/lib/verification/proof.js +96 -0
  351. package/lib/verification/property.d.ts +86 -0
  352. package/lib/verification/property.js +214 -0
  353. package/lib/verification/requirements.d.ts +25 -0
  354. package/lib/verification/requirements.js +69 -0
  355. package/lib/verification/self-check.d.ts +29 -0
  356. package/lib/verification/self-check.js +99 -0
  357. package/lib/verification/spike.d.ts +79 -0
  358. package/lib/verification/spike.js +124 -0
  359. package/package.json +100 -4
  360. package/profiles/expo-mobile.json +15 -0
  361. package/profiles/go-api.json +15 -0
  362. package/profiles/go-gotestsum.json +21 -0
  363. package/profiles/mobile-maestro.json +29 -0
  364. package/profiles/nest-api.json +26 -0
  365. package/profiles/next-product.json +72 -0
  366. package/profiles/node-checks.json +77 -0
  367. package/profiles/npm-ci.json +22 -0
  368. package/profiles/pnpm-frozen.json +24 -0
  369. package/profiles/python-api.json +32 -0
  370. package/profiles/python-uv.json +26 -0
  371. package/profiles/react-native.json +50 -0
  372. package/profiles/react-vite-admin.json +71 -0
  373. package/schemas/context-snapshot.schema.json +231 -0
  374. package/schemas/contract-document.schema.json +385 -0
  375. package/schemas/execution-document.schema.json +3695 -0
  376. package/schemas/execution-policy.schema.json +2196 -0
  377. package/schemas/joint-verification.schema.json +617 -0
  378. package/schemas/result-envelope.schema.json +1652 -0
  379. package/schemas/review-record.schema.json +177 -0
  380. package/schemas/run-event.schema.json +2458 -0
  381. package/schemas/run-result.schema.json +1570 -0
  382. package/schemas/stack-profile.schema.json +535 -0
  383. package/schemas/work-graph.schema.json +381 -0
  384. package/schemas/work-order.schema.json +256 -0
  385. package/skills/harness/SKILL.md +64 -0
  386. package/templates/AGENTS.md +13 -0
  387. package/templates/controller-mode/AGENTS.controller.md +58 -0
  388. package/templates/controller-mode/README.md +44 -0
  389. package/templates/graph.json +22 -0
  390. package/templates/policy.json +28 -0
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Версии договоров. Они независимы: смена формата состояния не обязана
3
+ * менять API, а новая версия пакета не меняет формат молча.
4
+ *
5
+ * - `apiVersion` — формат публичных входов/выходов (WorkOrder, RunResult...).
6
+ * - `stateVersion` — формат сохранённого состояния исполнения.
7
+ * - `policyVersion` — формат ExecutionPolicy; входит в digest политики.
8
+ *
9
+ * Пакет читает только перечисленные версии. Неизвестная версия отклоняется
10
+ * до любой мутации состояния и до выдачи claim.
11
+ */
12
+ export const API_VERSION = 'devcontour-harness/v1alpha1';
13
+ export const SUPPORTED_API_VERSIONS = [API_VERSION];
14
+ export const STATE_VERSION = 1;
15
+ export const SUPPORTED_STATE_VERSIONS = [STATE_VERSION];
16
+ export const POLICY_VERSION = 1;
17
+ export const SUPPORTED_POLICY_VERSIONS = [POLICY_VERSION];
@@ -0,0 +1,173 @@
1
+ import { z } from 'zod';
2
+ import type { ErrorIssue } from './errors.ts';
3
+ /**
4
+ * Откуда пришло поручение: коннектор, ключ записи в нём и её редакция.
5
+ * Результат всегда относится к этой редакции; более новая редакция в
6
+ * источнике старым результатом не закрывается.
7
+ */
8
+ export declare const sourceRefSchema: z.ZodObject<{
9
+ connector: z.ZodString;
10
+ ref: z.ZodString;
11
+ revision: z.ZodString;
12
+ }, z.core.$strict>;
13
+ export type SourceRef = z.infer<typeof sourceRefSchema>;
14
+ export declare const propertyIdSchema: z.ZodString;
15
+ /** Требование, закреплённое за поручением: текст, digest источника и проверяющий gate. */
16
+ export declare const requirementSchema: z.ZodObject<{
17
+ id: z.ZodString;
18
+ source: z.ZodString;
19
+ digest: z.ZodString;
20
+ text: z.ZodString;
21
+ gate: z.ZodString;
22
+ scenario: z.ZodString;
23
+ testId: z.ZodOptional<z.ZodString>;
24
+ }, z.core.$strict>;
25
+ /** Ссылка на закреплённый контракт: исполнение идёт против этого digest. */
26
+ export declare const contractRefSchema: z.ZodObject<{
27
+ id: z.ZodString;
28
+ digest: z.ZodString;
29
+ }, z.core.$strict>;
30
+ /** Проверка потребителя: компонент и его gates против integration результата. */
31
+ export declare const consumerSchema: z.ZodObject<{
32
+ repositoryId: z.ZodString;
33
+ gates: z.ZodArray<z.ZodString>;
34
+ }, z.core.$strict>;
35
+ /**
36
+ * Исполняемое поручение. Это снимок постановки, а не редактируемая задача:
37
+ * изменение любого поля — новая редакция источника и новый digest, и прежнее
38
+ * согласование к ней не относится.
39
+ */
40
+ export declare const workOrderSchema: z.ZodObject<{
41
+ apiVersion: z.ZodLiteral<"devcontour-harness/v1alpha1">;
42
+ id: z.ZodString;
43
+ source: z.ZodObject<{
44
+ connector: z.ZodString;
45
+ ref: z.ZodString;
46
+ revision: z.ZodString;
47
+ }, z.core.$strict>;
48
+ title: z.ZodString;
49
+ goal: z.ZodString;
50
+ acceptance: z.ZodArray<z.ZodString>;
51
+ role: z.ZodString;
52
+ repositoryId: z.ZodString;
53
+ relatedRepositories: z.ZodDefault<z.ZodArray<z.ZodString>>;
54
+ writeScope: z.ZodArray<z.ZodUnion<readonly [z.ZodLiteral<".">, z.ZodString]>>;
55
+ dependsOn: z.ZodDefault<z.ZodArray<z.ZodString>>;
56
+ gates: z.ZodOptional<z.ZodArray<z.ZodString>>;
57
+ consumers: z.ZodOptional<z.ZodArray<z.ZodObject<{
58
+ repositoryId: z.ZodString;
59
+ gates: z.ZodArray<z.ZodString>;
60
+ }, z.core.$strict>>>;
61
+ properties: z.ZodOptional<z.ZodArray<z.ZodString>>;
62
+ resources: z.ZodOptional<z.ZodArray<z.ZodString>>;
63
+ requirements: z.ZodDefault<z.ZodArray<z.ZodObject<{
64
+ id: z.ZodString;
65
+ source: z.ZodString;
66
+ digest: z.ZodString;
67
+ text: z.ZodString;
68
+ gate: z.ZodString;
69
+ scenario: z.ZodString;
70
+ testId: z.ZodOptional<z.ZodString>;
71
+ }, z.core.$strict>>>;
72
+ contracts: z.ZodDefault<z.ZodArray<z.ZodObject<{
73
+ id: z.ZodString;
74
+ digest: z.ZodString;
75
+ }, z.core.$strict>>>;
76
+ context: z.ZodDefault<z.ZodArray<z.ZodString>>;
77
+ }, z.core.$strict>;
78
+ export type WorkOrder = z.infer<typeof workOrderSchema>;
79
+ export type WorkOrderInput = z.input<typeof workOrderSchema>;
80
+ /**
81
+ * Выполненная внешняя предпосылка. Принимается, только если её подтвердил
82
+ * сам движок (`engine`) либо доверенный получателем receipt; в обоих случаях
83
+ * result SHA обязан быть доступен в целевой ветке. Отметка `done` без SHA
84
+ * доказательством не является.
85
+ */
86
+ export declare const prerequisiteSchema: z.ZodObject<{
87
+ id: z.ZodString;
88
+ repositoryId: z.ZodString;
89
+ resultSha: z.ZodString;
90
+ receipt: z.ZodDiscriminatedUnion<[z.ZodObject<{
91
+ kind: z.ZodLiteral<"engine">;
92
+ runId: z.ZodString;
93
+ }, z.core.$strict>, z.ZodObject<{
94
+ kind: z.ZodLiteral<"trusted">;
95
+ issuer: z.ZodString;
96
+ digest: z.ZodString;
97
+ }, z.core.$strict>], "kind">;
98
+ }, z.core.$strict>;
99
+ export type Prerequisite = z.infer<typeof prerequisiteSchema>;
100
+ /** Неизменяемый снимок состава и зависимостей поручений одного согласования. */
101
+ export declare const workGraphSchema: z.ZodObject<{
102
+ apiVersion: z.ZodLiteral<"devcontour-harness/v1alpha1">;
103
+ id: z.ZodString;
104
+ source: z.ZodObject<{
105
+ connector: z.ZodString;
106
+ ref: z.ZodString;
107
+ revision: z.ZodString;
108
+ }, z.core.$strict>;
109
+ orders: z.ZodArray<z.ZodObject<{
110
+ apiVersion: z.ZodLiteral<"devcontour-harness/v1alpha1">;
111
+ id: z.ZodString;
112
+ source: z.ZodObject<{
113
+ connector: z.ZodString;
114
+ ref: z.ZodString;
115
+ revision: z.ZodString;
116
+ }, z.core.$strict>;
117
+ title: z.ZodString;
118
+ goal: z.ZodString;
119
+ acceptance: z.ZodArray<z.ZodString>;
120
+ role: z.ZodString;
121
+ repositoryId: z.ZodString;
122
+ relatedRepositories: z.ZodDefault<z.ZodArray<z.ZodString>>;
123
+ writeScope: z.ZodArray<z.ZodUnion<readonly [z.ZodLiteral<".">, z.ZodString]>>;
124
+ dependsOn: z.ZodDefault<z.ZodArray<z.ZodString>>;
125
+ gates: z.ZodOptional<z.ZodArray<z.ZodString>>;
126
+ consumers: z.ZodOptional<z.ZodArray<z.ZodObject<{
127
+ repositoryId: z.ZodString;
128
+ gates: z.ZodArray<z.ZodString>;
129
+ }, z.core.$strict>>>;
130
+ properties: z.ZodOptional<z.ZodArray<z.ZodString>>;
131
+ resources: z.ZodOptional<z.ZodArray<z.ZodString>>;
132
+ requirements: z.ZodDefault<z.ZodArray<z.ZodObject<{
133
+ id: z.ZodString;
134
+ source: z.ZodString;
135
+ digest: z.ZodString;
136
+ text: z.ZodString;
137
+ gate: z.ZodString;
138
+ scenario: z.ZodString;
139
+ testId: z.ZodOptional<z.ZodString>;
140
+ }, z.core.$strict>>>;
141
+ contracts: z.ZodDefault<z.ZodArray<z.ZodObject<{
142
+ id: z.ZodString;
143
+ digest: z.ZodString;
144
+ }, z.core.$strict>>>;
145
+ context: z.ZodDefault<z.ZodArray<z.ZodString>>;
146
+ }, z.core.$strict>>;
147
+ prerequisites: z.ZodDefault<z.ZodArray<z.ZodObject<{
148
+ id: z.ZodString;
149
+ repositoryId: z.ZodString;
150
+ resultSha: z.ZodString;
151
+ receipt: z.ZodDiscriminatedUnion<[z.ZodObject<{
152
+ kind: z.ZodLiteral<"engine">;
153
+ runId: z.ZodString;
154
+ }, z.core.$strict>, z.ZodObject<{
155
+ kind: z.ZodLiteral<"trusted">;
156
+ issuer: z.ZodString;
157
+ digest: z.ZodString;
158
+ }, z.core.$strict>], "kind">;
159
+ }, z.core.$strict>>>;
160
+ }, z.core.$strict>;
161
+ export type WorkGraph = z.infer<typeof workGraphSchema>;
162
+ export type WorkGraphInput = z.input<typeof workGraphSchema>;
163
+ export declare const workOrderDigest: (order: WorkOrder) => string;
164
+ /** Digest графа закрепляет согласование: смена состава или поручения — новый digest. */
165
+ export declare const workGraphDigest: (graph: WorkGraph) => string;
166
+ /**
167
+ * Структурная проверка графа до любой записи: повтор ID, неизвестная
168
+ * зависимость, цикл. Зависимость разрешается поручением этого графа либо
169
+ * подтверждённой предпосылкой; другого способа считать её выполненной нет.
170
+ */
171
+ export declare function graphIssues(graph: WorkGraph): (ErrorIssue & {
172
+ code: string;
173
+ })[];
@@ -0,0 +1,178 @@
1
+ import { z } from 'zod';
2
+ import { API_VERSION } from "./version.js";
3
+ import { digestSchema, gateRefSchema, gitShaSchema, idSchema, relativePathSchema, roleIdSchema, trimmedText, } from "./primitives.js";
4
+ import { digestOf } from "./digest.js";
5
+ /**
6
+ * Откуда пришло поручение: коннектор, ключ записи в нём и её редакция.
7
+ * Результат всегда относится к этой редакции; более новая редакция в
8
+ * источнике старым результатом не закрывается.
9
+ */
10
+ export const sourceRefSchema = z.strictObject({
11
+ connector: idSchema,
12
+ ref: z.string().min(1).max(300),
13
+ revision: z.string().min(1).max(200),
14
+ });
15
+ export const propertyIdSchema = z
16
+ .string()
17
+ .regex(/^[A-Za-z0-9_.:-]{1,200}$/, 'property ID без пробелов');
18
+ /** Требование, закреплённое за поручением: текст, digest источника и проверяющий gate. */
19
+ export const requirementSchema = z.strictObject({
20
+ id: z.string().regex(/^REQ-[A-Za-z0-9_-]{1,64}$/),
21
+ source: relativePathSchema,
22
+ digest: digestSchema,
23
+ text: z.string().min(3).max(12000),
24
+ gate: idSchema,
25
+ scenario: z.string().min(3).max(1500),
26
+ /** testcase отчёта, который проверяет сценарий (`classname.name` в JUnit). */
27
+ testId: z.string().min(1).max(300).optional(),
28
+ });
29
+ /** Ссылка на закреплённый контракт: исполнение идёт против этого digest. */
30
+ export const contractRefSchema = z.strictObject({ id: idSchema, digest: digestSchema });
31
+ /** Проверка потребителя: компонент и его gates против integration результата. */
32
+ export const consumerSchema = z.strictObject({
33
+ repositoryId: idSchema,
34
+ gates: z.array(gateRefSchema).min(1).max(20),
35
+ });
36
+ /**
37
+ * Исполняемое поручение. Это снимок постановки, а не редактируемая задача:
38
+ * изменение любого поля — новая редакция источника и новый digest, и прежнее
39
+ * согласование к ней не относится.
40
+ */
41
+ export const workOrderSchema = z.strictObject({
42
+ apiVersion: z.literal(API_VERSION),
43
+ id: idSchema,
44
+ source: sourceRefSchema,
45
+ title: trimmedText(3, 180),
46
+ goal: trimmedText(10, 12000),
47
+ acceptance: z.array(trimmedText(3, 1500)).min(1).max(30),
48
+ role: roleIdSchema,
49
+ repositoryId: idSchema,
50
+ /** Связанные репозитории поручения уровня workspace; пусто — один репозиторий. */
51
+ relatedRepositories: z.array(idSchema).max(10).default([]),
52
+ /**
53
+ * Область записи: изменения вне неё отклоняют попытку. `["."]` — весь
54
+ * репозиторий (защищённые и генерируемые пути и область роли по-прежнему
55
+ * ограничивают запись); `.` допустим только как единственный элемент.
56
+ */
57
+ writeScope: z
58
+ .array(z.union([z.literal('.'), relativePathSchema]))
59
+ .min(1)
60
+ .max(100)
61
+ .refine((paths) => !paths.includes('.') || paths.length === 1, {
62
+ message: 'Весь репозиторий (".") — единственный элемент области записи',
63
+ }),
64
+ dependsOn: z.array(idSchema).max(100).default([]),
65
+ /** Область доказательства: подмножество gates/групп политики. Нет поля — все gates репозитория. */
66
+ gates: z.array(gateRefSchema).max(20).optional(),
67
+ consumers: z.array(consumerSchema).max(10).optional(),
68
+ /** Свойства договора проверки, которые поручение подтверждает сейчас. */
69
+ properties: z.array(propertyIdSchema).max(30).optional(),
70
+ resources: z.array(idSchema).max(20).optional(),
71
+ requirements: z.array(requirementSchema).max(30).default([]),
72
+ contracts: z.array(contractRefSchema).max(30).default([]),
73
+ /** Обязательные источники контекста по ID ContextSnapshot. */
74
+ context: z.array(idSchema).max(30).default([]),
75
+ });
76
+ /**
77
+ * Выполненная внешняя предпосылка. Принимается, только если её подтвердил
78
+ * сам движок (`engine`) либо доверенный получателем receipt; в обоих случаях
79
+ * result SHA обязан быть доступен в целевой ветке. Отметка `done` без SHA
80
+ * доказательством не является.
81
+ */
82
+ export const prerequisiteSchema = z.strictObject({
83
+ id: idSchema,
84
+ repositoryId: idSchema,
85
+ resultSha: gitShaSchema,
86
+ receipt: z.discriminatedUnion('kind', [
87
+ z.strictObject({ kind: z.literal('engine'), runId: z.string().min(1).max(200) }),
88
+ z.strictObject({
89
+ kind: z.literal('trusted'),
90
+ issuer: idSchema,
91
+ digest: digestSchema,
92
+ }),
93
+ ]),
94
+ });
95
+ /** Неизменяемый снимок состава и зависимостей поручений одного согласования. */
96
+ export const workGraphSchema = z.strictObject({
97
+ apiVersion: z.literal(API_VERSION),
98
+ id: idSchema,
99
+ source: sourceRefSchema,
100
+ orders: z.array(workOrderSchema).min(1).max(500),
101
+ prerequisites: z.array(prerequisiteSchema).max(500).default([]),
102
+ });
103
+ export const workOrderDigest = (order) => digestOf(order);
104
+ /** Digest графа закрепляет согласование: смена состава или поручения — новый digest. */
105
+ export const workGraphDigest = (graph) => digestOf(graph);
106
+ /**
107
+ * Структурная проверка графа до любой записи: повтор ID, неизвестная
108
+ * зависимость, цикл. Зависимость разрешается поручением этого графа либо
109
+ * подтверждённой предпосылкой; другого способа считать её выполненной нет.
110
+ */
111
+ export function graphIssues(graph) {
112
+ const issues = [];
113
+ const ids = new Map();
114
+ graph.orders.forEach((order, index) => {
115
+ if (ids.has(order.id))
116
+ issues.push({
117
+ code: 'GRAPH_DUPLICATE_ID',
118
+ path: ['orders', index, 'id'],
119
+ message: 'Повтор ID поручения: ' + order.id,
120
+ });
121
+ else
122
+ ids.set(order.id, index);
123
+ });
124
+ const external = new Set();
125
+ graph.prerequisites.forEach((p, index) => {
126
+ if (ids.has(p.id) || external.has(p.id))
127
+ issues.push({
128
+ code: 'GRAPH_DUPLICATE_ID',
129
+ path: ['prerequisites', index, 'id'],
130
+ message: 'Предпосылка повторяет ID: ' + p.id,
131
+ });
132
+ external.add(p.id);
133
+ });
134
+ graph.orders.forEach((order, index) => order.dependsOn.forEach((dep, d) => {
135
+ if (dep === order.id)
136
+ issues.push({
137
+ code: 'GRAPH_CYCLE',
138
+ path: ['orders', index, 'dependsOn', d],
139
+ message: 'Поручение зависит от себя: ' + dep,
140
+ });
141
+ else if (!ids.has(dep) && !external.has(dep))
142
+ issues.push({
143
+ code: 'GRAPH_UNKNOWN_DEPENDENCY',
144
+ path: ['orders', index, 'dependsOn', d],
145
+ message: `Неизвестная зависимость ${order.id} → ${dep}`,
146
+ });
147
+ }));
148
+ // Цикл ищется обходом в глубину с цветами; путь цикла называется в сообщении.
149
+ const color = new Map();
150
+ const byId = new Map(graph.orders.map((o) => [o.id, o]));
151
+ const reported = new Set();
152
+ const visit = (id, path) => {
153
+ color.set(id, 1);
154
+ for (const dep of byId.get(id)?.dependsOn ?? []) {
155
+ if (dep === id || !byId.has(dep))
156
+ continue;
157
+ if (color.get(dep) === 1) {
158
+ const cycle = [...path.slice(path.indexOf(dep)), dep];
159
+ const key = [...cycle].sort().join(',');
160
+ if (!reported.has(key)) {
161
+ reported.add(key);
162
+ issues.push({
163
+ code: 'GRAPH_CYCLE',
164
+ path: ['orders', ids.get(dep), 'dependsOn'],
165
+ message: 'Цикл зависимостей: ' + cycle.join(' → '),
166
+ });
167
+ }
168
+ }
169
+ else if (!color.has(dep))
170
+ visit(dep, [...path, dep]);
171
+ }
172
+ color.set(id, 2);
173
+ };
174
+ for (const order of graph.orders)
175
+ if (!color.has(order.id))
176
+ visit(order.id, [order.id]);
177
+ return issues;
178
+ }
@@ -0,0 +1,44 @@
1
+ import type { ExecutionPolicy, ExecutionStore, ExecutionView } from '../contracts/index.ts';
2
+ /** Сдвиг ref: репозиторий, ref, новое и ожидаемое прежнее значение. */
3
+ export type RefMove = (repoPath: string, target: string, to: string, from: string) => void | Promise<void>;
4
+ /**
5
+ * Сдвиг ref внутри транзакции проверки владения операцией: проверка и
6
+ * `git update-ref` неразделимы — отмена или перехват операции либо видны
7
+ * проверке, либо происходят уже после сдвига. Внутри транзакции не
8
+ * исполняется чужой код: hook `reference-transaction`, ждущий ту же базу,
9
+ * ждал бы её, а она — его; срок Git ограничен, зависший Git не держит базу.
10
+ */
11
+ export declare function fencedRefMove(store: ExecutionStore, fence: (view: ExecutionView) => void, timeoutMs?: number): RefMove;
12
+ export declare namespace fencedRefMove {
13
+ var timeoutMs: number;
14
+ }
15
+ export interface BaseMoved {
16
+ repositoryId: string;
17
+ targetRef: string;
18
+ from: string;
19
+ to: string;
20
+ commits: number;
21
+ kind: 'fast-forward' | 'merge';
22
+ }
23
+ /**
24
+ * Перенос базы: target ref каждого репозитория сводится с рабочей линией
25
+ * (HEAD checkout или переданный точный SHA). Цель — предок рабочей линии:
26
+ * fast-forward; обе линии несут работу — слияние без hooks в отдельном
27
+ * worktree. Прежнее значение ref проверяется CAS. `only` — переносить только
28
+ * эти репозитории и ровно на эти SHA: операция, проверившая одну редакцию,
29
+ * не переносит ту, что появилась позже. Очередь при этом должна стоять —
30
+ * это решает операция, а не перенос.
31
+ */
32
+ export declare function moveBase(input: {
33
+ policy: ExecutionPolicy;
34
+ baseDir: string;
35
+ stateDir: string;
36
+ only?: Record<string, string>;
37
+ move?: RefMove;
38
+ }): Promise<{
39
+ workingBehind?: {
40
+ repositoryId: string;
41
+ commits: number;
42
+ }[] | undefined;
43
+ updated: BaseMoved[];
44
+ }>;
@@ -0,0 +1,90 @@
1
+ import { execFileSync } from 'node:child_process';
2
+ import { isAbsolute, join, resolve } from 'node:path';
3
+ import { git } from "../environment/process.js";
4
+ /**
5
+ * Сдвиг ref внутри транзакции проверки владения операцией: проверка и
6
+ * `git update-ref` неразделимы — отмена или перехват операции либо видны
7
+ * проверке, либо происходят уже после сдвига. Внутри транзакции не
8
+ * исполняется чужой код: hook `reference-transaction`, ждущий ту же базу,
9
+ * ждал бы её, а она — его; срок Git ограничен, зависший Git не держит базу.
10
+ */
11
+ export function fencedRefMove(store, fence, timeoutMs = fencedRefMove.timeoutMs) {
12
+ return (repoPath, target, to, from) => store.transact('base.moved', (tx) => {
13
+ fence(tx);
14
+ execFileSync('git', ['-c', 'core.hooksPath=/dev/null', 'update-ref', target, to, from], {
15
+ cwd: repoPath,
16
+ timeout: timeoutMs,
17
+ stdio: 'pipe',
18
+ });
19
+ });
20
+ }
21
+ /** Предел git update-ref внутри транзакции; конечный и короче lease. */
22
+ fencedRefMove.timeoutMs = 15000;
23
+ /**
24
+ * Перенос базы: target ref каждого репозитория сводится с рабочей линией
25
+ * (HEAD checkout или переданный точный SHA). Цель — предок рабочей линии:
26
+ * fast-forward; обе линии несут работу — слияние без hooks в отдельном
27
+ * worktree. Прежнее значение ref проверяется CAS. `only` — переносить только
28
+ * эти репозитории и ровно на эти SHA: операция, проверившая одну редакцию,
29
+ * не переносит ту, что появилась позже. Очередь при этом должна стоять —
30
+ * это решает операция, а не перенос.
31
+ */
32
+ export async function moveBase(input) {
33
+ const move = input.move ??
34
+ ((repoPath, target, to, from) => git(repoPath, 'update-ref', target, to, from).then(() => undefined));
35
+ const path = (p) => (isAbsolute(p) ? p : resolve(input.baseDir, p));
36
+ const updated = [];
37
+ for (const repo of input.policy.repositories.filter((r) => !input.only || r.id in input.only)) {
38
+ const repoPath = path(repo.path);
39
+ const head = input.only?.[repo.id] ?? (await git(repoPath, 'rev-parse', 'HEAD'));
40
+ const tip = await git(repoPath, 'rev-parse', repo.targetRef);
41
+ if (head === tip)
42
+ continue;
43
+ const behind = Number(await git(repoPath, 'rev-list', '--count', `${repo.targetRef}..${head}`));
44
+ if (!behind)
45
+ continue;
46
+ const ahead = Number(await git(repoPath, 'rev-list', '--count', `${head}..${repo.targetRef}`));
47
+ if (!ahead) {
48
+ // Цель — предок рабочей линии: история не теряется и не переписывается.
49
+ await move(repoPath, repo.targetRef, head, tip);
50
+ updated.push({
51
+ repositoryId: repo.id,
52
+ targetRef: repo.targetRef,
53
+ from: tip,
54
+ to: head,
55
+ commits: behind,
56
+ kind: 'fast-forward',
57
+ });
58
+ continue;
59
+ }
60
+ // Обе линии несут работу: сводятся слиянием в отдельном worktree — target
61
+ // ref checkout-веткой быть не может и продвигается только через CAS.
62
+ const cwd = join(input.stateDir, 'worktrees', `base-${repo.id}-${Date.now()}`);
63
+ await git(repoPath, 'worktree', 'add', '--detach', cwd, tip);
64
+ try {
65
+ await git(cwd, '-c', 'core.hooksPath=/dev/null', 'merge', '--no-ff', '--no-edit', '--no-gpg-sign', head);
66
+ const merged = await git(cwd, 'rev-parse', 'HEAD');
67
+ await move(repoPath, repo.targetRef, merged, tip);
68
+ updated.push({
69
+ repositoryId: repo.id,
70
+ targetRef: repo.targetRef,
71
+ from: tip,
72
+ to: merged,
73
+ commits: behind,
74
+ kind: 'merge',
75
+ });
76
+ }
77
+ finally {
78
+ await git(repoPath, 'worktree', 'remove', '--force', cwd).catch(() => undefined);
79
+ }
80
+ }
81
+ // Обратное направление: принятая работа, которой нет в рабочей линии.
82
+ // Слияния не считаются: сам перенос оставляет коммит слияния без новой работы.
83
+ const workingBehind = [];
84
+ for (const repo of input.policy.repositories) {
85
+ const commits = Number(await git(path(repo.path), 'rev-list', '--no-merges', '--count', `HEAD..${repo.targetRef}`));
86
+ if (commits)
87
+ workingBehind.push({ repositoryId: repo.id, commits });
88
+ }
89
+ return { updated, ...(workingBehind.length ? { workingBehind } : {}) };
90
+ }
@@ -0,0 +1,50 @@
1
+ import { type ExecutionPolicy, type RuntimeAdapter, type RuntimeName } from '../contracts/index.ts';
2
+ import type { ToolchainReport } from '../toolchains/inspect.ts';
3
+ /** Диагностика toolchain политики (см. `inspectPolicy` в `./toolchains`). */
4
+ export type ToolchainInspect = (policy: ExecutionPolicy, baseDir: string) => Promise<ToolchainReport[]>;
5
+ /**
6
+ * Проверка среды без live-вызова модели: версия пакета и Node, наличие
7
+ * runtime (по `--version`) и их возможности, OS-sandbox, совместимость
8
+ * каталога состояния. Каталог состояния открывается только если он есть;
9
+ * для совместимого состояния называются версии движка, встреченные в нём
10
+ * (роль controller и попытки), — перед обновлением и откатом видно, какая
11
+ * версия последней писала состояние.
12
+ */
13
+ export declare function doctor(policy: ExecutionPolicy | undefined, adapters: Partial<Record<RuntimeName, RuntimeAdapter>>, stateDir?: string,
14
+ /**
15
+ * Диагностика toolchain репозиториев (инструменты и версии, lockfiles,
16
+ * scripts/tasks, cwd, отчёты) — только чтением и ограниченными пробами
17
+ * версий. Передаётся явно (CLI — `inspectPolicy` из `./toolchains`): корень
18
+ * пакета не тянет модуль toolchains сам.
19
+ */
20
+ toolchain?: {
21
+ baseDir: string;
22
+ inspect: ToolchainInspect;
23
+ }): Promise<{
24
+ problems: string[];
25
+ toolchains?: ToolchainReport[] | undefined;
26
+ state?: {
27
+ stateVersion: number;
28
+ compatible: boolean;
29
+ engineVersions?: {
30
+ controller?: string;
31
+ attempts: string[];
32
+ };
33
+ } | undefined;
34
+ harness: string;
35
+ apiVersion: "devcontour-harness/v1alpha1";
36
+ node: string;
37
+ platform: string;
38
+ runtimes: {
39
+ reason?: string | undefined;
40
+ structuredOutput: boolean;
41
+ cancellation: boolean;
42
+ version?: string | undefined;
43
+ runtime: "codex" | "claude";
44
+ available: boolean;
45
+ }[];
46
+ sandbox: {
47
+ reason?: string | undefined;
48
+ os: boolean;
49
+ };
50
+ }>;
@@ -0,0 +1,113 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { API_VERSION, HarnessError, STATE_VERSION, } from "../contracts/index.js";
4
+ import { isolationSupport } from "../environment/isolation.js";
5
+ import { HARNESS_VERSION } from "../package.js";
6
+ import { SqliteExecutionStore } from "../storage/sqlite.js";
7
+ /** Runtime, которые политика действительно назначает writer или reviewer. */
8
+ function usedRuntimes(policy) {
9
+ const used = new Set();
10
+ const rule = (r) => {
11
+ if (!r)
12
+ return;
13
+ used.add(r.reviewer.runtime);
14
+ for (const a of r.alternates ?? [])
15
+ used.add(a.runtime);
16
+ };
17
+ for (const k of ['code', 'plan', 'contract'])
18
+ rule(policy.review[k]);
19
+ const roles = [
20
+ ...Object.values(policy.roles),
21
+ ...policy.repositories.flatMap((r) => Object.values(r.roles ?? {})),
22
+ ];
23
+ for (const b of roles) {
24
+ used.add(b.writer.runtime);
25
+ rule(b.review);
26
+ }
27
+ for (const r of policy.repositories)
28
+ rule(r.review);
29
+ return used;
30
+ }
31
+ /**
32
+ * Проверка среды без live-вызова модели: версия пакета и Node, наличие
33
+ * runtime (по `--version`) и их возможности, OS-sandbox, совместимость
34
+ * каталога состояния. Каталог состояния открывается только если он есть;
35
+ * для совместимого состояния называются версии движка, встреченные в нём
36
+ * (роль controller и попытки), — перед обновлением и откатом видно, какая
37
+ * версия последней писала состояние.
38
+ */
39
+ export async function doctor(policy, adapters, stateDir,
40
+ /**
41
+ * Диагностика toolchain репозиториев (инструменты и версии, lockfiles,
42
+ * scripts/tasks, cwd, отчёты) — только чтением и ограниченными пробами
43
+ * версий. Передаётся явно (CLI — `inspectPolicy` из `./toolchains`): корень
44
+ * пакета не тянет модуль toolchains сам.
45
+ */
46
+ toolchain) {
47
+ const problems = [];
48
+ const runtimes = [];
49
+ // С политикой обязательны только назначенные ею runtime: сценарий одного
50
+ // вендора или fixture не требует второго CLI.
51
+ const required = policy ? usedRuntimes(policy) : new Set(['codex', 'claude']);
52
+ for (const runtime of ['codex', 'claude']) {
53
+ const caps = required.has(runtime)
54
+ ? await adapters[runtime]?.capabilities().catch(() => undefined)
55
+ : undefined;
56
+ if (!caps?.version && required.has(runtime))
57
+ problems.push(`${runtime}: не найден или не отвечает на --version`);
58
+ runtimes.push({
59
+ runtime,
60
+ available: Boolean(caps?.version),
61
+ ...(caps?.version ? { version: caps.version } : {}),
62
+ structuredOutput: caps?.structuredOutput ?? false,
63
+ cancellation: caps?.cancellation ?? false,
64
+ ...(required.has(runtime) ? {} : { reason: 'не назначен политикой' }),
65
+ });
66
+ }
67
+ const sandbox = isolationSupport();
68
+ if (!sandbox.ok && policy?.isolation.mode === 'os')
69
+ problems.push(sandbox.detail);
70
+ let state;
71
+ if (stateDir && existsSync(join(stateDir, 'state.sqlite'))) {
72
+ try {
73
+ const store = new SqliteExecutionStore(join(stateDir, 'state.sqlite'), 'devcontour-harness@' + HARNESS_VERSION,
74
+ // Диагностика не становится писателем: автор, PRAGMA и данные прежние.
75
+ { readOnly: true });
76
+ let engineVersions;
77
+ try {
78
+ engineVersions = store.read((view) => {
79
+ const controller = view.controller()?.engineVersion;
80
+ return {
81
+ ...(controller ? { controller } : {}),
82
+ attempts: [
83
+ ...new Set(view.attempts().flatMap((a) => (a.engineVersion ? [a.engineVersion] : []))),
84
+ ].sort(),
85
+ };
86
+ });
87
+ }
88
+ finally {
89
+ store.close();
90
+ }
91
+ state = { stateVersion: STATE_VERSION, compatible: true, engineVersions };
92
+ }
93
+ catch (error) {
94
+ state = { stateVersion: -1, compatible: false };
95
+ problems.push(error instanceof HarnessError ? error.message : String(error));
96
+ }
97
+ }
98
+ const toolchains = policy && toolchain ? await toolchain.inspect(policy, toolchain.baseDir) : undefined;
99
+ for (const r of toolchains ?? [])
100
+ for (const p of r.problems)
101
+ problems.push(`toolchain ${r.repositoryId}: ${p.code} ${p.field.join('.')}${p.path ? ` (${p.path})` : ''}: ${p.message}`);
102
+ return {
103
+ harness: HARNESS_VERSION,
104
+ apiVersion: API_VERSION,
105
+ node: process.version,
106
+ platform: `${process.platform}-${process.arch}`,
107
+ runtimes,
108
+ sandbox: { os: sandbox.ok, ...(sandbox.ok ? {} : { reason: sandbox.detail }) },
109
+ ...(state ? { state } : {}),
110
+ ...(toolchains ? { toolchains } : {}),
111
+ problems,
112
+ };
113
+ }