@starci/hfs 4.11.21 → 4.12.0

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 (1088) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +31 -31
  3. package/ci/cli.d.mts +15 -0
  4. package/ci/cli.mjs +71 -52
  5. package/ci/github.d.mts +43 -0
  6. package/ci/github.mjs +33 -28
  7. package/ci/local.d.mts +12 -0
  8. package/ci/local.mjs +145 -108
  9. package/ci/secret.d.mts +15 -0
  10. package/ci/secret.mjs +31 -21
  11. package/ci/vars.d.mts +17 -0
  12. package/ci/vars.mjs +47 -33
  13. package/ci/verify.d.mts +18 -0
  14. package/ci/verify.mjs +52 -40
  15. package/codemod/button-intent.d.mts +63 -0
  16. package/codemod/button-intent.mjs +338 -285
  17. package/coverage/cli.d.mts +28 -0
  18. package/coverage/cli.mjs +65 -51
  19. package/db/backup-receipt.d.mts +28 -0
  20. package/db/backup-receipt.mjs +78 -58
  21. package/db/drift.d.mts +86 -0
  22. package/db/drift.mjs +213 -180
  23. package/db/dump.d.mts +52 -0
  24. package/db/dump.mjs +63 -56
  25. package/db/entity-list.d.mts +4 -0
  26. package/db/entity-list.mjs +0 -2
  27. package/db/image.d.mts +8 -0
  28. package/db/image.mjs +1 -2
  29. package/db/migrate.d.mts +104 -0
  30. package/db/migrate.mjs +207 -167
  31. package/db/new.d.mts +11 -0
  32. package/db/new.mjs +25 -25
  33. package/db/post-cutover.d.mts +5 -0
  34. package/db/post-cutover.mjs +89 -82
  35. package/db/postgres.d.mts +71 -0
  36. package/db/postgres.mjs +62 -61
  37. package/emit/compiler.d.mts +3 -0
  38. package/emit/compiler.mjs +25 -27
  39. package/emit/contracts.d.mts +35 -0
  40. package/emit/contracts.mjs +46 -50
  41. package/emit/events.d.mts +9 -0
  42. package/emit/events.mjs +19 -20
  43. package/emit/form-bounds.d.mts +83 -0
  44. package/emit/form-bounds.mjs +133 -117
  45. package/emit/graphql-input-bounds.d.mts +63 -0
  46. package/emit/graphql-input-bounds.mjs +90 -79
  47. package/emit/input-bounds.d.mts +30 -0
  48. package/emit/input-bounds.mjs +29 -27
  49. package/emit/operation-schemas.d.mts +24 -0
  50. package/emit/operation-schemas.mjs +136 -116
  51. package/emit/operations-worker.d.mts +1 -0
  52. package/emit/operations-worker.mjs +9 -7
  53. package/emit/operations.d.mts +47 -0
  54. package/emit/operations.mjs +124 -133
  55. package/emit/schema-worker.d.mts +1 -0
  56. package/emit/schema-worker.mjs +57 -50
  57. package/emit/static-graph.d.mts +48 -0
  58. package/emit/static-graph.mjs +766 -640
  59. package/emit/type-schema.d.mts +29 -0
  60. package/emit/type-schema.mjs +153 -140
  61. package/lint/bound-sys.cjs +27 -33
  62. package/lint/chunk.d.mts +17 -0
  63. package/lint/chunk.mjs +41 -31
  64. package/lint/run.d.mts +120 -0
  65. package/lint/run.mjs +415 -322
  66. package/package.json +21 -4
  67. package/release/candidate-ci.d.mts +46 -0
  68. package/release/candidate-ci.mjs +76 -70
  69. package/release/gate.d.mts +120 -0
  70. package/release/gate.mjs +358 -330
  71. package/release/index.d.mts +68 -0
  72. package/release/index.mjs +95 -73
  73. package/release/lint-report.d.mts +14 -0
  74. package/release/lint-report.mjs +33 -32
  75. package/release/push.d.mts +32 -0
  76. package/release/push.mjs +92 -79
  77. package/release/records.d.mts +18 -0
  78. package/release/records.mjs +22 -21
  79. package/release/sonar-quality-gate.d.mts +18 -0
  80. package/release/sonar-quality-gate.mjs +60 -52
  81. package/release/urgent.d.mts +20 -0
  82. package/release/urgent.mjs +76 -74
  83. package/report/order.d.mts +1 -0
  84. package/report/sonar.d.mts +73 -0
  85. package/report/sonar.mjs +82 -89
  86. package/runtime/engine/admission.d.mts +209 -0
  87. package/runtime/engine/admission.mjs +225 -204
  88. package/runtime/engine/by-code-unit.d.mts +5 -0
  89. package/runtime/engine/by-code-unit.mjs +6 -6
  90. package/runtime/engine/config.d.mts +333 -0
  91. package/runtime/engine/config.mjs +486 -328
  92. package/runtime/engine/db/blob.d.mts +83 -0
  93. package/runtime/engine/db/blob.mjs +344 -268
  94. package/runtime/engine/db/event-compact.d.mts +21 -0
  95. package/runtime/engine/db/event-compact.mjs +34 -38
  96. package/runtime/engine/db/machine-connection.d.mts +68 -0
  97. package/runtime/engine/db/machine-connection.mjs +166 -119
  98. package/runtime/engine/db/machine-open.d.mts +26 -0
  99. package/runtime/engine/db/machine-open.mjs +59 -45
  100. package/runtime/engine/db/machine-schema.d.mts +34 -0
  101. package/runtime/engine/db/machine-schema.mjs +102 -83
  102. package/runtime/engine/db/machine.d.mts +1746 -0
  103. package/runtime/engine/db/machine.mjs +1040 -734
  104. package/runtime/engine/db/provider-reservations.d.mts +169 -0
  105. package/runtime/engine/db/provider-reservations.mjs +141 -119
  106. package/runtime/engine/db/provision-lock.d.mts +23 -0
  107. package/runtime/engine/db/provision-lock.mjs +14 -14
  108. package/runtime/engine/db/ref-value.d.mts +19 -0
  109. package/runtime/engine/db/ref-value.mjs +36 -35
  110. package/runtime/engine/db/sqlite-types.d.mts +28 -0
  111. package/runtime/engine/db/sqlite-types.mjs +4 -0
  112. package/runtime/engine/digest.d.mts +4 -0
  113. package/runtime/engine/digest.mjs +16 -7
  114. package/runtime/engine/invalid-config.d.mts +13 -0
  115. package/runtime/engine/invalid-config.mjs +17 -11
  116. package/runtime/engine/model-config.d.mts +91 -0
  117. package/runtime/engine/model-config.mjs +98 -76
  118. package/runtime/engine/orca-config.d.mts +26 -0
  119. package/runtime/engine/orca-config.mjs +22 -17
  120. package/runtime/engine/plain-object.d.mts +2 -0
  121. package/runtime/engine/refuse.d.mts +9 -0
  122. package/runtime/engine/refuse.mjs +3 -6
  123. package/runtime/engine/release-config.d.mts +19 -0
  124. package/runtime/engine/release-config.mjs +22 -18
  125. package/runtime/engine/removed-vocabulary.d.mts +12 -0
  126. package/runtime/engine/removed-vocabulary.mjs +21 -20
  127. package/runtime/engine/resources-config.d.mts +34 -0
  128. package/runtime/engine/resources-config.mjs +42 -35
  129. package/runtime/engine/runtime-root.d.mts +28 -0
  130. package/runtime/engine/runtime-root.mjs +11 -13
  131. package/runtime/engine/secrets.d.mts +41 -0
  132. package/runtime/engine/secrets.mjs +170 -102
  133. package/runtime/engine/sonar-config.d.mts +10 -0
  134. package/runtime/engine/sonar-config.mjs +16 -13
  135. package/runtime/engine/temp-root.d.mts +17 -0
  136. package/runtime/engine/temp-root.mjs +3 -9
  137. package/runtime/engine/yaml.d.mts +9 -0
  138. package/runtime/knowledge/hfs/canon-pins.yaml +10 -10
  139. package/runtime/knowledge/hfs/ci-standard.yaml +2 -2
  140. package/runtime/knowledge/hfs/rules.yaml +72 -6
  141. package/runtime/knowledge/hfs/slots.yaml +3 -3
  142. package/runtime/knowledge/sonar-gate.yaml +1 -1
  143. package/runtime/modules/kernel/failure-codes.yaml +43 -32
  144. package/runtime/modules/schemas/application-stacks.schema.yaml +3 -3
  145. package/runtime/packages/cli/src/catalog.generated.d.mts +13 -0
  146. package/runtime/packages/cli/src/catalog.generated.mjs +1853 -14824
  147. package/runtime/packages/cli/src/validate-args.d.mts +74 -0
  148. package/runtime/packages/cli/src/validate-args.mjs +183 -171
  149. package/runtime/packages/jest-preset/sonar-exclusions.cjs +5 -5
  150. package/runtime/scripts/api/fs/claim-file.d.mts +12 -0
  151. package/runtime/scripts/api/fs/claim-file.mjs +31 -17
  152. package/runtime/scripts/api/fs/ensure-temp-root.d.mts +4 -0
  153. package/runtime/scripts/api/fs/ensure-temp-root.mjs +15 -14
  154. package/runtime/scripts/api/fs/forbidden-root.d.mts +15 -0
  155. package/runtime/scripts/api/fs/forbidden-root.mjs +31 -23
  156. package/runtime/scripts/api/fs/is-link-like.d.mts +10 -0
  157. package/runtime/scripts/api/fs/is-link-like.mjs +25 -11
  158. package/runtime/scripts/api/fs/lib.d.mts +12 -0
  159. package/runtime/scripts/api/fs/lib.mjs +28 -8
  160. package/runtime/scripts/api/fs/make-temp-dir.d.mts +5 -0
  161. package/runtime/scripts/api/fs/make-temp-dir.mjs +3 -4
  162. package/runtime/scripts/api/fs/rename-over.d.mts +13 -0
  163. package/runtime/scripts/api/fs/rename-over.mjs +16 -10
  164. package/runtime/scripts/api/fs/safe-remove.d.mts +25 -0
  165. package/runtime/scripts/api/fs/safe-remove.mjs +129 -71
  166. package/runtime/scripts/api/git/add.d.mts +3 -0
  167. package/runtime/scripts/api/git/add.mjs +0 -1
  168. package/runtime/scripts/api/git/cat-file.d.mts +3 -0
  169. package/runtime/scripts/api/git/cat-file.mjs +0 -1
  170. package/runtime/scripts/api/git/check-ignore.d.mts +3 -0
  171. package/runtime/scripts/api/git/check-ignore.mjs +0 -1
  172. package/runtime/scripts/api/git/commit-tree.d.mts +3 -0
  173. package/runtime/scripts/api/git/commit-tree.mjs +0 -1
  174. package/runtime/scripts/api/git/config-get.d.mts +10 -0
  175. package/runtime/scripts/api/git/config-get.mjs +1 -2
  176. package/runtime/scripts/api/git/diff.d.mts +3 -0
  177. package/runtime/scripts/api/git/diff.mjs +0 -1
  178. package/runtime/scripts/api/git/init.d.mts +7 -0
  179. package/runtime/scripts/api/git/init.mjs +1 -2
  180. package/runtime/scripts/api/git/is-ancestor.d.mts +5 -0
  181. package/runtime/scripts/api/git/is-ancestor.mjs +0 -1
  182. package/runtime/scripts/api/git/lib.d.mts +67 -0
  183. package/runtime/scripts/api/git/lib.mjs +32 -32
  184. package/runtime/scripts/api/git/ls-files.d.mts +3 -0
  185. package/runtime/scripts/api/git/ls-files.mjs +0 -1
  186. package/runtime/scripts/api/git/ls-tree.d.mts +3 -0
  187. package/runtime/scripts/api/git/ls-tree.mjs +0 -1
  188. package/runtime/scripts/api/git/merge-base.d.mts +5 -0
  189. package/runtime/scripts/api/git/merge-base.mjs +2 -3
  190. package/runtime/scripts/api/git/remote.d.mts +3 -0
  191. package/runtime/scripts/api/git/remote.mjs +0 -1
  192. package/runtime/scripts/api/git/rev-parse-query.d.mts +3 -0
  193. package/runtime/scripts/api/git/rev-parse-query.mjs +0 -1
  194. package/runtime/scripts/api/git/show.d.mts +3 -0
  195. package/runtime/scripts/api/git/show.mjs +0 -1
  196. package/runtime/scripts/api/git/tag.d.mts +3 -0
  197. package/runtime/scripts/api/git/tag.mjs +0 -1
  198. package/runtime/scripts/api/git/update-ref.d.mts +7 -0
  199. package/runtime/scripts/api/git/update-ref.mjs +1 -2
  200. package/runtime/scripts/api/git/write-tree.d.mts +3 -0
  201. package/runtime/scripts/api/git/write-tree.mjs +0 -1
  202. package/runtime/scripts/api/node/lib.d.mts +7 -0
  203. package/runtime/scripts/api/node/lib.mjs +17 -7
  204. package/runtime/scripts/api/node/run-node.d.mts +3 -0
  205. package/runtime/scripts/api/node/run-node.mjs +0 -3
  206. package/runtime/scripts/api/process/resolve-real-tool.d.mts +12 -0
  207. package/runtime/scripts/api/process/resolve-real-tool.mjs +46 -38
  208. package/runtime/scripts/api/process/run-program.d.mts +3 -0
  209. package/runtime/scripts/api/process/run-program.mjs +5 -2
  210. package/runtime/scripts/api/sops/decrypt.d.mts +9 -0
  211. package/runtime/scripts/api/sops/decrypt.mjs +1 -2
  212. package/runtime/scripts/api/sops/lib.d.mts +149 -0
  213. package/runtime/scripts/api/sops/lib.mjs +417 -318
  214. package/runtime/scripts/api/sops/resolve-sops.d.mts +3 -0
  215. package/runtime/scripts/api/sops/resolve-sops.mjs +1 -2
  216. package/runtime/scripts/api/sops/seal.d.mts +19 -0
  217. package/runtime/scripts/api/sops/seal.mjs +14 -10
  218. package/runtime/scripts/api/sops/selected-age-recipient.d.mts +3 -0
  219. package/runtime/scripts/api/sops/selected-age-recipient.mjs +1 -2
  220. package/runtime/scripts/api/sops/update-keys.d.mts +16 -0
  221. package/runtime/scripts/api/sops/update-keys.mjs +11 -8
  222. package/runtime/scripts/cli/gen-catalog.d.mts +42 -0
  223. package/runtime/scripts/cli/gen-catalog.mjs +72 -0
  224. package/runtime/scripts/gates/runtime-host.d.mts +23 -0
  225. package/runtime/scripts/gates/runtime-host.mjs +48 -41
  226. package/runtime/scripts/gates/sonar-gate.d.mts +219 -0
  227. package/runtime/scripts/gates/sonar-gate.mjs +166 -153
  228. package/runtime/scripts/gates/starcistacks-ci.d.mts +35 -0
  229. package/runtime/scripts/gates/starcistacks-ci.mjs +47 -39
  230. package/runtime/scripts/gates/starcistacks-form.d.mts +80 -0
  231. package/runtime/scripts/gates/starcistacks-form.mjs +16 -14
  232. package/runtime/scripts/gates/starcistacks-runtime.d.mts +1 -0
  233. package/runtime/scripts/gates/starcistacks-value-file.d.mts +2 -0
  234. package/runtime/scripts/gates/starcistacks-value-file.mjs +14 -11
  235. package/runtime/scripts/gates/starcistacks.d.mts +141 -0
  236. package/runtime/scripts/gates/starcistacks.mjs +470 -355
  237. package/runtime/scripts/guards/git-pathspec.d.mts +48 -0
  238. package/runtime/scripts/guards/git-pathspec.mjs +215 -130
  239. package/runtime/scripts/guards/git-policy.d.mts +30 -0
  240. package/runtime/scripts/guards/git-policy.mjs +278 -235
  241. package/runtime/scripts/guards/hook-io.d.mts +22 -0
  242. package/runtime/scripts/guards/hook-io.mjs +77 -63
  243. package/runtime/scripts/guards/push-scan.d.mts +31 -0
  244. package/runtime/scripts/guards/push-scan.mjs +93 -80
  245. package/runtime/scripts/guards/release-push-gate.d.mts +22 -0
  246. package/runtime/scripts/guards/release-push-gate.mjs +40 -38
  247. package/runtime/scripts/hfs/allows.d.mts +24 -0
  248. package/runtime/scripts/hfs/allows.mjs +37 -38
  249. package/runtime/scripts/hfs/app-wiring.d.mts +72 -0
  250. package/runtime/scripts/hfs/app-wiring.mjs +197 -167
  251. package/runtime/scripts/hfs/architecture/ast-walks.d.mts +89 -0
  252. package/runtime/scripts/hfs/architecture/ast-walks.mjs +180 -163
  253. package/runtime/scripts/hfs/architecture/automatic-gates.d.mts +21 -0
  254. package/runtime/scripts/hfs/architecture/automatic-gates.mjs +80 -73
  255. package/runtime/scripts/hfs/architecture/backend-app-files.d.mts +10 -0
  256. package/runtime/scripts/hfs/architecture/backend-app-files.mjs +88 -77
  257. package/runtime/scripts/hfs/architecture/backend.d.mts +5 -0
  258. package/runtime/scripts/hfs/architecture/backend.mjs +378 -357
  259. package/runtime/scripts/hfs/architecture/background-unowned.d.mts +25 -0
  260. package/runtime/scripts/hfs/architecture/background-unowned.mjs +201 -187
  261. package/runtime/scripts/hfs/architecture/canon-clones.d.mts +8 -0
  262. package/runtime/scripts/hfs/architecture/canon-clones.mjs +260 -209
  263. package/runtime/scripts/hfs/architecture/canon-jsx-clones.d.mts +5 -0
  264. package/runtime/scripts/hfs/architecture/canon-jsx-clones.mjs +149 -116
  265. package/runtime/scripts/hfs/architecture/client-reaches-server.d.mts +28 -0
  266. package/runtime/scripts/hfs/architecture/client-reaches-server.mjs +72 -68
  267. package/runtime/scripts/hfs/architecture/clones.d.mts +54 -0
  268. package/runtime/scripts/hfs/architecture/clones.mjs +212 -217
  269. package/runtime/scripts/hfs/architecture/config-unread.d.mts +14 -0
  270. package/runtime/scripts/hfs/architecture/config-unread.mjs +7 -9
  271. package/runtime/scripts/hfs/architecture/config.d.mts +74 -0
  272. package/runtime/scripts/hfs/architecture/config.mjs +302 -268
  273. package/runtime/scripts/hfs/architecture/connection-map.d.mts +33 -0
  274. package/runtime/scripts/hfs/architecture/connection-map.mjs +258 -225
  275. package/runtime/scripts/hfs/architecture/constructor-deps.d.mts +57 -0
  276. package/runtime/scripts/hfs/architecture/constructor-deps.mjs +80 -71
  277. package/runtime/scripts/hfs/architecture/context-coupling.d.mts +21 -0
  278. package/runtime/scripts/hfs/architecture/context-coupling.mjs +82 -77
  279. package/runtime/scripts/hfs/architecture/context-map.d.mts +102 -0
  280. package/runtime/scripts/hfs/architecture/context-map.mjs +267 -234
  281. package/runtime/scripts/hfs/architecture/context-owner.d.mts +15 -0
  282. package/runtime/scripts/hfs/architecture/context-owner.mjs +73 -61
  283. package/runtime/scripts/hfs/architecture/context-platform-tables.d.mts +18 -0
  284. package/runtime/scripts/hfs/architecture/context-platform-tables.mjs +47 -43
  285. package/runtime/scripts/hfs/architecture/context-transaction.d.mts +20 -0
  286. package/runtime/scripts/hfs/architecture/context-transaction.mjs +54 -52
  287. package/runtime/scripts/hfs/architecture/contract-fixture-guard.d.mts +37 -0
  288. package/runtime/scripts/hfs/architecture/contract-fixture-guard.mjs +113 -104
  289. package/runtime/scripts/hfs/architecture/contracts-readonly.d.mts +32 -0
  290. package/runtime/scripts/hfs/architecture/contracts-readonly.mjs +288 -270
  291. package/runtime/scripts/hfs/architecture/contracts.d.mts +30 -0
  292. package/runtime/scripts/hfs/architecture/contracts.mjs +408 -393
  293. package/runtime/scripts/hfs/architecture/cross-app-duplicate.d.mts +27 -0
  294. package/runtime/scripts/hfs/architecture/cross-app-duplicate.mjs +50 -51
  295. package/runtime/scripts/hfs/architecture/dead-exports-bindings.d.mts +21 -0
  296. package/runtime/scripts/hfs/architecture/dead-exports-bindings.mjs +143 -119
  297. package/runtime/scripts/hfs/architecture/dead-exports.d.mts +32 -0
  298. package/runtime/scripts/hfs/architecture/dead-exports.mjs +286 -228
  299. package/runtime/scripts/hfs/architecture/default-deny.d.mts +21 -0
  300. package/runtime/scripts/hfs/architecture/default-deny.mjs +129 -110
  301. package/runtime/scripts/hfs/architecture/doc-language.d.mts +17 -0
  302. package/runtime/scripts/hfs/architecture/doc-language.mjs +24 -19
  303. package/runtime/scripts/hfs/architecture/entity-registry.d.mts +49 -0
  304. package/runtime/scripts/hfs/architecture/entity-registry.mjs +20 -22
  305. package/runtime/scripts/hfs/architecture/entrypoint.d.mts +13 -0
  306. package/runtime/scripts/hfs/architecture/entrypoint.mjs +47 -45
  307. package/runtime/scripts/hfs/architecture/error-codes.d.mts +16 -0
  308. package/runtime/scripts/hfs/architecture/error-codes.mjs +37 -35
  309. package/runtime/scripts/hfs/architecture/error-masked.d.mts +16 -0
  310. package/runtime/scripts/hfs/architecture/error-masked.mjs +55 -59
  311. package/runtime/scripts/hfs/architecture/fe-slot-allows.d.mts +38 -0
  312. package/runtime/scripts/hfs/architecture/fe-slot-allows.mjs +24 -23
  313. package/runtime/scripts/hfs/architecture/feature-shape.d.mts +26 -0
  314. package/runtime/scripts/hfs/architecture/feature-shape.mjs +40 -38
  315. package/runtime/scripts/hfs/architecture/framework-pinned.d.mts +3 -0
  316. package/runtime/scripts/hfs/architecture/framework-pinned.mjs +21 -21
  317. package/runtime/scripts/hfs/architecture/frontend-block-render.d.mts +26 -0
  318. package/runtime/scripts/hfs/architecture/frontend-block-render.mjs +106 -98
  319. package/runtime/scripts/hfs/architecture/frontend-grammar.d.mts +7 -0
  320. package/runtime/scripts/hfs/architecture/frontend-grammar.mjs +153 -150
  321. package/runtime/scripts/hfs/architecture/frontend-routing.d.mts +23 -0
  322. package/runtime/scripts/hfs/architecture/frontend-routing.mjs +161 -149
  323. package/runtime/scripts/hfs/architecture/frontend-world-render.d.mts +72 -0
  324. package/runtime/scripts/hfs/architecture/frontend-world-render.mjs +343 -305
  325. package/runtime/scripts/hfs/architecture/frontend-world.d.mts +94 -0
  326. package/runtime/scripts/hfs/architecture/frontend-world.mjs +340 -294
  327. package/runtime/scripts/hfs/architecture/frontend.d.mts +31 -0
  328. package/runtime/scripts/hfs/architecture/frontend.mjs +174 -184
  329. package/runtime/scripts/hfs/architecture/function-spec-providers.d.mts +27 -0
  330. package/runtime/scripts/hfs/architecture/function-spec-providers.mjs +124 -106
  331. package/runtime/scripts/hfs/architecture/hfs-graph.d.mts +78 -0
  332. package/runtime/scripts/hfs/architecture/hfs-graph.mjs +52 -48
  333. package/runtime/scripts/hfs/architecture/hfs.d.mts +135 -0
  334. package/runtime/scripts/hfs/architecture/hfs.mjs +328 -282
  335. package/runtime/scripts/hfs/architecture/hooks-are-hooks.d.mts +24 -0
  336. package/runtime/scripts/hfs/architecture/hooks-are-hooks.mjs +81 -71
  337. package/runtime/scripts/hfs/architecture/i18n-keys.d.mts +31 -0
  338. package/runtime/scripts/hfs/architecture/i18n-keys.mjs +155 -141
  339. package/runtime/scripts/hfs/architecture/identity.d.mts +7 -0
  340. package/runtime/scripts/hfs/architecture/identity.mjs +141 -125
  341. package/runtime/scripts/hfs/architecture/index.d.mts +101 -0
  342. package/runtime/scripts/hfs/architecture/index.mjs +272 -272
  343. package/runtime/scripts/hfs/architecture/injection-token-exported.d.mts +21 -0
  344. package/runtime/scripts/hfs/architecture/injection-token-exported.mjs +45 -43
  345. package/runtime/scripts/hfs/architecture/jwt-ast.d.mts +16 -0
  346. package/runtime/scripts/hfs/architecture/jwt-ast.mjs +195 -176
  347. package/runtime/scripts/hfs/architecture/machine-ast.d.mts +88 -0
  348. package/runtime/scripts/hfs/architecture/machine-ast.mjs +163 -155
  349. package/runtime/scripts/hfs/architecture/managed-scripts.d.mts +2 -0
  350. package/runtime/scripts/hfs/architecture/managed-scripts.mjs +19 -20
  351. package/runtime/scripts/hfs/architecture/module-per-transport.d.mts +35 -0
  352. package/runtime/scripts/hfs/architecture/module-per-transport.mjs +177 -172
  353. package/runtime/scripts/hfs/architecture/next-data-contract.d.mts +35 -0
  354. package/runtime/scripts/hfs/architecture/next-data-contract.mjs +100 -78
  355. package/runtime/scripts/hfs/architecture/next-data-inference.d.mts +39 -0
  356. package/runtime/scripts/hfs/architecture/next-data-inference.mjs +170 -154
  357. package/runtime/scripts/hfs/architecture/next-data-key.d.mts +50 -0
  358. package/runtime/scripts/hfs/architecture/next-data-key.mjs +311 -275
  359. package/runtime/scripts/hfs/architecture/next-data-symbols.d.mts +5 -0
  360. package/runtime/scripts/hfs/architecture/next-data-symbols.mjs +17 -17
  361. package/runtime/scripts/hfs/architecture/next-data.d.mts +36 -0
  362. package/runtime/scripts/hfs/architecture/next-data.mjs +351 -334
  363. package/runtime/scripts/hfs/architecture/operation-contract-roots.d.mts +10 -0
  364. package/runtime/scripts/hfs/architecture/operation-contract-roots.mjs +39 -37
  365. package/runtime/scripts/hfs/architecture/owners.d.mts +5 -0
  366. package/runtime/scripts/hfs/architecture/owners.mjs +97 -98
  367. package/runtime/scripts/hfs/architecture/package-shape.d.mts +26 -0
  368. package/runtime/scripts/hfs/architecture/package-shape.mjs +56 -55
  369. package/runtime/scripts/hfs/architecture/presentation.d.mts +24 -0
  370. package/runtime/scripts/hfs/architecture/presentation.mjs +138 -130
  371. package/runtime/scripts/hfs/architecture/reachability.d.mts +25 -0
  372. package/runtime/scripts/hfs/architecture/reachability.mjs +258 -241
  373. package/runtime/scripts/hfs/architecture/register-once.d.mts +18 -0
  374. package/runtime/scripts/hfs/architecture/register-once.mjs +152 -139
  375. package/runtime/scripts/hfs/architecture/registration.d.mts +15 -0
  376. package/runtime/scripts/hfs/architecture/registration.mjs +301 -267
  377. package/runtime/scripts/hfs/architecture/required-files.d.mts +64 -0
  378. package/runtime/scripts/hfs/architecture/required-files.mjs +172 -167
  379. package/runtime/scripts/hfs/architecture/route-files-thin.d.mts +23 -0
  380. package/runtime/scripts/hfs/architecture/route-files-thin.mjs +85 -77
  381. package/runtime/scripts/hfs/architecture/schema-owner.d.mts +34 -0
  382. package/runtime/scripts/hfs/architecture/schema-owner.mjs +206 -181
  383. package/runtime/scripts/hfs/architecture/server-action.d.mts +22 -0
  384. package/runtime/scripts/hfs/architecture/server-action.mjs +48 -48
  385. package/runtime/scripts/hfs/architecture/side-boundary.d.mts +3 -0
  386. package/runtime/scripts/hfs/architecture/side-boundary.mjs +2 -5
  387. package/runtime/scripts/hfs/architecture/source-names-shape.d.mts +85 -0
  388. package/runtime/scripts/hfs/architecture/source-names-shape.mjs +261 -259
  389. package/runtime/scripts/hfs/architecture/source-names.d.mts +7 -0
  390. package/runtime/scripts/hfs/architecture/source-names.mjs +304 -278
  391. package/runtime/scripts/hfs/architecture/sql-keys.d.mts +5 -0
  392. package/runtime/scripts/hfs/architecture/sql-keys.mjs +277 -194
  393. package/runtime/scripts/hfs/architecture/sql-owner.d.mts +30 -0
  394. package/runtime/scripts/hfs/architecture/sql-owner.mjs +236 -194
  395. package/runtime/scripts/hfs/architecture/sql-returning.d.mts +22 -0
  396. package/runtime/scripts/hfs/architecture/sql-returning.mjs +46 -45
  397. package/runtime/scripts/hfs/architecture/sql-token-forms.d.mts +21 -0
  398. package/runtime/scripts/hfs/architecture/sql-token-forms.mjs +86 -74
  399. package/runtime/scripts/hfs/architecture/sql-tokens.d.mts +32 -0
  400. package/runtime/scripts/hfs/architecture/sql-tokens.mjs +456 -341
  401. package/runtime/scripts/hfs/architecture/staged-consumers.d.mts +7 -0
  402. package/runtime/scripts/hfs/architecture/staged-consumers.mjs +35 -30
  403. package/runtime/scripts/hfs/architecture/surface.d.mts +26 -0
  404. package/runtime/scripts/hfs/architecture/surface.mjs +64 -74
  405. package/runtime/scripts/hfs/architecture/symbols.d.mts +11 -0
  406. package/runtime/scripts/hfs/architecture/symbols.mjs +209 -216
  407. package/runtime/scripts/hfs/architecture/test-world-files.d.mts +45 -0
  408. package/runtime/scripts/hfs/architecture/test-world-files.mjs +165 -155
  409. package/runtime/scripts/hfs/architecture/tiers.d.mts +29 -0
  410. package/runtime/scripts/hfs/architecture/tiers.mjs +161 -154
  411. package/runtime/scripts/hfs/architecture/transport-owner.d.mts +37 -0
  412. package/runtime/scripts/hfs/architecture/transport-owner.mjs +155 -149
  413. package/runtime/scripts/hfs/architecture/type-context.d.mts +60 -0
  414. package/runtime/scripts/hfs/architecture/type-context.mjs +255 -250
  415. package/runtime/scripts/hfs/architecture/typescript.d.mts +94 -0
  416. package/runtime/scripts/hfs/architecture/typescript.mjs +358 -318
  417. package/runtime/scripts/hfs/architecture/unit-spec-providers.d.mts +34 -0
  418. package/runtime/scripts/hfs/architecture/unit-spec-providers.mjs +110 -92
  419. package/runtime/scripts/hfs/architecture.d.mts +13 -0
  420. package/runtime/scripts/hfs/architecture.mjs +39 -29
  421. package/runtime/scripts/hfs/canon-pins.d.mts +26 -0
  422. package/runtime/scripts/hfs/canon-pins.mjs +5 -8
  423. package/runtime/scripts/hfs/catalog-once.d.mts +2 -0
  424. package/runtime/scripts/hfs/catalog-once.mjs +8 -9
  425. package/runtime/scripts/hfs/check-results.d.mts +29 -0
  426. package/runtime/scripts/hfs/check-results.mjs +14 -18
  427. package/runtime/scripts/hfs/check.d.mts +144 -0
  428. package/runtime/scripts/hfs/check.mjs +309 -329
  429. package/runtime/scripts/hfs/ci-declaration.d.mts +77 -0
  430. package/runtime/scripts/hfs/ci-declaration.mjs +99 -87
  431. package/runtime/scripts/hfs/coverage-from.d.mts +22 -0
  432. package/runtime/scripts/hfs/coverage-from.mjs +52 -49
  433. package/runtime/scripts/hfs/coverage-scope.d.mts +87 -0
  434. package/runtime/scripts/hfs/coverage-scope.mjs +139 -142
  435. package/runtime/scripts/hfs/db-baseline-verb.d.mts +84 -0
  436. package/runtime/scripts/hfs/db-baseline-verb.mjs +327 -266
  437. package/runtime/scripts/hfs/db-errors.d.mts +2 -0
  438. package/runtime/scripts/hfs/db-errors.mjs +2 -1
  439. package/runtime/scripts/hfs/db-plan-verb.d.mts +33 -0
  440. package/runtime/scripts/hfs/db-plan-verb.mjs +174 -128
  441. package/runtime/scripts/hfs/db-restamp-verb.d.mts +5 -0
  442. package/runtime/scripts/hfs/db-restamp-verb.mjs +94 -70
  443. package/runtime/scripts/hfs/db-squash.d.mts +117 -0
  444. package/runtime/scripts/hfs/db-squash.mjs +414 -351
  445. package/runtime/scripts/hfs/declaration-shape.d.mts +3 -0
  446. package/runtime/scripts/hfs/declaration-shape.mjs +140 -103
  447. package/runtime/scripts/hfs/declaration-slots.d.mts +18 -0
  448. package/runtime/scripts/hfs/declaration-slots.mjs +64 -61
  449. package/runtime/scripts/hfs/doc-commands.d.mts +71 -0
  450. package/runtime/scripts/hfs/doc-commands.mjs +257 -250
  451. package/runtime/scripts/hfs/edition-slots.d.mts +37 -0
  452. package/runtime/scripts/hfs/edition-slots.mjs +43 -40
  453. package/runtime/scripts/hfs/example-secrets.d.mts +18 -0
  454. package/runtime/scripts/hfs/example-secrets.mjs +173 -154
  455. package/runtime/scripts/hfs/gate-threshold.d.mts +20 -0
  456. package/runtime/scripts/hfs/gate-threshold.mjs +28 -0
  457. package/runtime/scripts/hfs/grammar-presentation.d.mts +74 -0
  458. package/runtime/scripts/hfs/grammar-presentation.mjs +109 -106
  459. package/runtime/scripts/hfs/linear-text.d.mts +6 -0
  460. package/runtime/scripts/hfs/linear-text.mjs +20 -20
  461. package/runtime/scripts/hfs/manifest-shape.d.mts +216 -0
  462. package/runtime/scripts/hfs/manifest-shape.mjs +399 -324
  463. package/runtime/scripts/hfs/param-names.d.mts +3 -0
  464. package/runtime/scripts/hfs/param-names.mjs +6 -11
  465. package/runtime/scripts/hfs/path-findings.d.mts +22 -0
  466. package/runtime/scripts/hfs/path-findings.mjs +120 -100
  467. package/runtime/scripts/hfs/pin-findings.d.mts +19 -0
  468. package/runtime/scripts/hfs/pin-findings.mjs +26 -21
  469. package/runtime/scripts/hfs/refusal-codes.d.mts +1 -0
  470. package/runtime/scripts/hfs/refusal-codes.mjs +2 -2
  471. package/runtime/scripts/hfs/repo-identity.d.mts +15 -0
  472. package/runtime/scripts/hfs/repo-identity.mjs +55 -42
  473. package/runtime/scripts/hfs/rule-catalog.d.mts +77 -0
  474. package/runtime/scripts/hfs/rule-catalog.mjs +353 -257
  475. package/runtime/scripts/hfs/rule-params-shape.d.mts +10 -0
  476. package/runtime/scripts/hfs/rule-params-shape.mjs +60 -47
  477. package/runtime/scripts/hfs/rules/a15-findings.d.mts +18 -0
  478. package/runtime/scripts/hfs/rules/a15-findings.mjs +59 -55
  479. package/runtime/scripts/hfs/rules/app-env-required.d.mts +9 -0
  480. package/runtime/scripts/hfs/rules/app-env-required.mjs +13 -13
  481. package/runtime/scripts/hfs/rules/app-root.d.mts +15 -0
  482. package/runtime/scripts/hfs/rules/app-root.mjs +20 -21
  483. package/runtime/scripts/hfs/rules/app-workflow-findings.d.mts +12 -0
  484. package/runtime/scripts/hfs/rules/app-workflow-findings.mjs +4 -4
  485. package/runtime/scripts/hfs/rules/assets.d.mts +13 -0
  486. package/runtime/scripts/hfs/rules/assets.mjs +58 -46
  487. package/runtime/scripts/hfs/rules/boot-proof.d.mts +11 -0
  488. package/runtime/scripts/hfs/rules/boot-proof.mjs +7 -8
  489. package/runtime/scripts/hfs/rules/bypass-markers.d.mts +9 -0
  490. package/runtime/scripts/hfs/rules/bypass-markers.mjs +7 -12
  491. package/runtime/scripts/hfs/rules/canon-copy-drift.d.mts +10 -0
  492. package/runtime/scripts/hfs/rules/canon-copy-drift.mjs +70 -68
  493. package/runtime/scripts/hfs/rules/ci-custody.d.mts +7 -0
  494. package/runtime/scripts/hfs/rules/ci-custody.mjs +72 -73
  495. package/runtime/scripts/hfs/rules/ci-standard.d.mts +12 -0
  496. package/runtime/scripts/hfs/rules/ci-standard.mjs +149 -142
  497. package/runtime/scripts/hfs/rules/cli.d.mts +7 -0
  498. package/runtime/scripts/hfs/rules/cli.mjs +24 -23
  499. package/runtime/scripts/hfs/rules/contract-compat.d.mts +11 -0
  500. package/runtime/scripts/hfs/rules/contract-compat.mjs +64 -53
  501. package/runtime/scripts/hfs/rules/contract.d.mts +26 -0
  502. package/runtime/scripts/hfs/rules/contract.mjs +126 -111
  503. package/runtime/scripts/hfs/rules/coverage-below.d.mts +17 -0
  504. package/runtime/scripts/hfs/rules/coverage-below.mjs +200 -167
  505. package/runtime/scripts/hfs/rules/database-access.d.mts +12 -0
  506. package/runtime/scripts/hfs/rules/database-access.mjs +231 -202
  507. package/runtime/scripts/hfs/rules/database-baseline.d.mts +22 -0
  508. package/runtime/scripts/hfs/rules/database-baseline.mjs +104 -104
  509. package/runtime/scripts/hfs/rules/database-config.d.mts +11 -0
  510. package/runtime/scripts/hfs/rules/database-config.mjs +101 -135
  511. package/runtime/scripts/hfs/rules/database-constants.d.mts +36 -0
  512. package/runtime/scripts/hfs/rules/database-constants.mjs +7 -7
  513. package/runtime/scripts/hfs/rules/database-migrations.d.mts +41 -0
  514. package/runtime/scripts/hfs/rules/database-migrations.mjs +428 -388
  515. package/runtime/scripts/hfs/rules/database-plpgsql.d.mts +5 -0
  516. package/runtime/scripts/hfs/rules/database-plpgsql.mjs +278 -259
  517. package/runtime/scripts/hfs/rules/database-queue-sources.d.mts +4 -0
  518. package/runtime/scripts/hfs/rules/database-queue-sources.mjs +48 -43
  519. package/runtime/scripts/hfs/rules/database-queues.d.mts +15 -0
  520. package/runtime/scripts/hfs/rules/database-queues.mjs +211 -182
  521. package/runtime/scripts/hfs/rules/database-sql.d.mts +16 -0
  522. package/runtime/scripts/hfs/rules/database-sql.mjs +394 -394
  523. package/runtime/scripts/hfs/rules/database-transaction.d.mts +3 -0
  524. package/runtime/scripts/hfs/rules/database-transaction.mjs +25 -27
  525. package/runtime/scripts/hfs/rules/database.d.mts +22 -0
  526. package/runtime/scripts/hfs/rules/database.mjs +42 -58
  527. package/runtime/scripts/hfs/rules/deploy-scan.d.mts +10 -0
  528. package/runtime/scripts/hfs/rules/deploy-scan.mjs +177 -168
  529. package/runtime/scripts/hfs/rules/deploy-workflow.d.mts +3 -0
  530. package/runtime/scripts/hfs/rules/deploy-workflow.mjs +194 -171
  531. package/runtime/scripts/hfs/rules/deps.d.mts +6 -0
  532. package/runtime/scripts/hfs/rules/deps.mjs +61 -60
  533. package/runtime/scripts/hfs/rules/doc-fresh.d.mts +11 -0
  534. package/runtime/scripts/hfs/rules/doc-fresh.mjs +242 -223
  535. package/runtime/scripts/hfs/rules/docker-health.d.mts +9 -0
  536. package/runtime/scripts/hfs/rules/docker-health.mjs +222 -196
  537. package/runtime/scripts/hfs/rules/docker.d.mts +13 -0
  538. package/runtime/scripts/hfs/rules/docker.mjs +234 -208
  539. package/runtime/scripts/hfs/rules/edition.d.mts +18 -0
  540. package/runtime/scripts/hfs/rules/edition.mjs +103 -101
  541. package/runtime/scripts/hfs/rules/env-parity.d.mts +25 -0
  542. package/runtime/scripts/hfs/rules/env-parity.mjs +278 -252
  543. package/runtime/scripts/hfs/rules/env-value-shape.d.mts +21 -0
  544. package/runtime/scripts/hfs/rules/env-value-shape.mjs +82 -67
  545. package/runtime/scripts/hfs/rules/event-bus.d.mts +17 -0
  546. package/runtime/scripts/hfs/rules/event-bus.mjs +145 -133
  547. package/runtime/scripts/hfs/rules/event-tables.d.mts +7 -0
  548. package/runtime/scripts/hfs/rules/event-tables.mjs +77 -75
  549. package/runtime/scripts/hfs/rules/fe-build-args.d.mts +10 -0
  550. package/runtime/scripts/hfs/rules/fe-build-args.mjs +116 -99
  551. package/runtime/scripts/hfs/rules/fe-contract-documents.d.mts +7 -0
  552. package/runtime/scripts/hfs/rules/fe-contract-documents.mjs +121 -111
  553. package/runtime/scripts/hfs/rules/fe-no-tests.d.mts +9 -0
  554. package/runtime/scripts/hfs/rules/fe-no-tests.mjs +37 -36
  555. package/runtime/scripts/hfs/rules/fe-stack.d.mts +23 -0
  556. package/runtime/scripts/hfs/rules/fe-stack.mjs +108 -105
  557. package/runtime/scripts/hfs/rules/feature-files.d.mts +10 -0
  558. package/runtime/scripts/hfs/rules/feature-files.mjs +14 -19
  559. package/runtime/scripts/hfs/rules/frontend-tree.d.mts +18 -0
  560. package/runtime/scripts/hfs/rules/frontend-tree.mjs +105 -91
  561. package/runtime/scripts/hfs/rules/integration-specs.d.mts +7 -0
  562. package/runtime/scripts/hfs/rules/integration-specs.mjs +190 -177
  563. package/runtime/scripts/hfs/rules/kinds.d.mts +6 -0
  564. package/runtime/scripts/hfs/rules/kinds.mjs +88 -81
  565. package/runtime/scripts/hfs/rules/lexicon.d.mts +9 -0
  566. package/runtime/scripts/hfs/rules/lexicon.mjs +43 -40
  567. package/runtime/scripts/hfs/rules/lint-suppression.d.mts +5 -0
  568. package/runtime/scripts/hfs/rules/lint-suppression.mjs +22 -20
  569. package/runtime/scripts/hfs/rules/lite-proof-lane.d.mts +9 -0
  570. package/runtime/scripts/hfs/rules/lite-proof-lane.mjs +59 -58
  571. package/runtime/scripts/hfs/rules/lite-secrets.d.mts +12 -0
  572. package/runtime/scripts/hfs/rules/lite-secrets.mjs +60 -63
  573. package/runtime/scripts/hfs/rules/mcp-tools.d.mts +7 -0
  574. package/runtime/scripts/hfs/rules/mcp-tools.mjs +56 -41
  575. package/runtime/scripts/hfs/rules/monorepo.d.mts +17 -0
  576. package/runtime/scripts/hfs/rules/monorepo.mjs +153 -138
  577. package/runtime/scripts/hfs/rules/peer-integrations.d.mts +7 -0
  578. package/runtime/scripts/hfs/rules/peer-integrations.mjs +39 -36
  579. package/runtime/scripts/hfs/rules/pipeline.d.mts +7 -0
  580. package/runtime/scripts/hfs/rules/pipeline.mjs +53 -53
  581. package/runtime/scripts/hfs/rules/proof-commands.d.mts +14 -0
  582. package/runtime/scripts/hfs/rules/proof-commands.mjs +52 -41
  583. package/runtime/scripts/hfs/rules/read.d.mts +7 -0
  584. package/runtime/scripts/hfs/rules/read.mjs +19 -15
  585. package/runtime/scripts/hfs/rules/release-provenance.d.mts +12 -0
  586. package/runtime/scripts/hfs/rules/release-provenance.mjs +196 -159
  587. package/runtime/scripts/hfs/rules/repo-local-checks.d.mts +5 -0
  588. package/runtime/scripts/hfs/rules/repo-local-checks.mjs +19 -17
  589. package/runtime/scripts/hfs/rules/rule-types.d.mts +40 -0
  590. package/runtime/scripts/hfs/rules/rule-types.mjs +4 -0
  591. package/runtime/scripts/hfs/rules/saga-delivery.d.mts +2 -0
  592. package/runtime/scripts/hfs/rules/saga-delivery.mjs +58 -50
  593. package/runtime/scripts/hfs/rules/saga.d.mts +7 -0
  594. package/runtime/scripts/hfs/rules/saga.mjs +151 -145
  595. package/runtime/scripts/hfs/rules/script-hygiene.d.mts +11 -0
  596. package/runtime/scripts/hfs/rules/script-hygiene.mjs +112 -95
  597. package/runtime/scripts/hfs/rules/secret-custody-only.d.mts +7 -0
  598. package/runtime/scripts/hfs/rules/secret-custody-only.mjs +29 -35
  599. package/runtime/scripts/hfs/rules/secrets.d.mts +29 -0
  600. package/runtime/scripts/hfs/rules/secrets.mjs +45 -44
  601. package/runtime/scripts/hfs/rules/services.d.mts +7 -0
  602. package/runtime/scripts/hfs/rules/services.mjs +148 -148
  603. package/runtime/scripts/hfs/rules/spec-placement.d.mts +8 -0
  604. package/runtime/scripts/hfs/rules/spec-placement.mjs +12 -13
  605. package/runtime/scripts/hfs/rules/stack-tooling.d.mts +11 -0
  606. package/runtime/scripts/hfs/rules/stack-tooling.mjs +45 -52
  607. package/runtime/scripts/hfs/rules/stacks-auth-cookie-domain.d.mts +30 -0
  608. package/runtime/scripts/hfs/rules/stacks-auth-cookie-domain.mjs +149 -0
  609. package/runtime/scripts/hfs/rules/stacks-deploy.d.mts +7 -0
  610. package/runtime/scripts/hfs/rules/stacks-deploy.mjs +32 -42
  611. package/runtime/scripts/hfs/rules/stacks-ingress.d.mts +16 -0
  612. package/runtime/scripts/hfs/rules/stacks-ingress.mjs +62 -64
  613. package/runtime/scripts/hfs/rules/stacks-lite-backing.d.mts +17 -0
  614. package/runtime/scripts/hfs/rules/stacks-lite-backing.mjs +111 -116
  615. package/runtime/scripts/hfs/rules/stacks-release.d.mts +26 -0
  616. package/runtime/scripts/hfs/rules/stacks-release.mjs +335 -309
  617. package/runtime/scripts/hfs/rules/stacks-runtime.d.mts +31 -0
  618. package/runtime/scripts/hfs/rules/stacks-runtime.mjs +130 -124
  619. package/runtime/scripts/hfs/rules/stacks-swarm.d.mts +5 -0
  620. package/runtime/scripts/hfs/rules/stacks-swarm.mjs +366 -344
  621. package/runtime/scripts/hfs/rules/stacks.d.mts +7 -0
  622. package/runtime/scripts/hfs/rules/stacks.mjs +37 -38
  623. package/runtime/scripts/hfs/rules/staged-specs.d.mts +10 -0
  624. package/runtime/scripts/hfs/rules/staged-specs.mjs +14 -18
  625. package/runtime/scripts/hfs/rules/supply-chain.d.mts +10 -0
  626. package/runtime/scripts/hfs/rules/supply-chain.mjs +143 -150
  627. package/runtime/scripts/hfs/rules/test-topology.d.mts +5 -0
  628. package/runtime/scripts/hfs/rules/test-topology.mjs +23 -21
  629. package/runtime/scripts/hfs/rules/text-integrity.d.mts +9 -0
  630. package/runtime/scripts/hfs/rules/text-integrity.mjs +47 -43
  631. package/runtime/scripts/hfs/rules/unused-symbol-flow.d.mts +22 -0
  632. package/runtime/scripts/hfs/rules/unused-symbol-flow.mjs +226 -204
  633. package/runtime/scripts/hfs/rules/unused-symbol-ledger.d.mts +7 -0
  634. package/runtime/scripts/hfs/rules/unused-symbol-ledger.mjs +70 -49
  635. package/runtime/scripts/hfs/rules/unused-symbol-properties.d.mts +12 -0
  636. package/runtime/scripts/hfs/rules/unused-symbol-properties.mjs +141 -126
  637. package/runtime/scripts/hfs/rules/unused-symbol-translators.d.mts +8 -0
  638. package/runtime/scripts/hfs/rules/unused-symbol-translators.mjs +160 -140
  639. package/runtime/scripts/hfs/rules/unused-symbol-values.d.mts +9 -0
  640. package/runtime/scripts/hfs/rules/unused-symbol-values.mjs +138 -125
  641. package/runtime/scripts/hfs/rules/unused-symbol.d.mts +11 -0
  642. package/runtime/scripts/hfs/rules/unused-symbol.mjs +283 -253
  643. package/runtime/scripts/hfs/rules/workflow-contexts.d.mts +8 -0
  644. package/runtime/scripts/hfs/rules/workflow-contexts.mjs +79 -67
  645. package/runtime/scripts/hfs/runtime-rules/git-triggers.d.mts +6 -0
  646. package/runtime/scripts/hfs/runtime-rules/git-triggers.mjs +36 -35
  647. package/runtime/scripts/hfs/runtime-rules/hook-shape.d.mts +10 -0
  648. package/runtime/scripts/hfs/runtime-rules/hook-shape.mjs +45 -42
  649. package/runtime/scripts/hfs/runtime-rules/source-ast.d.mts +39 -0
  650. package/runtime/scripts/hfs/runtime-rules/source-ast.mjs +130 -116
  651. package/runtime/scripts/hfs/runtime-rules/workflow-source.d.mts +5 -0
  652. package/runtime/scripts/hfs/runtime-rules/workflow-source.mjs +8 -7
  653. package/runtime/scripts/hfs/sealed-members.d.mts +22 -0
  654. package/runtime/scripts/hfs/sealed-members.mjs +79 -75
  655. package/runtime/scripts/hfs/secret-bootstrap-crypto.d.mts +12 -0
  656. package/runtime/scripts/hfs/secret-bootstrap-crypto.mjs +151 -167
  657. package/runtime/scripts/hfs/secret-document.d.mts +16 -0
  658. package/runtime/scripts/hfs/secret-document.mjs +59 -75
  659. package/runtime/scripts/hfs/secret-edit.d.mts +14 -0
  660. package/runtime/scripts/hfs/secret-edit.mjs +115 -135
  661. package/runtime/scripts/hfs/secret-errors.d.mts +4 -0
  662. package/runtime/scripts/hfs/secret-errors.mjs +19 -16
  663. package/runtime/scripts/hfs/secret-files.d.mts +20 -0
  664. package/runtime/scripts/hfs/secret-files.mjs +103 -112
  665. package/runtime/scripts/hfs/secret-init.d.mts +108 -0
  666. package/runtime/scripts/hfs/secret-init.mjs +304 -346
  667. package/runtime/scripts/hfs/secret-output.d.mts +18 -0
  668. package/runtime/scripts/hfs/secret-output.mjs +52 -80
  669. package/runtime/scripts/hfs/secret-prompt.d.mts +39 -0
  670. package/runtime/scripts/hfs/secret-prompt.mjs +108 -93
  671. package/runtime/scripts/hfs/secret-sops.d.mts +29 -0
  672. package/runtime/scripts/hfs/secret-sops.mjs +161 -199
  673. package/runtime/scripts/hfs/secret-standard.d.mts +89 -0
  674. package/runtime/scripts/hfs/secret-standard.mjs +300 -264
  675. package/runtime/scripts/hfs/secret-store.d.mts +36 -0
  676. package/runtime/scripts/hfs/secret-store.mjs +120 -136
  677. package/runtime/scripts/hfs/secret.d.mts +40 -0
  678. package/runtime/scripts/hfs/secret.mjs +254 -222
  679. package/runtime/scripts/hfs/slot-app-view.d.mts +11 -0
  680. package/runtime/scripts/hfs/slot-app-view.mjs +69 -74
  681. package/runtime/scripts/hfs/slot-classify.d.mts +75 -0
  682. package/runtime/scripts/hfs/slot-classify.mjs +42 -38
  683. package/runtime/scripts/hfs/slot-errors.d.mts +6 -0
  684. package/runtime/scripts/hfs/slot-errors.mjs +8 -7
  685. package/runtime/scripts/hfs/slot-imports.d.mts +8 -0
  686. package/runtime/scripts/hfs/slot-imports.mjs +70 -63
  687. package/runtime/scripts/hfs/slot-manifest-shape.d.mts +9 -0
  688. package/runtime/scripts/hfs/slot-manifest-shape.mjs +74 -52
  689. package/runtime/scripts/hfs/slot-match.d.mts +50 -0
  690. package/runtime/scripts/hfs/slot-match.mjs +122 -111
  691. package/runtime/scripts/hfs/slot-path.d.mts +1 -0
  692. package/runtime/scripts/hfs/slot-path.mjs +0 -2
  693. package/runtime/scripts/hfs/slot-required.d.mts +11 -0
  694. package/runtime/scripts/hfs/slot-required.mjs +65 -59
  695. package/runtime/scripts/hfs/slot-semantic-problems.d.mts +9 -0
  696. package/runtime/scripts/hfs/slot-semantic-problems.mjs +138 -112
  697. package/runtime/scripts/hfs/slot-side-problems.d.mts +3 -0
  698. package/runtime/scripts/hfs/slot-side-problems.mjs +23 -19
  699. package/runtime/scripts/hfs/slots.d.mts +188 -0
  700. package/runtime/scripts/hfs/slots.mjs +175 -152
  701. package/runtime/scripts/hfs/sql/pg-parse.d.mts +9 -0
  702. package/runtime/scripts/hfs/sql/pg-parse.mjs +25 -25
  703. package/runtime/scripts/hfs/stack-config-kit.d.mts +22 -0
  704. package/runtime/scripts/hfs/stack-config-kit.mjs +21 -17
  705. package/runtime/scripts/hfs/stack-deploy-config.d.mts +155 -0
  706. package/runtime/scripts/hfs/stack-deploy-config.mjs +345 -273
  707. package/runtime/scripts/hfs/stack-ingress-config.d.mts +74 -0
  708. package/runtime/scripts/hfs/stack-ingress-config.mjs +211 -177
  709. package/runtime/scripts/hfs/stacks-gate.d.mts +52 -0
  710. package/runtime/scripts/hfs/stacks-gate.mjs +557 -397
  711. package/runtime/scripts/hfs/starcistacks-runtime.d.mts +7 -0
  712. package/runtime/scripts/hfs/starcistacks-runtime.mjs +51 -40
  713. package/runtime/scripts/hfs/test-world-slot.d.mts +2 -0
  714. package/runtime/scripts/hfs/test-world-slot.mjs +0 -1
  715. package/runtime/scripts/hfs/trailing-slashes.d.mts +2 -0
  716. package/runtime/scripts/hfs/trailing-slashes.mjs +4 -4
  717. package/runtime/scripts/hfs/tree-findings.d.mts +9 -0
  718. package/runtime/scripts/hfs/tree-findings.mjs +18 -17
  719. package/runtime/scripts/hfs/tree.d.mts +35 -0
  720. package/runtime/scripts/hfs/tree.mjs +56 -46
  721. package/runtime/scripts/hfs/typescript-programs.d.mts +36 -0
  722. package/runtime/scripts/hfs/typescript-programs.mjs +66 -69
  723. package/runtime/scripts/hfs/view.d.mts +45 -0
  724. package/runtime/scripts/hfs/view.mjs +38 -40
  725. package/runtime/scripts/lib/bypass-markers.d.mts +15 -0
  726. package/runtime/scripts/lib/bypass-markers.mjs +210 -197
  727. package/runtime/scripts/lib/cli-arg.d.mts +67 -0
  728. package/runtime/scripts/lib/cli-arg.mjs +59 -63
  729. package/runtime/scripts/lib/command-tokens.d.mts +11 -0
  730. package/runtime/scripts/lib/command-tokens.mjs +77 -78
  731. package/runtime/scripts/lib/compose-values.d.mts +6 -0
  732. package/runtime/scripts/lib/compose-values.mjs +7 -7
  733. package/runtime/scripts/lib/css-rules.d.mts +36 -0
  734. package/runtime/scripts/lib/css-rules.mjs +181 -171
  735. package/runtime/scripts/lib/deployment-findings.d.mts +9 -0
  736. package/runtime/scripts/lib/deployment-findings.mjs +4 -4
  737. package/runtime/scripts/lib/dockerfile.d.mts +39 -0
  738. package/runtime/scripts/lib/dockerfile.mjs +131 -114
  739. package/runtime/scripts/lib/dom-tree.d.mts +7 -0
  740. package/runtime/scripts/lib/dom-tree.mjs +3 -3
  741. package/runtime/scripts/lib/env.d.mts +12 -0
  742. package/runtime/scripts/lib/env.mjs +3 -6
  743. package/runtime/scripts/lib/escape.d.mts +6 -0
  744. package/runtime/scripts/lib/escape.mjs +2 -6
  745. package/runtime/scripts/lib/event-contract.d.mts +21 -0
  746. package/runtime/scripts/lib/event-contract.mjs +73 -78
  747. package/runtime/scripts/lib/event-items.d.mts +20 -0
  748. package/runtime/scripts/lib/event-items.mjs +27 -15
  749. package/runtime/scripts/lib/example-refs.d.mts +69 -0
  750. package/runtime/scripts/lib/example-refs.mjs +145 -129
  751. package/runtime/scripts/lib/fs-kind.d.mts +12 -0
  752. package/runtime/scripts/lib/fs-kind.mjs +20 -10
  753. package/runtime/scripts/lib/git-hooks-dir.d.mts +15 -0
  754. package/runtime/scripts/lib/git-hooks-dir.mjs +16 -16
  755. package/runtime/scripts/lib/git.d.mts +24 -0
  756. package/runtime/scripts/lib/git.mjs +15 -16
  757. package/runtime/scripts/lib/glob.d.mts +4 -0
  758. package/runtime/scripts/lib/glob.mjs +23 -14
  759. package/runtime/scripts/lib/grammar-lint.d.mts +47 -0
  760. package/runtime/scripts/lib/grammar-lint.mjs +33 -37
  761. package/runtime/scripts/lib/grammar-vendor-parity-jsx.d.mts +40 -0
  762. package/runtime/scripts/lib/grammar-vendor-parity-jsx.mjs +285 -266
  763. package/runtime/scripts/lib/grammar-vendor-parity.d.mts +61 -0
  764. package/runtime/scripts/lib/grammar-vendor-parity.mjs +209 -216
  765. package/runtime/scripts/lib/graphql-contract.d.mts +53 -0
  766. package/runtime/scripts/lib/graphql-contract.mjs +201 -198
  767. package/runtime/scripts/lib/graphql-sdl.d.mts +66 -0
  768. package/runtime/scripts/lib/graphql-sdl.mjs +230 -205
  769. package/runtime/scripts/lib/hook-limits.d.mts +11 -0
  770. package/runtime/scripts/lib/hook-limits.mjs +0 -1
  771. package/runtime/scripts/lib/i18n.d.mts +48 -0
  772. package/runtime/scripts/lib/i18n.mjs +32 -35
  773. package/runtime/scripts/lib/in-order.d.mts +12 -0
  774. package/runtime/scripts/lib/in-order.mjs +57 -38
  775. package/runtime/scripts/lib/is-main.d.mts +6 -0
  776. package/runtime/scripts/lib/is-main.mjs +11 -4
  777. package/runtime/scripts/lib/js-lex.d.mts +32 -0
  778. package/runtime/scripts/lib/js-lex.mjs +196 -162
  779. package/runtime/scripts/lib/json-clone.d.mts +11 -0
  780. package/runtime/scripts/lib/json-clone.mjs +45 -36
  781. package/runtime/scripts/lib/json-schema.d.mts +2 -0
  782. package/runtime/scripts/lib/json-schema.mjs +127 -90
  783. package/runtime/scripts/lib/json.d.mts +16 -0
  784. package/runtime/scripts/lib/json.mjs +26 -14
  785. package/runtime/scripts/lib/language.d.mts +44 -0
  786. package/runtime/scripts/lib/language.mjs +133 -129
  787. package/runtime/scripts/lib/list.d.mts +17 -0
  788. package/runtime/scripts/lib/list.mjs +9 -14
  789. package/runtime/scripts/lib/loosening-budgets.d.mts +12 -0
  790. package/runtime/scripts/lib/loosening-budgets.mjs +35 -29
  791. package/runtime/scripts/lib/loosening-config.d.mts +64 -0
  792. package/runtime/scripts/lib/loosening-config.mjs +267 -236
  793. package/runtime/scripts/lib/loosening-state.d.mts +77 -0
  794. package/runtime/scripts/lib/loosening-state.mjs +134 -128
  795. package/runtime/scripts/lib/mutation-fence.d.mts +11 -0
  796. package/runtime/scripts/lib/mutation-fence.mjs +10 -5
  797. package/runtime/scripts/lib/normalize.d.mts +12 -0
  798. package/runtime/scripts/lib/normalize.mjs +11 -11
  799. package/runtime/scripts/lib/numeric-match.d.mts +2 -0
  800. package/runtime/scripts/lib/numeric-match.mjs +2 -2
  801. package/runtime/scripts/lib/package-at.d.mts +24 -0
  802. package/runtime/scripts/lib/package-at.mjs +34 -31
  803. package/runtime/scripts/lib/path-key.d.mts +76 -0
  804. package/runtime/scripts/lib/path-key.mjs +53 -38
  805. package/runtime/scripts/lib/pid-alive.d.mts +2 -0
  806. package/runtime/scripts/lib/pid-alive.mjs +10 -3
  807. package/runtime/scripts/lib/properties.d.mts +2 -0
  808. package/runtime/scripts/lib/properties.mjs +17 -11
  809. package/runtime/scripts/lib/provision-operation-schema.d.mts +4 -0
  810. package/runtime/scripts/lib/provision-operation-schema.mjs +4 -5
  811. package/runtime/scripts/lib/read-text.d.mts +12 -0
  812. package/runtime/scripts/lib/read-text.mjs +30 -17
  813. package/runtime/scripts/lib/redact.d.mts +52 -0
  814. package/runtime/scripts/lib/redact.mjs +135 -121
  815. package/runtime/scripts/lib/regex.d.mts +2 -0
  816. package/runtime/scripts/lib/regex.mjs +1 -2
  817. package/runtime/scripts/lib/roots.d.mts +32 -0
  818. package/runtime/scripts/lib/roots.mjs +5 -15
  819. package/runtime/scripts/lib/runtime-example.d.mts +1 -0
  820. package/runtime/scripts/lib/runtime-example.mjs +10 -8
  821. package/runtime/scripts/lib/same-text.d.mts +2 -0
  822. package/runtime/scripts/lib/same-text.mjs +0 -1
  823. package/runtime/scripts/lib/sealed-member.d.mts +16 -0
  824. package/runtime/scripts/lib/sealed-member.mjs +8 -13
  825. package/runtime/scripts/lib/secret-patterns.d.mts +30 -0
  826. package/runtime/scripts/lib/secret-patterns.mjs +175 -163
  827. package/runtime/scripts/lib/shell-source.d.mts +2 -0
  828. package/runtime/scripts/lib/shell-source.mjs +0 -1
  829. package/runtime/scripts/lib/sleep-sync.d.mts +2 -0
  830. package/runtime/scripts/lib/sleep-sync.mjs +5 -6
  831. package/runtime/scripts/lib/sops-envelope.d.mts +34 -0
  832. package/runtime/scripts/lib/sops-envelope.mjs +148 -120
  833. package/runtime/scripts/lib/source-phrases.d.mts +4 -0
  834. package/runtime/scripts/lib/source-phrases.mjs +13 -13
  835. package/runtime/scripts/lib/sql-ast.d.mts +13 -0
  836. package/runtime/scripts/lib/sql-ast.mjs +19 -18
  837. package/runtime/scripts/lib/sqlite.d.mts +21 -0
  838. package/runtime/scripts/lib/sqlite.mjs +3 -9
  839. package/runtime/scripts/lib/stack-declaration.d.mts +51 -0
  840. package/runtime/scripts/lib/stack-declaration.mjs +27 -20
  841. package/runtime/scripts/lib/stack-services.d.mts +73 -0
  842. package/runtime/scripts/lib/stack-services.mjs +128 -129
  843. package/runtime/scripts/lib/starciwork-boundary.d.mts +10 -0
  844. package/runtime/scripts/lib/starciwork-boundary.mjs +98 -92
  845. package/runtime/scripts/lib/swarm-stack.d.mts +24 -0
  846. package/runtime/scripts/lib/swarm-stack.mjs +112 -100
  847. package/runtime/scripts/lib/trim-end.d.mts +2 -0
  848. package/runtime/scripts/lib/trim-end.mjs +4 -4
  849. package/runtime/scripts/lib/ts-analysis.d.mts +24 -0
  850. package/runtime/scripts/lib/ts-analysis.mjs +28 -26
  851. package/runtime/scripts/lib/ts-ast.d.mts +36 -0
  852. package/runtime/scripts/lib/ts-ast.mjs +36 -42
  853. package/runtime/scripts/lib/walk.d.mts +19 -0
  854. package/runtime/scripts/lib/walk.mjs +33 -27
  855. package/runtime/scripts/lib/work-edition.d.mts +10 -0
  856. package/runtime/scripts/lib/work-edition.mjs +29 -21
  857. package/runtime/scripts/lib/workflow-events.d.mts +2 -0
  858. package/runtime/scripts/lib/workflow-events.mjs +5 -4
  859. package/runtime/scripts/lib/worktree-exclude.d.mts +16 -0
  860. package/runtime/scripts/lib/worktree-exclude.mjs +6 -8
  861. package/runtime/scripts/lib/yaml-cached.d.mts +2 -0
  862. package/runtime/scripts/lib/yaml-cached.mjs +4 -4
  863. package/runtime/scripts/machine/artifact-hold.d.mts +26 -0
  864. package/runtime/scripts/machine/artifact-hold.mjs +22 -23
  865. package/runtime/scripts/work/asset-slot.d.mts +96 -0
  866. package/runtime/scripts/work/asset-slot.mjs +201 -175
  867. package/runtime/scripts/work/draw/draw-dna-anatomy.d.mts +30 -0
  868. package/runtime/scripts/work/draw/draw-dna-anatomy.mjs +40 -36
  869. package/runtime/scripts/work/draw/draw-dna-findings.d.mts +12 -0
  870. package/runtime/scripts/work/draw/draw-dna-findings.mjs +240 -206
  871. package/runtime/scripts/work/draw/draw-dna-selector.d.mts +7 -0
  872. package/runtime/scripts/work/draw/draw-dna-selector.mjs +28 -28
  873. package/runtime/scripts/work/draw/draw-dna.d.mts +191 -0
  874. package/runtime/scripts/work/draw/draw-dna.mjs +283 -249
  875. package/runtime/scripts/work/grammar-proposal.d.mts +83 -0
  876. package/runtime/scripts/work/grammar-proposal.mjs +215 -178
  877. package/runtime/scripts/work/work-io.d.mts +62 -0
  878. package/runtime/scripts/work/work-io.mjs +82 -63
  879. package/scaffold/add-app.d.mts +15 -0
  880. package/scaffold/add-app.mjs +111 -163
  881. package/scaffold/add-cli-lite.d.mts +8 -0
  882. package/scaffold/add-cli-lite.mjs +173 -207
  883. package/scaffold/add-compose.d.mts +44 -0
  884. package/scaffold/add-compose.mjs +62 -62
  885. package/scaffold/add-database-lite.d.mts +2 -0
  886. package/scaffold/add-database-lite.mjs +10 -10
  887. package/scaffold/add-fe-layout.d.mts +16 -0
  888. package/scaffold/add-fe-layout.mjs +23 -23
  889. package/scaffold/add-fe-overlay.d.mts +17 -0
  890. package/scaffold/add-fe-overlay.mjs +76 -78
  891. package/scaffold/add-fe-page.d.mts +41 -0
  892. package/scaffold/add-fe-page.mjs +84 -89
  893. package/scaffold/add-fe-unit.d.mts +59 -0
  894. package/scaffold/add-fe-unit.mjs +205 -197
  895. package/scaffold/add-inbox-lite.d.mts +17 -0
  896. package/scaffold/add-inbox-lite.mjs +209 -264
  897. package/scaffold/add-plan.d.mts +68 -0
  898. package/scaffold/add-plan.mjs +164 -149
  899. package/scaffold/add-queue-lite.d.mts +34 -0
  900. package/scaffold/add-queue-lite.mjs +267 -267
  901. package/scaffold/add-table.d.mts +16 -0
  902. package/scaffold/add-table.mjs +405 -442
  903. package/scaffold/add.d.mts +34 -0
  904. package/scaffold/add.mjs +260 -246
  905. package/scaffold/app.d.mts +198 -0
  906. package/scaffold/app.mjs +413 -391
  907. package/scaffold/edition-gate.d.mts +49 -0
  908. package/scaffold/edition-gate.mjs +19 -29
  909. package/scaffold/image.d.mts +4 -0
  910. package/scaffold/image.mjs +12 -12
  911. package/scaffold/lite-exports.d.mts +15 -0
  912. package/scaffold/lite-exports.mjs +262 -255
  913. package/scaffold/pattern-file-slots.d.mts +7 -0
  914. package/scaffold/pattern-file-slots.mjs +29 -24
  915. package/scaffold/service.d.mts +60 -0
  916. package/scaffold/service.mjs +297 -279
  917. package/scaffold/test-world.d.mts +31 -0
  918. package/scaffold/test-world.mjs +71 -78
  919. package/scaffold/yaml-block-edit.d.mts +23 -0
  920. package/scaffold/yaml-block-edit.mjs +95 -87
  921. package/scripts/sync-bundles.d.mts +31 -0
  922. package/src/fixtures/bypass-index.d.mts +4 -0
  923. package/src/fixtures/hfs-stack-tooling/passing.d.mts +8 -0
  924. package/src/fixtures/hfs-stack-tooling/violating.d.mts +19 -0
  925. package/src/fixtures/hfs-stacks-full/model-unavailable.d.mts +10 -0
  926. package/src/fixtures/hfs-stacks-full/passing.d.mts +19 -0
  927. package/src/fixtures/hfs-stacks-full/violating.d.mts +10 -0
  928. package/src/fixtures/loosening/adversarial.d.mts +4 -0
  929. package/src/fixtures/loosening/forms.d.mts +16 -0
  930. package/src/fixtures/loosening/swallows.d.mts +5 -0
  931. package/src/helpers/hfs-arch-fixture.d.mts +47 -0
  932. package/src/helpers/hfs-cli-fixture.d.mts +65 -0
  933. package/src/helpers/hfs-hfs-docker-rules-fixture.d.mts +5 -0
  934. package/src/helpers/hfs-secret-fixture.d.mts +19 -0
  935. package/src/helpers/pre-push-fixture.d.mts +21 -0
  936. package/src/helpers/stack-compose.d.mts +6 -0
  937. package/src/helpers/swarm-stack-fixture.d.mts +18 -0
  938. package/src/helpers/tmpdir.d.mts +2 -0
  939. package/src/main.d.mts +58 -0
  940. package/src/main.mjs +494 -346
  941. package/stack/age-install.d.mts +13 -0
  942. package/stack/age-install.mjs +28 -25
  943. package/stack/backup.d.mts +11 -0
  944. package/stack/backup.mjs +76 -69
  945. package/stack/build-args.d.mts +7 -0
  946. package/stack/build-args.mjs +22 -23
  947. package/stack/build.d.mts +8 -0
  948. package/stack/build.mjs +138 -119
  949. package/stack/cli.d.mts +55 -0
  950. package/stack/cli.mjs +187 -130
  951. package/stack/config.d.mts +197 -0
  952. package/stack/config.mjs +47 -33
  953. package/stack/context.d.mts +122 -0
  954. package/stack/context.mjs +63 -65
  955. package/stack/converge.d.mts +13 -0
  956. package/stack/converge.mjs +73 -69
  957. package/stack/deploy.d.mts +88 -0
  958. package/stack/deploy.mjs +272 -224
  959. package/stack/digest.d.mts +8 -0
  960. package/stack/digest.mjs +1 -6
  961. package/stack/docker-transport.d.mts +5 -0
  962. package/stack/docker-transport.mjs +101 -93
  963. package/stack/edge-alias.d.mts +42 -0
  964. package/stack/edge-alias.mjs +62 -60
  965. package/stack/errors.d.mts +18 -0
  966. package/stack/errors.mjs +8 -10
  967. package/stack/image-scan.d.mts +20 -0
  968. package/stack/image-scan.mjs +114 -101
  969. package/stack/ingress-create.d.mts +4 -0
  970. package/stack/ingress-create.mjs +82 -86
  971. package/stack/ingress-publish.d.mts +6 -0
  972. package/stack/ingress-publish.mjs +194 -182
  973. package/stack/ingress.d.mts +55 -0
  974. package/stack/ingress.mjs +534 -524
  975. package/stack/jobs.d.mts +12 -0
  976. package/stack/jobs.mjs +35 -34
  977. package/stack/live-restore.d.mts +35 -0
  978. package/stack/live-restore.mjs +120 -125
  979. package/stack/post-deploy.d.mts +8 -0
  980. package/stack/post-deploy.mjs +25 -25
  981. package/stack/prepare.d.mts +24 -0
  982. package/stack/prepare.mjs +213 -200
  983. package/stack/private-file.d.mts +5 -0
  984. package/stack/private-file.mjs +9 -9
  985. package/stack/provision.d.mts +25 -0
  986. package/stack/provision.mjs +64 -63
  987. package/stack/prune.d.mts +11 -0
  988. package/stack/prune.mjs +38 -27
  989. package/stack/release-store.d.mts +28 -0
  990. package/stack/release-store.mjs +68 -67
  991. package/stack/remote.d.mts +83 -0
  992. package/stack/remote.mjs +176 -162
  993. package/stack/render.d.mts +14 -0
  994. package/stack/render.mjs +132 -124
  995. package/stack/retire.d.mts +9 -0
  996. package/stack/retire.mjs +31 -34
  997. package/stack/sealed.d.mts +46 -0
  998. package/stack/sealed.mjs +78 -82
  999. package/stack/server-key.d.mts +67 -0
  1000. package/stack/server-key.mjs +217 -203
  1001. package/stack/ssh-session.d.mts +29 -0
  1002. package/stack/ssh-session.mjs +133 -123
  1003. package/stack/target-binding.d.mts +21 -0
  1004. package/stack/target-binding.mjs +68 -44
  1005. package/stack/task.d.mts +12 -0
  1006. package/stack/task.mjs +26 -31
  1007. package/stack/urgent-floor.d.mts +106 -0
  1008. package/stack/urgent-floor.mjs +211 -160
  1009. package/stack/urgent-release.d.mts +38 -0
  1010. package/stack/urgent-release.mjs +26 -22
  1011. package/stack/verify.d.mts +14 -0
  1012. package/stack/verify.mjs +75 -65
  1013. package/sync/ci-commands.d.mts +26 -0
  1014. package/sync/ci-commands.mjs +181 -182
  1015. package/sync/cli.d.mts +8 -0
  1016. package/sync/cli.mjs +9 -9
  1017. package/sync/format.d.mts +38 -0
  1018. package/sync/format.mjs +30 -24
  1019. package/sync/hygiene.d.mts +23 -0
  1020. package/sync/hygiene.mjs +113 -99
  1021. package/sync/index.d.mts +237 -0
  1022. package/sync/index.mjs +457 -436
  1023. package/sync/managed.d.mts +23 -0
  1024. package/sync/managed.mjs +104 -94
  1025. package/sync/quality-services.d.mts +35 -0
  1026. package/sync/quality-services.mjs +48 -41
  1027. package/sync/readme-badges.d.mts +13 -0
  1028. package/sync/readme-badges.mjs +45 -44
  1029. package/sync/sonar-key.d.mts +31 -0
  1030. package/sync/sonar-key.mjs +29 -28
  1031. package/sync/stack-images.d.mts +36 -0
  1032. package/sync/stack-images.mjs +85 -95
  1033. package/sync/ts-strict.d.mts +14 -0
  1034. package/sync/ts-strict.mjs +43 -34
  1035. package/templates/app/ci-workflows/github/workflows/ci.yml +1 -1
  1036. package/templates/app/ci-workflows/github/workflows/images.yml +1 -1
  1037. package/templates/app/ci-workflows/github/workflows/stack-deploy.yml +2 -2
  1038. package/templates/app/ci-workflows/github/workflows/stack-key-restore.yml +1 -1
  1039. package/templates/app/ci-workflows-lite/github/workflows/ci.yml +1 -1
  1040. package/templates/app/ci-workflows-lite/github/workflows/db-deploy.yml +3 -3
  1041. package/templates/app/ci-workflows-lite/github/workflows/images.yml +1 -1
  1042. package/templates/app/ci-workflows-lite/github/workflows/stack-deploy.yml +2 -2
  1043. package/templates/app/ci-workflows-lite/github/workflows/stack-key-restore.yml +1 -1
  1044. package/templates/app/codegen-lite/scripts/codegen.mjs +1 -1
  1045. package/templates/app/db-bootstrap/db/bootstrap.sql +1 -1
  1046. package/templates/app/db-image/starcistacks/images/db/Dockerfile +1 -1
  1047. package/templates/app/hooks/husky/pre-commit +2 -2
  1048. package/templates/app/package-scripts/db.json +3 -3
  1049. package/templates/app/package-scripts/package.json +1 -1
  1050. package/templates/app/quality-config/sonar-project.properties +1 -1
  1051. package/templates/app/skeleton/.starcistacks/application-stacks.yaml +2 -2
  1052. package/templates/app/skeleton/.starcistacks/production/README.md +4 -4
  1053. package/templates/app/skeleton/.starcistacks/production/infra/swarm/stack.yaml +8 -3
  1054. package/templates/app/skeleton/README.md +1 -1
  1055. package/templates/app/skeleton-lite/.starcistacks/application-stacks.yaml +2 -2
  1056. package/templates/app/skeleton-lite/.starcistacks/dev/README.md +1 -1
  1057. package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/stack.yaml +6 -2
  1058. package/templates/app/skeleton-lite/.starcistacks/dev/runtime/env/KEYS.md +3 -2
  1059. package/templates/app/skeleton-lite/.starcistacks/production/README.md +5 -5
  1060. package/templates/app/skeleton-lite/.starcistacks/production/infra/swarm/stack.yaml +7 -2
  1061. package/templates/app/skeleton-lite/.starcistacks/production/runtime/env/KEYS.md +3 -2
  1062. package/templates/app/skeleton-lite/README.md +2 -2
  1063. package/templates/be/skeleton/apps/cli/src/compose/platform.ts +1 -1
  1064. package/templates/be/skeleton/apps/cli/src/main.ts +6 -4
  1065. package/templates/be/skeleton/src/modules/platform/config/env-source.config.ts +12 -0
  1066. package/templates/be/skeleton/src/modules/platform/database/abstract.entity.ts +19 -0
  1067. package/templates/be/skeleton/src/modules/platform/database/database.module.ts +7 -1
  1068. package/templates/be/skeleton/src/modules/platform/logging/json-logger.service.spec.ts +19 -0
  1069. package/templates/be/skeleton/src/modules/platform/logging/json-logger.service.ts +4 -3
  1070. package/templates/be/skeleton/src/modules/platform/logging/logging.module.ts +1 -1
  1071. package/templates/be/skeleton/src/modules/platform/logging/logging.options.ts +5 -2
  1072. package/templates/be/skeleton-lite/apps/cli/src/compose/platform.ts +1 -1
  1073. package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.config.spec.ts +40 -0
  1074. package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.config.ts +1 -0
  1075. package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.cookie.spec.ts +48 -1
  1076. package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.cookie.ts +5 -0
  1077. package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.options.ts +5 -0
  1078. package/templates/be/skeleton-lite/src/modules/integrations/passport/strategies/google.strategy.ts +1 -1
  1079. package/templates/be/skeleton-lite/src/modules/integrations/passport/strategies/oauth-state.store.ts +7 -3
  1080. package/templates/be/skeleton-lite/src/modules/platform/database/database.module.ts +7 -1
  1081. package/templates/be/table/entity.ts.tpl +4 -15
  1082. package/upgrade/check.d.mts +9 -0
  1083. package/upgrade/check.mjs +8 -8
  1084. package/upgrade/file-state.d.mts +6 -0
  1085. package/upgrade/file-state.mjs +32 -40
  1086. package/upgrade/index.d.mts +13 -0
  1087. package/upgrade/index.mjs +122 -151
  1088. package/vendor-types.d.mts +8 -0
@@ -1,98 +1,124 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { fileURLToPath } from 'node:url';
4
- import {skillRoot} from './runtime-root.mjs';
5
- import {parseYaml} from './yaml.mjs';
6
- import {isPlainObject as plain} from './plain-object.mjs';
7
- import {invalid,validateRoots,ROOT_KEYS} from './invalid-config.mjs';
8
- import {validateSonar} from './sonar-config.mjs';
9
- import {validateRelease,RELEASE_KEYS} from './release-config.mjs';
10
- import {validateOrca,ORCA_KEYS} from './orca-config.mjs';
11
- import {validateResources,RESOURCE_KEYS} from './resources-config.mjs';
4
+ import { skillRoot } from './runtime-root.mjs';
5
+ import { parseYaml } from './yaml.mjs';
6
+ import { isPlainObject as plain } from './plain-object.mjs';
7
+ import { invalid, validateRoots, ROOT_KEYS } from './invalid-config.mjs';
8
+ import { validateSonar } from './sonar-config.mjs';
9
+ import { validateRelease, RELEASE_KEYS } from './release-config.mjs';
10
+ import { validateOrca, ORCA_KEYS } from './orca-config.mjs';
11
+ import { validateResources, RESOURCE_KEYS } from './resources-config.mjs';
12
12
  import { ENV_NAME, connectorSecret } from './secrets.mjs';
13
- import {byCodeUnit} from './by-code-unit.mjs';
14
- import {EFFORT_LEVELS,MODELS_KEYS,validateModelsBlock,refuseRemovedKeys,shippedTiers} from './model-config.mjs';
15
- const knownNames=names=>[...names].sort(byCodeUnit).join(', ');
16
- export {readDotenv,connectorSecret} from './secrets.mjs';
17
-
18
- export const configRoot=skillRoot;
13
+ import { byCodeUnit } from './by-code-unit.mjs';
14
+ import { EFFORT_LEVELS, MODELS_KEYS, validateModelsBlock, refuseRemovedKeys, shippedTiers } from './model-config.mjs';
15
+ const knownNames = (names) => [...names].sort(byCodeUnit).join(', ');
16
+ export { readDotenv, connectorSecret } from './secrets.mjs';
17
+ export const configRoot = skillRoot;
19
18
  /** The owner-facing language when config.yaml `language` is absent — the one default every reader shares (scripts/lib/i18n.mjs ownerLanguage). A base-tier value: machine (home.mjs) re-exports it, not the reverse. */
20
- export const DEFAULT_OWNER_LANGUAGE='en';
19
+ export const DEFAULT_OWNER_LANGUAGE = 'en';
21
20
  // Parsed once per file version (mtime + size) per process; each caller gets its own copy.
22
21
  // The one runtimes.yaml loader: it throws on a missing or unparsable file, so no caller ever
23
22
  // reasons on a silent empty document. The pool map (provider, roles, models, maxParallel)
24
23
  // lives in the ONE model catalog — modules/models/registry.yaml `pools` — and is merged in
25
24
  // under `runtimes` so consumers read the same shape as before.
26
- let runtimeProfileCache=null;
27
- export function runtimeProfile(){
28
- const source=fileURLToPath(new URL('../modules/models/runtimes.yaml',import.meta.url));
29
- const registryFile=fileURLToPath(new URL('../modules/models/registry.yaml',import.meta.url));
30
- let stat;try{stat=fs.statSync(source);}catch{throw new Error('Missing modules/models/runtimes.yaml');}
31
- let rstat;try{rstat=fs.statSync(registryFile);}catch{throw new Error('Missing modules/models/registry.yaml');}
32
- const version=`${stat.mtimeMs}:${stat.size}:${rstat.mtimeMs}:${rstat.size}`;
33
- if(runtimeProfileCache?.version!==version){
34
- const doc=parseYaml(fs.readFileSync(source,'utf8'));
35
- const registry=parseYaml(fs.readFileSync(registryFile,'utf8'));
36
- runtimeProfileCache={version,profile:{...doc,runtimes:registry?.pools??{},models:registry?.models??{}}};
37
- }
38
- return structuredClone(runtimeProfileCache.profile);
25
+ let runtimeProfileCache = null;
26
+ export function runtimeProfile() {
27
+ const source = fileURLToPath(new URL('../modules/models/runtimes.yaml', import.meta.url));
28
+ const registryFile = fileURLToPath(new URL('../modules/models/registry.yaml', import.meta.url));
29
+ let stat;
30
+ try {
31
+ stat = fs.statSync(source);
32
+ }
33
+ catch {
34
+ throw new Error('Missing modules/models/runtimes.yaml');
35
+ }
36
+ let rstat;
37
+ try {
38
+ rstat = fs.statSync(registryFile);
39
+ }
40
+ catch {
41
+ throw new Error('Missing modules/models/registry.yaml');
42
+ }
43
+ const version = `${stat.mtimeMs}:${stat.size}:${rstat.mtimeMs}:${rstat.size}`;
44
+ if (runtimeProfileCache?.version !== version) {
45
+ const doc = parseYaml(fs.readFileSync(source, 'utf8'));
46
+ const registry = parseYaml(fs.readFileSync(registryFile, 'utf8'));
47
+ runtimeProfileCache = { version, profile: { ...doc, runtimes: registry?.pools ?? {}, models: registry?.models ?? {} } };
48
+ }
49
+ return structuredClone(runtimeProfileCache.profile);
39
50
  }
40
51
  /**
41
52
  * modules/models/runtimes.yaml `allocation` — where the workers' operating numbers live: the dispatch lease
42
53
  * TTL, the liveness and cadence windows, the slicing weights, the failure cooldowns. Code reads them from
43
54
  * here; a literal copy of any of them in a source file would be a second authority.
44
55
  */
45
- export function allocationSettings(){
46
- const allocation=runtimeProfile()?.allocation??{};
47
- return allocation.admission?{...allocation,admission:{...allocation.admission,...shippedTiers().usage}}:allocation;
56
+ export function allocationSettings() {
57
+ const allocation = runtimeProfile()?.allocation ?? {};
58
+ return allocation.admission ? { ...allocation, admission: { ...allocation.admission, ...shippedTiers().usage } } : allocation;
48
59
  }
49
60
  // These are current owner declarations, never a historical runtime owner's consent.
50
- function validateOwnerProfile(value,name,pathsKey){
51
- if(value===null)return;
52
- const bad=invalid(name);
53
- if(!plain(value))bad(' must be an owner approval mapping or null.');
54
- const keys=new Set(['approvedBy','approvalRef',pathsKey,...(name==='launchTrust'?['profile']:[])]);
55
- for(const key of Object.keys(value))if(!keys.has(key))bad(` has unknown key ${key}.`);
56
- if(value.approvedBy!=='owner')bad('.approvedBy must be owner; adoption must come from the current owner.');
57
- if(typeof value.approvalRef!=='string'||!value.approvalRef.trim())bad('.approvalRef must identify the current owner approval.');
58
- if(name==='launchTrust'&&!['automatic','declined'].includes(value.profile))bad('.profile must be automatic or declined.');
59
- const paths=value[pathsKey];
60
- if(!Array.isArray(paths)||!paths.length||paths.some(p=>typeof p!=='string'||!(path.isAbsolute(p)||path.win32.isAbsolute(p))||/[\r\n\0]/.test(p)))bad(`.${pathsKey} must list exact absolute repository roots.`);
61
+ function validateOwnerProfile(value, name, pathsKey) {
62
+ if (value === null)
63
+ return;
64
+ const bad = invalid(name);
65
+ if (!plain(value))
66
+ bad(' must be an owner approval mapping or null.');
67
+ const keys = new Set(['approvedBy', 'approvalRef', pathsKey, ...(name === 'launchTrust' ? ['profile'] : [])]);
68
+ for (const key of Object.keys(value))
69
+ if (!keys.has(key))
70
+ bad(` has unknown key ${key}.`);
71
+ if (value.approvedBy !== 'owner')
72
+ bad('.approvedBy must be owner; adoption must come from the current owner.');
73
+ if (typeof value.approvalRef !== 'string' || !value.approvalRef.trim())
74
+ bad('.approvalRef must identify the current owner approval.');
75
+ if (name === 'launchTrust' && !(typeof value.profile === 'string' && ['automatic', 'declined'].includes(value.profile)))
76
+ bad('.profile must be automatic or declined.');
77
+ const paths = value[pathsKey];
78
+ if (!Array.isArray(paths) || !paths.length || paths.some((p) => typeof p !== 'string' || !(path.isAbsolute(p) || path.win32.isAbsolute(p)) || /[\r\n\0]/.test(p)))
79
+ bad(`.${pathsKey} must list exact absolute repository roots.`);
61
80
  }
62
- export function launchTrustSettings(config=loadConfig()){
63
- const value=config?.launchTrust??null;validateOwnerProfile(value,'launchTrust','roots');return value;
81
+ export function launchTrustSettings(config = loadConfig()) {
82
+ const value = config?.launchTrust ?? null;
83
+ validateOwnerProfile(value, 'launchTrust', 'roots');
84
+ return value;
64
85
  }
65
- export function workflowPurgeSettings(config=loadConfig()){
66
- const retention=config?.retention??null;
67
- if(retention===null)return null;
68
- const bad=invalid('retention');
69
- if(!plain(retention)||Object.keys(retention).some(k=>k!=='workflowPurge'))bad(' must be {workflowPurge?} or null.');
70
- const value=retention.workflowPurge??null;validateOwnerProfile(value,'retention.workflowPurge','repos');return value;
86
+ export function workflowPurgeSettings(config = loadConfig()) {
87
+ const retention = config?.retention ?? null;
88
+ if (retention === null)
89
+ return null;
90
+ const bad = invalid('retention');
91
+ if (!plain(retention) || Object.keys(retention).some(k => k !== 'workflowPurge'))
92
+ bad(' must be {workflowPurge?} or null.');
93
+ const value = retention.workflowPurge ?? null;
94
+ validateOwnerProfile(value, 'retention.workflowPurge', 'repos');
95
+ return value;
71
96
  }
72
97
  /** One positive millisecond value out of `allocation`, by dotted key. Refuses when the contract omits it. */
73
- export function allocationMs(dotted){
74
- const raw=dotted.split('.').reduce((node,key)=>(node==null?node:node[key]),allocationSettings());
75
- const value=Number(raw);
76
- if(!Number.isFinite(value)||value<=0)throw new Error(`modules/models/runtimes.yaml allocation.${dotted} must declare a positive number of milliseconds`);
77
- return value;
98
+ export function allocationMs(dotted) {
99
+ const raw = dotted.split('.').reduce((node, key) => (node == null ? node : node[key]), allocationSettings());
100
+ const value = Number(raw);
101
+ if (!Number.isFinite(value) || value <= 0)
102
+ throw new Error(`modules/models/runtimes.yaml allocation.${dotted} must declare a positive number of milliseconds`);
103
+ return value;
78
104
  }
79
105
  /**
80
106
  * modules/models/runtimes.yaml allocation.slicing.gears — the declared gear vocabulary. A gear is an
81
107
  * index into allocation.slicing.size.<class>.agents, so the two lists are one authority; code never
82
108
  * carries a literal gear. Refuses when the contract omits the list or spells it with a non-integer.
83
109
  */
84
- export function slicingGears(){
85
- const gears=allocationSettings()?.slicing?.gears;
86
- if(!Array.isArray(gears)||!gears.length||gears.some(value=>!Number.isInteger(value)||value<1))
87
- throw new Error('modules/models/runtimes.yaml allocation.slicing.gears must declare a non-empty list of positive integers');
88
- return [...gears];
110
+ export function slicingGears() {
111
+ const gears = allocationSettings()?.slicing?.gears;
112
+ if (!Array.isArray(gears) || !gears.length || gears.some(value => !Number.isInteger(value) || value < 1))
113
+ throw new Error('modules/models/runtimes.yaml allocation.slicing.gears must declare a non-empty list of positive integers');
114
+ return [...gears];
89
115
  }
90
116
  /** The gear an absent `parallel` block means. The first declared gear, never a literal. */
91
- export const defaultParallelGear=()=>slicingGears()[0];
117
+ export const defaultParallelGear = () => slicingGears()[0];
92
118
  /** The owner's parallelism gear: config.yaml `parallel.gear`, or the first declared gear when absent. */
93
- export function parallelGear(config=loadConfig()){
94
- validateConfig(config);
95
- return config?.parallel?.gear??defaultParallelGear();
119
+ export function parallelGear(config = loadConfig()) {
120
+ validateConfig(config);
121
+ return config?.parallel?.gear ?? defaultParallelGear();
96
122
  }
97
123
  /**
98
124
  * config.yaml `connectors` — the owner's public ask channel (docs/connectors.md). Everything defaults off.
@@ -100,96 +126,130 @@ export function parallelGear(config=loadConfig()){
100
126
  * (or `<NAME>_FILE` pointing at a custody file); a value that is not an env var name is refused, so a
101
127
  * pasted token fails closed instead of landing in a plain-text file.
102
128
  */
103
- const CLOUDFLARE_MODES=['off','quick','named'];
129
+ const CLOUDFLARE_MODES = ['off', 'quick', 'named'];
104
130
  /** The serve-ask port band, both ends included (scripts/kernel/ask-server.mjs scans [first..last]); the gateway stays outside it. */
105
- export const ASK_PORT_BAND=[6969,7069];
106
- export const CONNECTOR_DEFAULTS=Object.freeze({
107
- repos:[],
108
- gateway:{port:7070},
109
- cloudflare:{mode:'off',tunnel:null,credentialsFile:null,tokenEnv:'CLOUDFLARE_TUNNEL_TOKEN',hostname:null,access:false},
110
- telegram:{enabled:false,botTokenEnv:'TELEGRAM_BOT_TOKEN',chatId:null,exposeCredentialAsks:false},
131
+ export const ASK_PORT_BAND = [6969, 7069];
132
+ export const CONNECTOR_DEFAULTS = Object.freeze({
133
+ repos: [],
134
+ gateway: { port: 7070 },
135
+ cloudflare: { mode: 'off', tunnel: null, credentialsFile: null, tokenEnv: 'CLOUDFLARE_TUNNEL_TOKEN', hostname: null, access: false },
136
+ telegram: { enabled: false, botTokenEnv: 'TELEGRAM_BOT_TOKEN', chatId: null, exposeCredentialAsks: false },
111
137
  });
112
- const HOSTNAME=/^(?=.{1,253}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,63}$/i;
113
- const TUNNEL_REF=/^[A-Za-z0-9][A-Za-z0-9_.-]{0,63}$/;
114
- const CHAT_ID=/^(?:-?\d{1,20}|@[A-Za-z]\w{4,31})$/;
115
- const SECRET_KEYS=new Set(['token','tunnelToken','botToken','secret','apiToken','password']);
116
- const closedConnectorShape=(bad,node,where,keys)=>{
117
- if(!plain(node))bad(`.${where} must be a mapping.`);
118
- for(const key of Object.keys(node)){
119
- if(SECRET_KEYS.has(key))bad(`.${where}.${key}: secrets never live in config.yaml — set the environment variable the *Env key names instead.`);
120
- if(!keys.includes(key))bad(`.${where} has unknown key ${key} (allowed: ${keys.join(', ')}).`);
121
- }
122
- };
123
- const connectorEnvName=(bad,where,value)=>{if(value!==undefined&&(typeof value!=='string'||!ENV_NAME.test(value)))bad(`.${where} must name an environment variable (UPPER_SNAKE), never hold the secret itself.`);};
124
- const validateCloudflareMode=(cf,bad)=>{if(cf.mode!==undefined&&!CLOUDFLARE_MODES.includes(cf.mode))bad(`.cloudflare.mode must be one of ${CLOUDFLARE_MODES.join(' | ')}.`);};
125
- const validateCloudflareDetails=(cf,bad)=>{
126
- if(cf.hostname!==undefined&&cf.hostname!==null&&(typeof cf.hostname!=='string'||!HOSTNAME.test(cf.hostname)))bad('.cloudflare.hostname must be a bare hostname (no scheme, no path) or null.');
127
- if(cf.access!==undefined&&typeof cf.access!=='boolean')bad('.cloudflare.access must be true or false.');
128
- if(cf.tunnel!==undefined&&cf.tunnel!==null&&(typeof cf.tunnel!=='string'||!TUNNEL_REF.test(cf.tunnel)))bad('.cloudflare.tunnel must be the named tunnel UUID (or name) or null.');
129
- if(cf.credentialsFile!==undefined&&cf.credentialsFile!==null&&(typeof cf.credentialsFile!=='string'||!cf.credentialsFile.trim()))bad('.cloudflare.credentialsFile must be the tunnel credentials JSON path or null.');
138
+ const HOSTNAME = /^(?=.{1,253}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,63}$/i;
139
+ const TUNNEL_REF = /^[A-Za-z0-9][A-Za-z0-9_.-]{0,63}$/;
140
+ const CHAT_ID = /^(?:-?\d{1,20}|@[A-Za-z]\w{4,31})$/;
141
+ const SECRET_KEYS = new Set(['token', 'tunnelToken', 'botToken', 'secret', 'apiToken', 'password']);
142
+ function closedConnectorShape(bad, node, where, keys) {
143
+ if (!plain(node))
144
+ bad(`.${where} must be a mapping.`);
145
+ for (const key of Object.keys(node)) {
146
+ if (SECRET_KEYS.has(key))
147
+ bad(`.${where}.${key}: secrets never live in config.yaml — set the environment variable the *Env key names instead.`);
148
+ if (!keys.includes(key))
149
+ bad(`.${where} has unknown key ${key} (allowed: ${keys.join(', ')}).`);
150
+ }
151
+ }
152
+ const connectorEnvName = (bad, where, value) => { if (value !== undefined && (typeof value !== 'string' || !ENV_NAME.test(value)))
153
+ bad(`.${where} must name an environment variable (UPPER_SNAKE), never hold the secret itself.`); };
154
+ const validateCloudflareMode = (cf, bad) => { if (cf.mode !== undefined && (typeof cf.mode !== 'string' || !CLOUDFLARE_MODES.includes(cf.mode)))
155
+ bad(`.cloudflare.mode must be one of ${CLOUDFLARE_MODES.join(' | ')}.`); };
156
+ const validateCloudflareDetails = (cf, bad) => {
157
+ if (cf.hostname !== undefined && cf.hostname !== null && (typeof cf.hostname !== 'string' || !HOSTNAME.test(cf.hostname)))
158
+ bad('.cloudflare.hostname must be a bare hostname (no scheme, no path) or null.');
159
+ if (cf.access !== undefined && typeof cf.access !== 'boolean')
160
+ bad('.cloudflare.access must be true or false.');
161
+ if (cf.tunnel !== undefined && cf.tunnel !== null && (typeof cf.tunnel !== 'string' || !TUNNEL_REF.test(cf.tunnel)))
162
+ bad('.cloudflare.tunnel must be the named tunnel UUID (or name) or null.');
163
+ if (cf.credentialsFile !== undefined && cf.credentialsFile !== null && (typeof cf.credentialsFile !== 'string' || !cf.credentialsFile.trim()))
164
+ bad('.cloudflare.credentialsFile must be the tunnel credentials JSON path or null.');
130
165
  };
131
- const validateCloudflareModeConstraints=(cf,bad)=>{if(cf.credentialsFile&&!cf.tunnel){bad('.cloudflare.credentialsFile needs cloudflare.tunnel (the tunnel UUID the credentials belong to).');}if(cf.mode==='named'&&!cf.hostname){bad('.cloudflare.mode named needs cloudflare.hostname (the public hostname routed to the gateway).');}if(cf.mode==='quick'&&(cf.tunnel||cf.credentialsFile)){bad('.cloudflare.mode quick runs no named tunnel; tunnel/credentialsFile are for mode named.');}if(cf.mode==='quick'&&cf.hostname){bad('.cloudflare.mode quick gets a random trycloudflare.com hostname; set hostname only for mode named.');}};
132
- const validateCloudflareConnector=cf=>{
133
- const bad=invalid('connectors');
134
- closedConnectorShape(bad,cf,'cloudflare',['mode','tunnel','credentialsFile','tokenEnv','hostname','access']);
135
- validateCloudflareMode(cf,bad);
136
- connectorEnvName(bad,'cloudflare.tokenEnv',cf.tokenEnv);
137
- validateCloudflareDetails(cf,bad);
138
- validateCloudflareModeConstraints(cf,bad);
166
+ const validateCloudflareModeConstraints = (cf, bad) => { if (cf.credentialsFile && !cf.tunnel) {
167
+ bad('.cloudflare.credentialsFile needs cloudflare.tunnel (the tunnel UUID the credentials belong to).');
168
+ } if (cf.mode === 'named' && !cf.hostname) {
169
+ bad('.cloudflare.mode named needs cloudflare.hostname (the public hostname routed to the gateway).');
170
+ } if (cf.mode === 'quick' && (cf.tunnel || cf.credentialsFile)) {
171
+ bad('.cloudflare.mode quick runs no named tunnel; tunnel/credentialsFile are for mode named.');
172
+ } if (cf.mode === 'quick' && cf.hostname) {
173
+ bad('.cloudflare.mode quick gets a random trycloudflare.com hostname; set hostname only for mode named.');
174
+ } };
175
+ const validateCloudflareConnector = (cf) => {
176
+ const bad = invalid('connectors');
177
+ closedConnectorShape(bad, cf, 'cloudflare', ['mode', 'tunnel', 'credentialsFile', 'tokenEnv', 'hostname', 'access']);
178
+ validateCloudflareMode(cf, bad);
179
+ connectorEnvName(bad, 'cloudflare.tokenEnv', cf.tokenEnv);
180
+ validateCloudflareDetails(cf, bad);
181
+ validateCloudflareModeConstraints(cf, bad);
139
182
  };
140
- const validateTelegramConnector=tg=>{
141
- const bad=invalid('connectors');
142
- closedConnectorShape(bad,tg,'telegram',['enabled','botTokenEnv','chatId','exposeCredentialAsks']);
143
- if(tg.enabled!==undefined&&typeof tg.enabled!=='boolean')bad('.telegram.enabled must be true or false.');
144
- connectorEnvName(bad,'telegram.botTokenEnv',tg.botTokenEnv);
145
- if(tg.chatId!==undefined&&tg.chatId!==null&&!((Number.isSafeInteger(tg.chatId))||(typeof tg.chatId==='string'&&CHAT_ID.test(tg.chatId))))
146
- bad('.telegram.chatId must be a numeric chat id, an @channel name, or null.');
147
- if(tg.exposeCredentialAsks!==undefined&&typeof tg.exposeCredentialAsks!=='boolean')bad('.telegram.exposeCredentialAsks must be true or false.');
148
- if(tg.enabled===true&&(tg.chatId===undefined||tg.chatId===null))bad('.telegram.enabled needs telegram.chatId.');
183
+ const validateTelegramConnector = (tg) => {
184
+ const bad = invalid('connectors');
185
+ closedConnectorShape(bad, tg, 'telegram', ['enabled', 'botTokenEnv', 'chatId', 'exposeCredentialAsks']);
186
+ if (tg.enabled !== undefined && typeof tg.enabled !== 'boolean')
187
+ bad('.telegram.enabled must be true or false.');
188
+ connectorEnvName(bad, 'telegram.botTokenEnv', tg.botTokenEnv);
189
+ if (tg.chatId !== undefined && tg.chatId !== null && !((Number.isSafeInteger(tg.chatId)) || (typeof tg.chatId === 'string' && CHAT_ID.test(tg.chatId))))
190
+ bad('.telegram.chatId must be a numeric chat id, an @channel name, or null.');
191
+ if (tg.exposeCredentialAsks !== undefined && typeof tg.exposeCredentialAsks !== 'boolean')
192
+ bad('.telegram.exposeCredentialAsks must be true or false.');
193
+ if (tg.enabled === true && (tg.chatId === undefined || tg.chatId === null))
194
+ bad('.telegram.enabled needs telegram.chatId.');
149
195
  };
150
- function validateConnectors(connectors){
151
- if(connectors===null)return;
152
- const bad=invalid('connectors');
153
- if(!plain(connectors))bad(' must be {repos?, gateway?, cloudflare?, telegram?} or null.');
154
- closedConnectorShape(bad,connectors,'',['repos','gateway','cloudflare','telegram']);
155
- if(connectors.repos!==undefined&&(!Array.isArray(connectors.repos)||connectors.repos.some(repo=>typeof repo!=='string'||!repo.trim())))bad('.repos must be a list of repository paths.');
156
- if(connectors.gateway!==undefined){
157
- closedConnectorShape(bad,connectors.gateway,'gateway',['port']);
158
- const port=connectors.gateway.port;
159
- if(port!==undefined&&(!Number.isInteger(port)||port<1024||port>65535||(port>=ASK_PORT_BAND[0]&&port<=ASK_PORT_BAND[1])))
160
- bad(`.gateway.port must be an integer port in 1024..65535 outside the serve-ask band ${ASK_PORT_BAND.join('..')}.`);
161
- }
162
- if(connectors.cloudflare!==undefined)validateCloudflareConnector(connectors.cloudflare);
163
- if(connectors.telegram!==undefined)validateTelegramConnector(connectors.telegram);
196
+ function validateConnectors(connectors) {
197
+ if (connectors === null)
198
+ return;
199
+ const bad = invalid('connectors');
200
+ if (!plain(connectors))
201
+ bad(' must be {repos?, gateway?, cloudflare?, telegram?} or null.');
202
+ closedConnectorShape(bad, connectors, '', ['repos', 'gateway', 'cloudflare', 'telegram']);
203
+ if (connectors.repos !== undefined && (!Array.isArray(connectors.repos) || connectors.repos.some(repo => typeof repo !== 'string' || !repo.trim())))
204
+ bad('.repos must be a list of repository paths.');
205
+ if (connectors.gateway !== undefined) {
206
+ closedConnectorShape(bad, connectors.gateway, 'gateway', ['port']);
207
+ const port = connectors.gateway.port;
208
+ if (port !== undefined && (typeof port !== 'number' || !Number.isInteger(port) || port < 1024 || port > 65535 || (port >= ASK_PORT_BAND[0] && port <= ASK_PORT_BAND[1])))
209
+ bad(`.gateway.port must be an integer port in 1024..65535 outside the serve-ask band ${ASK_PORT_BAND.join('..')}.`);
210
+ }
211
+ if (connectors.cloudflare !== undefined)
212
+ validateCloudflareConnector(connectors.cloudflare);
213
+ if (connectors.telegram !== undefined)
214
+ validateTelegramConnector(connectors.telegram);
164
215
  }
165
216
  /**
166
217
  * The normalized `connectors` block: defaults filled in, the secrets' PRESENCE (booleans, never values),
167
218
  * whether each connector is ready to run, and the owner-facing warnings the security posture earns.
168
219
  */
169
- const connectorWarnings=(cloudflare,telegram)=>{
170
- const warnings=[];
171
- if(cloudflare.mode==='quick')warnings.push('cloudflare.mode quick: the ask link is public and unauthenticated — anyone holding the URL can open and answer the form; the hostname changes on every restart.');
172
- if(cloudflare.mode==='named'&&!cloudflare.access)warnings.push(`cloudflare.access is false: ${cloudflare.hostname} is not fronted by Cloudflare Access, so the nonce URL is the only credential.`);
173
- if(cloudflare.auth==='credentials-file'&&!cloudflare.credentialsPresent)warnings.push(`cloudflare.credentialsFile ${cloudflare.credentialsFile} does not exist.`);
174
- if(cloudflare.auth==='token'&&!cloudflare.tokenPresent)warnings.push(`cloudflare.mode named: the tunnel token is not set — export ${cloudflare.tokenEnv} (or ${cloudflare.tokenEnv}_FILE).`);
175
- if(telegram.enabled&&!telegram.botTokenPresent)warnings.push(`telegram.enabled: the bot token is not set — export ${telegram.botTokenEnv} (or ${telegram.botTokenEnv}_FILE).`);
176
- if(telegram.exposeCredentialAsks)warnings.push('telegram.exposeCredentialAsks is true: credential asks are posted as public links; Telegram cloud chats are not end-to-end encrypted.');
177
- return warnings;
220
+ const connectorWarnings = (cloudflare, telegram) => {
221
+ const warnings = [];
222
+ if (cloudflare.mode === 'quick')
223
+ warnings.push('cloudflare.mode quick: the ask link is public and unauthenticated — anyone holding the URL can open and answer the form; the hostname changes on every restart.');
224
+ if (cloudflare.mode === 'named' && !cloudflare.access)
225
+ warnings.push(`cloudflare.access is false: ${cloudflare.hostname} is not fronted by Cloudflare Access, so the nonce URL is the only credential.`);
226
+ if (cloudflare.auth === 'credentials-file' && !cloudflare.credentialsPresent)
227
+ warnings.push(`cloudflare.credentialsFile ${cloudflare.credentialsFile} does not exist.`);
228
+ if (cloudflare.auth === 'token' && !cloudflare.tokenPresent)
229
+ warnings.push(`cloudflare.mode named: the tunnel token is not set — export ${cloudflare.tokenEnv} (or ${cloudflare.tokenEnv}_FILE).`);
230
+ if (telegram.enabled && !telegram.botTokenPresent)
231
+ warnings.push(`telegram.enabled: the bot token is not set — export ${telegram.botTokenEnv} (or ${telegram.botTokenEnv}_FILE).`);
232
+ if (telegram.exposeCredentialAsks)
233
+ warnings.push('telegram.exposeCredentialAsks is true: credential asks are posted as public links; Telegram cloud chats are not end-to-end encrypted.');
234
+ return warnings;
178
235
  };
179
- export function connectorsConfig(config=loadConfig(),env=process.env,root=configRoot){
180
- validateConfig(config);
181
- const raw=plain(config?.connectors)?config.connectors:{},d=CONNECTOR_DEFAULTS;
182
- const cloudflare={...d.cloudflare,...raw.cloudflare},telegram={...d.telegram,...raw.telegram};
183
- if(cloudflare.credentialsFile)cloudflare.credentialsFile=path.resolve(root,cloudflare.credentialsFile.replace(/^~(?=[\\/])/,env.USERPROFILE??env.HOME??'~'));
184
- cloudflare.tokenPresent=connectorSecret(cloudflare.tokenEnv,env)!==null;
185
- cloudflare.credentialsPresent=Boolean(cloudflare.credentialsFile&&fs.existsSync(cloudflare.credentialsFile));
186
- const namedAuth=cloudflare.credentialsFile?'credentials-file':'token';
187
- cloudflare.auth=cloudflare.mode==='named'?namedAuth:null;
188
- cloudflare.publicBase=cloudflare.mode==='named'?`https://${cloudflare.hostname}`:null;
189
- telegram.chatId=telegram.chatId===null?null:String(telegram.chatId);
190
- telegram.botTokenPresent=connectorSecret(telegram.botTokenEnv,env)!==null;
191
- telegram.configured=telegram.enabled===true&&telegram.chatId!==null&&telegram.botTokenPresent;
192
- return {repos:[...(raw.repos??d.repos)],gateway:{...d.gateway,...raw.gateway},cloudflare,telegram,warnings:connectorWarnings(cloudflare,telegram)};
236
+ export function connectorsConfig(config = loadConfig(), env = process.env, root = configRoot) {
237
+ validateConfig(config);
238
+ const raw = plain(config?.connectors) ? config.connectors : {}, d = CONNECTOR_DEFAULTS;
239
+ const cloudflare = { ...d.cloudflare, ...raw.cloudflare, tokenPresent: false, credentialsPresent: false, auth: null, publicBase: null };
240
+ const telegram = { ...d.telegram, ...raw.telegram, chatId: null, botTokenPresent: false, configured: false };
241
+ const chatId = raw.telegram?.chatId ?? d.telegram.chatId;
242
+ if (cloudflare.credentialsFile)
243
+ cloudflare.credentialsFile = path.resolve(root, cloudflare.credentialsFile.replace(/^~(?=[\\/])/, env.USERPROFILE ?? env.HOME ?? '~'));
244
+ cloudflare.tokenPresent = connectorSecret(cloudflare.tokenEnv, env) !== null;
245
+ cloudflare.credentialsPresent = Boolean(cloudflare.credentialsFile && fs.existsSync(cloudflare.credentialsFile));
246
+ const namedAuth = cloudflare.credentialsFile ? 'credentials-file' : 'token';
247
+ cloudflare.auth = cloudflare.mode === 'named' ? namedAuth : null;
248
+ cloudflare.publicBase = cloudflare.mode === 'named' ? `https://${cloudflare.hostname}` : null;
249
+ telegram.chatId = chatId === null ? null : String(chatId);
250
+ telegram.botTokenPresent = connectorSecret(telegram.botTokenEnv, env) !== null;
251
+ telegram.configured = telegram.enabled === true && telegram.chatId !== null && telegram.botTokenPresent;
252
+ return { repos: [...(raw.repos ?? d.repos)], gateway: { ...d.gateway, ...raw.gateway }, cloudflare, telegram, warnings: connectorWarnings(cloudflare, telegram) };
193
253
  }
194
254
  /**
195
255
  * config.yaml `asks` — whether an owner ask that carries a recommended option is answered with it
@@ -199,200 +259,291 @@ export function connectorsConfig(config=loadConfig(),env=process.env,root=config
199
259
  * `draw-review` (the opt-out: an interface.draw drawing review the owner did not ask for is otherwise
200
260
  * accepted without the owner) and any ask kind of modules/ops/ops/provision.ask.yaml.
201
261
  */
202
- export const ASK_KINDS=Object.freeze(['information','credential','account','access','consent','authority','business-decision','irreversible-confirmation']);
203
- const ASK_EXCLUDE_CLASSES=Object.freeze([...ASK_KINDS,'handover','draw-review']);
204
- export const ASKS_DEFAULTS=Object.freeze({autoAcceptRecommended:false,excludes:Object.freeze(['credential','irreversible-confirmation','handover'])});
205
- const validateAskExcludes=(bad,excludes)=>{
206
- if(!Array.isArray(excludes))bad(`.excludes must be a list of ask classes (${ASK_EXCLUDE_CLASSES.join(', ')}).`);
207
- for(const entry of excludes)if(typeof entry!=='string'||!ASK_EXCLUDE_CLASSES.includes(entry))bad(`.excludes entry ${JSON.stringify(entry)} is not an ask class (${ASK_EXCLUDE_CLASSES.join(', ')}).`);
208
- if(new Set(excludes).size!==excludes.length)bad('.excludes names each class once.');
262
+ export const ASK_KINDS = Object.freeze(['information', 'credential', 'account', 'access', 'consent', 'authority', 'business-decision', 'irreversible-confirmation']);
263
+ const ASK_EXCLUDE_CLASSES = Object.freeze([...ASK_KINDS, 'handover', 'draw-review']);
264
+ export const ASKS_DEFAULTS = Object.freeze({ autoAcceptRecommended: false, excludes: Object.freeze(['credential', 'irreversible-confirmation', 'handover']) });
265
+ const validateAskExcludes = (bad, excludes) => {
266
+ if (!Array.isArray(excludes))
267
+ bad(`.excludes must be a list of ask classes (${ASK_EXCLUDE_CLASSES.join(', ')}).`);
268
+ for (const entry of excludes)
269
+ if (typeof entry !== 'string' || !ASK_EXCLUDE_CLASSES.includes(entry))
270
+ bad(`.excludes entry ${JSON.stringify(entry)} is not an ask class (${ASK_EXCLUDE_CLASSES.join(', ')}).`);
271
+ if (new Set(excludes).size !== excludes.length)
272
+ bad('.excludes names each class once.');
209
273
  };
210
- function validateAsks(asks){
211
- if(asks===null)return;
212
- const bad=invalid('asks');
213
- if(!plain(asks))bad(' must be {autoAcceptRecommended?, excludes?} or null.');
214
- for(const key of Object.keys(asks))if(!CONFIG_KEY_TREE.asks.includes(key))bad(` has unknown key ${key} (allowed: ${CONFIG_KEY_TREE.asks.join(', ')}).`);
215
- if(asks.autoAcceptRecommended!==undefined&&typeof asks.autoAcceptRecommended!=='boolean')bad('.autoAcceptRecommended must be true or false.');
216
- const excludes=asks.excludes;
217
- if(excludes!==undefined&&excludes!==null)validateAskExcludes(bad,excludes);
274
+ function validateAsks(asks) {
275
+ if (asks === null)
276
+ return;
277
+ const bad = invalid('asks');
278
+ if (!plain(asks))
279
+ bad(' must be {autoAcceptRecommended?, excludes?} or null.');
280
+ for (const key of Object.keys(asks))
281
+ if (!CONFIG_KEY_TREE.asks.includes(key))
282
+ bad(` has unknown key ${key} (allowed: ${CONFIG_KEY_TREE.asks.join(', ')}).`);
283
+ if (asks.autoAcceptRecommended !== undefined && typeof asks.autoAcceptRecommended !== 'boolean')
284
+ bad('.autoAcceptRecommended must be true or false.');
285
+ const excludes = asks.excludes;
286
+ if (excludes !== undefined && excludes !== null)
287
+ validateAskExcludes(bad, excludes);
218
288
  }
219
289
  /**
220
290
  * config.yaml `uat` — the machine-wide UAT ceiling: at most `maxConcurrent` UAT runs (browsers, players,
221
291
  * the apps they drive) execute at once on this host; the rest queue for a slot (scripts/uat/uat-slots.mjs).
222
292
  */
223
- export const UAT_DEFAULTS=Object.freeze({maxConcurrent:10});
224
- function validateUat(uat){
225
- if(uat===null)return;
226
- const bad=invalid('uat');
227
- if(!plain(uat))bad(' must be {maxConcurrent?} or null.');
228
- for(const key of Object.keys(uat))if(!CONFIG_KEY_TREE.uat.includes(key))bad(` has unknown key ${key} (allowed: ${CONFIG_KEY_TREE.uat.join(', ')}).`);
229
- if(uat.maxConcurrent!==undefined&&uat.maxConcurrent!==null&&!(Number.isInteger(uat.maxConcurrent)&&uat.maxConcurrent>=1))bad('.maxConcurrent must be a positive integer (default 10) or null.');
293
+ export const UAT_DEFAULTS = Object.freeze({ maxConcurrent: 10 });
294
+ function validateUat(uat) {
295
+ if (uat === null)
296
+ return;
297
+ const bad = invalid('uat');
298
+ if (!plain(uat))
299
+ bad(' must be {maxConcurrent?} or null.');
300
+ for (const key of Object.keys(uat))
301
+ if (!CONFIG_KEY_TREE.uat.includes(key))
302
+ bad(` has unknown key ${key} (allowed: ${CONFIG_KEY_TREE.uat.join(', ')}).`);
303
+ if (uat.maxConcurrent !== undefined && uat.maxConcurrent !== null && !(typeof uat.maxConcurrent === 'number' && Number.isInteger(uat.maxConcurrent) && uat.maxConcurrent >= 1))
304
+ bad('.maxConcurrent must be a positive integer (default 10) or null.');
230
305
  }
231
306
  /** The owner's UAT concurrency settings: {maxConcurrent, source}. An absent or null block is the default. */
232
- export function uatSettings(config=loadConfig()){
233
- if(config?.uat!==undefined)validateUat(config.uat);
234
- const value=plain(config?.uat)?config.uat.maxConcurrent:null;
235
- return Number.isInteger(value)?{maxConcurrent:value,source:'uat'}:{maxConcurrent:UAT_DEFAULTS.maxConcurrent,source:'default'};
307
+ export function uatSettings(config = loadConfig()) {
308
+ if (config?.uat !== undefined)
309
+ validateUat(config.uat);
310
+ const value = plain(config?.uat) ? config.uat.maxConcurrent : null;
311
+ return typeof value === 'number' && Number.isInteger(value) ? { maxConcurrent: value, source: 'uat' } : { maxConcurrent: UAT_DEFAULTS.maxConcurrent, source: 'default' };
236
312
  }
237
313
  /**
238
314
  * config.yaml `allocation` — {grants?}: ['<pool>=<slots>@<role>+<role>'], the owner's default grant, applied to every
239
315
  * workflow, that opens a capacityAuthority explicit-workflow-quota pool (Devin) for those roles up to <slots> running jobs.
240
316
  */
241
- const GRANT=/^([a-z0-9][a-z0-9.-]*)=(\d+)@([a-z]+(?:\+[a-z]+)*)$/;
317
+ const GRANT = /^([a-z0-9][a-z0-9.-]*)=(\d+)@([a-z]+(?:\+[a-z]+)*)$/;
242
318
  /** One grant string `<pool>=<slots>@<role>+<role>` as {pool, slots, roles}, or null when it is not that shape. */
243
319
  /** The closed set of top-level config.yaml blocks: the validator accepts these and refuses every other key. */
244
- const CONFIG_BLOCKS=Object.freeze(['language','model','effort','models','allocation','kernel','budgets','supervisor','parallel','delegation','connectors','asks','uat','specs','reconciler','debugLoop','orca','roots','resources','sonar','release','launchTrust','retention']);
245
- function parseAllocationGrant(text){
246
- const m=typeof text==='string'?GRANT.exec(text.trim()):null;
247
- return m?{pool:m[1],slots:Number(m[2]),roles:m[3].split('+')}:null;
320
+ const CONFIG_BLOCKS = Object.freeze(['language', 'model', 'effort', 'models', 'allocation', 'kernel', 'budgets', 'supervisor', 'parallel', 'delegation', 'connectors', 'asks', 'uat', 'specs', 'reconciler', 'debugLoop', 'orca', 'roots', 'resources', 'sonar', 'release', 'launchTrust', 'retention']);
321
+ function parseAllocationGrant(text) {
322
+ const m = typeof text === 'string' ? GRANT.exec(text.trim()) : null;
323
+ return m ? { pool: m[1], slots: Number(m[2]), roles: m[3].split('+') } : null;
248
324
  }
249
- const validateGrant=(bad,text,runtimes,seen)=>{
250
- const grant=parseAllocationGrant(text);
251
- if(!grant)bad(`grants entry ${JSON.stringify(text)} is not "<pool>=<slots>@<role>+<role>" (e.g. devin-agent=10@implement+verify+write).`);
252
- const runtime=runtimes[grant.pool];
253
- if(!plain(runtime))bad(`grants entry ${text}: ${grant.pool} is not a modules/models/registry.yaml pool.`);
254
- if(seen.has(grant.pool)){bad(`grants names ${grant.pool} more than once.`);} seen.add(grant.pool);
255
- const max=Number(runtime.maxParallel); if(!(grant.slots>=1&&(!Number.isFinite(max)||grant.slots<=max)))bad(`grants entry ${text}: slots must be 1..${Number.isFinite(max)?max:'maxParallel'} (registry.yaml maxParallel).`);
256
- const unserved=grant.roles.filter(role=>!(runtime.roles??[]).includes(role)); if(unserved.length)bad(`grants entry ${text}: ${grant.pool} does not serve role ${unserved.join(', ')} (registry.yaml roles: ${(runtime.roles??[]).join(', ')}).`);
325
+ const validateGrant = (bad, text, runtimes, seen) => {
326
+ const grant = parseAllocationGrant(text);
327
+ if (!grant)
328
+ bad(`grants entry ${JSON.stringify(text)} is not "<pool>=<slots>@<role>+<role>" (e.g. devin-agent=10@implement+verify+write).`);
329
+ const runtime = runtimes[grant.pool];
330
+ if (!plain(runtime))
331
+ bad(`grants entry ${text}: ${grant.pool} is not a modules/models/registry.yaml pool.`);
332
+ if (seen.has(grant.pool)) {
333
+ bad(`grants names ${grant.pool} more than once.`);
334
+ }
335
+ seen.add(grant.pool);
336
+ const max = Number(runtime.maxParallel);
337
+ if (!(grant.slots >= 1 && (!Number.isFinite(max) || grant.slots <= max)))
338
+ bad(`grants entry ${text}: slots must be 1..${Number.isFinite(max) ? max : 'maxParallel'} (registry.yaml maxParallel).`);
339
+ const unserved = grant.roles.filter(role => !(runtime.roles ?? []).includes(role));
340
+ if (unserved.length)
341
+ bad(`grants entry ${text}: ${grant.pool} does not serve role ${unserved.join(', ')} (registry.yaml roles: ${(runtime.roles ?? []).join(', ')}).`);
257
342
  };
258
- const validateGrants=(bad,grants,runtimes)=>{
259
- if(!Array.isArray(grants))bad('grants must be a list of "<pool>=<slots>@<role>+<role>" strings.');
260
- const seen=new Set();
261
- for(const text of grants)validateGrant(bad,text,runtimes,seen);
343
+ const validateGrants = (bad, grants, runtimes) => {
344
+ if (!Array.isArray(grants))
345
+ bad('grants must be a list of "<pool>=<slots>@<role>+<role>" strings.');
346
+ const seen = new Set();
347
+ for (const text of grants)
348
+ validateGrant(bad, text, runtimes, seen);
262
349
  };
263
- function validateAgentSeat(seat,name,profile){
264
- const runtimes=profile?.runtimes??{},knownProviders=new Set(Object.values(runtimes).map(runtime=>runtime?.provider).filter(Boolean));
265
- if(!plain(seat)||Object.keys(seat).some(key=>!SEAT_KEYS.includes(key))||Object.values(seat).some(value=>value!==null&&(typeof value!=='string'||!value.trim())))
266
- throw new Error(`Invalid config.yaml: ${name} must be {agent?, model?, effort?} with string-or-null values (agent and model pin the seat: an \`only\` bias).`);
267
- if(typeof seat.agent==='string'&&!knownProviders.has(seat.agent))throw new Error(`Invalid config.yaml: ${name}.agent ${seat.agent} is not declared by a runtime (known: ${knownNames(knownProviders)}).`);
268
- if(seat.effort!==undefined&&seat.effort!==null&&!EFFORT_LEVELS.has(seat.effort))throw new Error(`Invalid config.yaml: ${name}.effort must use the effort vocabulary.`);
350
+ function validateAgentSeat(seat, name, profile) {
351
+ const runtimes = profile?.runtimes ?? {}, knownProviders = new Set(Object.values(runtimes).map(runtime => runtime?.provider).filter((provider) => Boolean(provider)));
352
+ if (!plain(seat) || Object.keys(seat).some(key => !SEAT_KEYS.includes(key)) || Object.values(seat).some(value => value !== null && (typeof value !== 'string' || !value.trim())))
353
+ throw new Error(`Invalid config.yaml: ${name} must be {agent?, model?, effort?} with string-or-null values (agent and model pin the seat: an \`only\` bias).`);
354
+ if (typeof seat.agent === 'string' && !knownProviders.has(seat.agent))
355
+ throw new Error(`Invalid config.yaml: ${name}.agent ${seat.agent} is not declared by a runtime (known: ${knownNames(knownProviders)}).`);
356
+ if (seat.effort !== undefined && seat.effort !== null && !(typeof seat.effort === 'string' && EFFORT_LEVELS.has(seat.effort)))
357
+ throw new Error(`Invalid config.yaml: ${name}.effort must use the effort vocabulary.`);
269
358
  }
270
- const validateReconcilerBlock=r=>{
271
- const ctl=r?.controllers;
272
- if(!plain(r)||Object.keys(r).some(key=>!CONFIG_KEY_TREE.reconciler.includes(key))||(r.enabled!==undefined&&typeof r.enabled!=='boolean')||!(r.profile===undefined||r.profile===null||['operational','observe'].includes(r.profile))||!(ctl===undefined||ctl===null||(plain(ctl)&&Object.entries(ctl).every(([name,c])=>/^[a-z][a-z0-9-]*$/.test(name)&&plain(c)&&Object.keys(c).every(key=>key==='mode')&&['off','shadow','active'].includes(c.mode)))))
273
- throw new Error('Invalid config.yaml: reconciler must be {enabled?: boolean, profile?: operational|observe, controllers?: {<name>: {mode: off|shadow|active}}}, or null.');
274
- };
275
- const validateAllocationBlock=(allocation,runtimes)=>{
276
- if(!plain(allocation)||Object.keys(allocation).some(key=>!CONFIG_KEY_TREE.allocation.includes(key)))
277
- throw new Error('Invalid config.yaml: allocation must be {grants?: [<pool>=<slots>@<role>+<role>]}.');
278
- if(allocation.grants!==undefined&&allocation.grants!==null)validateGrants(invalid('allocation.'),allocation.grants,runtimes);
359
+ const validateReconcilerBlock = (r) => {
360
+ const ctl = plain(r) ? r.controllers : undefined;
361
+ if (!plain(r) || Object.keys(r).some(key => !CONFIG_KEY_TREE.reconciler.includes(key)) || (r.enabled !== undefined && typeof r.enabled !== 'boolean') || !(r.profile === undefined || r.profile === null || (typeof r.profile === 'string' && ['operational', 'observe'].includes(r.profile))) || !(ctl === undefined || ctl === null || (plain(ctl) && Object.entries(ctl).every(([name, c]) => /^[a-z][a-z0-9-]*$/.test(name) && plain(c) && Object.keys(c).every(key => key === 'mode') && typeof c.mode === 'string' && ['off', 'shadow', 'active'].includes(c.mode)))))
362
+ throw new Error('Invalid config.yaml: reconciler must be {enabled?: boolean, profile?: operational|observe, controllers?: {<name>: {mode: off|shadow|active}}}, or null.');
279
363
  };
280
- const validateParallelBlock=parallel=>{
281
- const gears=slicingGears();
282
- if(!plain(parallel)||Object.keys(parallel).some(key=>!CONFIG_KEY_TREE.parallel.includes(key))||!Number.isInteger(parallel.gear))
283
- throw new Error('Invalid config.yaml: parallel must be {gear: <integer>}.');
284
- if(!gears.includes(parallel.gear))
285
- throw new Error(`Invalid config.yaml: parallel.gear ${parallel.gear} is not declared by modules/models/runtimes.yaml allocation.slicing.gears (known: ${gears.join(', ')}).`);
364
+ const validateAllocationBlock = (allocation, runtimes) => {
365
+ if (!plain(allocation) || Object.keys(allocation).some(key => !CONFIG_KEY_TREE.allocation.includes(key)))
366
+ throw new Error('Invalid config.yaml: allocation must be {grants?: [<pool>=<slots>@<role>+<role>]}.');
367
+ if (allocation.grants !== undefined && allocation.grants !== null)
368
+ validateGrants(invalid('allocation.'), allocation.grants, runtimes);
286
369
  };
287
- const validateSupervisorBlock=(supervisor,profile)=>{
288
- const interval=supervisor?.pollIntervalMs,repos=supervisor?.repos,stall=supervisor?.stallMinutes;
289
- if(!plain(supervisor)||Object.keys(supervisor).some(key=>!CONFIG_KEY_TREE.supervisor.includes(key))||!(interval===null||interval===undefined||(Number.isInteger(interval)&&interval>=60000)))
290
- throw new Error('Invalid config.yaml: supervisor must be {mode?, pollIntervalMs?, repos?, stallMinutes?, kernel?, workers?, landGate?, frozenMinutes?} with an integer of at least 60000 ms, or null.');
291
- // mode: where the Supervisor role runs - chat (default: the owner's desktop
292
- // chat session owns channel 'main' and ticks itself) or kernel (the optional [Supervisor] Orca kernel, start-supervisor.mjs).
293
- if(!(supervisor.mode===undefined||supervisor.mode===null||['chat','kernel'].includes(supervisor.mode)))
294
- throw new Error('Invalid config.yaml: supervisor.mode must be chat or kernel, or null.');
295
- // The [Supervisor] kernel seat: kernel {agent?, model?, effort?}
296
- // pins its agent (default: the kernel pin), workers {base?, max?} its adaptive [Worker] cap (0 to 10; the shipped default is 0 fix workers),
297
- // landGate {mode?: shared|exclusive, push?: boolean} the land gate (scripts/supervisor/land.mjs); push (default true, owner ruling push-main-after-scoped-check) lets `starci release push` send main to the remote after a green scoped check, false is the owner's lock on the remote.
298
- const seat=supervisor.kernel,workers=supervisor.workers,gate=supervisor.landGate;
299
- if(seat!==undefined&&seat!==null)validateAgentSeat(seat,'supervisor.kernel',profile);
300
- if(!(workers===undefined||workers===null||(plain(workers)&&Object.keys(workers).every(key=>['base','max'].includes(key)&&Number.isInteger(workers[key])&&workers[key]>=0&&workers[key]<=10))))
301
- throw new Error('Invalid config.yaml: supervisor.workers must be {base?, max?} integers from 0 to 10, or null.');
302
- if(!(gate===undefined||gate===null||(plain(gate)&&Object.keys(gate).every(key=>(key==='mode'&&['shared','exclusive'].includes(gate.mode))||(key==='push'&&typeof gate.push==='boolean')))))
303
- throw new Error('Invalid config.yaml: supervisor.landGate must be {mode?: shared|exclusive, push?: boolean}, or null.');
304
- // stallMinutes: scripts/supervisor/stall.mjs calls a running workflow STALLED after this many minutes with no progress.
305
- // frozenMinutes: scripts/supervisor/supervisor-watchdog.mjs reads a busy seat frame with no turn progress for this long as frozen.
306
- const frozen=supervisor.frozenMinutes;
307
- if(!(frozen===undefined||frozen===null||(Number.isInteger(frozen)&&frozen>=1)))
308
- throw new Error('Invalid config.yaml: supervisor.frozenMinutes must be an integer of at least 1, or null.');
309
- if(!(stall===undefined||stall===null||(Number.isInteger(stall)&&stall>=5)))
310
- throw new Error('Invalid config.yaml: supervisor.stallMinutes must be an integer of at least 5, or null.');
311
- if(!(repos===undefined||repos===null||(Array.isArray(repos)&&repos.every(repo=>typeof repo==='string'&&repo.trim()))))
312
- throw new Error('Invalid config.yaml: supervisor.repos must be a list of ledger-owner repository paths, or null.');
370
+ const validateParallelBlock = (parallel) => {
371
+ const gears = slicingGears();
372
+ if (!plain(parallel) || Object.keys(parallel).some(key => !CONFIG_KEY_TREE.parallel.includes(key)) || typeof parallel.gear !== 'number' || !Number.isInteger(parallel.gear))
373
+ throw new Error('Invalid config.yaml: parallel must be {gear: <integer>}.');
374
+ if (!gears.includes(parallel.gear))
375
+ throw new Error(`Invalid config.yaml: parallel.gear ${parallel.gear} is not declared by modules/models/runtimes.yaml allocation.slicing.gears (known: ${gears.join(', ')}).`);
313
376
  };
314
- const validateDelegationBlock=d=>{if(!plain(d)||Object.keys(d).some(key=>!CONFIG_KEY_TREE.delegation.includes(key))||typeof d.asks!=='string'||!d.asks.trim()||typeof d.until!=='string'||Number.isNaN(Date.parse(d.until))||(d.excludes!==undefined&&(!Array.isArray(d.excludes)||d.excludes.some(x=>typeof x!=='string'))))throw new Error('Invalid config.yaml: delegation must be {asks: <delegate>, until: <ISO time>, excludes?: [<class>], note?} or null.');};
315
- const validateBudgetsBlock=budgets=>{if(!plain(budgets)||Object.keys(budgets).some(key=>!CONFIG_KEY_TREE.budgets.includes(key))||Object.values(budgets).some(value=>value!==null&&!(Number.isInteger(value)&&value>0)))throw new Error('Invalid config.yaml: budgets must be {maxOps?} with positive-integer-or-null values.');};
316
- const validateRootKeys=(config)=>{
317
- const allowed=new Set(CONFIG_BLOCKS);
318
- if(!plain(config)||Object.keys(config).some(key=>!allowed.has(key))||typeof config.language!=='string'||!/^[a-z]{2,3}(?:-[A-Za-z0-9]+)*$/.test(config.language)||!(config.model===null||typeof config.model==='string'&&config.model.trim())||!EFFORT_LEVELS.has(config.effort))
319
- throw new Error('Invalid config.yaml: expected language, model, effort and the closed set of config blocks.');
377
+ const validateSupervisorBlock = (supervisor, profile) => {
378
+ const fields = plain(supervisor) ? supervisor : {};
379
+ const interval = fields.pollIntervalMs, repos = fields.repos, stall = fields.stallMinutes;
380
+ if (!plain(supervisor) || Object.keys(supervisor).some(key => !CONFIG_KEY_TREE.supervisor.includes(key)) || !(interval === null || interval === undefined || (typeof interval === 'number' && Number.isInteger(interval) && interval >= 60000)))
381
+ throw new Error('Invalid config.yaml: supervisor must be {mode?, pollIntervalMs?, repos?, stallMinutes?, kernel?, workers?, landGate?, frozenMinutes?} with an integer of at least 60000 ms, or null.');
382
+ // mode: where the Supervisor role runs - chat (default: the owner's desktop
383
+ // chat session owns channel 'main' and ticks itself) or kernel (the optional [Supervisor] Orca kernel, start-supervisor.mjs).
384
+ if (!(supervisor.mode === undefined || supervisor.mode === null || typeof supervisor.mode === 'string' && ['chat', 'kernel'].includes(supervisor.mode)))
385
+ throw new Error('Invalid config.yaml: supervisor.mode must be chat or kernel, or null.');
386
+ // The [Supervisor] kernel seat: kernel {agent?, model?, effort?}
387
+ // pins its agent (default: the kernel pin), workers {base?, max?} its adaptive [Worker] cap (0 to 10; the shipped default is 0 fix workers),
388
+ // landGate {mode?: shared|exclusive, push?: boolean} the land gate (scripts/supervisor/land.mjs); push (default true, owner ruling push-main-after-scoped-check) lets `starci release push` send main to the remote after a green scoped check, false is the owner's lock on the remote.
389
+ const seat = supervisor.kernel, workers = supervisor.workers, gate = supervisor.landGate;
390
+ if (seat !== undefined && seat !== null)
391
+ validateAgentSeat(seat, 'supervisor.kernel', profile);
392
+ if (!(workers === undefined || workers === null || (plain(workers) && Object.keys(workers).every(key => { const n = workers[key]; return ['base', 'max'].includes(key) && typeof n === 'number' && Number.isInteger(n) && n >= 0 && n <= 10; }))))
393
+ throw new Error('Invalid config.yaml: supervisor.workers must be {base?, max?} integers from 0 to 10, or null.');
394
+ if (!(gate === undefined || gate === null || (plain(gate) && Object.keys(gate).every(key => (key === 'mode' && typeof gate.mode === 'string' && ['shared', 'exclusive'].includes(gate.mode)) || (key === 'push' && typeof gate.push === 'boolean')))))
395
+ throw new Error('Invalid config.yaml: supervisor.landGate must be {mode?: shared|exclusive, push?: boolean}, or null.');
396
+ // stallMinutes: scripts/supervisor/stall.mjs calls a running workflow STALLED after this many minutes with no progress.
397
+ // frozenMinutes: scripts/supervisor/supervisor-watchdog.mjs reads a busy seat frame with no turn progress for this long as frozen.
398
+ const frozen = supervisor.frozenMinutes;
399
+ if (!(frozen === undefined || frozen === null || (typeof frozen === 'number' && Number.isInteger(frozen) && frozen >= 1)))
400
+ throw new Error('Invalid config.yaml: supervisor.frozenMinutes must be an integer of at least 1, or null.');
401
+ if (!(stall === undefined || stall === null || (typeof stall === 'number' && Number.isInteger(stall) && stall >= 5)))
402
+ throw new Error('Invalid config.yaml: supervisor.stallMinutes must be an integer of at least 5, or null.');
403
+ if (!(repos === undefined || repos === null || (Array.isArray(repos) && repos.every(repo => typeof repo === 'string' && repo.trim()))))
404
+ throw new Error('Invalid config.yaml: supervisor.repos must be a list of ledger-owner repository paths, or null.');
320
405
  };
321
- const validateEarlyConfigBlocks=(config)=>{
322
- if(config?.launchTrust!==undefined){launchTrustSettings(config);} if(config?.retention!==undefined){workflowPurgeSettings(config);}
323
- if(config?.connectors!==undefined){validateConnectors(config.connectors);} if(config?.asks!==undefined){validateAsks(config.asks);}
324
- if(config?.uat!==undefined){validateUat(config.uat);} if(config?.debugLoop!==undefined){validateDebugLoop(config.debugLoop);}
325
- if(config?.orca!==undefined){validateOrca(config.orca);} if(config?.roots!==undefined){validateRoots(config.roots);} if(config?.resources!==undefined){validateResources(config.resources);} if(config?.sonar!==undefined){validateSonar(config.sonar);} if(config?.release!==undefined){validateRelease(config.release);}
406
+ const validateDelegationBlock = (d) => { if (!plain(d) || Object.keys(d).some(key => !CONFIG_KEY_TREE.delegation.includes(key)) || typeof d.asks !== 'string' || !d.asks.trim() || typeof d.until !== 'string' || Number.isNaN(Date.parse(d.until)) || (d.excludes !== undefined && (!Array.isArray(d.excludes) || d.excludes.some(x => typeof x !== 'string'))))
407
+ throw new Error('Invalid config.yaml: delegation must be {asks: <delegate>, until: <ISO time>, excludes?: [<class>], note?} or null.'); };
408
+ const validateBudgetsBlock = (budgets) => { if (!plain(budgets) || Object.keys(budgets).some(key => !CONFIG_KEY_TREE.budgets.includes(key)) || Object.values(budgets).some(value => value !== null && !(typeof value === 'number' && Number.isInteger(value) && value > 0)))
409
+ throw new Error('Invalid config.yaml: budgets must be {maxOps?} with positive-integer-or-null values.'); };
410
+ function validateRootKeys(config) {
411
+ const allowed = new Set(CONFIG_BLOCKS);
412
+ if (!plain(config) || Object.keys(config).some(key => !allowed.has(key)) || typeof config.language !== 'string' || !/^[a-z]{2,3}(?:-[A-Za-z0-9]+)*$/.test(config.language) || !(config.model === null || typeof config.model === 'string' && config.model.trim()) || !(typeof config.effort === 'string' && EFFORT_LEVELS.has(config.effort)))
413
+ throw new Error('Invalid config.yaml: expected language, model, effort and the closed set of config blocks.');
414
+ }
415
+ const validateEarlyConfigBlocks = (config) => {
416
+ const blocks = plain(config) ? config : {};
417
+ if (blocks.launchTrust !== undefined) {
418
+ launchTrustSettings(blocks);
419
+ }
420
+ if (blocks.retention !== undefined) {
421
+ workflowPurgeSettings(blocks);
422
+ }
423
+ if (blocks.connectors !== undefined) {
424
+ validateConnectors(blocks.connectors);
425
+ }
426
+ if (blocks.asks !== undefined) {
427
+ validateAsks(blocks.asks);
428
+ }
429
+ if (blocks.uat !== undefined) {
430
+ validateUat(blocks.uat);
431
+ }
432
+ if (blocks.debugLoop !== undefined) {
433
+ validateDebugLoop(blocks.debugLoop);
434
+ }
435
+ if (blocks.orca !== undefined) {
436
+ validateOrca(blocks.orca);
437
+ }
438
+ if (blocks.roots !== undefined) {
439
+ validateRoots(blocks.roots);
440
+ }
441
+ if (blocks.resources !== undefined) {
442
+ validateResources(blocks.resources);
443
+ }
444
+ if (blocks.sonar !== undefined) {
445
+ validateSonar(blocks.sonar);
446
+ }
447
+ if (blocks.release !== undefined) {
448
+ validateRelease(blocks.release);
449
+ }
326
450
  };
327
- const validateConfigBlocks=(config,knownProviders,runtimes,profile)=>{
328
- // specs (owner 2026-09-28): {unit?, e2e?} booleans - each family a boolean; absent = its default (SPEC_DEFAULTS: unit on, e2e off; specsSettings).
329
- if(config?.specs!==undefined&&config.specs!==null&&(!plain(config.specs)||Object.keys(config.specs).some(key=>!CONFIG_KEY_TREE.specs.includes(key)||typeof config.specs[key]!=='boolean')))throw new Error(`Invalid config.yaml: specs must be {${SPEC_FAMILIES.map(k=>k+'?: boolean').join(', ')}}, or null.`);
330
- // reconciler (scripts/reconciler/state.mjs reconcilerConfig): {enabled?: boolean, profile?: operational|observe, controllers?: {<name>: {mode: off|shadow|active}}}, or null.
331
- if(config?.reconciler!==undefined&&config.reconciler!==null){validateReconcilerBlock(config.reconciler);} if(config?.allocation!==undefined){validateAllocationBlock(config.allocation,runtimes);}
332
- if(config?.kernel!==undefined){validateAgentSeat(config.kernel,'kernel',profile);} if(config?.parallel!==undefined){validateParallelBlock(config.parallel);}
333
- if(config?.supervisor!==undefined){validateSupervisorBlock(config.supervisor,profile);} if(config?.delegation!==undefined&&config.delegation!==null){validateDelegationBlock(config.delegation);} if(config?.budgets!==undefined){validateBudgetsBlock(config.budgets);}
451
+ const validateConfigBlocks = (config, _knownProviders, runtimes, profile) => {
452
+ const blocks = plain(config) ? config : {};
453
+ // specs (owner 2026-09-28): {unit?, e2e?} booleans - each family a boolean; absent = its default (SPEC_DEFAULTS: unit on, e2e off; specsSettings).
454
+ const specs = blocks.specs;
455
+ if (specs !== undefined && specs !== null && (!plain(specs) || Object.keys(specs).some(key => !CONFIG_KEY_TREE.specs.includes(key) || typeof specs[key] !== 'boolean')))
456
+ throw new Error(`Invalid config.yaml: specs must be {${SPEC_FAMILIES.map(k => k + '?: boolean').join(', ')}}, or null.`);
457
+ // reconciler (scripts/reconciler/state.mjs reconcilerConfig): {enabled?: boolean, profile?: operational|observe, controllers?: {<name>: {mode: off|shadow|active}}}, or null.
458
+ if (blocks.reconciler !== undefined && blocks.reconciler !== null) {
459
+ validateReconcilerBlock(blocks.reconciler);
460
+ }
461
+ if (blocks.allocation !== undefined) {
462
+ validateAllocationBlock(blocks.allocation, runtimes);
463
+ }
464
+ if (blocks.kernel !== undefined) {
465
+ validateAgentSeat(blocks.kernel, 'kernel', profile);
466
+ }
467
+ if (blocks.parallel !== undefined) {
468
+ validateParallelBlock(blocks.parallel);
469
+ }
470
+ if (blocks.supervisor !== undefined) {
471
+ validateSupervisorBlock(blocks.supervisor, profile);
472
+ }
473
+ if (blocks.delegation !== undefined && blocks.delegation !== null) {
474
+ validateDelegationBlock(blocks.delegation);
475
+ }
476
+ if (blocks.budgets !== undefined) {
477
+ validateBudgetsBlock(blocks.budgets);
478
+ }
334
479
  };
335
- export function validateConfig(config){
336
- const profile=runtimeProfile(),runtimes=profile?.runtimes??{};
337
- refuseRemovedKeys(config);
338
- validateEarlyConfigBlocks(config);
339
- const knownProviders=new Set(Object.values(runtimes).map(runtime=>runtime?.provider).filter(Boolean));
340
- validateConfigBlocks(config,knownProviders,runtimes,profile);
341
- validateRootKeys(config);
342
- validateModelsBlock(config,profile);
343
- return config;
480
+ /** Validates a parsed config.yaml against the closed schema and returns it as the owner config; throws `Invalid config.yaml: ...` otherwise. */
481
+ export function validateConfig(config) {
482
+ const profile = runtimeProfile(), runtimes = profile?.runtimes ?? {};
483
+ refuseRemovedKeys(config);
484
+ validateEarlyConfigBlocks(config);
485
+ const knownProviders = new Set(Object.values(runtimes).map(runtime => runtime?.provider).filter((provider) => Boolean(provider)));
486
+ validateConfigBlocks(config, knownProviders, runtimes, profile);
487
+ validateRootKeys(config);
488
+ validateModelsBlock(config, profile);
489
+ return config;
344
490
  }
345
491
  /**
346
492
  * The owner allocation control: `allocation.grants` only. null = the owner declared no grants list: grant-gated pools
347
493
  * keep their ungated routing; a declared list, even [], is the whole set of grants.
348
494
  */
349
- export function configuredAllocationPolicy(config=loadConfig()){
350
- validateConfig(config);
351
- const allocation=plain(config.allocation)?config.allocation:null;
352
- const grants=Array.isArray(allocation?.grants)
353
- ?Object.fromEntries(allocation.grants.map(parseAllocationGrant).map(({pool,slots,roles})=>[pool,{slots,roles}]))
354
- :null;
355
- return {grants,source:allocation?'allocation':'default'};
495
+ export function configuredAllocationPolicy(config = loadConfig()) {
496
+ validateConfig(config);
497
+ const allocation = plain(config.allocation) ? config.allocation : null;
498
+ const grants = Array.isArray(allocation?.grants)
499
+ ? Object.fromEntries(allocation.grants.map(parseAllocationGrant).map(grant => { const { pool, slots, roles } = grant; return [pool, { slots, roles }]; }))
500
+ : null;
501
+ return { grants, source: allocation ? 'allocation' : 'default' };
356
502
  }
357
503
  /** The variable that confines the owner's `config.yaml` to a directory tree: set, an owner file is read only from under that directory (e2e/setup/isolated-temp.mjs points it at a spec's own temp root). */
358
- const OWNER_CONFIG_WITHIN_ENV='STARCI_OWNER_CONFIG_WITHIN';
359
- const foldPath=p=>process.platform==='win32'?path.resolve(p).toLowerCase():path.resolve(p);
504
+ const OWNER_CONFIG_WITHIN_ENV = 'STARCI_OWNER_CONFIG_WITHIN';
505
+ const foldPath = (p) => process.platform === 'win32' ? path.resolve(p).toLowerCase() : path.resolve(p);
360
506
  /**
361
507
  * Whether the owner's `config.yaml` under `root` is visible to this process. Unconfined (the variable unset) every owner file is visible; confined, only the files under the named
362
508
  * directory, where a fixture wrote them, so the checkout's live file (roots.temp, supervisor.landGate, launch trust) never decides a result and a lane clone, the release host and a
363
509
  * clean checkout run the same suite. The engine knows nothing of why a caller confines it: the spec preload sets the variable.
364
510
  */
365
- const ownerFileVisible=(root,env=process.env)=>{
366
- const within=env[OWNER_CONFIG_WITHIN_ENV];
367
- if(!within)return true;
368
- const rel=path.relative(foldPath(within),foldPath(root));
369
- return !rel.startsWith('..')&&!path.isAbsolute(rel);
511
+ const ownerFileVisible = (root, env = process.env) => {
512
+ const within = env[OWNER_CONFIG_WITHIN_ENV];
513
+ if (!within)
514
+ return true;
515
+ const rel = path.relative(foldPath(within), foldPath(root));
516
+ return !rel.startsWith('..') && !path.isAbsolute(rel);
370
517
  };
371
518
  /** The validated config one yaml file holds, or null when the file is absent. */
372
- const readYamlConfig=(root,name)=>{const yaml=path.join(root,name);return fs.existsSync(yaml)?validateConfig(parseYaml(fs.readFileSync(yaml,'utf8'))):null;};
373
- function readExample(root=configRoot){const example=readYamlConfig(root,'config.example.yaml');if(example!==null){return example;}throw new Error('Missing config.example.yaml');}
519
+ const readYamlConfig = (root, name) => { const yaml = path.join(root, name); return fs.existsSync(yaml) ? validateConfig(parseYaml(fs.readFileSync(yaml, 'utf8'))) : null; };
520
+ function readExample(root = configRoot) { const example = readYamlConfig(root, 'config.example.yaml'); if (example !== null) {
521
+ return example;
522
+ } throw new Error('Missing config.example.yaml'); }
374
523
  /**
375
524
  * The owner config reader: `config.yaml` is the per-project file (gitignored,
376
525
  * seeded verbatim from `config.example.yaml` by the installer — comments and
377
526
  * all). Returns the validated owner config, or null when that file does not
378
527
  * exist; falling back to the example's defaults is `loadConfig`.
379
528
  */
380
- function readOwnerConfig(root=configRoot){return ownerFileVisible(root)?readYamlConfig(root,'config.yaml'):null;}
381
- /**
382
- * The tolerant read the kernel boot and the router share: an owner file that is absent, unparsable or
383
- * short of the closed schema must never stop a workflow from routing. Returns
384
- * {file, config, error, invalid} — `config` is the validated config, or the raw parse when it fails the
385
- * schema (with `invalid` naming the reason), or null when the file is absent or `error` says why it
386
- * could not be read at all.
387
- */
388
- export function inspectOwnerConfig(root=configRoot){
389
- const file=path.join(root,'config.yaml');
390
- if(!ownerFileVisible(root)||!fs.existsSync(file))return {file,config:null,error:null,invalid:null};
391
- let parsed;
392
- try{parsed=parseYaml(fs.readFileSync(file,'utf8'))??null;}
393
- catch(error){return {file,config:null,error:`config.yaml unparsable: ${error.message}`,invalid:null};}
394
- try{return {file,config:validateConfig(parsed),error:null,invalid:null};}
395
- catch(error){return {file,config:parsed,error:null,invalid:error.message};}
529
+ function readOwnerConfig(root = configRoot) { return ownerFileVisible(root) ? readYamlConfig(root, 'config.yaml') : null; }
530
+ export function inspectOwnerConfig(root = configRoot) {
531
+ const file = path.join(root, 'config.yaml');
532
+ if (!ownerFileVisible(root) || !fs.existsSync(file))
533
+ return { file, config: null, error: null, invalid: null };
534
+ let parsed;
535
+ try {
536
+ parsed = parseYaml(fs.readFileSync(file, 'utf8')) ?? null;
537
+ }
538
+ catch (error) {
539
+ return { file, config: null, error: `config.yaml unparsable: ${error.message}`, invalid: null };
540
+ }
541
+ try {
542
+ return { file, config: validateConfig(parsed), error: null, invalid: null };
543
+ }
544
+ catch (error) {
545
+ return { file, config: parsed, error: null, invalid: error.message };
546
+ }
396
547
  }
397
548
  /**
398
549
  * The owner's spec switches (config.yaml root `specs`, owner 2026-09-28; defaults set 2026-09-29, none needs a config block):
@@ -403,59 +554,66 @@ export function inspectOwnerConfig(root=configRoot){
403
554
  * or `starci kernel run-deferred-tests`); e2e.verify then runs the FULL e2e suite.
404
555
  * An absent, null or unreadable owner file reads as the defaults: unit on, e2e off. The runtime itself has no specs (owner ruling no-runtime-specs), so there is no switch for them.
405
556
  */
406
- const SPEC_FAMILIES=Object.freeze(['unit','e2e']);
407
- const SEAT_KEYS=Object.freeze(['agent','model','effort']);
557
+ const SPEC_FAMILIES = Object.freeze(['unit', 'e2e']);
558
+ const SEAT_KEYS = Object.freeze(['agent', 'model', 'effort']);
408
559
  /** The keys each config block accepts directly under it (the validators read these lists; docs/config-format.md and the example are checked against them). Blocks whose keys are open or deeper than this are not listed. */
409
- const CONFIG_KEY_TREE=Object.freeze({
410
- models:MODELS_KEYS,allocation:Object.freeze(['grants']),kernel:SEAT_KEYS,budgets:Object.freeze(['maxOps']),parallel:Object.freeze(['gear']),
411
- supervisor:Object.freeze(['mode','pollIntervalMs','repos','stallMinutes','kernel','workers','landGate','frozenMinutes']),
412
- delegation:Object.freeze(['asks','until','excludes','note']),asks:Object.freeze(['autoAcceptRecommended','excludes']),uat:Object.freeze(['maxConcurrent']),
413
- specs:SPEC_FAMILIES,reconciler:Object.freeze(['enabled','profile','controllers']),debugLoop:Object.freeze(['interval','worktreeLimit']),
414
- orca:ORCA_KEYS,roots:ROOT_KEYS,resources:RESOURCE_KEYS,release:RELEASE_KEYS,
560
+ const CONFIG_KEY_TREE = Object.freeze({
561
+ models: MODELS_KEYS, allocation: Object.freeze(['grants']), kernel: SEAT_KEYS, budgets: Object.freeze(['maxOps']), parallel: Object.freeze(['gear']),
562
+ supervisor: Object.freeze(['mode', 'pollIntervalMs', 'repos', 'stallMinutes', 'kernel', 'workers', 'landGate', 'frozenMinutes']),
563
+ delegation: Object.freeze(['asks', 'until', 'excludes', 'note']), asks: Object.freeze(['autoAcceptRecommended', 'excludes']), uat: Object.freeze(['maxConcurrent']),
564
+ specs: SPEC_FAMILIES, reconciler: Object.freeze(['enabled', 'profile', 'controllers']), debugLoop: Object.freeze(['interval', 'worktreeLimit']),
565
+ orca: ORCA_KEYS, roots: ROOT_KEYS, resources: RESOURCE_KEYS, release: RELEASE_KEYS,
415
566
  });
416
- export const SPEC_DEFAULTS=Object.freeze({unit:true,e2e:false});
417
- export function specsSettings(config){const specs=plain(config?.specs)?config.specs:{};return Object.fromEntries(SPEC_FAMILIES.map(key=>[key,typeof specs[key]==='boolean'?specs[key]:SPEC_DEFAULTS[key]]));}
567
+ export const SPEC_DEFAULTS = Object.freeze({ unit: true, e2e: false });
568
+ export function specsSettings(config) { const specs = plain(config?.specs) ? config.specs : {}; return Object.fromEntries(SPEC_FAMILIES.map(key => [key, typeof specs[key] === 'boolean' ? specs[key] : SPEC_DEFAULTS[key]])); }
418
569
  /**
419
570
  * config.yaml `debugLoop` (skills/starci, scripts/debug/digest): {interval?, worktreeLimit?}, each optional.
420
571
  * interval <n>s | <n>m | <n>h — the cadence of the chat /loop that runs `starci debug digest`.
421
572
  * worktreeLimit integer >= 1 — more registered worktrees than this in one repository is a core-watch alert.
422
573
  */
423
- export const DURATION_PATTERN=/^(\d+)([smh])$/;
424
- export const DEBUG_LOOP_DEFAULTS=Object.freeze({interval:'10m',worktreeLimit:40});
574
+ export const DURATION_PATTERN = /^(\d+)([smh])$/;
575
+ export const DEBUG_LOOP_DEFAULTS = Object.freeze({ interval: '10m', worktreeLimit: 40 });
425
576
  /** '10m' | '90s' | '1h' in milliseconds, or null for anything else (zero included). */
426
- export function durationMs(text){const m=DURATION_PATTERN.exec(String(text??'').trim());const ms=m?Number(m[1])*{s:1000,m:60000,h:3600000}[m[2]]:0;return ms>0?ms:null;}
427
- function validateDebugLoop(block){
428
- if(block===null)return;
429
- const bad=invalid('debugLoop');
430
- if(!plain(block))bad(' must be {interval?: <n>s|<n>m|<n>h, worktreeLimit?: <integer >= 1>} or null.');
431
- for(const key of Object.keys(block))if(!CONFIG_KEY_TREE.debugLoop.includes(key))bad(` has unknown key ${key} (allowed: ${CONFIG_KEY_TREE.debugLoop.join(', ')}).`);
432
- if(block.interval!==undefined&&durationMs(block.interval)===null)bad('.interval must be <n>s, <n>m or <n>h with n >= 1 (e.g. 10m).');
433
- if(block.worktreeLimit!==undefined&&!(Number.isInteger(block.worktreeLimit)&&block.worktreeLimit>=1))bad('.worktreeLimit must be an integer >= 1.');
577
+ export function durationMs(text) { const m = DURATION_PATTERN.exec(String(text ?? '').trim()); const ms = m ? Number(m[1]) * { s: 1000, m: 60000, h: 3600000 }[m[2]] : 0; return ms > 0 ? ms : null; }
578
+ function validateDebugLoop(block) {
579
+ if (block === null)
580
+ return;
581
+ const bad = invalid('debugLoop');
582
+ if (!plain(block))
583
+ bad(' must be {interval?: <n>s|<n>m|<n>h, worktreeLimit?: <integer >= 1>} or null.');
584
+ for (const key of Object.keys(block))
585
+ if (!CONFIG_KEY_TREE.debugLoop.includes(key))
586
+ bad(` has unknown key ${key} (allowed: ${CONFIG_KEY_TREE.debugLoop.join(', ')}).`);
587
+ if (block.interval !== undefined && durationMs(block.interval) === null)
588
+ bad('.interval must be <n>s, <n>m or <n>h with n >= 1 (e.g. 10m).');
589
+ if (block.worktreeLimit !== undefined && !(typeof block.worktreeLimit === 'number' && Number.isInteger(block.worktreeLimit) && block.worktreeLimit >= 1))
590
+ bad('.worktreeLimit must be an integer >= 1.');
434
591
  }
435
592
  /** The owner's debug-loop settings: {interval, intervalMs, worktreeLimit}; an absent block or key is the default. */
436
- export function debugLoopSettings(config=loadConfig()){
437
- validateDebugLoop(config?.debugLoop??null);
438
- const block=plain(config?.debugLoop)?config.debugLoop:{};
439
- const interval=block.interval??DEBUG_LOOP_DEFAULTS.interval;
440
- return {interval,intervalMs:durationMs(interval),worktreeLimit:block.worktreeLimit??DEBUG_LOOP_DEFAULTS.worktreeLimit};
593
+ export function debugLoopSettings(config = loadConfig()) {
594
+ validateDebugLoop(config?.debugLoop ?? null);
595
+ const block = plain(config?.debugLoop) ? config.debugLoop : {};
596
+ const interval = block.interval ?? DEBUG_LOOP_DEFAULTS.interval;
597
+ return { interval, intervalMs: durationMs(interval), worktreeLimit: block.worktreeLimit ?? DEBUG_LOOP_DEFAULTS.worktreeLimit };
441
598
  }
442
599
  /** The owner config of `root` (config.yaml), else the shipped example. The installer seeds config.yaml (scripts/install/install.mjs seedConfig); this reader never writes. */
443
- export function loadConfig(root=configRoot){
444
- return readOwnerConfig(root)??readExample(root);
600
+ export function loadConfig(root = configRoot) {
601
+ return readOwnerConfig(root) ?? readExample(root);
445
602
  }
446
-
447
603
  /** The owner's standing delegation of ask answers (config.yaml `delegation`), or null when absent or expired. */
448
- export function activeDelegation(config=loadConfig(),now=Date.now()){const d=config?.delegation;if(!d||Date.parse(d.until)<=now){return null;}return {asks:d.asks,until:d.until,excludes:d.excludes??[],note:d.note??null};}
449
-
604
+ export function activeDelegation(config = loadConfig(), now = Date.now()) { const d = config?.delegation; if (!d || Date.parse(d.until) <= now) {
605
+ return null;
606
+ } return { asks: d.asks, until: d.until, excludes: d.excludes ?? [], note: d.note ?? null }; }
450
607
  /**
451
608
  * The owner's auto-accept policy for recommended asks (config.yaml `asks`): {autoAcceptRecommended,
452
609
  * excludes, source}. An absent or null block is the default (off). `handover` is always in excludes.
453
610
  * Validates first, so a malformed block throws rather than reading as on.
454
611
  */
455
- export function askAutoAcceptPolicy(config=loadConfig()){
456
- if(config?.asks!==undefined)validateAsks(config.asks);
457
- const asks=plain(config?.asks)?config.asks:null;
458
- const listed=asks&&Array.isArray(asks.excludes)?asks.excludes:ASKS_DEFAULTS.excludes;
459
- const excludes=listed.includes('handover')?[...listed]:[...listed,'handover'];
460
- return {autoAcceptRecommended:asks?.autoAcceptRecommended===true,excludes,source:asks?'asks':'default'};
612
+ export function askAutoAcceptPolicy(config = loadConfig()) {
613
+ if (config?.asks !== undefined)
614
+ validateAsks(config.asks);
615
+ const asks = plain(config?.asks) ? config.asks : null;
616
+ const listed = asks && Array.isArray(asks.excludes) ? asks.excludes : ASKS_DEFAULTS.excludes;
617
+ const excludes = listed.includes('handover') ? [...listed] : [...listed, 'handover'];
618
+ return { autoAcceptRecommended: asks?.autoAcceptRecommended === true, excludes, source: asks ? 'asks' : 'default' };
461
619
  }