@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,292 @@
1
+ import { requiresOperatorGate } from '../schema.js';
2
+ // Deliberately wider than the two registries in hand: a `declare -a`/`readonly` prefix, a
3
+ // digit in the array name, and a trailing comment are all ordinary bash that the first
4
+ // version silently failed to recognise — and failing to recognise the opening line produced
5
+ // a clean empty success, which is the worst outcome this module can return.
6
+ const ARRAY_RE = /^\s*(?:(?:declare|readonly|local|export)\s+(?:-\w+\s+)?)?[A-Z][A-Z0-9_]*_SIBLINGS=\(\s*(?:#.*)?$/;
7
+ const ROW_RE = /^\s*"([^"]+)"\s*(?:#.*)?$/;
8
+ const IGNORABLE_RE = /^\s*(?:#.*)?$/;
9
+ // The vocabularies are closed. A word outside them is reported, never mapped to a default:
10
+ // `mount: 'none'` means "the stack never binds this", so quietly choosing it for an
11
+ // unrecognised lane removes a repository from every bring-up — and the old code then stamped
12
+ // that invented value `from: 'read'`, which is the exact inversion of this module's contract.
13
+ const LANE_MOUNT = { app: 'always', host: 'host' };
14
+ const TIER_ACQUIRE = { required: 'auto', auto: 'auto', manual: 'manual', local: 'none' };
15
+ /**
16
+ * Parse a `*_SIBLINGS=( "a|b|c" ... )` block out of an `env/lib/instance.sh`.
17
+ *
18
+ * The dialect is decided by field count, the one signal that is unambiguous. The lane
19
+ * dialect writes six fields (`slug|dir|remote|lane|var|purpose`); the tier dialect writes
20
+ * seven, adding `branch` and replacing the fourth-position `lane` with a fifth-position
21
+ * `tier`. A row of any other width is recorded as skipped rather than bent into shape.
22
+ */
23
+ export function parseRegistry(source) {
24
+ const lines = source.split('\n');
25
+ const rows = [];
26
+ const unparsed = [];
27
+ let inside = false;
28
+ let found = false;
29
+ let openingLine;
30
+ let declarationCount = 0;
31
+ let duplicateOpeningLine;
32
+ for (const [index, line] of lines.entries()) {
33
+ if (!inside) {
34
+ if (ARRAY_RE.test(line)) {
35
+ inside = true;
36
+ found = true;
37
+ openingLine = index + 1;
38
+ declarationCount += 1;
39
+ if (declarationCount === 2)
40
+ duplicateOpeningLine = index + 1;
41
+ }
42
+ continue;
43
+ }
44
+ if (/^\s*\)\s*(?:#.*)?$/.test(line)) {
45
+ inside = false;
46
+ continue;
47
+ }
48
+ const m = ROW_RE.exec(line);
49
+ if (m && !/[$`\\]/.test(m[1]))
50
+ rows.push({ line: m[1], sourceLine: index + 1 });
51
+ else if (!IGNORABLE_RE.test(line)) {
52
+ const unsupportedShellSyntax = /^\s*"/.test(line) && /[$`\\]/.test(line);
53
+ unparsed.push({
54
+ sourceLine: index + 1,
55
+ reason: unsupportedShellSyntax
56
+ ? 'unsupported shell expansion, substitution, or escape in double-quoted row'
57
+ : 'not a double-quoted row; single-quoted rows are valid Bash but unsupported',
58
+ });
59
+ }
60
+ }
61
+ const skipped = [];
62
+ skipped.push(...unparsed);
63
+ if (inside) {
64
+ skipped.push({ sourceLine: openingLine, reason: 'unterminated sibling registry array' });
65
+ return { found: true, dialect: null, siblings: [], skipped };
66
+ }
67
+ if (duplicateOpeningLine !== undefined) {
68
+ skipped.push({
69
+ sourceLine: duplicateOpeningLine,
70
+ reason: 'multiple sibling registry arrays are ambiguous',
71
+ code: 'REGISTRY_AMBIGUOUS',
72
+ });
73
+ return { found: true, dialect: null, siblings: [], skipped };
74
+ }
75
+ const parsed = [];
76
+ for (const { line, sourceLine } of rows) {
77
+ const fields = line.split('|');
78
+ if (fields.length === 6 || fields.length === 7)
79
+ parsed.push({ fields, sourceLine });
80
+ else
81
+ skipped.push({ sourceLine, reason: `${fields.length} fields; expected 6 (lane) or 7 (tier)` });
82
+ }
83
+ // No opening line at all is a different fact from an empty registry, and both are
84
+ // different from "every row was malformed". `found` preserves that distinction while
85
+ // `dialect` remains null until at least one usable row establishes a width.
86
+ if (!found)
87
+ return { found: false, dialect: null, siblings: [], skipped };
88
+ if (parsed.length === 0)
89
+ return { found: true, dialect: null, siblings: [], skipped };
90
+ // A registry is written in one dialect. Mixed widths mean the file was hand-edited into
91
+ // an inconsistent state, and the minority rows are reported rather than reinterpreted.
92
+ // Plain mode over the widths. A tie is NOT resolved by picking one: with equal counts
93
+ // there is no minority, and silently discarding half the registry into `skipped` is the
94
+ // fail-open this module exists to prevent.
95
+ const counts = new Map();
96
+ for (const { fields } of parsed)
97
+ counts.set(fields.length, (counts.get(fields.length) ?? 0) + 1);
98
+ const ranked = [...counts.entries()].sort((a, b) => b[1] - a[1]);
99
+ if (ranked.length > 1 && ranked[0][1] === ranked[1][1]) {
100
+ for (const { sourceLine } of parsed) {
101
+ skipped.push({
102
+ sourceLine,
103
+ reason: `ambiguous registry: ${ranked[0][1]} rows of ${ranked[0][0]} fields and ${ranked[1][1]} of ${ranked[1][0]} — no majority dialect`,
104
+ code: 'REGISTRY_AMBIGUOUS',
105
+ });
106
+ }
107
+ return { found: true, dialect: null, siblings: [], skipped };
108
+ }
109
+ const dialectWidth = ranked[0][0];
110
+ const dialect = dialectWidth === 7 ? 'tier' : 'lane';
111
+ const siblings = [];
112
+ const seen = {
113
+ slug: new Map(),
114
+ dir: new Map(),
115
+ var: new Map(),
116
+ };
117
+ for (const { fields, sourceLine } of parsed) {
118
+ if (fields.length !== dialectWidth) {
119
+ skipped.push({ sourceLine, reason: `${fields.length} fields in a ${dialectWidth}-field registry` });
120
+ continue;
121
+ }
122
+ const row = dialect === 'lane' ? fromLane(fields) : fromTier(fields);
123
+ if ('reason' in row)
124
+ skipped.push({ sourceLine, ...row });
125
+ else {
126
+ const reason = sourceValidationReason(row);
127
+ if (reason)
128
+ skipped.push({ sourceLine, reason });
129
+ else {
130
+ const duplicate = ['slug', 'dir', 'var']
131
+ .map((field) => ({ field, firstLine: seen[field].get(row[field]) }))
132
+ .find(({ firstLine }) => firstLine !== undefined);
133
+ if (duplicate) {
134
+ skipped.push({
135
+ sourceLine,
136
+ reason: `duplicate ${duplicate.field}; first declared on source line ${duplicate.firstLine}`,
137
+ });
138
+ continue;
139
+ }
140
+ siblings.push(row);
141
+ for (const field of ['slug', 'dir', 'var'])
142
+ seen[field].set(row[field], sourceLine);
143
+ }
144
+ }
145
+ }
146
+ return { found: true, dialect, siblings, skipped };
147
+ }
148
+ function sourceValidationReason(row) {
149
+ if (row.acquire.value === 'none' && row.remote !== undefined) {
150
+ return 'acquire "none" means nothing is ever cloned — remove `remote`';
151
+ }
152
+ if (row.acquire.value !== 'none' && !row.remote) {
153
+ return 'acquire "auto" or "manual" needs a `remote` to clone from';
154
+ }
155
+ if (!/^[a-z0-9][a-z0-9-]*$/.test(row.slug)) {
156
+ return 'invalid slug; expected lowercase letters, digits, and hyphens';
157
+ }
158
+ if (!row.dir)
159
+ return 'invalid dir; expected a non-empty value';
160
+ if (row.branch !== undefined && !row.branch)
161
+ return 'invalid branch; expected a non-empty value';
162
+ if (!/^[A-Z][A-Z0-9_]*$/.test(row.var)) {
163
+ return 'invalid var; expected an exported shell variable name';
164
+ }
165
+ if (!row.purpose)
166
+ return 'invalid purpose; expected a non-empty value';
167
+ return undefined;
168
+ }
169
+ /** Lane dialect: `slug|dir|remote|lane|var|purpose`. Both axes read without inference. */
170
+ function fromLane(f) {
171
+ const [slug, dir, remote, lane, varName, purpose] = f.map((s) => s.trim());
172
+ const mount = /^profile:[a-z0-9-]+$/.test(lane) ? lane : LANE_MOUNT[lane];
173
+ if (!mount)
174
+ return { reason: `unknown lane "${lane}" — expected app, host or profile:<name>` };
175
+ return {
176
+ slug,
177
+ dir,
178
+ remote,
179
+ mount: { value: mount, from: 'read' },
180
+ // Every row in a lane-dialect registry is cloned by setup from its recorded remote;
181
+ // the dialect has no way to say otherwise, so this is the dialect's meaning, not a guess.
182
+ acquire: { value: 'auto', from: 'read' },
183
+ var: varName,
184
+ purpose,
185
+ };
186
+ }
187
+ /**
188
+ * Tier dialect: `slug|dir|remote|branch|tier|var|purpose`.
189
+ *
190
+ * `acquire` maps straight off `tier`. `mount` does not exist in this dialect and must be
191
+ * inferred — a tier says how a checkout arrives, never whether the stack binds it. The
192
+ * purpose column carries the answer in practice, because a registry that cannot state the
193
+ * mount tends to write the activating command into the prose instead
194
+ * (`EXTRA_PROFILES=<name> …`, `make <target> — …`). That is a real signal, but it is prose,
195
+ * so every value here is marked inferred and printed for confirmation.
196
+ */
197
+ function fromTier(f) {
198
+ const [slug, dir, remote, branch, tier, varName, purpose] = f.map((s) => s.trim());
199
+ const acquire = TIER_ACQUIRE[tier];
200
+ // Without this the old code mapped a typo to `none`, retained the remote, and the schema
201
+ // then rejected the row naming `remote` — sending the operator to fix the wrong field.
202
+ if (!acquire)
203
+ return { reason: `unknown tier "${tier}" — expected required, auto, manual or local` };
204
+ const assignments = [...purpose.matchAll(/(?:^|[\s;(])EXTRA_PROFILES=([^\s;)]*)/gi)].map((match) => match[1].toLowerCase());
205
+ if (assignments.some((assignment) => !/^[a-z0-9-]+$/.test(assignment))) {
206
+ return {
207
+ reason: 'ambiguous purpose; EXTRA_PROFILES assignment does not name exactly one profile',
208
+ code: 'REGISTRY_AMBIGUOUS',
209
+ };
210
+ }
211
+ const profiles = assignments;
212
+ const uniqueProfiles = [...new Set(profiles)];
213
+ if (uniqueProfiles.length > 1) {
214
+ return {
215
+ reason: 'ambiguous purpose; multiple EXTRA_PROFILES assignments',
216
+ code: 'REGISTRY_AMBIGUOUS',
217
+ };
218
+ }
219
+ let mount;
220
+ const profile = uniqueProfiles[0];
221
+ // Anchored: an unanchored \bmake\b matched ordinary English ("utilities to make sure
222
+ // the fixtures stay in sync" inferred a host lane on target "sure"), and a false host
223
+ // lane removes a repository from every bring-up.
224
+ const makeTarget = /^make\s+([a-z0-9-]+)\b/.exec(purpose);
225
+ if (tier === 'required') {
226
+ mount = { value: 'always', from: 'inferred', because: 'tier "required" — the stack bind-mounts it or preflight fails' };
227
+ }
228
+ else if (profile) {
229
+ mount = { value: `profile:${profile}`, from: 'inferred', because: `purpose names EXTRA_PROFILES=${profile}` };
230
+ }
231
+ else if (makeTarget) {
232
+ mount = { value: 'host', from: 'inferred', because: `purpose opens with \`make ${makeTarget[1]}\` — a target, never a compose service` };
233
+ }
234
+ else {
235
+ mount = { value: 'none', from: 'inferred', because: 'no profile and no make target in purpose — read, never mounted' };
236
+ }
237
+ return {
238
+ slug,
239
+ dir,
240
+ remote: remote === '-' ? undefined : remote,
241
+ branch: branch === '-' ? undefined : branch,
242
+ mount,
243
+ acquire: { value: acquire, from: tier === 'required' ? 'inferred' : 'read', because: tier === 'required' ? 'tier "required" implies setup clones it' : undefined },
244
+ var: varName,
245
+ purpose,
246
+ };
247
+ }
248
+ /**
249
+ * Fields a registry draft cannot answer from any existing registry.
250
+ *
251
+ * This is deliberately not solved by writing a plausible value. A host lane's reason — a
252
+ * simulator that cannot be containerised, a renderer that lives in a vendor's cloud — is a
253
+ * constraint someone discovered, and a generated guess at it is worse than a blank, because
254
+ * the next reader treats it as established. So a registry draft is a config that is
255
+ * incomplete *and says which parts*, and the operator answers once.
256
+ */
257
+ export function gaps(siblings) {
258
+ const out = [];
259
+ for (const s of siblings) {
260
+ if (s.mount.value === 'host') {
261
+ out.push({
262
+ slug: s.slug,
263
+ field: 'host_reason',
264
+ why: 'no registry dialect records why a repository cannot join the stack',
265
+ blocking: true,
266
+ });
267
+ }
268
+ // A lane touching live data or local hardware must never run automatically, and no
269
+ // dialect records that. Left unasked, a draft would be a config in which a lane that
270
+ // renders against a live third-party store is an ordinary automatic target.
271
+ if (requiresOperatorGate({ mount: s.mount.value, purpose: s.purpose })) {
272
+ out.push({
273
+ slug: s.slug,
274
+ field: 'gate',
275
+ why: 'this lane can touch live data or local hardware and must require an operator',
276
+ blocking: true,
277
+ fix: 'set gate = "operator" before running this lane',
278
+ });
279
+ }
280
+ // Not asked for a row that is not a repository: it has no install step, and a gap
281
+ // nobody can answer is noise that degrades the signal of the ones that matter.
282
+ if (s.acquire.value !== 'none') {
283
+ out.push({
284
+ slug: s.slug,
285
+ field: 'toolchain',
286
+ why: 'not recorded by any registry dialect; defaults to "none" until confirmed',
287
+ blocking: false,
288
+ });
289
+ }
290
+ }
291
+ return out;
292
+ }
@@ -0,0 +1,18 @@
1
+ export interface ResolvedRegistryShellModel {
2
+ formatVersion: string;
3
+ cockpitSourceRevision: string;
4
+ registrySha256: string;
5
+ dialect: 'lane' | 'tier';
6
+ siblingsRoot: string;
7
+ rows: readonly string[];
8
+ paths: readonly string[];
9
+ variables: readonly {
10
+ name: string;
11
+ defaultPath: string;
12
+ }[];
13
+ adoption: readonly {
14
+ layer: string;
15
+ state: string;
16
+ }[];
17
+ }
18
+ export declare function renderResolvedRegistryShell(model: ResolvedRegistryShellModel): string;
@@ -0,0 +1,36 @@
1
+ function quote(value) {
2
+ if (/\0|\r|\n|\$\(|\$\{|`/.test(value)) {
3
+ throw new Error('REGISTRY_ARTIFACT_UNSAFE: value contains executable or multiline shell syntax');
4
+ }
5
+ return `'${value.replaceAll("'", `'"'"'`)}'`;
6
+ }
7
+ function array(name, values) {
8
+ return [`${name}=(`, ...values.map((value) => ` ${quote(value)}`), ')'];
9
+ }
10
+ export function renderResolvedRegistryShell(model) {
11
+ if (model.siblingsRoot.includes('|') ||
12
+ model.variables.some(({ defaultPath }) => defaultPath.includes('|')) ||
13
+ model.paths.some((path) => path.split('|').length !== 3) ||
14
+ model.rows.some((row) => row.split('|').length !== (model.dialect === 'lane' ? 6 : 7)) ||
15
+ model.adoption.some(({ layer, state }) => layer.includes('|') || state.includes('|'))) {
16
+ throw new Error('REGISTRY_ARTIFACT_UNSAFE: a field contains the shell record delimiter');
17
+ }
18
+ for (const variable of model.variables) {
19
+ if (!/^[A-Z][A-Z0-9_]*_DIR$/.test(variable.name)) {
20
+ throw new Error('REGISTRY_VARIABLE_INVALID: expected a namespaced sibling directory variable');
21
+ }
22
+ }
23
+ return [
24
+ `COCKPIT_REGISTRY_FORMAT=${quote(model.formatVersion)}`,
25
+ `COCKPIT_SOURCE_REVISION=${quote(model.cockpitSourceRevision)}`,
26
+ `COCKPIT_REGISTRY_SHA256=${quote(model.registrySha256)}`,
27
+ `COCKPIT_REGISTRY_DIALECT=${quote(model.dialect)}`,
28
+ `COCKPIT_SIBLINGS_ROOT=${quote(model.siblingsRoot)}`,
29
+ ...array('COCKPIT_REGISTRY_ROWS', model.rows),
30
+ ...array('COCKPIT_SIBLING_PATHS', model.paths),
31
+ ...array('COCKPIT_SIBLING_VARIABLES', model.variables.map((variable) => `${variable.name}|${variable.defaultPath}`)),
32
+ ...array('COCKPIT_ADOPTION_STATES', model.adoption.map(({ layer, state }) => `${layer}|${state}`)),
33
+ ...model.variables.map(({ name, defaultPath }) => `export ${name}=${quote(defaultPath)}`),
34
+ '',
35
+ ].join('\n');
36
+ }
@@ -0,0 +1,29 @@
1
+ import { type ExecutableFix, type FixContext } from './fix-catalogue.js';
2
+ import type { Diagnostic } from './diagnostics.js';
3
+ interface RepairInput {
4
+ readonly changes: readonly Readonly<{
5
+ operation: 'write' | 'remove';
6
+ path: string;
7
+ }>[];
8
+ readonly repairAuthority?: Readonly<{
9
+ kind: 'generated-environment-repair';
10
+ }>;
11
+ readonly outcome: Readonly<{
12
+ diagnostics: readonly Diagnostic[];
13
+ }>;
14
+ readonly fixContext: FixContext;
15
+ readonly repairRefusal?: Readonly<{
16
+ kind: 'refusal';
17
+ artifact: null;
18
+ destination: null;
19
+ diagnostics: readonly Diagnostic[];
20
+ result: null;
21
+ status: 2;
22
+ }>;
23
+ }
24
+ export declare function proposeGeneratedEnvironmentRepair(currentness: RepairInput): Readonly<{
25
+ changes: RepairInput['changes'];
26
+ fixes: readonly ExecutableFix[];
27
+ refusal: RepairInput['repairRefusal'] | null;
28
+ }>;
29
+ export {};
package/dist/repair.js ADDED
@@ -0,0 +1,8 @@
1
+ import { admitDiagnosticFix } from './fix-catalogue.js';
2
+ export function proposeGeneratedEnvironmentRepair(currentness) {
3
+ const fixes = currentness.repairAuthority ? currentness.outcome.diagnostics.flatMap(diagnostic => {
4
+ const admitted = admitDiagnosticFix(diagnostic, currentness.fixContext);
5
+ return admitted ? [admitted] : [];
6
+ }) : [];
7
+ return { changes: currentness.changes, fixes, refusal: currentness.repairRefusal ?? null };
8
+ }
@@ -0,0 +1,9 @@
1
+ /** A path or glob (`*`, `?`, `**`), optionally `<slug>:`-prefixed, without `..` segments or a leading `/`. */
2
+ export declare const REPOSITORY_PATH: RegExp;
3
+ /** One file or directory, optionally `<slug>:`-prefixed: no wildcards, no `.` or `..` segments, no leading or trailing `/`. */
4
+ export declare const REPOSITORY_FILE: RegExp;
5
+ /** The sibling a reference names, if any, and the path inside that repository. */
6
+ export declare function splitRepositoryPath(reference: string): Readonly<{
7
+ slug?: string;
8
+ path: string;
9
+ }>;
@@ -0,0 +1,16 @@
1
+ // A path in one of a cockpit's repositories: `path` in the cockpit's own, or `<sibling slug>:path` in a
2
+ // sibling. Work-order scopes and sources, the [docs] declarations and every reader of them spell and
3
+ // split it here, so a reference one admits is the reference the other resolves.
4
+ const SLUG = '[a-z0-9][a-z0-9-]{0,63}';
5
+ // Never absolute or climbing. The climbing check looks past the sibling prefix, which would otherwise
6
+ // consume the `:` before `..`.
7
+ /** A path or glob (`*`, `?`, `**`), optionally `<slug>:`-prefixed, without `..` segments or a leading `/`. */
8
+ export const REPOSITORY_PATH = new RegExp(`^(?!(?:${SLUG}:)?(?:.*/)?\\.\\.(?:/|$))(?:${SLUG}:)?(?![/])[A-Za-z0-9._@+\\-/*?]+$`);
9
+ /** One file or directory, optionally `<slug>:`-prefixed: no wildcards, no `.` or `..` segments, no leading or trailing `/`. */
10
+ export const REPOSITORY_FILE = new RegExp(`^(?!(?:${SLUG}:)?(?:.*/)?\\.\\.?(?:/|$))(?:${SLUG}:)?(?![/])[A-Za-z0-9._@+\\-/]*[A-Za-z0-9._@+-]$`);
11
+ const PREFIXED = new RegExp(`^(${SLUG}):(.*)$`);
12
+ /** The sibling a reference names, if any, and the path inside that repository. */
13
+ export function splitRepositoryPath(reference) {
14
+ const match = PREFIXED.exec(reference);
15
+ return match ? { slug: match[1], path: match[2] } : { path: reference };
16
+ }
@@ -0,0 +1 @@
1
+ export declare function renderCockpitSchema(): string;
@@ -0,0 +1,177 @@
1
+ import { z } from 'zod';
2
+ import { CockpitConfig, OPERATOR_GATE_RISK_PATTERN, STRUCTURED_INPUT_ENTRY_LIMIT } from './schema.js';
3
+ export function renderCockpitSchema() {
4
+ const schema = z.toJSONSchema(CockpitConfig, {
5
+ io: 'input',
6
+ target: 'draft-04',
7
+ // Draft 4 has no propertyNames, and Zod drops a record's key schema there, so an editor
8
+ // accepted any key the loader refuses. Every key must match one pattern property instead.
9
+ override: ({ zodSchema, jsonSchema }) => {
10
+ const def = zodSchema._zod.def;
11
+ if (def.type !== 'record')
12
+ return;
13
+ const { type, pattern, maxLength, ...rest } = z.toJSONSchema(def.keyType, { io: 'input', target: 'draft-7' });
14
+ delete rest.$schema;
15
+ const value = jsonSchema.additionalProperties;
16
+ if (type !== 'string' || typeof pattern !== 'string' || Object.keys(rest).length > 0 || maxLength !== undefined && typeof maxLength !== 'number'
17
+ || value === undefined || typeof value === 'boolean') {
18
+ throw new Error(`record key schema ${JSON.stringify({ type, pattern, maxLength, ...rest })} has no draft-04 rendering`);
19
+ }
20
+ const key = maxLength === undefined ? pattern : `^(?=[\\s\\S]{0,${maxLength}}$)(?:${pattern})`;
21
+ jsonSchema.patternProperties = { [key]: value };
22
+ jsonSchema.additionalProperties = false;
23
+ },
24
+ });
25
+ const properties = schema.properties;
26
+ const sibling = properties.sibling.items;
27
+ const environment = properties.environment;
28
+ const environmentProperties = environment.properties;
29
+ const browser = properties.browser;
30
+ const browserProperties = browser.properties;
31
+ const testing = properties.testing;
32
+ const postureRequirement = environmentProperties.posture_requirements.items;
33
+ postureRequirement.anyOf = [{ required: ['addressing'] }, { required: ['authentication'] }];
34
+ for (const field of ['resettable_volumes', 'generated_host_paths', 'compose_files']) {
35
+ environmentProperties[field].uniqueItems = true;
36
+ }
37
+ ;
38
+ browserProperties.harnesses.uniqueItems = true;
39
+ const browserProxy = browserProperties.proxy;
40
+ const browserProxyProperties = browserProxy.properties;
41
+ browserProxyProperties.allowed_hosts.uniqueItems = true;
42
+ browser.anyOf = [
43
+ { properties: { request_preapproval: { enum: [false] } } },
44
+ { properties: { data: { required: ['mode', 'isolated', 'reaches_production'], properties: {
45
+ mode: { enum: ['empty', 'seeded'] }, isolated: { enum: [true] }, reaches_production: { enum: [false] },
46
+ } } } },
47
+ ];
48
+ testing.oneOf = [
49
+ { properties: { native_hooks: { properties: { install: { enum: ['inspect'] } } } }, not: { required: ['native_hook_policy'] } },
50
+ { properties: { native_hooks: { properties: { install: { enum: ['explicit-native'] } }, required: ['install'] } }, required: ['native_hooks', 'native_hook_policy'] },
51
+ ];
52
+ sibling.allOf = [
53
+ {
54
+ oneOf: [
55
+ {
56
+ properties: { acquire: { enum: ['none'] } },
57
+ not: { required: ['remote'] },
58
+ },
59
+ {
60
+ properties: { acquire: { enum: ['auto', 'manual'] } },
61
+ required: ['remote'],
62
+ },
63
+ ],
64
+ },
65
+ {
66
+ anyOf: [
67
+ {
68
+ properties: { mount: { enum: ['host'] } },
69
+ required: ['host_reason'],
70
+ },
71
+ {
72
+ properties: { mount: { not: { enum: ['host'] } } },
73
+ },
74
+ ],
75
+ },
76
+ {
77
+ anyOf: [
78
+ {
79
+ properties: { gate: { enum: ['operator'] } },
80
+ required: ['gate'],
81
+ },
82
+ {
83
+ not: {
84
+ anyOf: [
85
+ { properties: { mount: { enum: ['host'] } } },
86
+ { properties: { purpose: { pattern: OPERATOR_GATE_RISK_PATTERN } } },
87
+ ],
88
+ },
89
+ },
90
+ ],
91
+ },
92
+ ];
93
+ schema['x-cockpit-runtime-checks'] = [
94
+ { path: 'sibling[].slug', rule: 'unique' },
95
+ { path: 'sibling[].dir', rule: 'unique' },
96
+ { path: 'sibling[].var', rule: 'unique' },
97
+ { path: 'sibling[].environment_files[].instance', rule: 'unique' },
98
+ { path: 'environment.resettable_volumes[]', rule: 'unique' },
99
+ { path: 'environment.resettable_volumes[]', rule: 'declared_compose_volume_key' },
100
+ { path: 'environment.generated_host_paths[]', rule: 'unique' },
101
+ { path: 'environment.compose_files[]', rule: 'unique' },
102
+ { path: 'environment.entrypoint_derivatives[].name', rule: 'unique' },
103
+ { path: 'environment.install_policies[].toolchain+lockfile', rule: 'unique' },
104
+ { path: 'environment.posture_requirements[].requirement', rule: 'unique' },
105
+ { path: 'environment.entrypoint_derivatives[].port', rule: 'declared_in_ports.bases' },
106
+ { path: 'browser.artifacts', rule: 'exact_environment.generated_host_paths_coverage' },
107
+ { path: 'browser', rule: 'matches_environment_addressing_authentication_and_client_cap' },
108
+ { path: 'browser.images', rule: 'mcp_driver_shares_major_minor_with_project_driver' },
109
+ { path: 'testing.gates[].stages[]', rule: 'references_declared_testing_stage' },
110
+ { path: 'sibling[].environment_files[]', rule: 'template_and_instance_differ' },
111
+ { path: 'sibling[].derives.mcp_sources[].path', rule: 'unique_within_sibling' },
112
+ { path: 'environment.seed', rule: 'requires_resettable_volumes' },
113
+ {
114
+ path: 'ports',
115
+ rule: 'count_times_base_count_within_shared_input_budget',
116
+ limit: STRUCTURED_INPUT_ENTRY_LIMIT,
117
+ },
118
+ { path: 'surfaces.overlays[].name', rule: 'unique' },
119
+ { path: 'surfaces.overlays[].sources[].path', rule: 'unique' },
120
+ { path: 'surfaces.overlays[].sources', rule: 'feature_namespace_and_complete_skill_definitions' },
121
+ { path: 'surfaces.overlays[].sources[].bytes', rule: 'utf8_byte_length_at_most_65536' },
122
+ { path: 'surfaces.overlays[].replaces.name', rule: 'matches_declaration_name' },
123
+ { path: 'surfaces.harnesses[].name', rule: 'unique' },
124
+ { path: 'surfaces.harnesses[].capabilities', rule: 'one_row_per_surface_feature' },
125
+ { path: 'surfaces.harnesses[].capabilities[].spelling', rule: 'unique_when_emitted' },
126
+ { path: 'surfaces.adopted[].path', rule: 'unique' },
127
+ { path: 'surfaces.adopted[].source', rule: 'equals_cockpit/adopted/<path>' },
128
+ { path: 'surfaces.adopted[].provenance.kind', rule: 'declared' },
129
+ ];
130
+ schema['x-cockpit-editor-limitations'] = [
131
+ { path: 'sibling[].environment_files[].instance', rule: 'unique' },
132
+ { path: 'sibling[].environment_files[]', rule: 'template_and_instance_differ' },
133
+ { path: 'sibling[].derives.mcp_sources[].path', rule: 'unique_within_sibling' },
134
+ { path: 'environment.seed', rule: 'requires_resettable_volumes' },
135
+ { path: 'environment.entrypoint_derivatives[].name', rule: 'unique' },
136
+ { path: 'environment.entrypoint_derivatives[].port', rule: 'declared_in_ports.bases' },
137
+ { path: 'environment.install_policies[].toolchain+lockfile', rule: 'unique' },
138
+ { path: 'environment.posture_requirements[].requirement', rule: 'unique' },
139
+ { path: 'browser.artifacts', rule: 'exact_environment.generated_host_paths_coverage' },
140
+ { path: 'browser', rule: 'matches_environment_addressing_authentication_and_client_cap' },
141
+ { path: 'browser.images', rule: 'mcp_driver_shares_major_minor_with_project_driver' },
142
+ { path: 'testing.gates[].stages[]', rule: 'references_declared_testing_stage' },
143
+ { path: 'surfaces.overlays[].name', rule: 'unique' },
144
+ { path: 'surfaces.overlays[].sources[].path', rule: 'unique' },
145
+ { path: 'surfaces.overlays[].sources', rule: 'feature_namespace_and_complete_skill_definitions' },
146
+ { path: 'surfaces.overlays[].sources[].bytes', rule: 'utf8_byte_length_at_most_65536' },
147
+ { path: 'surfaces.overlays[].replaces.name', rule: 'matches_declaration_name' },
148
+ { path: 'surfaces.harnesses[].name', rule: 'unique' },
149
+ { path: 'surfaces.harnesses[].capabilities', rule: 'one_row_per_surface_feature' },
150
+ { path: 'surfaces.harnesses[].capabilities[].spelling', rule: 'unique_when_emitted' },
151
+ { path: 'surfaces.adopted[].path', rule: 'unique' },
152
+ { path: 'surfaces.adopted[].source', rule: 'equals_cockpit/adopted/<path>' },
153
+ { path: 'surfaces.adopted[].provenance.kind', rule: 'declared' },
154
+ ];
155
+ // Found by shape, so every table that embeds a provenance carries the rule, wherever it sits.
156
+ const requireProvenanceEvidence = (node) => {
157
+ const fields = node.properties;
158
+ if (fields?.observed_on && fields.kind && fields.source) {
159
+ node.anyOf = [
160
+ { properties: { kind: { enum: ['declared'] } } },
161
+ { properties: { kind: { enum: ['measured'] } }, required: ['observed_on'] },
162
+ { properties: { kind: { enum: ['inferred'] }, confirmed: { enum: [true] } }, required: ['confirmed', 'observed_on'] },
163
+ ];
164
+ }
165
+ for (const value of Object.values(node)) {
166
+ if (Array.isArray(value))
167
+ for (const entry of value) {
168
+ if (entry !== null && typeof entry === 'object')
169
+ requireProvenanceEvidence(entry);
170
+ }
171
+ else if (value !== null && typeof value === 'object')
172
+ requireProvenanceEvidence(value);
173
+ }
174
+ };
175
+ requireProvenanceEvidence(schema);
176
+ return `${JSON.stringify(schema, null, 2)}\n`;
177
+ }