@b00dea/cockpit 0.0.0-stage → 0.1.1

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 (426) hide show
  1. package/CHANGELOG.md +474 -0
  2. package/LICENSE +21 -0
  3. package/README.md +25 -2
  4. package/bin/cockpit.mjs +33 -0
  5. package/cockpit.schema.json +2644 -0
  6. package/dist/autonomy.d.ts +45 -0
  7. package/dist/autonomy.js +173 -0
  8. package/dist/bounded-read.d.ts +14 -0
  9. package/dist/bounded-read.js +42 -0
  10. package/dist/browser/commands.d.ts +117 -0
  11. package/dist/browser/commands.js +453 -0
  12. package/dist/browser/package-paths.d.ts +1 -0
  13. package/dist/browser/package-paths.js +6 -0
  14. package/dist/browser/proxy.d.ts +22 -0
  15. package/dist/browser/proxy.js +37 -0
  16. package/dist/browser/runtime.d.ts +118 -0
  17. package/dist/browser/runtime.js +186 -0
  18. package/dist/browser/surfaces.d.ts +141 -0
  19. package/dist/browser/surfaces.js +561 -0
  20. package/dist/browser/ui-gate.d.ts +15 -0
  21. package/dist/browser/ui-gate.js +18 -0
  22. package/dist/canonical-json.d.ts +2 -0
  23. package/dist/canonical-json.js +5 -0
  24. package/dist/cli-guard.d.ts +5 -0
  25. package/dist/cli-guard.js +71 -0
  26. package/dist/cli-main.d.ts +176 -0
  27. package/dist/cli-main.js +1584 -0
  28. package/dist/cli.d.ts +3 -0
  29. package/dist/cli.js +16 -0
  30. package/dist/command-table.d.ts +121 -0
  31. package/dist/command-table.js +664 -0
  32. package/dist/config/load.d.ts +183 -0
  33. package/dist/config/load.js +367 -0
  34. package/dist/config/sibling-toml.d.ts +48 -0
  35. package/dist/config/sibling-toml.js +160 -0
  36. package/dist/config/surface-tools-toml.d.ts +9 -0
  37. package/dist/config/surface-tools-toml.js +37 -0
  38. package/dist/config/unconfirmed-toml.d.ts +9 -0
  39. package/dist/config/unconfirmed-toml.js +29 -0
  40. package/dist/config/verified-write.d.ts +43 -0
  41. package/dist/config/verified-write.js +89 -0
  42. package/dist/diagnostics.d.ts +26 -0
  43. package/dist/diagnostics.js +344 -0
  44. package/dist/docs/check.d.ts +16 -0
  45. package/dist/docs/check.js +197 -0
  46. package/dist/docs/decision-record.d.ts +34 -0
  47. package/dist/docs/decision-record.js +135 -0
  48. package/dist/docs/decisions.d.ts +16 -0
  49. package/dist/docs/decisions.js +103 -0
  50. package/dist/docs/markdown.d.ts +36 -0
  51. package/dist/docs/markdown.js +88 -0
  52. package/dist/docs/references.d.ts +32 -0
  53. package/dist/docs/references.js +148 -0
  54. package/dist/emit.d.ts +22 -0
  55. package/dist/emit.js +43 -0
  56. package/dist/entrypoint-kinds.d.ts +3 -0
  57. package/dist/entrypoint-kinds.js +11 -0
  58. package/dist/environment/application/confirm.d.ts +35 -0
  59. package/dist/environment/application/confirm.js +122 -0
  60. package/dist/environment/application/destructive.d.ts +6 -0
  61. package/dist/environment/application/destructive.js +429 -0
  62. package/dist/environment/application/doctor.d.ts +94 -0
  63. package/dist/environment/application/doctor.js +247 -0
  64. package/dist/environment/application/guard.d.ts +49 -0
  65. package/dist/environment/application/guard.js +677 -0
  66. package/dist/environment/application/health.d.ts +143 -0
  67. package/dist/environment/application/health.js +385 -0
  68. package/dist/environment/application/lifecycle-context.d.ts +235 -0
  69. package/dist/environment/application/lifecycle-context.js +154 -0
  70. package/dist/environment/application/lifecycle-state.d.ts +25 -0
  71. package/dist/environment/application/lifecycle-state.js +202 -0
  72. package/dist/environment/application/lifecycle.d.ts +18 -0
  73. package/dist/environment/application/lifecycle.js +737 -0
  74. package/dist/environment/application/logs.d.ts +45 -0
  75. package/dist/environment/application/logs.js +127 -0
  76. package/dist/environment/application/services.d.ts +60 -0
  77. package/dist/environment/application/services.js +89 -0
  78. package/dist/environment/application/setup.d.ts +129 -0
  79. package/dist/environment/application/setup.js +741 -0
  80. package/dist/environment/application/shared.d.ts +11 -0
  81. package/dist/environment/application/shared.js +61 -0
  82. package/dist/environment/application/siblings.d.ts +43 -0
  83. package/dist/environment/application/siblings.js +269 -0
  84. package/dist/environment/claims.d.ts +389 -0
  85. package/dist/environment/claims.js +1701 -0
  86. package/dist/environment/codex-hook-trust.d.ts +11 -0
  87. package/dist/environment/codex-hook-trust.js +185 -0
  88. package/dist/environment/compose-environment.d.ts +1 -0
  89. package/dist/environment/compose-environment.js +6 -0
  90. package/dist/environment/compose-ownership.d.ts +21 -0
  91. package/dist/environment/compose-ownership.js +117 -0
  92. package/dist/environment/compose-topology.d.ts +125 -0
  93. package/dist/environment/compose-topology.js +490 -0
  94. package/dist/environment/contract.d.ts +522 -0
  95. package/dist/environment/contract.js +794 -0
  96. package/dist/environment/declaration.d.ts +14 -0
  97. package/dist/environment/declaration.js +39 -0
  98. package/dist/environment/destructive-composition.d.ts +29 -0
  99. package/dist/environment/destructive-composition.js +333 -0
  100. package/dist/environment/docker-daemon.d.ts +30 -0
  101. package/dist/environment/docker-daemon.js +78 -0
  102. package/dist/environment/docker-endpoint.d.ts +10 -0
  103. package/dist/environment/docker-endpoint.js +19 -0
  104. package/dist/environment/env-schema-observation.d.ts +46 -0
  105. package/dist/environment/env-schema-observation.js +241 -0
  106. package/dist/environment/evidence.d.ts +136 -0
  107. package/dist/environment/evidence.js +437 -0
  108. package/dist/environment/executable.d.ts +38 -0
  109. package/dist/environment/executable.js +190 -0
  110. package/dist/environment/generated-application/authorities.d.ts +5 -0
  111. package/dist/environment/generated-application/authorities.js +61 -0
  112. package/dist/environment/generated-application/bootstrap-skill.d.ts +16 -0
  113. package/dist/environment/generated-application/bootstrap-skill.js +328 -0
  114. package/dist/environment/generated-application/check.d.ts +13 -0
  115. package/dist/environment/generated-application/check.js +191 -0
  116. package/dist/environment/generated-application/custom-surface.d.ts +26 -0
  117. package/dist/environment/generated-application/custom-surface.js +289 -0
  118. package/dist/environment/generated-application/prepare.d.ts +29 -0
  119. package/dist/environment/generated-application/prepare.js +278 -0
  120. package/dist/environment/generated-application/publication.d.ts +33 -0
  121. package/dist/environment/generated-application/publication.js +547 -0
  122. package/dist/environment/generated-application/shared.d.ts +492 -0
  123. package/dist/environment/generated-application/shared.js +354 -0
  124. package/dist/environment/generated-application/surface-observation.d.ts +4 -0
  125. package/dist/environment/generated-application/surface-observation.js +69 -0
  126. package/dist/environment/generated-application/surface-outputs.d.ts +71 -0
  127. package/dist/environment/generated-application/surface-outputs.js +400 -0
  128. package/dist/environment/generated-application/surfaces.d.ts +3 -0
  129. package/dist/environment/generated-application/surfaces.js +704 -0
  130. package/dist/environment/generated-application/sync.d.ts +6 -0
  131. package/dist/environment/generated-application/sync.js +124 -0
  132. package/dist/environment/git-observation.d.ts +202 -0
  133. package/dist/environment/git-observation.js +553 -0
  134. package/dist/environment/health-composition.d.ts +12 -0
  135. package/dist/environment/health-composition.js +645 -0
  136. package/dist/environment/host-observation.d.ts +56 -0
  137. package/dist/environment/host-observation.js +52 -0
  138. package/dist/environment/identity.d.ts +48 -0
  139. package/dist/environment/identity.js +85 -0
  140. package/dist/environment/init-application.d.ts +333 -0
  141. package/dist/environment/init-application.js +726 -0
  142. package/dist/environment/install-policy-observation.d.ts +71 -0
  143. package/dist/environment/install-policy-observation.js +284 -0
  144. package/dist/environment/install-policy.d.ts +48 -0
  145. package/dist/environment/install-policy.js +96 -0
  146. package/dist/environment/instance.d.ts +33 -0
  147. package/dist/environment/instance.js +122 -0
  148. package/dist/environment/instruction-pointer.d.ts +19 -0
  149. package/dist/environment/instruction-pointer.js +144 -0
  150. package/dist/environment/lifecycle-composition.d.ts +31 -0
  151. package/dist/environment/lifecycle-composition.js +742 -0
  152. package/dist/environment/lifecycle-stage.d.ts +120 -0
  153. package/dist/environment/lifecycle-stage.js +408 -0
  154. package/dist/environment/lifecycle.d.ts +75 -0
  155. package/dist/environment/lifecycle.js +354 -0
  156. package/dist/environment/logs.d.ts +132 -0
  157. package/dist/environment/logs.js +519 -0
  158. package/dist/environment/map-limit.d.ts +2 -0
  159. package/dist/environment/map-limit.js +13 -0
  160. package/dist/environment/mcp-observation.d.ts +9 -0
  161. package/dist/environment/mcp-observation.js +46 -0
  162. package/dist/environment/observations.d.ts +46 -0
  163. package/dist/environment/observations.js +346 -0
  164. package/dist/environment/operator-application.d.ts +79 -0
  165. package/dist/environment/operator-application.js +197 -0
  166. package/dist/environment/ports.d.ts +182 -0
  167. package/dist/environment/ports.js +1205 -0
  168. package/dist/environment/posture-observation.d.ts +68 -0
  169. package/dist/environment/posture-observation.js +253 -0
  170. package/dist/environment/presence.d.ts +4 -0
  171. package/dist/environment/presence.js +26 -0
  172. package/dist/environment/read-composition.d.ts +32 -0
  173. package/dist/environment/read-composition.js +397 -0
  174. package/dist/environment/readiness-composition.d.ts +57 -0
  175. package/dist/environment/readiness-composition.js +151 -0
  176. package/dist/environment/reclaim.d.ts +370 -0
  177. package/dist/environment/reclaim.js +1940 -0
  178. package/dist/environment/repository-identity.d.ts +13 -0
  179. package/dist/environment/repository-identity.js +108 -0
  180. package/dist/environment/runtime-measurement-record.d.ts +38 -0
  181. package/dist/environment/runtime-measurement-record.js +111 -0
  182. package/dist/environment/runtime-measurement.d.ts +27 -0
  183. package/dist/environment/runtime-measurement.js +224 -0
  184. package/dist/environment/runtime-probe-residue.d.ts +43 -0
  185. package/dist/environment/runtime-probe-residue.js +148 -0
  186. package/dist/environment/runtime-probe.d.ts +72 -0
  187. package/dist/environment/runtime-probe.js +725 -0
  188. package/dist/environment/runtime.d.ts +551 -0
  189. package/dist/environment/runtime.js +2405 -0
  190. package/dist/environment/seed.d.ts +16 -0
  191. package/dist/environment/seed.js +43 -0
  192. package/dist/environment/selftest-application.d.ts +86 -0
  193. package/dist/environment/selftest-application.js +713 -0
  194. package/dist/environment/sensitive.d.ts +3 -0
  195. package/dist/environment/sensitive.js +48 -0
  196. package/dist/environment/served-checkout-grants.d.ts +53 -0
  197. package/dist/environment/served-checkout-grants.js +182 -0
  198. package/dist/environment/setup-observation.d.ts +62 -0
  199. package/dist/environment/setup-observation.js +1473 -0
  200. package/dist/environment/setup.d.ts +43 -0
  201. package/dist/environment/setup.js +176 -0
  202. package/dist/environment/sibling-checkouts.d.ts +119 -0
  203. package/dist/environment/sibling-checkouts.js +254 -0
  204. package/dist/environment/sibling-instructions.d.ts +33 -0
  205. package/dist/environment/sibling-instructions.js +224 -0
  206. package/dist/environment/sibling-observation.d.ts +27 -0
  207. package/dist/environment/sibling-observation.js +45 -0
  208. package/dist/environment/sibling-pull.d.ts +23 -0
  209. package/dist/environment/sibling-pull.js +95 -0
  210. package/dist/environment/sibling-registration.d.ts +45 -0
  211. package/dist/environment/sibling-registration.js +538 -0
  212. package/dist/environment/stack-inputs.d.ts +18 -0
  213. package/dist/environment/stack-inputs.js +210 -0
  214. package/dist/environment/stack-inventory-prune.d.ts +31 -0
  215. package/dist/environment/stack-inventory-prune.js +129 -0
  216. package/dist/environment/stack-inventory-report.d.ts +44 -0
  217. package/dist/environment/stack-inventory-report.js +78 -0
  218. package/dist/environment/stack-inventory.d.ts +94 -0
  219. package/dist/environment/stack-inventory.js +418 -0
  220. package/dist/environment/state.d.ts +410 -0
  221. package/dist/environment/state.js +2153 -0
  222. package/dist/environment/templates.d.ts +140 -0
  223. package/dist/environment/templates.js +685 -0
  224. package/dist/environment/tool-registration.d.ts +9 -0
  225. package/dist/environment/tool-registration.js +92 -0
  226. package/dist/environment/upgrade-application.d.ts +19 -0
  227. package/dist/environment/upgrade-application.js +175 -0
  228. package/dist/environment-exec/execute.d.ts +105 -0
  229. package/dist/environment-exec/execute.js +1179 -0
  230. package/dist/environment-exec/libexec-program.d.ts +6 -0
  231. package/dist/environment-exec/libexec-program.js +22 -0
  232. package/dist/environment-exec/verified-digest.d.ts +4 -0
  233. package/dist/environment-exec/verified-digest.js +16 -0
  234. package/dist/exact-keys.d.ts +7 -0
  235. package/dist/exact-keys.js +15 -0
  236. package/dist/fix-catalogue.d.ts +32 -0
  237. package/dist/fix-catalogue.js +60 -0
  238. package/dist/freshness.d.ts +29 -0
  239. package/dist/freshness.js +75 -0
  240. package/dist/generated-environment/adopt.d.ts +16 -0
  241. package/dist/generated-environment/adopt.js +271 -0
  242. package/dist/generated-environment/command.d.ts +47 -0
  243. package/dist/generated-environment/command.js +75 -0
  244. package/dist/generated-environment/transaction.d.ts +186 -0
  245. package/dist/generated-environment/transaction.js +1770 -0
  246. package/dist/grading/evaluate.d.ts +71 -0
  247. package/dist/grading/evaluate.js +403 -0
  248. package/dist/grading/grade.d.ts +33 -0
  249. package/dist/grading/grade.js +57 -0
  250. package/dist/guards/answer-confirmation.d.ts +41 -0
  251. package/dist/guards/answer-confirmation.js +109 -0
  252. package/dist/guards/evaluator.d.ts +130 -0
  253. package/dist/guards/evaluator.js +1258 -0
  254. package/dist/guards/harness-adapter.d.ts +17 -0
  255. package/dist/guards/harness-adapter.js +57 -0
  256. package/dist/guards/host.d.ts +8 -0
  257. package/dist/guards/host.js +10 -0
  258. package/dist/guards/invocation-parser.d.ts +66 -0
  259. package/dist/guards/invocation-parser.js +595 -0
  260. package/dist/guards/local-identity.d.ts +44 -0
  261. package/dist/guards/local-identity.js +129 -0
  262. package/dist/guards/policy-change.d.ts +43 -0
  263. package/dist/guards/policy-change.js +159 -0
  264. package/dist/guards/policy-pin.d.ts +13 -0
  265. package/dist/guards/policy-pin.js +177 -0
  266. package/dist/guards/protected-locations.d.ts +62 -0
  267. package/dist/guards/protected-locations.js +461 -0
  268. package/dist/guards/read-only.d.ts +37 -0
  269. package/dist/guards/read-only.js +315 -0
  270. package/dist/init/command.d.ts +41 -0
  271. package/dist/init/command.js +330 -0
  272. package/dist/init/derive.d.ts +53 -0
  273. package/dist/init/derive.js +819 -0
  274. package/dist/init/draft.d.ts +140 -0
  275. package/dist/init/draft.js +1228 -0
  276. package/dist/init/render.d.ts +47 -0
  277. package/dist/init/render.js +104 -0
  278. package/dist/init/scaffold.d.ts +53 -0
  279. package/dist/init/scaffold.js +121 -0
  280. package/dist/invocation.d.ts +13 -0
  281. package/dist/invocation.js +20 -0
  282. package/dist/package-metadata.d.ts +9 -0
  283. package/dist/package-metadata.js +19 -0
  284. package/dist/readiness.d.ts +53 -0
  285. package/dist/readiness.js +88 -0
  286. package/dist/references/named.d.ts +24 -0
  287. package/dist/references/named.js +27 -0
  288. package/dist/refusal.d.ts +15 -0
  289. package/dist/refusal.js +7 -0
  290. package/dist/registry-derivation.d.ts +31 -0
  291. package/dist/registry-derivation.js +32 -0
  292. package/dist/registry-resolve/command.d.ts +47 -0
  293. package/dist/registry-resolve/command.js +226 -0
  294. package/dist/registry-resolve/registry.d.ts +68 -0
  295. package/dist/registry-resolve/registry.js +292 -0
  296. package/dist/registry-resolve/render-shell.d.ts +18 -0
  297. package/dist/registry-resolve/render-shell.js +36 -0
  298. package/dist/repair.d.ts +29 -0
  299. package/dist/repair.js +8 -0
  300. package/dist/repository-path.d.ts +9 -0
  301. package/dist/repository-path.js +16 -0
  302. package/dist/schema-json.d.ts +1 -0
  303. package/dist/schema-json.js +177 -0
  304. package/dist/schema.d.ts +1289 -0
  305. package/dist/schema.js +985 -0
  306. package/dist/setup/command.d.ts +4 -0
  307. package/dist/setup/command.js +4 -0
  308. package/dist/sha256.d.ts +1 -0
  309. package/dist/sha256.js +4 -0
  310. package/dist/surfaces/adoption.d.ts +41 -0
  311. package/dist/surfaces/adoption.js +217 -0
  312. package/dist/surfaces/consumer-instructions.d.ts +20 -0
  313. package/dist/surfaces/consumer-instructions.js +43 -0
  314. package/dist/surfaces/contract.d.ts +42 -0
  315. package/dist/surfaces/contract.js +169 -0
  316. package/dist/surfaces/core-declarations.d.ts +24 -0
  317. package/dist/surfaces/core-declarations.js +79 -0
  318. package/dist/surfaces/development-markers.d.ts +1 -0
  319. package/dist/surfaces/development-markers.js +18 -0
  320. package/dist/surfaces/generator-adapter.d.ts +71 -0
  321. package/dist/surfaces/generator-adapter.js +809 -0
  322. package/dist/surfaces/harness-registry.d.ts +260 -0
  323. package/dist/surfaces/harness-registry.js +146 -0
  324. package/dist/surfaces/mcp.d.ts +145 -0
  325. package/dist/surfaces/mcp.js +349 -0
  326. package/dist/surfaces/merge.d.ts +30 -0
  327. package/dist/surfaces/merge.js +105 -0
  328. package/dist/surfaces/ownership.d.ts +190 -0
  329. package/dist/surfaces/ownership.js +795 -0
  330. package/dist/surfaces/projection.d.ts +136 -0
  331. package/dist/surfaces/projection.js +716 -0
  332. package/dist/surfaces/render.d.ts +79 -0
  333. package/dist/surfaces/render.js +636 -0
  334. package/dist/surfaces/restrictions.d.ts +130 -0
  335. package/dist/surfaces/restrictions.js +447 -0
  336. package/dist/surfaces/selftest.d.ts +162 -0
  337. package/dist/surfaces/selftest.js +393 -0
  338. package/dist/surfaces/skill-contract.d.ts +85 -0
  339. package/dist/surfaces/skill-contract.js +449 -0
  340. package/dist/surfaces/sync.d.ts +57 -0
  341. package/dist/surfaces/sync.js +164 -0
  342. package/dist/team-metadata.d.ts +41 -0
  343. package/dist/team-metadata.js +513 -0
  344. package/dist/testing/gates.d.ts +91 -0
  345. package/dist/testing/gates.js +182 -0
  346. package/dist/testing/hooks.d.ts +59 -0
  347. package/dist/testing/hooks.js +139 -0
  348. package/dist/testing/test-protection.d.ts +34 -0
  349. package/dist/testing/test-protection.js +137 -0
  350. package/dist/testing/verdict.d.ts +29 -0
  351. package/dist/testing/verdict.js +62 -0
  352. package/dist/testing/weakening.d.ts +15 -0
  353. package/dist/testing/weakening.js +30 -0
  354. package/dist/work-orders/application-closure.d.ts +7 -0
  355. package/dist/work-orders/application-closure.js +76 -0
  356. package/dist/work-orders/application-context.d.ts +162 -0
  357. package/dist/work-orders/application-context.js +137 -0
  358. package/dist/work-orders/application-decisions.d.ts +27 -0
  359. package/dist/work-orders/application-decisions.js +121 -0
  360. package/dist/work-orders/application-grading.d.ts +11 -0
  361. package/dist/work-orders/application-grading.js +212 -0
  362. package/dist/work-orders/application.d.ts +6 -0
  363. package/dist/work-orders/application.js +223 -0
  364. package/dist/work-orders/composition.d.ts +101 -0
  365. package/dist/work-orders/composition.js +1202 -0
  366. package/dist/work-orders/contract.d.ts +77 -0
  367. package/dist/work-orders/contract.js +140 -0
  368. package/dist/work-orders/decisions.d.ts +23 -0
  369. package/dist/work-orders/decisions.js +55 -0
  370. package/dist/work-orders/store.d.ts +207 -0
  371. package/dist/work-orders/store.js +291 -0
  372. package/docs/README.md +68 -0
  373. package/docs/architecture.md +136 -0
  374. package/docs/capability-matrix.md +58 -0
  375. package/docs/cli-reference.md +807 -0
  376. package/docs/command-list.md +62 -0
  377. package/docs/compose-patterns.md +99 -0
  378. package/docs/concepts.md +173 -0
  379. package/docs/configuration-essentials.md +177 -0
  380. package/docs/configuration-reference.md +636 -0
  381. package/docs/docs-check.md +76 -0
  382. package/docs/environment.md +948 -0
  383. package/docs/extending.md +43 -0
  384. package/docs/faq.md +113 -0
  385. package/docs/getting-started.md +692 -0
  386. package/docs/harness-surfaces.md +500 -0
  387. package/docs/joining-a-team.md +76 -0
  388. package/docs/release-policy.md +49 -0
  389. package/docs/safety-and-autonomy.md +463 -0
  390. package/docs/troubleshooting.md +382 -0
  391. package/docs/tutorial.md +171 -0
  392. package/docs/work-and-browser.md +420 -0
  393. package/libexec/browser-operation.cjs +270 -0
  394. package/libexec/fence-marker.cjs +101 -0
  395. package/libexec/fence-marker.d.cts +24 -0
  396. package/libexec/fenced-group-runner.cjs +77 -0
  397. package/libexec/fenced-supervisor.cjs +137 -0
  398. package/libexec/generated-filesystem.cjs +731 -0
  399. package/libexec/install-filesystem.cjs +225 -0
  400. package/libexec/rulesync-generator.NOTICE.md +1112 -0
  401. package/libexec/rulesync-generator.cjs +743 -0
  402. package/libexec/surface-generator-worker.cjs +266 -0
  403. package/libexec/surface-output-reader.cjs +99 -0
  404. package/libexec/surface-scratch-cleanup.cjs +14 -0
  405. package/libexec/validate-harness-evidence.d.mts +183 -0
  406. package/libexec/validate-harness-evidence.mjs +1312 -0
  407. package/package.json +74 -4
  408. package/surfaces/README.md +33 -0
  409. package/surfaces/agents/evaluator.agent.md +76 -0
  410. package/surfaces/guards/bash-guard.sh +48 -0
  411. package/surfaces/guards/claude.adapter.json +25 -0
  412. package/surfaces/guards/codex.adapter.json +24 -0
  413. package/surfaces/guards/opencode.adapter.json +25 -0
  414. package/surfaces/references/browser.md +63 -0
  415. package/surfaces/references/test-data-and-personas.md +84 -0
  416. package/surfaces/references/testing-tiers.md +11 -0
  417. package/surfaces/skills/cockpit-browser.skill.md +59 -0
  418. package/surfaces/skills/cockpit-explain.skill.md +32 -0
  419. package/surfaces/skills/cockpit-fix.skill.md +37 -0
  420. package/surfaces/skills/cockpit-setup.skill.md +39 -0
  421. package/surfaces/skills/cockpit-work.skill.md +34 -0
  422. package/surfaces/skills/collection.json +5 -0
  423. package/surfaces/skills/procedures/explain.md +66 -0
  424. package/surfaces/skills/procedures/fix.md +95 -0
  425. package/surfaces/skills/procedures/setup.md +452 -0
  426. package/surfaces/skills/procedures/work.md +134 -0
@@ -0,0 +1,15 @@
1
+ import { type DiffFile, type WeakeningFinding } from './test-protection.js';
2
+ export interface TestFilePatterns {
3
+ readonly tests: readonly string[];
4
+ readonly configuration: readonly string[];
5
+ /** Where the test-file patterns came from, stated beside every finding. */
6
+ readonly source: string;
7
+ }
8
+ export declare function testFilePatterns(declared: readonly string[] | undefined): TestFilePatterns;
9
+ export declare function weakeningFindings(files: readonly DiffFile[], patterns: TestFilePatterns): WeakeningFinding[];
10
+ export interface RepositoryWeakening extends WeakeningFinding {
11
+ readonly repository: string;
12
+ }
13
+ /** The digest an acknowledgement binds: exactly these findings, and no others, are acknowledged. */
14
+ export declare function testWeakeningDigest(findings: readonly RepositoryWeakening[]): string;
15
+ export declare const describeWeakening: (row: RepositoryWeakening) => string;
@@ -0,0 +1,30 @@
1
+ // Owns which files a checkout treats as tests and how a finding is identified: the patterns a
2
+ // consumer declared under [testing] test_files or, failing that, the common layouts, plus the digest
3
+ // a person's acknowledgement binds to. The detection itself is test-protection.ts.
4
+ import { sha256Hex } from '../sha256.js';
5
+ import { scopeMatcher } from '../work-orders/contract.js';
6
+ import { classifyTestWeakening } from './test-protection.js';
7
+ const DEFAULT_TEST_FILES = Object.freeze([
8
+ '**/*.test.*', '**/*.spec.*', '**/__tests__/**', '**/tests/**', '**/test/**', '**/e2e/**',
9
+ '**/test_*.py', '**/*_test.py', '**/conftest.py', '**/*_test.go', '**/*Test.java', '**/*_spec.rb',
10
+ ]);
11
+ // What decides which tests run; a declared test_files list does not replace these.
12
+ const TEST_CONFIGURATION = Object.freeze([
13
+ '**/vitest.config.*', '**/vitest.workspace.*', '**/jest.config.*', '**/playwright.config.*', '**/.mocharc*',
14
+ '**/pytest.ini', '**/tox.ini', '**/package.json',
15
+ ]);
16
+ export function testFilePatterns(declared) {
17
+ return declared?.length
18
+ ? { tests: declared, configuration: TEST_CONFIGURATION, source: '[testing] test_files in cockpit.toml' }
19
+ : { tests: DEFAULT_TEST_FILES, configuration: TEST_CONFIGURATION, source: 'the built-in test-file patterns for common layouts (no [testing] test_files declared)' };
20
+ }
21
+ export function weakeningFindings(files, patterns) {
22
+ const roles = { isTest: scopeMatcher(patterns.tests), isTestConfig: scopeMatcher(patterns.configuration) };
23
+ return classifyTestWeakening(files, roles);
24
+ }
25
+ /** The digest an acknowledgement binds: exactly these findings, and no others, are acknowledged. */
26
+ export function testWeakeningDigest(findings) {
27
+ const rows = findings.map(row => `${row.repository}\0${row.path}\0${row.kind}`).sort();
28
+ return sha256Hex(JSON.stringify(rows));
29
+ }
30
+ export const describeWeakening = (row) => `${row.repository === '.' ? row.path : `${row.repository}:${row.path}`}: ${row.kind} (${row.reason})`;
@@ -0,0 +1,7 @@
1
+ import type { WorkRecord } from './store.js';
2
+ import { type WorkApplicationOutcome, type WorkContext, type WorkRequest } from './application-context.js';
3
+ export declare const pendingCi: (record: WorkRecord) => string[];
4
+ export declare function signoff(request: Extract<WorkRequest, {
5
+ operation: 'work-signoff';
6
+ }>, context: WorkContext): Promise<WorkApplicationOutcome>;
7
+ export declare function verify(context: WorkContext): Promise<WorkApplicationOutcome>;
@@ -0,0 +1,76 @@
1
+ // How an order closes: `work signoff` closes it as done, partial or abandoned, and `work verify`, the CI
2
+ // job, checks every closure and confirms the steps that awaited CI.
3
+ import { runningInCi } from '../autonomy.js';
4
+ import { RefusalError } from '../environment/contract.js';
5
+ import { complete, now, refusal, TERMINAL, unsatisfied } from './application-context.js';
6
+ import { decisions, recordProblem, unresolvedDecisions } from './application-decisions.js';
7
+ const CLOSURE = Object.freeze({ independent: 'graded', checks: 'checks-passed', self: 'self-reported' });
8
+ export const pendingCi = (record) => [
9
+ ...record.acknowledgements.filter(row => row.by === 'ci').map(() => 'acknowledge'),
10
+ ...record.accepted.by === 'ci' ? ['accept'] : [], ...record.amendments.filter(row => row.by === 'ci').map(() => 'amend'),
11
+ ...record.notes.filter(row => row.kind === 'answer' && row.by === 'ci').map(() => 'answer'), ...record.closure?.by === 'ci' ? ['signoff'] : [],
12
+ ];
13
+ export async function signoff(request, context) {
14
+ const { composition, operation, policy, store, current, gated } = context;
15
+ const record = await store.read(request.workOrderId);
16
+ if (!record)
17
+ return refusal(operation, `${request.workOrderId} is not accepted`);
18
+ if (TERMINAL.has(record.state))
19
+ return refusal(operation, `${request.workOrderId} is already closed (${record.state})`);
20
+ let how;
21
+ if (request.close === 'done') {
22
+ const { order } = await current(request.workOrderId);
23
+ if (record.state !== 'graded' || !record.grade?.passed || record.grade.digest !== record.accepted.digest) {
24
+ return refusal(operation, `${order.id} is ${record.state}, not graded; run npx --no cockpit work grade --config <your cockpit.toml> --work-order ${order.id}`);
25
+ }
26
+ const open = await Promise.all(order.depends_on.map(async (dependency) => (await store.read(dependency))?.state === 'done' ? undefined : dependency));
27
+ if (open.some(Boolean))
28
+ return refusal(operation, `${order.id} depends on ${open.filter(Boolean).join(', ')}, which ${open.filter(Boolean).length === 1 ? 'is' : 'are'} not done`);
29
+ how = CLOSURE[record.grade.mode];
30
+ }
31
+ else {
32
+ if (!request.note)
33
+ return refusal(operation, `closing ${request.workOrderId} as ${request.close} needs --note saying what was and was not done`);
34
+ how = request.close;
35
+ }
36
+ // Every closure, partial and abandoned too, settles the order's decisions: work that stops still made them.
37
+ const open = await unresolvedDecisions(record, composition);
38
+ if (open.length)
39
+ return refusal(operation, `${request.workOrderId} cannot close while ${open.join('; ')}`);
40
+ // A grade found the agent's own amendments lowered the bar; a re-grade must not let it close the order unseen.
41
+ const failedReview = record.amendmentsFailedReview;
42
+ const decider = failedReview && policy.gates.signoff === 'agent' ? 'person' : policy.gates.signoff;
43
+ return gated('signoff', decider, [`Close ${request.workOrderId} as ${request.close} (${how})`, ...request.note ? [`Note: ${request.note}`] : [],
44
+ ...failedReview ? [`A grade found the agent's amendments lower the bar: ${failedReview.note}`] : [],
45
+ ...record.grade ? record.grade.criteria.map(row => `${row.id}: ${row.verdict} — ${row.note}`) : [],
46
+ ...decisions(record).map(row => `Decision ${row.decision} (${row.title}): ${row.resolution.record ? `recorded in ${row.resolution.record}` : `no lasting decision — ${row.resolution.text}`}`)], 'signoff', async (by) => store.update(request.workOrderId, async (existing) => {
47
+ if (!existing || existing.state !== record.state || existing.accepted.digest !== record.accepted.digest || existing.notes.length !== record.notes.length) {
48
+ throw new RefusalError(`${request.workOrderId} changed while this prompt was open`);
49
+ }
50
+ const state = request.close;
51
+ return { record: { ...existing, state, closure: { at: now(), by, how, ...request.note ? { note: request.note } : {} } },
52
+ value: complete(operation, { id: request.workOrderId, state, closedAs: how, by }) };
53
+ }));
54
+ }
55
+ export async function verify(context) {
56
+ const { composition, operation, store, observe } = context;
57
+ const problems = [];
58
+ const awaiting = [];
59
+ for (const record of await store.list()) {
60
+ const row = await observe(record.id);
61
+ if (!TERMINAL.has(record.state) && row.digest !== record.accepted.digest)
62
+ problems.push(`${record.id} changed since it was accepted; accept the change or revert it`);
63
+ if (record.state === 'done' && (!record.grade?.passed || record.grade.digest !== record.accepted.digest || !record.closure))
64
+ problems.push(`${record.id} is done without a passing grade of its accepted contract`);
65
+ for (const row of decisions(record)) {
66
+ const cited = row.resolution?.record;
67
+ const problem = cited && recordProblem(await composition.readDocsFile(cited), cited, record.id, false);
68
+ if (problem)
69
+ problems.push(`${record.id} decision ${row.decision}: ${problem}`);
70
+ }
71
+ awaiting.push(...pendingCi(record).map(step => `${record.id} ${step}`));
72
+ }
73
+ const ci = runningInCi(composition.env);
74
+ const value = { ci, problems, ...ci ? { confirmed: awaiting } : { awaitingCi: awaiting } };
75
+ return problems.length ? unsatisfied(operation, value, problems.join('; ')) : complete(operation, value);
76
+ }
@@ -0,0 +1,162 @@
1
+ import { z } from 'zod';
2
+ import { type WorkGate } from '../autonomy.js';
3
+ import type { Decider } from '../schema.js';
4
+ import { type WorkOrder } from './contract.js';
5
+ import type { CheckResult, WorkRecord, WorkStore } from './store.js';
6
+ import type { WorkApplicationComposition } from './composition.js';
7
+ declare const Request: z.ZodDiscriminatedUnion<[z.ZodObject<{
8
+ operation: z.ZodLiteral<"work-status">;
9
+ workOrderId: z.ZodOptional<z.ZodString>;
10
+ }, z.core.$strict>, z.ZodObject<{
11
+ operation: z.ZodLiteral<"work-accept">;
12
+ workOrderId: z.ZodString;
13
+ }, z.core.$strict>, z.ZodObject<{
14
+ operation: z.ZodLiteral<"work-check">;
15
+ workOrderId: z.ZodString;
16
+ }, z.core.$strict>, z.ZodObject<{
17
+ operation: z.ZodLiteral<"work-note">;
18
+ workOrderId: z.ZodString;
19
+ text: z.ZodString;
20
+ kind: z.ZodEnum<{
21
+ decision: "decision";
22
+ note: "note";
23
+ question: "question";
24
+ }>;
25
+ }, z.core.$strict>, z.ZodObject<{
26
+ operation: z.ZodLiteral<"work-decide">;
27
+ workOrderId: z.ZodString;
28
+ decision: z.ZodNumber;
29
+ resolution: z.ZodDiscriminatedUnion<[z.ZodObject<{
30
+ kind: z.ZodLiteral<"record">;
31
+ path: z.ZodOptional<z.ZodString>;
32
+ supersedes: z.ZodOptional<z.ZodString>;
33
+ }, z.core.$strict>, z.ZodObject<{
34
+ kind: z.ZodLiteral<"none">;
35
+ reason: z.ZodString;
36
+ }, z.core.$strict>], "kind">;
37
+ }, z.core.$strict>, z.ZodObject<{
38
+ operation: z.ZodLiteral<"work-answer">;
39
+ workOrderId: z.ZodString;
40
+ question: z.ZodNumber;
41
+ text: z.ZodString;
42
+ }, z.core.$strict>, z.ZodObject<{
43
+ operation: z.ZodLiteral<"work-grade">;
44
+ workOrderId: z.ZodString;
45
+ verdict: z.ZodOptional<z.ZodEnum<{
46
+ met: "met";
47
+ unmet: "unmet";
48
+ }>>;
49
+ note: z.ZodOptional<z.ZodString>;
50
+ }, z.core.$strict>, z.ZodObject<{
51
+ operation: z.ZodLiteral<"work-signoff">;
52
+ workOrderId: z.ZodString;
53
+ close: z.ZodEnum<{
54
+ abandoned: "abandoned";
55
+ done: "done";
56
+ partial: "partial";
57
+ }>;
58
+ note: z.ZodOptional<z.ZodString>;
59
+ }, z.core.$strict>, z.ZodObject<{
60
+ operation: z.ZodLiteral<"work-acknowledge">;
61
+ workOrderId: z.ZodString;
62
+ note: z.ZodString;
63
+ }, z.core.$strict>, z.ZodObject<{
64
+ operation: z.ZodLiteral<"work-recover">;
65
+ workOrderId: z.ZodString;
66
+ apply: z.ZodBoolean;
67
+ }, z.core.$strict>, z.ZodObject<{
68
+ operation: z.ZodLiteral<"work-verify">;
69
+ }, z.core.$strict>, z.ZodObject<{
70
+ operation: z.ZodLiteral<"grade-criteria">;
71
+ criteriaFile: z.ZodString;
72
+ }, z.core.$strict>, z.ZodObject<{
73
+ operation: z.ZodEnum<{
74
+ "browser-drive": "browser-drive";
75
+ "browser-screenshot": "browser-screenshot";
76
+ }>;
77
+ role: z.ZodOptional<z.ZodString>;
78
+ route: z.ZodOptional<z.ZodString>;
79
+ failOnFindings: z.ZodOptional<z.ZodBoolean>;
80
+ }, z.core.$strict>, z.ZodObject<{
81
+ operation: z.ZodLiteral<"test-run">;
82
+ gate: z.ZodString;
83
+ mode: z.ZodEnum<{
84
+ canonical: "canonical";
85
+ full: "full";
86
+ staged: "staged";
87
+ }>;
88
+ select: z.ZodOptional<z.ZodObject<{
89
+ kind: z.ZodEnum<{
90
+ file: "file";
91
+ route: "route";
92
+ spec: "spec";
93
+ test: "test";
94
+ }>;
95
+ value: z.ZodString;
96
+ }, z.core.$strict>>;
97
+ }, z.core.$strict>], "operation">;
98
+ export type WorkApplicationRequest = z.infer<typeof Request>;
99
+ export type Operation = WorkApplicationRequest['operation'];
100
+ export declare function captureWorkApplicationRequest(value: unknown): WorkApplicationRequest | undefined;
101
+ interface WorkApplicationContinuation {
102
+ readonly digest: string;
103
+ readonly prompt: string;
104
+ readonly decide: (answer: string) => Promise<WorkApplicationOutcome>;
105
+ }
106
+ export type WorkApplicationOutcome = Readonly<{
107
+ kind: 'complete';
108
+ operation: Operation;
109
+ value: unknown;
110
+ }> | Readonly<{
111
+ kind: 'human-needed';
112
+ operation: Operation;
113
+ continuation: WorkApplicationContinuation;
114
+ }> | Readonly<{
115
+ kind: 'refusal';
116
+ operation: Operation | 'invalid';
117
+ reason: string;
118
+ }> | Readonly<{
119
+ kind: 'unsatisfied';
120
+ operation: Operation;
121
+ value: unknown;
122
+ reason: string;
123
+ weakening?: string;
124
+ }>;
125
+ export declare const refusal: (operation: Operation | 'invalid', reason: string) => WorkApplicationOutcome;
126
+ export declare const complete: (operation: Operation, value: unknown) => WorkApplicationOutcome;
127
+ export declare const unsatisfied: (operation: Operation, value: unknown, reason: string, weakening?: string) => WorkApplicationOutcome;
128
+ export declare const now: () => string;
129
+ export declare const TERMINAL: Set<"abandoned" | "accepted" | "checked" | "done" | "graded" | "partial">;
130
+ export type Observed = Readonly<{
131
+ id: string;
132
+ order?: WorkOrder;
133
+ problems: readonly string[];
134
+ record?: WorkRecord;
135
+ digest?: string;
136
+ }>;
137
+ export declare function displayState(row: Observed, attemptCap: number): string;
138
+ export declare function openQuestions(record: WorkRecord): readonly number[];
139
+ export type WorkRequest = Exclude<WorkApplicationRequest, {
140
+ operation: 'test-run' | `browser-${string}`;
141
+ }>;
142
+ export interface WorkContext {
143
+ readonly composition: WorkApplicationComposition;
144
+ readonly operation: WorkRequest['operation'];
145
+ readonly policy: WorkApplicationComposition['policy'];
146
+ readonly store: WorkStore;
147
+ readonly observe: (orderId: string) => Promise<Observed>;
148
+ /** How many accepted orders are not closed. */
149
+ readonly wip: () => Promise<number>;
150
+ /** The records under [docs] decisions, read once per command. */
151
+ readonly decisionRecords: () => Promise<ReadonlyMap<string, string>>;
152
+ readonly gated: (gate: WorkGate, decider: Decider, lines: readonly string[], verb: string, apply: (by: Decider) => Promise<WorkApplicationOutcome>) => Promise<WorkApplicationOutcome>;
153
+ /** The order and its record, refused unless it is accepted, open, unchanged and not blocked. */
154
+ readonly current: (orderId: string) => Promise<Readonly<{
155
+ row: Observed;
156
+ record: WorkRecord;
157
+ order: WorkOrder;
158
+ }>>;
159
+ readonly runChecks: (order: Pick<WorkOrder, 'criteria' | 'gates'>) => Promise<readonly CheckResult[]>;
160
+ }
161
+ export declare function workContext(composition: WorkApplicationComposition, operation: WorkRequest['operation']): WorkContext;
162
+ export {};
@@ -0,0 +1,137 @@
1
+ // What every work command shares: the request it takes, the outcome it returns, the state it reads an
2
+ // order in, and the steps several commands take (observing an order, passing a gate, running its
3
+ // checks), bound to one composition.
4
+ import { createHash } from 'node:crypto';
5
+ import { z } from 'zod';
6
+ import { decide } from '../autonomy.js';
7
+ import { RefusalError } from '../environment/contract.js';
8
+ import { REPOSITORY_FILE } from '../repository-path.js';
9
+ import { WORK_ORDER_ID, workOrderDigest } from './contract.js';
10
+ const id = z.string().regex(WORK_ORDER_ID);
11
+ const note = z.string().trim().min(1).max(4_096).regex(/^[^\x00-\x08\x0b-\x1f\x7f]*$/);
12
+ const docsReference = z.string().max(1_024).regex(REPOSITORY_FILE);
13
+ const relativeFile = z.string().max(4_096).regex(/^(?!\/)(?!.*(?:^|\/)\.\.(?:\/|$))[A-Za-z0-9._@+\-/]+$/);
14
+ const Request = z.discriminatedUnion('operation', [
15
+ z.object({ operation: z.literal('work-status'), workOrderId: id.optional() }).strict(),
16
+ z.object({ operation: z.literal('work-accept'), workOrderId: id }).strict(),
17
+ z.object({ operation: z.literal('work-check'), workOrderId: id }).strict(),
18
+ z.object({ operation: z.literal('work-note'), workOrderId: id, text: note, kind: z.enum(['note', 'question', 'decision']) }).strict(),
19
+ z.object({ operation: z.literal('work-decide'), workOrderId: id, decision: z.number().int().min(1), resolution: z.discriminatedUnion('kind', [
20
+ z.object({ kind: z.literal('record'), path: docsReference.optional(), supersedes: z.string().regex(/^\d{1,9}$/).optional() }).strict(),
21
+ z.object({ kind: z.literal('none'), reason: note }).strict(),
22
+ ]) }).strict(),
23
+ z.object({ operation: z.literal('work-answer'), workOrderId: id, question: z.number().int().min(1), text: note }).strict(),
24
+ z.object({ operation: z.literal('work-grade'), workOrderId: id, verdict: z.enum(['met', 'unmet']).optional(), note: note.optional() }).strict(),
25
+ z.object({ operation: z.literal('work-signoff'), workOrderId: id, close: z.enum(['done', 'partial', 'abandoned']), note: note.optional() }).strict(),
26
+ z.object({ operation: z.literal('work-acknowledge'), workOrderId: id, note }).strict(),
27
+ z.object({ operation: z.literal('work-recover'), workOrderId: id, apply: z.boolean() }).strict(),
28
+ z.object({ operation: z.literal('work-verify') }).strict(),
29
+ z.object({ operation: z.literal('grade-criteria'), criteriaFile: relativeFile }).strict(),
30
+ z.object({ operation: z.enum(['browser-drive', 'browser-screenshot']), role: z.string().regex(/^[a-z0-9][a-z0-9_-]{0,63}$/).optional(),
31
+ route: z.string().regex(/^(?:[a-z0-9][a-z0-9_-]{0,63}|\/(?!\/)[^\s#\x00-\x1f\x7f]{0,511})$/).optional(),
32
+ failOnFindings: z.boolean().optional() }).strict(),
33
+ z.object({ operation: z.literal('test-run'), gate: z.string().regex(/^[a-z][a-z0-9-]{0,63}$/), mode: z.enum(['canonical', 'full', 'staged']),
34
+ select: z.object({ kind: z.enum(['spec', 'file', 'test', 'route']), value: z.string().min(1).refine(value => Buffer.byteLength(value) <= 4_096 && !/[\x00-\x1f\x7f]/.test(value)) }).strict().optional() }).strict(),
35
+ ]);
36
+ export function captureWorkApplicationRequest(value) {
37
+ const parsed = Request.safeParse(value);
38
+ return parsed.success ? parsed.data : undefined;
39
+ }
40
+ export const refusal = (operation, reason) => ({ kind: 'refusal', operation, reason });
41
+ export const complete = (operation, value) => ({ kind: 'complete', operation, value });
42
+ export const unsatisfied = (operation, value, reason, weakening) => ({ kind: 'unsatisfied', operation, value, reason, ...weakening ? { weakening } : {} });
43
+ export const now = () => new Date().toISOString();
44
+ // Agent-written text is shown to a person with its control and bidirectional-override characters
45
+ // replaced, so it can neither drive their terminal nor read in an order other than the one recorded.
46
+ const shown = (value) => value.replace(/[\x00-\x09\x0b-\x1f\x7f-\x9f‪-‮⁦-⁩]/g, '�');
47
+ export const TERMINAL = new Set(['done', 'partial', 'abandoned']);
48
+ export function displayState(row, attemptCap) {
49
+ if (!row.record)
50
+ return row.order ? 'draft' : 'invalid';
51
+ if (TERMINAL.has(row.record.state))
52
+ return row.record.state;
53
+ if (!row.order || row.digest !== row.record.accepted.digest)
54
+ return 'stale';
55
+ if (openQuestions(row.record).length || row.record.attempts >= attemptCap)
56
+ return 'blocked';
57
+ return row.record.state;
58
+ }
59
+ export function openQuestions(record) {
60
+ return record.notes.flatMap((row, index) => row.kind === 'question' && !record.notes.some(answer => answer.answers === index + 1) ? [index + 1] : []);
61
+ }
62
+ export function workContext(composition, operation) {
63
+ const { policy, store } = composition;
64
+ const observe = async (orderId) => {
65
+ const [parsed, record] = await Promise.all([composition.readOrder(orderId), store.read(orderId)]);
66
+ if (!parsed.ok)
67
+ return { id: orderId, problems: parsed.problems, ...(record ? { record } : {}) };
68
+ // A source that cannot be read leaves this order without a digest, and so stale, not the whole listing refused.
69
+ let sources;
70
+ try {
71
+ sources = await composition.sourceDigests(parsed.value);
72
+ }
73
+ catch (error) {
74
+ if (!(error instanceof RefusalError))
75
+ throw error;
76
+ return { id: orderId, order: parsed.value, problems: [`a cited source cannot be read: ${error.message}`], ...(record ? { record } : {}) };
77
+ }
78
+ const base = record?.accepted.base ?? await composition.heads();
79
+ return { id: orderId, order: parsed.value, problems: [], ...(record ? { record } : {}), digest: workOrderDigest(parsed.value, sources, base) };
80
+ };
81
+ const wip = async () => (await store.list()).filter(record => !TERMINAL.has(record.state)).length;
82
+ let records;
83
+ const decisionRecords = () => records ??= composition.decisionRecords();
84
+ // Who passed a gate: the agent; a person, after typing the digest of exactly what they were shown;
85
+ // or CI, which locally records the step as awaiting the `work verify` job and passes it there.
86
+ const gated = async (gate, decider, lines, verb, apply) => {
87
+ const decision = decide(decider, composition.env);
88
+ if (decision === 'proceed')
89
+ return apply(decider === 'ci' ? 'ci' : 'agent');
90
+ if (decision === 'ci-only')
91
+ return apply('ci');
92
+ const digest = createHash('sha256').update(JSON.stringify({ gate, lines })).digest('hex');
93
+ let used = false;
94
+ return { kind: 'human-needed', operation, continuation: { digest,
95
+ prompt: [...lines.map(shown), `[work.gates] ${gate} = person. To ${verb} exactly this, type ${verb} ${digest}`].join('\n'),
96
+ decide: async (answer) => {
97
+ if (used)
98
+ return refusal(operation, `this prompt was already answered; rerun work ${operation.slice('work-'.length)}`);
99
+ used = true;
100
+ return answer.trim() === `${verb} ${digest}` ? apply('person') : refusal(operation, `Nothing was recorded: the person did not type ${verb} ${digest}`);
101
+ } } };
102
+ };
103
+ // Who closes or amends a capped order is whoever [work.gates] says; a sentence naming a person where the policy names the agent is a control that is not there.
104
+ const attemptCapRemedy = (orderId) => {
105
+ const by = (gate, verb, command) => policy.gates[gate] === 'person' ? `a person ${verb} it with ${command} --interactive`
106
+ : `${verb.replace(/s$/, '')} it with ${command}${policy.gates[gate] === 'ci' ? ', which the work verify job confirms in CI' : ''}`;
107
+ return `${by('signoff', 'closes', `npx --no cockpit work signoff --config <your cockpit.toml> --work-order ${orderId} --partial|--abandon --note <why>`)}, `
108
+ + `or ${by('amend', 'amends', `an edit to workorders/${orderId}.md and npx --no cockpit work accept --config <your cockpit.toml> --work-order ${orderId}`)}`;
109
+ };
110
+ const current = async (orderId) => {
111
+ const row = await observe(orderId);
112
+ if (!row.record)
113
+ throw new RefusalError(`${orderId} is not accepted yet; run npx --no cockpit work accept --config <your cockpit.toml> --work-order ${orderId}`);
114
+ const state = displayState(row, composition.caps.attempts);
115
+ if (TERMINAL.has(row.record.state))
116
+ throw new RefusalError(`${orderId} is closed (${row.record.state})`);
117
+ if (state === 'stale')
118
+ throw new RefusalError(`${orderId} changed since it was accepted${row.problems.length ? ` and does not parse: ${row.problems.join('; ')}` : ''}; `
119
+ + `run npx --no cockpit work accept --config <your cockpit.toml> --work-order ${orderId} to accept the change`);
120
+ if (state === 'blocked')
121
+ throw new RefusalError(openQuestions(row.record).length
122
+ ? `${orderId} has open question ${openQuestions(row.record).join(', ')}; it is answered with work answer`
123
+ : `${orderId} reached the attempt cap (${composition.caps.attempts}); record the root cause with work note, then ${attemptCapRemedy(orderId)}`);
124
+ return { row, record: row.record, order: row.order };
125
+ };
126
+ const runChecks = async (order) => {
127
+ const results = [];
128
+ // Checks share the product's state, so they run in the order the contract lists them.
129
+ for (const criterion of order.criteria)
130
+ if (criterion.check)
131
+ results.push(await composition.runCheck(criterion.id, criterion.check));
132
+ for (const gate of order.gates)
133
+ results.push(await composition.runCheck(`gate:${gate}`, { gate }));
134
+ return results;
135
+ };
136
+ return { composition, operation, policy, store, observe, wip, decisionRecords, gated, current, runChecks };
137
+ }
@@ -0,0 +1,27 @@
1
+ import type { WorkOrder } from './contract.js';
2
+ import type { WorkRecord } from './store.js';
3
+ import type { WorkApplicationComposition } from './composition.js';
4
+ import { type WorkApplicationOutcome, type WorkContext, type WorkRequest } from './application-context.js';
5
+ export declare const DECISIONS_UNDECLARED: string;
6
+ export declare function decisions(record: WorkRecord): {
7
+ decision: number;
8
+ title: string;
9
+ resolution: {
10
+ at: string;
11
+ by: "agent" | "ci" | "person";
12
+ kind: "answer" | "decision" | "note" | "question" | "resolution";
13
+ text: string;
14
+ answers?: number | undefined;
15
+ resolves?: number | undefined;
16
+ record?: string | undefined;
17
+ } | undefined;
18
+ }[];
19
+ export declare function recordProblem(text: string | undefined, reference: string, orderId: string, filled: boolean): string | undefined;
20
+ export declare function unresolvedDecisions(record: WorkRecord, composition: WorkApplicationComposition): Promise<readonly string[]>;
21
+ export declare function relatedDecisions(context: WorkContext, order: WorkOrder): Promise<Readonly<{
22
+ relatedDecisions?: readonly string[];
23
+ relatedDecisionsUnavailable?: string;
24
+ }>>;
25
+ export declare function decideOrder(request: Extract<WorkRequest, {
26
+ operation: 'work-decide';
27
+ }>, context: WorkContext): Promise<WorkApplicationOutcome>;
@@ -0,0 +1,121 @@
1
+ // The decisions an order records: which are unresolved, whether a cited record still stands for its
2
+ // decision, the records that bear on an order's scope, and `work decide`, which writes or cites one.
3
+ import { posix } from 'node:path';
4
+ import { RefusalError } from '../environment/contract.js';
5
+ import { recordNumber, withStatus } from '../docs/decision-record.js';
6
+ import { splitRepositoryPath } from '../repository-path.js';
7
+ import { citedOrders, holdsPlaceholders, namesScope, nextRecordName, recordTitle, scaffoldRecord } from './decisions.js';
8
+ import { complete, now, refusal, TERMINAL } from './application-context.js';
9
+ // Decisions recorded on an order, each with its latest resolution.
10
+ export const DECISIONS_UNDECLARED = 'decision records need a directory: declare one in cockpit.toml, for example [docs] decisions = "docs/adr", '
11
+ + 'with decisions_index = "docs/adr/README.md" if the records have an index';
12
+ export function decisions(record) {
13
+ return record.notes.flatMap((row, index) => row.kind !== 'decision' ? [] : [{ decision: index + 1, title: recordTitle(row.text),
14
+ resolution: record.notes.findLast(note => note.kind === 'resolution' && note.resolves === index + 1) }]);
15
+ }
16
+ // Why a cited record no longer stands for its decision; `filled` also requires that the template's placeholders are gone.
17
+ export function recordProblem(text, reference, orderId, filled) {
18
+ if (text === undefined)
19
+ return `its record ${reference} does not exist`;
20
+ if (!citedOrders(text).includes(orderId))
21
+ return `its record ${reference} does not cite ${orderId}; add the line "Work order: ${orderId}"`;
22
+ return filled && holdsPlaceholders(text) ? `its record ${reference} still holds the template's placeholders; replace each "<!-- cockpit: replace this ... -->" comment` : undefined;
23
+ }
24
+ export async function unresolvedDecisions(record, composition) {
25
+ const rows = [];
26
+ for (const row of decisions(record)) {
27
+ const name = `decision ${row.decision} (${row.title})`;
28
+ if (!row.resolution)
29
+ rows.push(`${name} is unresolved; run npx --no cockpit work decide --config <your cockpit.toml> --work-order ${record.id} --decision ${row.decision} --record, or with --none --reason "<why>"`);
30
+ else if (row.resolution.record) {
31
+ const problem = recordProblem(await composition.readDocsFile(row.resolution.record), row.resolution.record, record.id, true);
32
+ if (problem)
33
+ rows.push(`${name}: ${problem}`);
34
+ }
35
+ }
36
+ return rows;
37
+ }
38
+ // Decision records whose text names a path the order may change, so earlier decisions about that code are in front of the agent.
39
+ export async function relatedDecisions(context, order) {
40
+ const { composition } = context;
41
+ const directory = composition.docs?.decisions;
42
+ if (!directory)
43
+ return {};
44
+ const { slug } = splitRepositoryPath(directory);
45
+ const prefix = slug === undefined ? '' : `${slug}:`;
46
+ let read;
47
+ // The listing is advice, so a directory cockpit cannot read is reported here and refused where a record is written.
48
+ try {
49
+ read = await context.decisionRecords();
50
+ }
51
+ catch (error) {
52
+ if (!(error instanceof RefusalError))
53
+ throw error;
54
+ return { relatedDecisionsUnavailable: error.message };
55
+ }
56
+ return { relatedDecisions: [...read.entries()]
57
+ .filter(([reference, text]) => reference !== composition.docs?.decisionsIndex && namesScope(text, order.scope, prefix)).map(([reference]) => reference) };
58
+ }
59
+ export async function decideOrder(request, context) {
60
+ const { composition, operation, store } = context;
61
+ const directory = composition.docs?.decisions;
62
+ if (!directory)
63
+ return refusal(operation, DECISIONS_UNDECLARED);
64
+ const orderId = request.workOrderId;
65
+ const record = await store.read(orderId);
66
+ if (!record || TERMINAL.has(record.state))
67
+ return refusal(operation, `${orderId} has no open record to resolve a decision on`);
68
+ const decision = record.notes[request.decision - 1];
69
+ if (decision?.kind !== 'decision')
70
+ return refusal(operation, `note ${request.decision} on ${orderId} is not a decision; work status lists each order's open decisions`);
71
+ const resolve = (fields, writes) => store.update(orderId, async (existing) => {
72
+ if (!existing || TERMINAL.has(existing.state))
73
+ throw new RefusalError(`${orderId} closed while its decision was being resolved`);
74
+ await writes?.();
75
+ return { record: { ...existing, notes: [...existing.notes, { at: now(), by: 'agent', kind: 'resolution', resolves: request.decision, ...fields }] }, value: undefined };
76
+ });
77
+ const resolution = request.resolution;
78
+ if (resolution.kind === 'none') {
79
+ await resolve({ text: resolution.reason });
80
+ return complete(operation, { id: orderId, decision: request.decision, resolution: 'none', reason: resolution.reason });
81
+ }
82
+ if (resolution.path !== undefined) {
83
+ if (!resolution.path.startsWith(`${directory}/`))
84
+ return refusal(operation, `${resolution.path} is not inside [docs] decisions ${directory}; write the record there`);
85
+ if (resolution.supersedes)
86
+ return refusal(operation, '--supersedes applies when cockpit writes the record; with a path, write the Supersedes line and the old record\'s status yourself');
87
+ const problem = recordProblem(await composition.readDocsFile(resolution.path), resolution.path, orderId, false);
88
+ if (problem)
89
+ return refusal(operation, `${resolution.path} cannot resolve decision ${request.decision}: ${problem}`);
90
+ await resolve({ text: `recorded in ${resolution.path}`, record: resolution.path });
91
+ return complete(operation, { id: orderId, decision: request.decision, record: resolution.path });
92
+ }
93
+ // Scaffold the record; the agent fills in its body, and signoff refuses while the template's placeholders remain.
94
+ const existing = await context.decisionRecords();
95
+ const names = [...existing.keys()].map(reference => reference.slice(directory.length + 1));
96
+ const title = recordTitle(decision.text);
97
+ const named = nextRecordName(names, title, now().slice(0, 10));
98
+ const reference = `${directory}/${named.name}`;
99
+ const writes = [{ reference, create: true, text: '' }];
100
+ let supersedes;
101
+ if (resolution.supersedes) {
102
+ if (!named.number)
103
+ return refusal(operation, `the records in ${directory} are named by date, so none has a number to supersede`);
104
+ const old = names.filter(name => Number(recordNumber(name)) === Number(resolution.supersedes));
105
+ if (old.length !== 1)
106
+ return refusal(operation, `${directory} has ${old.length ? `${old.length} records` : 'no record'} numbered ${resolution.supersedes}; pass the number of exactly one`);
107
+ supersedes = recordNumber(old[0]);
108
+ const marked = withStatus(existing.get(`${directory}/${old[0]}`), `superseded by ${named.number}`);
109
+ if (marked === undefined)
110
+ return refusal(operation, `${directory}/${old[0]} has no Status line to mark superseded; add "Status: accepted" to it`);
111
+ writes.push({ reference: `${directory}/${old[0]}`, create: false, text: marked });
112
+ }
113
+ writes[0].text = scaffoldRecord({ title, workOrder: orderId, ...named.number ? { number: named.number } : {}, ...supersedes ? { supersedes } : {} });
114
+ const index = composition.docs?.decisionsIndex;
115
+ if (index !== undefined && await composition.readDocsFile(index) === undefined)
116
+ return refusal(operation, `[docs] decisions_index ${index} does not exist; create it, or remove decisions_index from cockpit.toml`);
117
+ const link = index === undefined ? undefined : posix.relative(posix.dirname(splitRepositoryPath(index).path), splitRepositoryPath(reference).path);
118
+ await resolve({ text: `recorded in ${reference}`, record: reference }, () => composition.writeDecisionRecords(writes, link === undefined ? undefined : `- [${named.number ? `${named.number}. ` : ''}${title}](${link})`));
119
+ return complete(operation, { id: orderId, decision: request.decision, record: reference, written: writes.map(row => row.reference),
120
+ next: `fill in ${reference}: signoff refuses while its "cockpit: replace this" placeholders remain` });
121
+ }
@@ -0,0 +1,11 @@
1
+ import type { WorkApplicationComposition } from './composition.js';
2
+ import { type WorkApplicationOutcome, type WorkApplicationRequest, type WorkContext, type WorkRequest } from './application-context.js';
3
+ export declare function testRun(captured: Extract<WorkApplicationRequest, {
4
+ operation: 'test-run';
5
+ }>, composition: WorkApplicationComposition): Promise<WorkApplicationOutcome>;
6
+ export declare function gradeOrder(request: Extract<WorkRequest, {
7
+ operation: 'work-grade';
8
+ }>, context: WorkContext): Promise<WorkApplicationOutcome>;
9
+ export declare function gradeCriteriaFile(request: Extract<WorkRequest, {
10
+ operation: 'grade-criteria';
11
+ }>, context: WorkContext): Promise<WorkApplicationOutcome>;