@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,513 @@
1
+ import { createHash, randomBytes } from 'node:crypto';
2
+ import { constants } from 'node:fs';
3
+ import { lstat, open, realpath, rename, unlink } from 'node:fs/promises';
4
+ import { basename, dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+ import { loadCockpitConfig, readBoundedInput } from './config/load.js';
7
+ import { ENVIRONMENT_CODES, errorCause, hasErrorCode, isMissing, parseJsonText, RefusalError, tryParseJson } from './environment/contract.js';
8
+ import { installedPackageMetadata } from './package-metadata.js';
9
+ import { cockpitCommand } from './fix-catalogue.js';
10
+ import { GUARD_LOCAL_PATHS, parseGuardLocalIdentity } from './guards/local-identity.js';
11
+ import { guardLauncherEvidence } from './guards/protected-locations.js';
12
+ const PACKAGE = installedPackageMetadata.name;
13
+ const VERSION = installedPackageMetadata.version;
14
+ const INSTALLED_PACKAGE_ROOT = dirname(fileURLToPath(new URL('../package.json', import.meta.url)));
15
+ const SCRIPTS = Object.freeze({
16
+ 'cockpit:setup': 'cockpit setup --config cockpit.toml',
17
+ 'cockpit:sync': 'cockpit sync --config cockpit.toml',
18
+ 'cockpit:check': 'cockpit check --config cockpit.toml',
19
+ });
20
+ const START = '# cockpit:ignore:start';
21
+ const END = '# cockpit:ignore:end';
22
+ const OTHER_DEPENDENCY_SECTIONS = ['dependencies', 'optionalDependencies', 'peerDependencies'];
23
+ const PACKAGE_JSON_SHAPE = 'an npm package.json object';
24
+ function refuse(code, message, target) {
25
+ return { kind: 'refusal', code, message, target };
26
+ }
27
+ // The loader already names the failing key and reason; replacing it with a generic refusal left
28
+ // sync reporting "cockpit.toml must be valid" for every malformed configuration.
29
+ function configRefusal(loaded, configPath) {
30
+ const reason = loaded.diagnostics.find(diagnostic => diagnostic.severity === 'error');
31
+ if (!reason)
32
+ return refuse('TEAM_METADATA_UNAVAILABLE', 'cockpit.toml could not be loaded and the loader gave no reason', configPath);
33
+ return { kind: 'refusal', code: reason.code, message: reason.message, target: reason.target ?? configPath, ...(reason.fix ? { fix: reason.fix } : {}) };
34
+ }
35
+ function object(value) {
36
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
37
+ }
38
+ async function captureRoot(root) {
39
+ if (await realpath(root) !== root)
40
+ throw new RefusalError('cockpit root is not canonical');
41
+ const chain = [];
42
+ for (let path = root;; path = dirname(path)) {
43
+ const status = await lstat(path);
44
+ if (!status.isDirectory() || status.isSymbolicLink())
45
+ throw new RefusalError('cockpit root ancestry is redirected');
46
+ chain.push({ path, dev: status.dev, ino: status.ino });
47
+ if (dirname(path) === path)
48
+ break;
49
+ if (chain.length >= 64)
50
+ throw new RefusalError('cockpit root ancestry exceeds the verification bound');
51
+ }
52
+ return { root, chain };
53
+ }
54
+ async function authenticateRoot(authority) {
55
+ if (await realpath(authority.root) !== authority.root)
56
+ throw new RefusalError('cockpit root was redirected');
57
+ for (const expected of authority.chain) {
58
+ const status = await lstat(expected.path);
59
+ if (!status.isDirectory() || status.isSymbolicLink() || status.dev !== expected.dev || status.ino !== expected.ino) {
60
+ throw new RefusalError('cockpit root ancestry changed');
61
+ }
62
+ }
63
+ }
64
+ async function readOptional(authority, path) {
65
+ if (dirname(path) !== authority.root)
66
+ throw new RefusalError('team metadata escaped the cockpit root');
67
+ await authenticateRoot(authority);
68
+ try {
69
+ const stat = await lstat(path);
70
+ if (!stat.isFile() || stat.isSymbolicLink())
71
+ throw new RefusalError('not a regular file');
72
+ const read = await readBoundedInput(path);
73
+ if (read.kind !== 'complete')
74
+ throw new RefusalError('file exceeds the input limit');
75
+ await authenticateRoot(authority);
76
+ let text;
77
+ try {
78
+ text = new TextDecoder('utf-8', { fatal: true }).decode(read.bytes);
79
+ }
80
+ catch {
81
+ throw new RefusalError(`${basename(path)} is not valid UTF-8`);
82
+ } // The decoder throws only for invalid UTF-8.
83
+ return { bytes: text, mode: stat.mode,
84
+ identity: `${stat.dev}:${stat.ino}:${stat.size}:${stat.mtimeMs}:${createHash('sha256').update(read.bytes).digest('hex')}` };
85
+ }
86
+ catch (error) {
87
+ if (hasErrorCode(error, 'ENOENT')) {
88
+ await authenticateRoot(authority);
89
+ return undefined;
90
+ }
91
+ throw error;
92
+ }
93
+ }
94
+ async function replaceAuthenticated(authority, path, before, bytes) {
95
+ const directory = dirname(path);
96
+ if (directory !== authority.root)
97
+ throw new RefusalError('team metadata escaped the cockpit root');
98
+ const temporary = join(directory, `.${path.slice(path.lastIndexOf('/') + 1)}.cockpit-${randomBytes(8).toString('hex')}`);
99
+ let created = false;
100
+ try {
101
+ await authenticateRoot(authority);
102
+ const handle = await open(temporary, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, before?.mode ?? 0o644);
103
+ created = true;
104
+ try {
105
+ await handle.writeFile(bytes);
106
+ await handle.sync();
107
+ }
108
+ finally {
109
+ await handle.close();
110
+ }
111
+ await authenticateRoot(authority);
112
+ const current = await readOptional(authority, path);
113
+ if (current?.identity !== before?.identity)
114
+ throw new RefusalError('file changed before replacement');
115
+ await authenticateRoot(authority);
116
+ await rename(temporary, path);
117
+ created = false;
118
+ await authenticateRoot(authority);
119
+ const parent = await open(directory, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
120
+ try {
121
+ const status = await parent.stat();
122
+ const expected = authority.chain[0];
123
+ if (!status.isDirectory() || status.dev !== expected.dev || status.ino !== expected.ino)
124
+ throw new RefusalError('cockpit root changed before sync');
125
+ await parent.sync();
126
+ }
127
+ finally {
128
+ await parent.close();
129
+ }
130
+ }
131
+ finally {
132
+ if (created) {
133
+ await authenticateRoot(authority);
134
+ await unlink(temporary);
135
+ }
136
+ }
137
+ }
138
+ /**
139
+ * The package manifest without the entries sync owns in it: the cockpit dependency, under any spec,
140
+ * and each cockpit script while it is the exact command sync writes. Sections those removals leave
141
+ * empty are dropped, so a manifest cockpit created and one it edited project the same way.
142
+ */
143
+ export function productPackageManifest(manifest) {
144
+ const product = { ...manifest };
145
+ if (object(manifest.devDependencies)) {
146
+ const { [PACKAGE]: _pin, ...rest } = manifest.devDependencies;
147
+ product.devDependencies = rest;
148
+ }
149
+ if (object(manifest.scripts))
150
+ product.scripts = Object.fromEntries(Object.entries(manifest.scripts)
151
+ .filter(([name, command]) => SCRIPTS[name] !== command));
152
+ for (const section of ['devDependencies', 'scripts']) {
153
+ const value = product[section];
154
+ if (object(value) && Object.keys(value).length === 0)
155
+ delete product[section];
156
+ }
157
+ return product;
158
+ }
159
+ function packageDependencySlots(manifest) {
160
+ const devDependencies = manifest.devDependencies === undefined ? {} : manifest.devDependencies;
161
+ if (!object(devDependencies))
162
+ return undefined;
163
+ const misplaced = [];
164
+ for (const section of OTHER_DEPENDENCY_SECTIONS) {
165
+ const dependencies = manifest[section];
166
+ if (dependencies !== undefined && !object(dependencies))
167
+ return undefined;
168
+ if (object(dependencies) && dependencies[PACKAGE] !== undefined)
169
+ misplaced.push(section);
170
+ }
171
+ return { devDependencies, misplaced };
172
+ }
173
+ function misplacedDependencyMessage(sections) {
174
+ return `package.json declares ${PACKAGE} in ${sections.join(', ')} outside cockpit-owned devDependencies; cockpit requires one exact development dependency so the team has one version authority. Run npm uninstall ${PACKAGE} && npm install --save-dev --save-exact ${PACKAGE}@${VERSION}, then run npx --no cockpit sync --config <your cockpit.toml> again`;
175
+ }
176
+ function packageCandidate(bytes, verifiedLocalPin) {
177
+ let parsed = {};
178
+ if (bytes !== undefined && bytes.trim() !== '') {
179
+ const decoded = parseJsonText(bytes, 'package.json', PACKAGE_JSON_SHAPE);
180
+ if ('refusal' in decoded)
181
+ return { bytes: '', valid: false, cause: decoded.refusal };
182
+ parsed = decoded.value;
183
+ }
184
+ if (!object(parsed))
185
+ return { bytes: '', valid: false };
186
+ const slots = packageDependencySlots(parsed);
187
+ const scripts = parsed.scripts === undefined ? {} : parsed.scripts;
188
+ if (!slots || !object(scripts))
189
+ return { bytes: '', valid: false };
190
+ if (slots.misplaced.length > 0)
191
+ return { bytes: '', valid: false, misplaced: slots.misplaced };
192
+ const conflictingDependency = slots.devDependencies[PACKAGE] !== undefined && slots.devDependencies[PACKAGE] !== VERSION
193
+ && slots.devDependencies[PACKAGE] !== verifiedLocalPin;
194
+ const conflictingScript = Object.entries(SCRIPTS).some(([name, command]) => scripts[name] !== undefined && scripts[name] !== command);
195
+ if (conflictingDependency || conflictingScript)
196
+ return { bytes: '', valid: false };
197
+ return { valid: true, bytes: `${JSON.stringify({ ...parsed,
198
+ scripts: { ...scripts, ...SCRIPTS }, devDependencies: { ...slots.devDependencies, [PACKAGE]: verifiedLocalPin ?? VERSION },
199
+ }, null, 2)}\n` };
200
+ }
201
+ // `npm install -D <archive>` pins a `file:` spec, and before a release that archive is the only
202
+ // source of the package. The pin is accepted as the team's version when the lockfile resolves it
203
+ // to this exact running package; rewriting it to the bare version named a release that may not
204
+ // exist and changed a dependency the user chose without asking. The installed copy is compared by
205
+ // manifest bytes, not by location: the bootstrap route runs init from a temporary install of the
206
+ // same archive, and requiring the running copy to be the root's own install refused that resume.
207
+ async function verifiedLocalPin(authority, packageBytes) {
208
+ // An unparseable package.json is refused by the caller's own parse of the same bytes.
209
+ const manifest = packageBytes === undefined ? undefined : tryParseJson(packageBytes);
210
+ const dependencies = object(manifest) && object(manifest.devDependencies) ? manifest.devDependencies : undefined;
211
+ const spec = dependencies?.[PACKAGE];
212
+ if (typeof spec !== 'string' || !spec.startsWith('file:') || spec.length > 4096)
213
+ return undefined;
214
+ const installedPath = join(authority.root, 'node_modules', ...PACKAGE.split('/'));
215
+ let installed;
216
+ try {
217
+ installed = await lstat(installedPath);
218
+ }
219
+ catch (error) {
220
+ if (hasErrorCode(error, 'ENOENT'))
221
+ return undefined;
222
+ throw error;
223
+ }
224
+ if (!installed.isDirectory() || installed.isSymbolicLink())
225
+ return undefined;
226
+ if (await realpath(installedPath) !== await realpath(INSTALLED_PACKAGE_ROOT)) {
227
+ const installedManifest = await readBoundedInput(join(installedPath, 'package.json'));
228
+ const runningManifest = await readBoundedInput(join(INSTALLED_PACKAGE_ROOT, 'package.json'));
229
+ if (installedManifest.kind !== 'complete' || runningManifest.kind !== 'complete'
230
+ || !Buffer.from(installedManifest.bytes).equals(Buffer.from(runningManifest.bytes)))
231
+ return undefined;
232
+ }
233
+ const lockFile = await readOptional(authority, join(authority.root, 'package-lock.json'));
234
+ if (!lockFile)
235
+ return undefined;
236
+ // An unparseable lockfile verifies no pin; the caller then refuses the file: pin as unresolved.
237
+ const lock = tryParseJson(lockFile.bytes);
238
+ const packages = object(lock) && object(lock.packages) ? lock.packages : undefined;
239
+ const rootEntry = packages?.[''];
240
+ const rootDependencies = object(rootEntry) && object(rootEntry.devDependencies) ? rootEntry.devDependencies : undefined;
241
+ const installedEntry = packages?.[`node_modules/${PACKAGE}`];
242
+ if (!rootDependencies || rootDependencies[PACKAGE] !== spec || !object(installedEntry)
243
+ || installedEntry.version !== VERSION || installedEntry.resolved !== spec)
244
+ return undefined;
245
+ return spec;
246
+ }
247
+ // A checkout is its own Git repository; left visible inside the cockpit root, a `git add` embeds
248
+ // it as a gitlink, or fails on one with no commit yet. Cloned rows are repositories by declaration.
249
+ // `acquire = "none"` rows are local data the cockpit itself may track, except a directory that
250
+ // already holds Git metadata: `sibling add --create` records a new repository as "none" until it
251
+ // has a remote, so for those rows the disk decides.
252
+ async function ignoreBlock(root, config) {
253
+ const siblingsRoot = config.cockpit.siblings_root;
254
+ if (/[\0\r\n]/.test(siblingsRoot))
255
+ return undefined;
256
+ // Claude Code's machine-local settings, where `cockpit use` grants a served checkout.
257
+ const patterns = ['/node_modules/', '/.cockpit/local/', '/.claude/settings.local.json', '/env/.secrets/*', '!/env/.secrets/.gitignore'];
258
+ const inside = (path, kind = 'directory') => {
259
+ const local = relative(root, path);
260
+ if (local === '' || local === '..' || local.startsWith(`..${sep}`) || isAbsolute(local))
261
+ return undefined;
262
+ return `/${local.split(sep).map((part) => part.replace(/[\\*?\[\] !#]/g, '\\$&')).join('/')}${kind === 'directory' ? '/' : ''}`;
263
+ };
264
+ // Generated host paths (browser artifact roots among them) hold run output such as screenshots;
265
+ // left unignored, `git add` commits them. One may be a file bound into a service, which a
266
+ // trailing-slash pattern would not match.
267
+ for (const generated of config.environment.generated_host_paths) {
268
+ const pattern = inside(resolve(root, generated), 'any');
269
+ if (pattern && !patterns.includes(pattern))
270
+ patterns.push(pattern);
271
+ }
272
+ const siblings = resolve(root, siblingsRoot);
273
+ const siblingsPattern = inside(siblings);
274
+ if (siblingsPattern)
275
+ patterns.push(siblingsPattern);
276
+ else
277
+ for (const sibling of config.sibling) {
278
+ const path = resolve(siblings, sibling.dir);
279
+ const pattern = inside(path);
280
+ if (pattern && !patterns.includes(pattern) && (sibling.acquire !== 'none' || await holdsGitMetadata(path)))
281
+ patterns.push(pattern);
282
+ }
283
+ const body = patterns.join('\n');
284
+ return `${blockHeader(body)}\n${body}\n${END}`;
285
+ }
286
+ async function holdsGitMetadata(directory) {
287
+ try {
288
+ await lstat(join(directory, '.git'));
289
+ return true;
290
+ }
291
+ catch (error) {
292
+ if (isMissing(error))
293
+ return false;
294
+ throw error;
295
+ }
296
+ }
297
+ // The start marker carries a digest of the region it opens, which is what separates a region a
298
+ // changed configuration made stale (rewritten) from one a person edited (refused). Without it,
299
+ // adding a sibling under a siblings_root of "." made every later sync refuse the cockpit's own block.
300
+ function blockHeader(body) {
301
+ return `${START} sha256=${createHash('sha256').update(body).digest('hex').slice(0, 16)}`;
302
+ }
303
+ function writtenByCockpit(region) {
304
+ const header = region.slice(0, Math.max(0, region.indexOf('\n')));
305
+ const body = region.slice(header.length + 1, region.length - END.length - 1);
306
+ return header !== '' && region.endsWith(`\n${END}`) && header === blockHeader(body);
307
+ }
308
+ function ignoreCandidate(original, block) {
309
+ const start = original.indexOf(START);
310
+ const end = original.indexOf(END);
311
+ if ((start < 0) !== (end < 0) || start >= 0 && (original.indexOf(START, start + 1) >= 0 || original.indexOf(END, end + 1) >= 0 || end < start))
312
+ return undefined;
313
+ if (start < 0)
314
+ return `${original}${original.length > 0 && !original.endsWith('\n') ? '\n' : ''}${block}\n`;
315
+ const region = original.slice(start, end + END.length);
316
+ if (region === block)
317
+ return original;
318
+ return writtenByCockpit(region) ? `${original.slice(0, start)}${block}${original.slice(end + END.length)}` : undefined;
319
+ }
320
+ const issuedMetadata = new WeakMap();
321
+ export async function prepareTeamMetadata(configPath) {
322
+ const root = dirname(resolve(configPath));
323
+ try {
324
+ const authority = await captureRoot(root);
325
+ const loaded = await loadCockpitConfig(configPath);
326
+ await authenticateRoot(authority);
327
+ if (loaded.kind !== 'complete')
328
+ return configRefusal(loaded, configPath);
329
+ const block = await ignoreBlock(root, loaded.result.config);
330
+ if (block === undefined) {
331
+ return refuse('TEAM_METADATA_UNAVAILABLE', `cockpit.siblings_root must be a single-line path without NUL because .gitignore cannot represent it literally; fix cockpit.toml and run ${cockpitCommand('sync', { config: configPath })}`, 'cockpit.siblings_root');
332
+ }
333
+ const packagePath = join(root, 'package.json');
334
+ const packageBefore = await readOptional(authority, packagePath);
335
+ const candidate = packageCandidate(packageBefore?.bytes, await verifiedLocalPin(authority, packageBefore?.bytes));
336
+ if (candidate.misplaced)
337
+ return refuse('TEAM_METADATA_UNAVAILABLE', misplacedDependencyMessage(candidate.misplaced), packagePath);
338
+ if (candidate.cause)
339
+ return refuse('TEAM_METADATA_UNAVAILABLE', `${candidate.cause}; repair it, then run ${cockpitCommand('sync', { config: configPath })}`, packagePath);
340
+ if (!candidate.valid)
341
+ return refuse('TEAM_METADATA_UNAVAILABLE', `package.json conflicts with cockpit's exact ${PACKAGE} pin or cockpit:* scripts; resolve the conflict and run ${cockpitCommand('sync', { config: configPath })}`, packagePath);
342
+ const ignorePath = join(root, '.gitignore');
343
+ const ignoreBefore = await readOptional(authority, ignorePath);
344
+ const nextIgnore = ignoreCandidate(ignoreBefore?.bytes ?? '', block);
345
+ if (nextIgnore === undefined)
346
+ return refuse('TEAM_METADATA_UNAVAILABLE', `the cockpit-owned .gitignore block was edited or malformed; restore it exactly, or remove the whole ${START} through ${END} region and run ${cockpitCommand('sync', { config: configPath })}`, ignorePath);
347
+ const files = [
348
+ { path: packagePath, before: packageBefore, bytes: candidate.bytes },
349
+ { path: ignorePath, before: ignoreBefore, bytes: nextIgnore },
350
+ ];
351
+ const changes = files.filter(file => file.before?.bytes !== file.bytes)
352
+ .map(file => ({ operation: 'write', path: relative(root, file.path) }));
353
+ const prepared = { changes };
354
+ issuedMetadata.set(prepared, { authority, configPath: loaded.result.source.path,
355
+ sourceSha256: loaded.result.source.sha256, files });
356
+ return { kind: 'complete', authority: prepared };
357
+ }
358
+ catch (error) {
359
+ return refuse('TEAM_METADATA_UNAVAILABLE', `team metadata could not be updated (${errorCause(error)})`, root);
360
+ }
361
+ }
362
+ export async function applyTeamMetadata(prepared) {
363
+ const record = issuedMetadata.get(prepared);
364
+ if (!record)
365
+ return refuse('TEAM_METADATA_UNAVAILABLE', 'this team metadata update was already applied or was never prepared; run npx --no cockpit sync --config <your cockpit.toml> again', 'team-metadata');
366
+ issuedMetadata.delete(prepared);
367
+ const { authority, files } = record;
368
+ try {
369
+ const loaded = await loadCockpitConfig(record.configPath);
370
+ if (loaded.kind !== 'complete' || loaded.result.source.sha256 !== record.sourceSha256)
371
+ throw new RefusalError('configuration changed before metadata replacement');
372
+ await authenticateRoot(authority);
373
+ for (const file of files) {
374
+ if ((await readOptional(authority, file.path))?.identity !== file.before?.identity)
375
+ throw new RefusalError('team metadata changed before replacement');
376
+ }
377
+ for (const file of files)
378
+ if (file.before?.bytes !== file.bytes)
379
+ await replaceAuthenticated(authority, file.path, file.before, file.bytes);
380
+ return { kind: 'complete', changed: prepared.changes.length > 0, changes: prepared.changes };
381
+ }
382
+ catch (error) {
383
+ return refuse('TEAM_METADATA_UNAVAILABLE', `team metadata could not be updated (${errorCause(error)})`, authority.root);
384
+ }
385
+ }
386
+ export async function validateTeamMetadata(configPath) {
387
+ const root = dirname(resolve(configPath));
388
+ const packagePath = join(root, 'package.json');
389
+ try {
390
+ const authority = await captureRoot(root);
391
+ const current = await readOptional(authority, packagePath);
392
+ const decoded = current ? parseJsonText(current.bytes, 'package.json', PACKAGE_JSON_SHAPE) : { value: undefined };
393
+ if ('refusal' in decoded)
394
+ return refuse('TEAM_METADATA_UNAVAILABLE', `${decoded.refusal}; repair it, then run ${cockpitCommand('sync', { config: configPath })}`, packagePath);
395
+ const parsed = decoded.value;
396
+ const slots = object(parsed) ? packageDependencySlots(parsed) : undefined;
397
+ if (object(parsed) && !slots)
398
+ return refuse('TEAM_METADATA_UNAVAILABLE', 'package.json direct npm dependency sections must be objects; repair package.json, then run npx --no cockpit sync --config <your cockpit.toml> again', packagePath);
399
+ if (slots?.misplaced.length)
400
+ return refuse('TEAM_METADATA_UNAVAILABLE', misplacedDependencyMessage(slots.misplaced), packagePath);
401
+ const pinned = slots?.devDependencies[PACKAGE];
402
+ if (typeof pinned === 'string' && pinned.startsWith('file:')) {
403
+ if (pinned !== await verifiedLocalPin(authority, current?.bytes))
404
+ return refuse('COCKPIT_VERSION_MISMATCH', `package.json pins ${PACKAGE} to ${pinned}, but package-lock.json and node_modules do not resolve that archive to the running ${VERSION}; run npm install, then ${cockpitCommand('check')}`, packagePath);
405
+ }
406
+ else if (pinned !== VERSION)
407
+ return refuse('COCKPIT_VERSION_MISMATCH', `package.json pins ${PACKAGE} ${String(pinned ?? '(missing)')}, but ${VERSION} is running; run npm install --save-dev --save-exact ${PACKAGE}@${VERSION}`, packagePath);
408
+ const scripts = object(parsed) && object(parsed.scripts) ? parsed.scripts : undefined;
409
+ if (!scripts || Object.entries(SCRIPTS).some(([name, command]) => scripts[name] !== command)) {
410
+ return refuse('TEAM_METADATA_UNAVAILABLE', `package.json does not contain the exact cockpit:* scripts that sync writes; run ${cockpitCommand('sync', { config: configPath })}`, packagePath);
411
+ }
412
+ const loaded = await loadCockpitConfig(configPath);
413
+ await authenticateRoot(authority);
414
+ if (loaded.kind !== 'complete')
415
+ return configRefusal(loaded, configPath);
416
+ const block = await ignoreBlock(root, loaded.result.config);
417
+ if (block === undefined) {
418
+ return refuse('TEAM_METADATA_UNAVAILABLE', `cockpit.siblings_root must be a single-line path without NUL because .gitignore cannot represent it literally; fix cockpit.toml and run ${cockpitCommand('sync', { config: configPath })}`, 'cockpit.siblings_root');
419
+ }
420
+ const ignorePath = join(root, '.gitignore');
421
+ const ignore = await readOptional(authority, ignorePath);
422
+ const expected = ignoreCandidate(ignore?.bytes ?? '', block);
423
+ if (expected === undefined)
424
+ return refuse('TEAM_METADATA_UNAVAILABLE', `the cockpit-owned .gitignore block was edited; restore it exactly, or remove the whole ${START} through ${END} region and run npx --no cockpit sync --config <your cockpit.toml> again`, ignorePath);
425
+ if (!ignore || expected !== ignore.bytes)
426
+ return refuse('TEAM_METADATA_UNAVAILABLE', 'the cockpit-owned .gitignore block is missing or does not match cockpit.toml and the sibling directories on disk; run npx --no cockpit sync --config <your cockpit.toml> to rewrite it', ignorePath);
427
+ return { kind: 'complete', changed: false, changes: [] };
428
+ }
429
+ catch (error) {
430
+ return refuse('TEAM_METADATA_UNAVAILABLE', `team metadata could not be verified (${errorCause(error)})`, root);
431
+ }
432
+ }
433
+ /**
434
+ * What a teammate's clone of this cockpit, or the cockpit itself, will lack, read from the
435
+ * cockpit root and its declaration without changing anything. `init` runs from a temporary copy of
436
+ * cockpit and leaves none in the cockpit, so every printed `npx --no cockpit` then failed and the
437
+ * guard hook stayed bound to that copy; and a sibling repository recorded without a remote is one
438
+ * a teammate's `setup` silently leaves absent.
439
+ */
440
+ export async function inspectTeamReadiness(configPath) {
441
+ const root = dirname(resolve(configPath));
442
+ const findings = [];
443
+ const installed = join(root, 'node_modules', ...PACKAGE.split('/'));
444
+ const present = await lstat(installed).then((status) => status.isDirectory(), (error) => {
445
+ if (isMissing(error))
446
+ return false;
447
+ throw error;
448
+ });
449
+ if (!present) {
450
+ findings.push({
451
+ severity: 'warning',
452
+ // A literal, so the diagnostic inventory captures the fix beside the code.
453
+ code: 'COCKPIT_NOT_INSTALLED',
454
+ message: `cockpit is not installed in ${root}, so every npx --no cockpit command the generated instructions print fails there, and the guard hook runs whichever copy last ran sync`,
455
+ target: 'node_modules',
456
+ fix: `in a terminal in ${root}, run npm install (package.json pins the version), then run npx --no cockpit sync --config <your cockpit.toml> so the guard hook runs the installed copy`,
457
+ });
458
+ }
459
+ else {
460
+ const read = await readBoundedInput(join(root, GUARD_LOCAL_PATHS.identity)).catch((error) => {
461
+ if (hasErrorCode(error, 'ENOENT'))
462
+ return undefined;
463
+ throw error;
464
+ });
465
+ const identity = read?.kind === 'complete' ? parseGuardLocalIdentity(Buffer.from(read.bytes).toString('utf8')) : undefined;
466
+ const own = await realpath(installed);
467
+ if (identity && relative(own, identity.module).startsWith('..'))
468
+ findings.push({
469
+ severity: 'warning',
470
+ // A literal, so the diagnostic inventory captures the fix beside the code.
471
+ code: 'COCKPIT_NOT_INSTALLED',
472
+ message: `the guard hook runs the cockpit copy at ${identity.module}, not the one installed in ${own}, so it stops working when that copy is removed and can differ from the pinned version`,
473
+ target: GUARD_LOCAL_PATHS.identity,
474
+ fix: `in a terminal in ${root}, run npx --no cockpit sync --config <your cockpit.toml>, which binds the guard hook to the installed copy`,
475
+ });
476
+ // The guard's own proof, run as it runs it from the cockpit root once it runs the installed copy.
477
+ // A root .npmrc, a bin the root package.json declares, or a link that is not npm's leaves every
478
+ // printed npx form asking a person.
479
+ const launcher = join(own, 'bin/cockpit.mjs');
480
+ if (!findings.length && guardLauncherEvidence('npx', ['--no', 'cockpit'], root, root, launcher, '') === undefined) {
481
+ const linked = guardLauncherEvidence('./node_modules/.bin/cockpit', [], root, root, launcher, '') !== undefined;
482
+ findings.push({
483
+ severity: 'warning',
484
+ // A literal, so the diagnostic inventory captures the fix beside the code.
485
+ code: 'COCKPIT_LAUNCHER_UNPROVEN',
486
+ message: `the guard cannot prove that npx --no cockpit run in ${root} starts the cockpit installed there, so it asks a person before every such command and Codex and OpenCode refuse it; `
487
+ + (linked ? './node_modules/.bin/cockpit <command> --config cockpit.toml is proven by its path and runs without asking'
488
+ : 'node_modules/.bin/cockpit is not npm\'s link to the installed package either'),
489
+ target: 'npx --no cockpit',
490
+ fix: 'run cockpit as ./node_modules/.bin/cockpit <command> --config <your cockpit.toml> from the cockpit root, or remove what stops npx from proving it: a .npmrc in the cockpit root, a bin or directories.bin in its package.json, or a node_modules/.bin/cockpit that npm install did not link to the installed package',
491
+ });
492
+ }
493
+ }
494
+ const loaded = await loadCockpitConfig(configPath);
495
+ if (loaded.kind !== 'complete')
496
+ return findings;
497
+ const siblings = resolve(root, loaded.result.config.cockpit.siblings_root);
498
+ for (const sibling of loaded.result.config.sibling) {
499
+ const path = resolve(siblings, sibling.dir);
500
+ // The cockpit's own directory is cloned with the cockpit; a plain directory is tracked by it.
501
+ if (sibling.acquire !== 'none' || path === root || !await holdsGitMetadata(path))
502
+ continue;
503
+ findings.push({
504
+ severity: 'warning',
505
+ // A literal, so the diagnostic inventory captures the fix beside the code.
506
+ code: 'SIBLING_REMOTE_UNRECORDED',
507
+ message: `sibling ${sibling.slug} is its own Git repository, but cockpit.toml records no remote for it (acquire = "none"), so setup in a teammate's clone of this cockpit leaves ${sibling.dir} absent`,
508
+ target: `sibling:${sibling.slug}`,
509
+ fix: `once the repository is pushed, run npx --no cockpit sibling add ${sibling.slug} --remote <url> --config <your cockpit.toml> and commit cockpit.toml`,
510
+ });
511
+ }
512
+ return findings;
513
+ }
@@ -0,0 +1,91 @@
1
+ import { type EnvironmentSnapshot, type RuntimeEvidence } from '../environment/evidence.js';
2
+ import { type ProjectionPlannerInput, type StandingCost } from '../surfaces/projection.js';
3
+ import { type RunClassification, type StageObservation, type StageResult } from './verdict.js';
4
+ export declare const GATE_SELECTOR_OPTION = "--select";
5
+ export type GateSelector = Readonly<{
6
+ kind: 'spec';
7
+ value: string;
8
+ }> | Readonly<{
9
+ kind: 'file';
10
+ value: string;
11
+ }> | Readonly<{
12
+ kind: 'test';
13
+ value: string;
14
+ }> | Readonly<{
15
+ kind: 'route';
16
+ value: string;
17
+ }>;
18
+ export interface GateStage {
19
+ readonly id: string;
20
+ readonly command: readonly [string, ...string[]];
21
+ readonly requiresStack: boolean;
22
+ readonly timeoutSeconds: number;
23
+ readonly select?: Readonly<Partial<Record<GateSelector['kind'], readonly string[]>>>;
24
+ }
25
+ export interface GateDefinition {
26
+ readonly id: string;
27
+ readonly tier: 'targeted' | 'full';
28
+ readonly stages: readonly string[];
29
+ readonly changes: string;
30
+ }
31
+ export interface GateDeclarations {
32
+ readonly stages: readonly GateStage[];
33
+ readonly gates: readonly GateDefinition[];
34
+ }
35
+ export interface GateRegistry extends GateDeclarations {
36
+ readonly kind: 'bound' | 'unbound';
37
+ readonly tiers: readonly Readonly<{
38
+ id: 'none' | 'targeted' | 'full';
39
+ title: string;
40
+ changes: string;
41
+ gate: string;
42
+ }>[];
43
+ }
44
+ export declare const TESTING_GUIDE_REGISTRY: GateRegistry;
45
+ export declare function createGateRegistry(input: GateDeclarations): GateRegistry;
46
+ export interface ProductGateInvocation {
47
+ readonly gate: string;
48
+ readonly stage: string;
49
+ readonly command: readonly [string, ...string[]];
50
+ readonly timeoutSeconds: number;
51
+ readonly select: GateSelector | null;
52
+ }
53
+ export interface GateRequest {
54
+ readonly gate: string;
55
+ readonly stages?: readonly string[];
56
+ readonly select?: GateSelector;
57
+ readonly staged?: boolean;
58
+ readonly stack?: Readonly<{
59
+ state: 'available';
60
+ }> | Readonly<{
61
+ state: 'unavailable';
62
+ reason: string;
63
+ }>;
64
+ }
65
+ export interface GateDependencies {
66
+ readonly subject: () => Promise<string>;
67
+ readonly runProductTool: (invocation: ProductGateInvocation) => Promise<StageResult>;
68
+ readonly runtime?: Readonly<{
69
+ snapshot: EnvironmentSnapshot;
70
+ capture: () => Promise<RuntimeEvidence>;
71
+ }>;
72
+ }
73
+ export interface GateRun extends RunClassification {
74
+ readonly observations: readonly StageObservation[];
75
+ }
76
+ export declare function runGate(registry: GateRegistry, request: GateRequest, deps: GateDependencies): Promise<GateRun>;
77
+ export type TierGuideResult = Readonly<{
78
+ kind: 'complete';
79
+ bytes: string;
80
+ lines: number;
81
+ costs: readonly StandingCost[];
82
+ }> | Readonly<{
83
+ kind: 'refusal';
84
+ reason: string;
85
+ exceeded?: Readonly<{
86
+ target: string;
87
+ value: number;
88
+ cap: number;
89
+ }>;
90
+ }>;
91
+ export declare function renderTierGuide(registry: GateRegistry, projection: ProjectionPlannerInput): TierGuideResult;