@starci/hfs 4.11.22 → 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 +23 -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 +414 -325
  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 +14 -22
  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 +493 -348
  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
@@ -25,11 +25,12 @@
25
25
  // Exit 0 clean (suspects allowed), 1 refused, 2 usage.
26
26
  import fs from 'node:fs';
27
27
  import path from 'node:path';
28
- import { lsFiles } from '../api/git/ls-files.mjs'; import { checkIgnore } from '../api/git/check-ignore.mjs';
28
+ import { lsFiles } from '../api/git/ls-files.mjs';
29
+ import { checkIgnore } from '../api/git/check-ignore.mjs';
29
30
  import { repositoryName, repositoryHome } from '../hfs/repo-identity.mjs';
30
31
  import { isMain } from '../lib/is-main.mjs';
31
32
  import { skillRoot } from '../../engine/runtime-root.mjs';
32
- import {isPlainObject as plain} from '../../engine/plain-object.mjs';
33
+ import { isPlainObject as plain } from '../../engine/plain-object.mjs';
33
34
  import { parseYaml } from '../../engine/yaml.mjs';
34
35
  import { list } from '../lib/list.mjs';
35
36
  import { isFile, isDir } from '../lib/fs-kind.mjs';
@@ -42,410 +43,512 @@ import { escapeRegExp } from '../lib/regex.mjs';
42
43
  import { isInfrastructureValueFile } from './starcistacks-value-file.mjs';
43
44
  import { STACK_DECLARATION_TEMPLATE } from '../lib/example-refs.mjs';
44
45
  import { checkSecretForm, kindOf } from './starcistacks-form.mjs';
45
- import { SERVICE_CATALOG, checkServiceForm, checkCiDeclaration, checkQualityService } from './starcistacks-ci.mjs';
46
+ import { SERVICE_CATALOG, catalogOf, checkServiceForm, checkCiDeclaration, checkQualityService } from './starcistacks-ci.mjs';
46
47
  import { applicationStackFindings } from './starcistacks-runtime.mjs';
47
48
  export { DECLARATION, STACK_ROOT, findStackDeclaration };
48
49
  const RESULT_SCHEMA = 'starci/starcistacks-check@1';
49
50
  const DECLARATION_SCHEMA = 'starci/application-stacks@1';
50
51
  const FOLLOW_UP = { op: 'workspace.manage', params: { mode: 'stacks' },
51
- detail: `author the services block of the repository stack declaration (sonar and every other delivery/quality service) from ${STACK_DECLARATION_TEMPLATE}` };
52
+ detail: `author the services block of the repository stack declaration (sonar and every other delivery/quality service) from ${STACK_DECLARATION_TEMPLATE}` };
52
53
  /** Every finding code this check can emit. */
53
54
  export const CODES = [
54
- 'STACKS_BADGE_TOKEN_SERVICE', 'STACKS_DECLARATION_MISSING', 'STACKS_DECLARATION_INVALID', 'STACKS_SERVICES_MISSING',
55
- 'STACKS_SCHEMA_INVALID', 'STACKS_SERVICE_UNKNOWN', 'STACKS_PROVIDER_UNKNOWN',
56
- 'STACKS_SERVICE_AMBIGUOUS', 'STACKS_STACK_UNRESOLVED', 'STACKS_CUSTODY_MISSING', 'STACKS_CUSTODY_UNVERIFIED',
57
- 'STACKS_OWNER_ACTION_REDUNDANT', 'STACKS_PROJECT_MISSING', 'STACKS_PROJECT_DRIFT', 'STACKS_HOST_DRIFT',
58
- 'STACKS_SERVICE_UNDECLARED', 'STACKS_CI_CONTRADICTION', 'STACKS_CI_UNUSED', 'STACKS_CI_NAME_UNREFERENCED',
59
- 'STACKS_PLAINTEXT_TRACKED', 'STACKS_ENC_TWIN_MISSING', 'STACKS_GITIGNORE_OPEN', 'STACKS_DECLARATION_IGNORED',
60
- 'STACKS_GITIGNORE_VALUE_OPEN', 'STACKS_RUNBOOK_IGNORED', 'STACKS_QUALITY_GATE_DRIFT',
61
- 'CI_DECLARATION_INVALID', 'STACKS_CI_LEGACY_FORM',
62
- 'STACKS_EXAMPLE_FORM', 'STACKS_PRODUCT_FORM', 'HFS_STACKS_SHAPE',
55
+ 'STACKS_BADGE_TOKEN_SERVICE', 'STACKS_DECLARATION_MISSING', 'STACKS_DECLARATION_INVALID', 'STACKS_SERVICES_MISSING',
56
+ 'STACKS_SCHEMA_INVALID', 'STACKS_SERVICE_UNKNOWN', 'STACKS_PROVIDER_UNKNOWN',
57
+ 'STACKS_SERVICE_AMBIGUOUS', 'STACKS_STACK_UNRESOLVED', 'STACKS_CUSTODY_MISSING', 'STACKS_CUSTODY_UNVERIFIED',
58
+ 'STACKS_OWNER_ACTION_REDUNDANT', 'STACKS_PROJECT_MISSING', 'STACKS_PROJECT_DRIFT', 'STACKS_HOST_DRIFT',
59
+ 'STACKS_SERVICE_UNDECLARED', 'STACKS_CI_CONTRADICTION', 'STACKS_CI_UNUSED', 'STACKS_CI_NAME_UNREFERENCED',
60
+ 'STACKS_PLAINTEXT_TRACKED', 'STACKS_ENC_TWIN_MISSING', 'STACKS_GITIGNORE_OPEN', 'STACKS_DECLARATION_IGNORED',
61
+ 'STACKS_GITIGNORE_VALUE_OPEN', 'STACKS_RUNBOOK_IGNORED', 'STACKS_QUALITY_GATE_DRIFT',
62
+ 'CI_DECLARATION_INVALID', 'STACKS_CI_LEGACY_FORM',
63
+ 'STACKS_EXAMPLE_FORM', 'STACKS_PRODUCT_FORM', 'HFS_STACKS_SHAPE',
63
64
  ];
64
65
  /** Paths (under <root>/<environment>/) the ignore rules must deny; they need not exist. */
65
66
  const INFRA_VALUE_PROBES = ['infra/compose/.env', 'infra/compose/.env.generated', 'infra/compose/service/.env.local',
66
- 'infra/compose/tls.key', 'infra/terraform/terraform.tfvars'];
67
+ 'infra/compose/tls.key', 'infra/terraform/terraform.tfvars'];
68
+ const usedBy = (ciUses, id) => ciUses[id] ?? [];
67
69
  let schemaCache = null;
68
70
  function declarationSchema() {
69
- if (!schemaCache) schemaCache = parseYaml(fs.readFileSync(path.join(skillRoot, 'modules', 'schemas', 'application-stacks.schema.yaml'), 'utf8'));
70
- return schemaCache;
71
+ if (!schemaCache)
72
+ schemaCache = parseYaml(fs.readFileSync(path.join(skillRoot, 'modules', 'schemas', 'application-stacks.schema.yaml'), 'utf8'));
73
+ return schemaCache;
71
74
  }
72
- const appendSchemaArrayErrors = (out, node, s, where, run) => { node.forEach((item, index) => { if (s.items) out.push(...run(item, s.items, `${where}[${index}]`)); }); };
75
+ const appendSchemaArrayErrors = (out, node, s, where, run) => { node.forEach((item, index) => { if (s.items)
76
+ out.push(...run(item, s.items, `${where}[${index}]`)); }); };
73
77
  const appendSchemaObjectErrors = (out, node, s, where, run) => {
74
- if (s.minProperties && Object.keys(node).length < s.minProperties) out.push({ path: where, message: 'object has too few properties' });
75
- for (const key of s.required ?? []) if (!Object.hasOwn(node, key)) out.push({ path: where, message: `missing ${key}` });
76
- for (const [key, child] of Object.entries(node)) {
77
- if (Object.hasOwn(s.properties ?? {}, key)) out.push(...run(child, s.properties[key], `${where}.${key}`));
78
- else if (s.additionalProperties === false) out.push({ path: `${where}.${key}`, message: 'unknown field' });
79
- else if (plain(s.additionalProperties)) out.push(...run(child, s.additionalProperties, `${where}.${key}`));
80
- }
78
+ if (s.minProperties && Object.keys(node).length < s.minProperties)
79
+ out.push({ path: where, message: 'object has too few properties' });
80
+ for (const key of s.required ?? [])
81
+ if (!Object.hasOwn(node, key))
82
+ out.push({ path: where, message: `missing ${key}` });
83
+ for (const [key, child] of Object.entries(node)) {
84
+ if (Object.hasOwn(s.properties ?? {}, key))
85
+ out.push(...run(child, s.properties[key], `${where}.${key}`));
86
+ else if (s.additionalProperties === false)
87
+ out.push({ path: `${where}.${key}`, message: 'unknown field' });
88
+ else if (plain(s.additionalProperties))
89
+ out.push(...run(child, s.additionalProperties, `${where}.${key}`));
90
+ }
81
91
  };
82
92
  /** The subset of JSON Schema the declaration schema uses: type, const, enum, pattern, minLength, required, properties, additionalProperties, items, minProperties, minimum, maximum, $ref, anyOf. */
83
93
  function schemaErrors(value, shape, at = '$', root = declarationSchema()) {
84
- const resolve = (ref) => ref.slice(2).split('/').reduce((node, key) => node?.[key], root);
85
- const typeOk = (node, s) => ({ object: plain, array: Array.isArray, integer: Number.isInteger }[s.type] ?? ((n) => (s.type ? typeof n === s.type : true)))(node);
86
- const run = (node, s, where) => {
87
- if (!plain(s)) return [];
88
- if (s.$ref) return run(node, resolve(s.$ref), where);
89
- if (Array.isArray(s.anyOf) && !s.anyOf.some((option) => run(node, option, where).length === 0))
90
- return [{ path: where, message: 'matches none of the allowed shapes' }];
91
- if (!typeOk(node, s)) return [{ path: where, message: `expected ${s.type}` }];
92
- const out = [];
93
- if (Object.hasOwn(s, 'const') && node !== s.const) out.push({ path: where, message: `must be ${JSON.stringify(s.const)}` });
94
- if (Array.isArray(s.enum) && !s.enum.includes(node)) out.push({ path: where, message: `must be one of ${s.enum.join(', ')}` });
95
- if (typeof node === 'number' && ((s.minimum !== undefined && node < s.minimum) || (s.maximum !== undefined && node > s.maximum))) out.push({ path: where, message: 'number outside allowed range' });
96
- if (typeof node === 'string') out.push(...stringErrors(node, s, where));
97
- if (Array.isArray(node)) { appendSchemaArrayErrors(out, node, s, where, run); return out; }
98
- if (!plain(node)) return out;
99
- appendSchemaObjectErrors(out, node, s, where, run);
100
- return out;
101
- };
102
- const stringErrors = (node, s, where) => {
103
- const out = [];
104
- if (s.minLength && node.length < s.minLength) out.push({ path: where, message: 'string too short' });
105
- if (s.pattern && !new RegExp(s.pattern, 'u').test(node)) out.push({ path: where, message: `does not match ${s.pattern}` });
106
- return out;
107
- };
108
- return run(value, shape, at);
94
+ const resolve = (ref) => ref.slice(2).split('/').reduce((node, key) => node?.[key], root);
95
+ const typeOk = (node, s) => {
96
+ const byType = { object: plain, array: Array.isArray, integer: Number.isInteger };
97
+ return (byType[s.type] ?? ((n) => (s.type ? typeof n === s.type : true)))(node);
98
+ };
99
+ const run = (node, shape, where) => {
100
+ if (!plain(shape))
101
+ return [];
102
+ // A schema node stays the parsed-document view the walk reads by key; plain() only proved it a mapping.
103
+ const s = shape;
104
+ if (s.$ref)
105
+ return run(node, resolve(s.$ref), where);
106
+ if (Array.isArray(s.anyOf) && !s.anyOf.some((option) => run(node, option, where).length === 0))
107
+ return [{ path: where, message: 'matches none of the allowed shapes' }];
108
+ if (!typeOk(node, s))
109
+ return [{ path: where, message: `expected ${s.type}` }];
110
+ const out = [];
111
+ if (Object.hasOwn(s, 'const') && node !== s.const)
112
+ out.push({ path: where, message: `must be ${JSON.stringify(s.const)}` });
113
+ if (Array.isArray(s.enum) && !s.enum.includes(node))
114
+ out.push({ path: where, message: `must be one of ${s.enum.join(', ')}` });
115
+ if (typeof node === 'number' && ((s.minimum !== undefined && node < s.minimum) || (s.maximum !== undefined && node > s.maximum)))
116
+ out.push({ path: where, message: 'number outside allowed range' });
117
+ if (typeof node === 'string')
118
+ out.push(...stringErrors(node, s, where));
119
+ if (Array.isArray(node)) {
120
+ appendSchemaArrayErrors(out, node, s, where, run);
121
+ return out;
122
+ }
123
+ if (!plain(node))
124
+ return out;
125
+ appendSchemaObjectErrors(out, node, s, where, run);
126
+ return out;
127
+ };
128
+ const stringErrors = (node, s, where) => {
129
+ const out = [];
130
+ if (s.minLength && node.length < s.minLength)
131
+ out.push({ path: where, message: 'string too short' });
132
+ if (s.pattern && !new RegExp(s.pattern, 'u').test(node))
133
+ out.push({ path: where, message: `does not match ${s.pattern}` });
134
+ return out;
135
+ };
136
+ return run(value, shape, at);
109
137
  }
110
138
  // ---- locating declarations and repositories -------------------------------------------------------------
111
139
  /** A frontend declared by its backend's stack: a sibling declaration whose `sources` lists this repository. */
112
140
  function governingDeclaration(repo) {
113
- const name = repositoryName(repo);
114
- const parent = path.dirname(repositoryHome(repo));
115
- let entries = []; try { entries = fs.readdirSync(parent, { withFileTypes: true }); } catch { return null; }
116
- for (const entry of entries) {
117
- if (!entry.isDirectory() || entry.name === name) continue;
118
- const file = path.join(parent, entry.name, STACK_ROOT, DECLARATION);
119
- if (!isFile(file)) continue;
120
- const read = readDeclaration(file);
121
- if (read.doc && list(read.doc.sources).some((source) => source?.repository === name))
122
- return { repo: path.join(parent, entry.name), root: STACK_ROOT, file, governs: name, ...read };
123
- }
124
- return null;
141
+ const name = repositoryName(repo);
142
+ const parent = path.dirname(repositoryHome(repo));
143
+ let entries = [];
144
+ try {
145
+ entries = fs.readdirSync(parent, { withFileTypes: true });
146
+ }
147
+ catch {
148
+ return null;
149
+ }
150
+ for (const entry of entries) {
151
+ if (!entry.isDirectory() || entry.name === name)
152
+ continue;
153
+ const file = path.join(parent, entry.name, STACK_ROOT, DECLARATION);
154
+ if (!isFile(file))
155
+ continue;
156
+ const read = readDeclaration(file);
157
+ if (read.doc && list(read.doc.sources).some((source) => source?.repository === name))
158
+ return { repo: path.join(parent, entry.name), root: STACK_ROOT, file, governs: name, ...read };
159
+ }
160
+ return null;
125
161
  }
126
162
  // ---- normalizing a service ------------------------------------------------------------------------------
127
163
  const normalizeCredential = (credential, declaringRepo) => {
128
- const custody = plain(credential.custody) ? credential.custody : {};
129
- const repo = resolveDeclaredRepository(custody.repository, { fromRepo: declaringRepo });
130
- const rel = text(custody.path);
131
- const abs = resolveCustodyFile(repo, rel);
132
- return { id: text(credential.id), env: text(credential.env), key: text(credential.key), purpose: text(credential.purpose),
133
- custody: { repository: text(custody.repository), path: rel, resolved: Boolean(repo), file: abs,
134
- encPresent: abs ? isFile(`${abs}.enc`) : null, plainPresent: abs ? isFile(abs) : null } };
164
+ const custody = plain(credential.custody) ? credential.custody : {};
165
+ const repo = resolveDeclaredRepository(custody.repository, { fromRepo: declaringRepo });
166
+ const rel = text(custody.path);
167
+ const abs = resolveCustodyFile(repo, rel);
168
+ return { id: text(credential.id), env: text(credential.env), key: text(credential.key), purpose: text(credential.purpose),
169
+ custody: { repository: text(custody.repository), path: rel, resolved: Boolean(repo), file: abs,
170
+ encPresent: abs ? isFile(`${abs}.enc`) : null, plainPresent: abs ? isFile(abs) : null } };
135
171
  };
136
172
  const ownerActionOf = (s) => {
137
- const noAction = s.ownerAction === 'none' ? 'none' : null;
138
- return plain(s.ownerAction) ? { needed: text(s.ownerAction.needed), reason: text(s.ownerAction.reason) } : noAction;
173
+ const noAction = s.ownerAction === 'none' ? 'none' : null;
174
+ return plain(s.ownerAction) ? { needed: text(s.ownerAction.needed), reason: text(s.ownerAction.reason) } : noAction;
139
175
  };
140
176
  /** Resolve a declared stack to its host or repository directory without reading custody values. */
141
177
  function normalizeStack(stack, entry, declaringRepo) {
142
- // owner: host - the stack lives inside the installed runtime tree (.claude/ext/<service>);
143
- // `environment` is the slot the extension serves, not a path segment (<root>/<compose> resolves it).
144
- const hostOwned = declaredStack(entry)?.hostOwned ?? false;
145
- const hostRoot = hostOwned ? slash(stack.root) : null;
146
- const extDir = hostRoot && hostRoot.startsWith('.claude/') && !hostRoot.split('/').includes('..')
147
- ? path.join(skillRoot, hostRoot.replace(/^\.claude\//, '')) : null;
148
- const stackRepo = stack && !hostOwned ? resolveDeclaredRepository(stack.repository, { fromRepo: declaringRepo }) : null;
149
- let stackDir = null;
150
- if (hostOwned) stackDir = extDir;
151
- else if (stackRepo && text(stack.root) && text(stack.environment)) stackDir = path.join(stackRepo, stack.root, stack.environment);
152
- return stack ? { owner: text(stack.owner), repository: text(stack.repository), root: text(stack.root), environment: text(stack.environment), compose: text(stack.compose),
153
- container: text(stack.container), publishedBy: text(stack.publishedBy), hostOwned, repo: stackRepo, dir: stackDir,
154
- composeFile: stackDir && text(stack.compose) ? path.join(stackDir, stack.compose) : null } : null;
178
+ // owner: host - the stack lives inside the installed runtime tree (.claude/ext/<service>);
179
+ // `environment` is the slot the extension serves, not a path segment (<root>/<compose> resolves it).
180
+ const hostOwned = declaredStack(entry)?.hostOwned ?? false;
181
+ const hostRoot = hostOwned ? slash(stack.root) : null;
182
+ const extDir = hostRoot && hostRoot.startsWith('.claude/') && !hostRoot.split('/').includes('..')
183
+ ? path.join(skillRoot, hostRoot.replace(/^\.claude\//, '')) : null;
184
+ const stackRepo = stack && !hostOwned ? resolveDeclaredRepository(stack.repository, { fromRepo: declaringRepo }) : null;
185
+ let stackDir = null;
186
+ if (hostOwned)
187
+ stackDir = extDir;
188
+ else if (stackRepo && text(stack.root) && text(stack.environment))
189
+ stackDir = path.join(stackRepo, stack.root, stack.environment);
190
+ return stack ? { owner: text(stack.owner), repository: text(stack.repository), root: text(stack.root), environment: text(stack.environment), compose: text(stack.compose),
191
+ container: text(stack.container), publishedBy: text(stack.publishedBy), hostOwned, repo: stackRepo, dir: stackDir,
192
+ composeFile: stackDir && text(stack.compose) ? path.join(stackDir, stack.compose) : null } : null;
155
193
  }
156
194
  /** One declared service with its custody resolved to presence (never a value). */
157
195
  export function normalizeService(id, entry, { declaringRepo } = {}) {
158
- const s = plain(entry) ? entry : {};
159
- const stack = plain(s.stack) ? s.stack : null;
160
- const normalizedStack = normalizeStack(stack, entry, declaringRepo);
161
- const credentials = list(s.credentials).filter(plain).map((credential) => normalizeCredential(credential, declaringRepo));
162
- const ci = plain(s.ci) ? s.ci : {};
163
- return {
164
- id, organization: text(s.organization), provider: text(s.provider), mode: text(s.mode), purpose: text(s.purpose), reason: text(s.reason), auth: text(s.auth), qualityGate: text(s.qualityGate),
165
- host: { local: text(s.host?.local), public: text(s.host?.public), fromCredential: text(s.host?.fromCredential) },
166
- stack: normalizedStack,
167
- projects: list(s.projects).filter(plain).map((project) => ({ repository: text(project.repository), key: text(project.key), name: text(project.name) })),
168
- credentials,
169
- runtimeSecrets: list(s.runtimeSecrets).map(String),
170
- ci: { token: text(ci.token), hostVar: text(ci.hostVar),
171
- organizationVar: text(ci.organizationVar) || (s.provider === 'sonarcloud' && list(s.runtimeSecrets).length && !credentials.length ? 'SONAR_ORGANIZATION' : null), wiring: text(ci.wiring), workflow: text(ci.workflow), permissions: list(ci.permissions).map(String),
172
- secrets: list(ci.secrets).filter(plain).map((item) => ({ name: text(item.name), credential: text(item.credential) })),
173
- vars: list(ci.vars).filter(plain).map((item) => ({ name: text(item.name), value: text(item.value) })),
174
- provisioning: text(ci.provisioning) },
175
- ownerAction: ownerActionOf(s),
176
- };
196
+ const s = plain(entry) ? entry : {};
197
+ const stack = plain(s.stack) ? s.stack : null;
198
+ const normalizedStack = normalizeStack(stack, entry, declaringRepo);
199
+ const credentials = list(s.credentials).filter(plain).map((credential) => normalizeCredential(credential, declaringRepo));
200
+ const ci = plain(s.ci) ? s.ci : {};
201
+ return {
202
+ id, organization: text(s.organization), provider: text(s.provider), mode: text(s.mode), purpose: text(s.purpose), reason: text(s.reason), auth: text(s.auth), qualityGate: text(s.qualityGate),
203
+ host: { local: text(s.host?.local), public: text(s.host?.public), fromCredential: text(s.host?.fromCredential) },
204
+ stack: normalizedStack,
205
+ projects: list(s.projects).filter(plain).map((project) => ({ repository: text(project.repository), key: text(project.key), name: text(project.name) })),
206
+ credentials,
207
+ runtimeSecrets: list(s.runtimeSecrets).map(String),
208
+ ci: { token: text(ci.token), hostVar: text(ci.hostVar),
209
+ organizationVar: text(ci.organizationVar) || (s.provider === 'sonarcloud' && list(s.runtimeSecrets).length && !credentials.length ? 'SONAR_ORGANIZATION' : null), wiring: text(ci.wiring), workflow: text(ci.workflow), permissions: list(ci.permissions).map(String),
210
+ secrets: list(ci.secrets).filter(plain).map((item) => ({ name: text(item.name), credential: text(item.credential) })),
211
+ vars: list(ci.vars).filter(plain).map((item) => ({ name: text(item.name), value: text(item.value) })),
212
+ provisioning: text(ci.provisioning) },
213
+ ownerAction: ownerActionOf(s),
214
+ };
177
215
  }
178
- const custodyHeld = (service) => (service.credentials.length ? service.credentials.every((credential) => credential.custody.encPresent || credential.custody.plainPresent) : ['oidc', 'github-token', 'none'].includes(service.auth));
216
+ const custodyHeld = (service) => (service.credentials.length ? service.credentials.every((credential) => credential.custody.encPresent || credential.custody.plainPresent) : ['oidc', 'github-token', 'none'].includes(service.auth ?? ''));
179
217
  /**
180
218
  * What a tool (scripts/gates/sonar-local.mjs, a CI wiring leg) reads for one service of one repository:
181
219
  * the repository's own (or governing) declaration first, then the source host's declaration when its entry
182
220
  * lists this repository among its projects. Null when nothing declares it.
183
221
  */
184
222
  export function resolveStackService(repoRoot, serviceId) {
185
- const repo = path.resolve(String(repoRoot ?? ''));
186
- const name = repositoryName(repo);
187
- const own = findStackDeclaration(repo);
188
- const candidates = [own.doc ? own : governingDeclaration(repo)].filter(Boolean);
189
- const host = runtimeHostRoot();
190
- if (path.resolve(host) !== repo) { const source = findStackDeclaration(host); if (source.doc) candidates.push({ ...source, sourceHost: true }); }
191
- for (const declaration of candidates) {
192
- const entry = declaration.doc?.services?.[serviceId];
193
- if (!plain(entry)) continue;
194
- let service = normalizeService(serviceId, entry, { declaringRepo: declaration.repo });
195
- const listed = service.projects.some((project) => project.repository === name);
196
- if (declaration.sourceHost && !listed) continue;
197
- // Through the source host a product gets the shared server, its shared credentials and its own project
198
- // token (a credential whose id is a project key); other projects' tokens and the host's CI wiring are not its.
199
- const ownKey = service.projects.find((project) => project.repository === name)?.key;
200
- const projectKeys = new Set(service.projects.map((project) => project.key));
201
- if (declaration.sourceHost) service = { ...service, credentials: service.credentials.filter((credential) => !projectKeys.has(credential.id) || credential.id === ownKey),
202
- ci: { wiring: null, workflow: null, permissions: [], secrets: [], vars: [], provisioning: null } };
203
- return { declaration: declaration.file, declaringRepo: declaration.repo, sourceHost: Boolean(declaration.sourceHost),
204
- repository: name, projectKey: service.projects.find((project) => project.repository === name)?.key ?? null, ...service };
205
- }
206
- return null;
223
+ const repo = path.resolve(String(repoRoot ?? ''));
224
+ const name = repositoryName(repo);
225
+ const own = findStackDeclaration(repo);
226
+ const first = own.doc ? own : governingDeclaration(repo);
227
+ const candidates = first ? [first] : [];
228
+ const host = runtimeHostRoot();
229
+ if (path.resolve(host) !== repo) {
230
+ const source = findStackDeclaration(host);
231
+ if (source.doc)
232
+ candidates.push({ ...source, sourceHost: true });
233
+ }
234
+ for (const declaration of candidates) {
235
+ const entry = declaration.doc?.services?.[serviceId];
236
+ if (!plain(entry))
237
+ continue;
238
+ let service = normalizeService(serviceId, entry, { declaringRepo: declaration?.repo });
239
+ const listed = service.projects.some((project) => project.repository === name);
240
+ if (declaration.sourceHost && !listed)
241
+ continue;
242
+ // Through the source host a product gets the shared server, its shared credentials and its own project
243
+ // token (a credential whose id is a project key); other projects' tokens and the host's CI wiring are not its.
244
+ const ownKey = service.projects.find((project) => project.repository === name)?.key;
245
+ const projectKeys = new Set(service.projects.map((project) => project.key));
246
+ if (declaration.sourceHost)
247
+ service = { ...service, credentials: service.credentials.filter((credential) => !projectKeys.has(credential.id) || credential.id === ownKey),
248
+ ci: { wiring: null, workflow: null, permissions: [], secrets: [], vars: [], provisioning: null } };
249
+ return { declaration: declaration.file, declaringRepo: declaration.repo, sourceHost: Boolean(declaration.sourceHost),
250
+ repository: name, projectKey: service.projects.find((project) => project.repository === name)?.key ?? null, ...service };
251
+ }
252
+ return null;
207
253
  }
208
254
  // ---- repository evidence --------------------------------------------------------------------------------
209
255
  function workflowTexts(repo) {
210
- const dir = path.join(repo, '.github', 'workflows');
211
- let entries = []; try { entries = fs.readdirSync(dir).filter((name) => /\.ya?ml$/i.test(name)); } catch { return []; }
212
- return entries.map((name) => ({ file: `.github/workflows/${name}`, text: readText(path.join(dir, name)) ?? '' }));
256
+ const dir = path.join(repo, '.github', 'workflows');
257
+ let entries = [];
258
+ try {
259
+ entries = fs.readdirSync(dir).filter((name) => /\.ya?ml$/i.test(name));
260
+ }
261
+ catch {
262
+ return [];
263
+ }
264
+ return entries.map((name) => ({ file: `.github/workflows/${name}`, text: readText(path.join(dir, name)) ?? '' }));
213
265
  }
214
266
  function git(call, repo, args) {
215
- try {
216
- const result = call(args, { dir: repo, timeout: 8000 });
217
- if (result.error) return null;
218
- return { status: result.status, stdout: String(result.stdout ?? '') };
219
- } catch { return null; }
267
+ try {
268
+ const result = call(args, { dir: repo, timeout: 8000 });
269
+ if (result.error)
270
+ return null;
271
+ return { status: result.status, stdout: String(result.stdout ?? '') };
272
+ }
273
+ catch {
274
+ return null;
275
+ }
220
276
  }
221
277
  // ---- the check ------------------------------------------------------------------------------------------
222
278
  const checkLocalStack = (service, at, add, ambiguous) => {
223
- if (!service.host.local) ambiguous('a local service names host.local, the URL the stack serves it on');
224
- if (!service.stack) ambiguous('a local service names the stack that runs it (repository, root, environment, compose) or owns it as a host extension (owner: host, root: .claude/ext/<service>)');
225
- else if (service.stack.hostOwned) {
226
- if (!service.stack.composeFile || !isFile(service.stack.composeFile))
227
- add('refuse', 'STACKS_STACK_UNRESOLVED', `${at}.stack`, `the host extension file ${service.stack.root}/${service.stack.compose} does not exist under the runtime tree (${slash(skillRoot)})`);
228
- const publisher = service.stack.publishedBy;
229
- if (publisher && service.stack.dir && !isFile(path.join(service.stack.dir, publisher)))
230
- add('refuse', 'STACKS_STACK_UNRESOLVED', `${at}.stack.publishedBy`, `the host extension file ${service.stack.root}/${publisher} does not exist under the runtime tree (${slash(skillRoot)})`);
231
- }
232
- else if (!service.stack.repo) add('suspect', 'STACKS_STACK_UNRESOLVED', `${at}.stack`, `repository ${service.stack.repository} is not checked out on this machine; its compose file and custody are unverified`);
233
- else if (!service.stack.composeFile || !isFile(service.stack.composeFile))
234
- add('refuse', 'STACKS_STACK_UNRESOLVED', `${at}.stack`, `${service.stack.repository}/${service.stack.root}/${service.stack.environment}/${service.stack.compose} does not exist`);
279
+ if (!service.host.local)
280
+ ambiguous('a local service names host.local, the URL the stack serves it on');
281
+ if (!service.stack)
282
+ ambiguous('a local service names the stack that runs it (repository, root, environment, compose) or owns it as a host extension (owner: host, root: .claude/ext/<service>)');
283
+ else if (service.stack.hostOwned) {
284
+ if (!service.stack.composeFile || !isFile(service.stack.composeFile))
285
+ add('refuse', 'STACKS_STACK_UNRESOLVED', `${at}.stack`, `the host extension file ${service.stack.root}/${service.stack.compose} does not exist under the runtime tree (${slash(skillRoot)})`);
286
+ const publisher = service.stack.publishedBy;
287
+ if (publisher && service.stack.dir && !isFile(path.join(service.stack.dir, publisher)))
288
+ add('refuse', 'STACKS_STACK_UNRESOLVED', `${at}.stack.publishedBy`, `the host extension file ${service.stack.root}/${publisher} does not exist under the runtime tree (${slash(skillRoot)})`);
289
+ }
290
+ else if (!service.stack.repo)
291
+ add('suspect', 'STACKS_STACK_UNRESOLVED', `${at}.stack`, `repository ${service.stack.repository} is not checked out on this machine; its compose file and custody are unverified`);
292
+ else if (!service.stack.composeFile || !isFile(service.stack.composeFile))
293
+ add('refuse', 'STACKS_STACK_UNRESOLVED', `${at}.stack`, `${service.stack.repository}/${service.stack.root}/${service.stack.environment}/${service.stack.compose} does not exist`);
235
294
  };
236
295
  const checkServiceCredentials = (service, at, add, ambiguous) => {
237
- const ids = new Set();
238
- for (const credential of service.credentials) {
239
- const where = `${at}.credentials.${credential.id ?? '?'}`;
240
- if (credential.id && ids.has(credential.id)) ambiguous(`credential id ${credential.id} is declared twice`);
241
- ids.add(credential.id);
242
- if (!credential.env && !credential.key) ambiguous(`credential ${credential.id ?? '?'} names the env variable or the file key its consumer reads`);
243
- if (!credential.custody.resolved) add('suspect', 'STACKS_CUSTODY_UNVERIFIED', where, `custody repository ${credential.custody.repository} is not checked out on this machine`);
244
- else if (!credential.custody.encPresent)
245
- add('refuse', 'STACKS_CUSTODY_MISSING', where, `${credential.custody.repository}/${credential.custody.path}.enc is not in custody; repair the stack custody (secret:gen / stack-secret) - never ask the owner for a value the declaration says the runtime holds`);
246
- }
247
- return ids;
296
+ const ids = new Set();
297
+ for (const credential of service.credentials) {
298
+ const where = `${at}.credentials.${credential.id ?? '?'}`;
299
+ if (credential.id && ids.has(credential.id))
300
+ ambiguous(`credential id ${credential.id} is declared twice`);
301
+ ids.add(credential.id);
302
+ if (!credential.env && !credential.key)
303
+ ambiguous(`credential ${credential.id ?? '?'} names the env variable or the file key its consumer reads`);
304
+ if (!credential.custody.resolved)
305
+ add('suspect', 'STACKS_CUSTODY_UNVERIFIED', where, `custody repository ${credential.custody.repository} is not checked out on this machine`);
306
+ else if (!credential.custody.encPresent)
307
+ add('refuse', 'STACKS_CUSTODY_MISSING', where, `${credential.custody.repository}/${credential.custody.path}.enc is not in custody; repair the stack custody (secret:gen / stack-secret) - never ask the owner for a value the declaration says the runtime holds`);
308
+ }
309
+ return ids;
248
310
  };
249
- const checkCiNames = (service, id, at, ciUses, ciText, add) => { if (['required', 'optional-follow-up'].includes(service.ci.wiring) && ciUses[id].length) for (const item of [...service.ci.secrets, ...service.ci.vars]) if (item.name && !new RegExp(String.raw`\b${escapeRegExp(item.name)}\b`).test(ciText)) add('suspect', 'STACKS_CI_NAME_UNREFERENCED', `${at}.ci`, `${item.name} is declared for CI but no workflow reads it`); };
311
+ const checkCiNames = (service, id, at, ciUses, ciText, add) => { if (['required', 'optional-follow-up'].includes(service.ci.wiring ?? '') && usedBy(ciUses, id).length)
312
+ for (const item of [...service.ci.secrets, ...service.ci.vars])
313
+ if (item.name && !new RegExp(String.raw `\b${escapeRegExp(item.name)}\b`).test(ciText))
314
+ add('suspect', 'STACKS_CI_NAME_UNREFERENCED', `${at}.ci`, `${item.name} is declared for CI but no workflow reads it`); };
250
315
  const checkServiceCi = (service, id, at, { governing, name, ciUses, ciText, add, ambiguous }) => {
251
- const ids = checkServiceCredentials(service, at, add, ambiguous);
252
- for (const secret of service.ci.secrets)
253
- if (secret.credential && !ids.has(secret.credential)) ambiguous(`ci secret ${secret.name} names credential ${secret.credential}, which the service does not declare`);
254
- if (plain(service.ownerAction) && service.credentials.length && custodyHeld(service))
255
- add('refuse', 'STACKS_OWNER_ACTION_REDUNDANT', `${at}.ownerAction`, 'every credential is already in custody; ownerAction must be none - the owner is never asked for what the runtime holds');
256
- if (service.ci.wiring === 'not-used' && ciUses[id].length)
257
- add('refuse', 'STACKS_CI_CONTRADICTION', `${at}.ci`, `ci.wiring not-used, but ${ciUses[id].join(', ')} call it`);
258
- // A frontend governed by its backend declaration answers only for the services that name it as a project:
259
- // the backend CI wiring (its image registry, say) is not the frontend's to call.
260
- const servesThisRepository = !governing || service.projects.some((project) => project.repository === name);
261
- if (service.ci.wiring === 'required' && !ciUses[id].length && SERVICE_CATALOG[id].ci.length && servesThisRepository)
262
- add('suspect', 'STACKS_CI_UNUSED', `${at}.ci`, 'ci.wiring required, but no workflow calls the service');
263
- checkCiNames(service, id, at, ciUses, ciText, add);
264
- if (ciUses[id].length && service.projects.length && !service.projects.some((project) => project.repository === name) && !governing)
265
- add('refuse', 'STACKS_PROJECT_MISSING', `${at}.projects`, `the workflows call ${id} but no project entry names repository ${name}`);
316
+ const ids = checkServiceCredentials(service, at, add, ambiguous);
317
+ for (const secret of service.ci.secrets)
318
+ if (secret.credential && !ids.has(secret.credential))
319
+ ambiguous(`ci secret ${secret.name} names credential ${secret.credential}, which the service does not declare`);
320
+ if (plain(service.ownerAction) && service.credentials.length && custodyHeld(service))
321
+ add('refuse', 'STACKS_OWNER_ACTION_REDUNDANT', `${at}.ownerAction`, 'every credential is already in custody; ownerAction must be none - the owner is never asked for what the runtime holds');
322
+ if (service.ci.wiring === 'not-used' && usedBy(ciUses, id).length)
323
+ add('refuse', 'STACKS_CI_CONTRADICTION', `${at}.ci`, `ci.wiring not-used, but ${usedBy(ciUses, id).join(', ')} call it`);
324
+ // A frontend governed by its backend declaration answers only for the services that name it as a project:
325
+ // the backend CI wiring (its image registry, say) is not the frontend's to call.
326
+ const servesThisRepository = !governing || service.projects.some((project) => project.repository === name);
327
+ if (service.ci.wiring === 'required' && !usedBy(ciUses, id).length && catalogOf(id).ci.length && servesThisRepository)
328
+ add('suspect', 'STACKS_CI_UNUSED', `${at}.ci`, 'ci.wiring required, but no workflow calls the service');
329
+ checkCiNames(service, id, at, ciUses, ciText, add);
330
+ if (usedBy(ciUses, id).length && service.projects.length && !service.projects.some((project) => project.repository === name) && !governing)
331
+ add('refuse', 'STACKS_PROJECT_MISSING', `${at}.projects`, `the workflows call ${id} but no project entry names repository ${name}`);
266
332
  };
267
- const checkDisabledService = (service, id, at, ciUses, ambiguous, add) => { if (service.mode !== 'disabled') { return false; } if (!service.reason) { ambiguous('a disabled service states its reason'); } if (ciUses[id].length) { add('refuse', 'STACKS_CI_CONTRADICTION', at, `declared disabled, but ${ciUses[id].join(', ')} call it`); } return true; };
333
+ const checkDisabledService = (service, id, at, ciUses, ambiguous, add) => { if (service.mode !== 'disabled') {
334
+ return false;
335
+ } if (!service.reason) {
336
+ ambiguous('a disabled service states its reason');
337
+ } if (usedBy(ciUses, id).length) {
338
+ add('refuse', 'STACKS_CI_CONTRADICTION', at, `declared disabled, but ${usedBy(ciUses, id).join(', ')} call it`);
339
+ } return true; };
268
340
  const checkEnabledService = (service, id, at, { governing, name, ciUses, ciText, add, kind }, ambiguous) => {
269
- checkSecretForm(service, id, at, kind, add);
270
- if (service.mode === 'local') checkLocalStack(service, at, add, ambiguous);
271
- if (service.mode === 'hosted' && !service.host.public && !service.host.fromCredential) ambiguous('a hosted service names host.public, or host.fromCredential when the endpoint travels inside a credential');
272
- if (ciUses[id].length && service.ci.wiring !== 'not-used' && !service.host.public) ambiguous('GitHub CI reaches a service only through host.public; name it');
273
- if (!service.auth) ambiguous('auth is token, oidc, github-token or none');
274
- if (service.auth === 'token' && !service.credentials.length && !service.runtimeSecrets.length && !service.ci.token) ambiguous('auth token names at least one credential in custody, or the runtime secret.env variables an example reads (runtimeSecrets)');
275
- if (service.host.fromCredential && !service.credentials.some((credential) => credential.id === service.host.fromCredential)) ambiguous(`host.fromCredential names ${service.host.fromCredential}, which is not one of its credentials`);
276
- if (!service.projects.length) ambiguous('an enabled service lists the project key of every repository it serves');
277
- checkServiceCi(service, id, at, { governing, name, ciUses, ciText, add, ambiguous });
341
+ checkSecretForm(service, id, at, kind, add);
342
+ if (service.mode === 'local')
343
+ checkLocalStack(service, at, add, ambiguous);
344
+ if (service.mode === 'hosted' && !service.host.public && !service.host.fromCredential)
345
+ ambiguous('a hosted service names host.public, or host.fromCredential when the endpoint travels inside a credential');
346
+ if (usedBy(ciUses, id).length && service.ci.wiring !== 'not-used' && !service.host.public)
347
+ ambiguous('GitHub CI reaches a service only through host.public; name it');
348
+ if (!service.auth)
349
+ ambiguous('auth is token, oidc, github-token or none');
350
+ if (service.auth === 'token' && !service.credentials.length && !service.runtimeSecrets.length && !service.ci.token)
351
+ ambiguous('auth token names at least one credential in custody, or the runtime secret.env variables an example reads (runtimeSecrets)');
352
+ if (service.host.fromCredential && !service.credentials.some((credential) => credential.id === service.host.fromCredential))
353
+ ambiguous(`host.fromCredential names ${service.host.fromCredential}, which is not one of its credentials`);
354
+ if (!service.projects.length)
355
+ ambiguous('an enabled service lists the project key of every repository it serves');
356
+ checkServiceCi(service, id, at, { governing, name, ciUses, ciText, add, ambiguous });
278
357
  };
279
358
  const checkService = (id, entry, { schema, declFile, declaration, governing, name, ciUses, ciText, normalized, add, kind }) => {
280
- if (entry?.badgeToken !== undefined && !['codecov', 'sonar'].includes(id)) add('refuse', 'STACKS_BADGE_TOKEN_SERVICE', `${declFile}#services.${id}.badgeToken`, 'badgeToken is allowed only on codecov or sonar');
281
- if (!Object.hasOwn(SERVICE_CATALOG, id)) {
282
- add('refuse', 'STACKS_SERVICE_UNKNOWN', `${declFile}#services.${id}`, `${id} is not a catalogued service (${Object.keys(SERVICE_CATALOG).join(', ')}); a service the catalog does not know is one nobody can read unambiguously`);
283
- return;
284
- }
285
- for (const issue of schemaErrors(entry, ['dependabot', 'audit'].includes(id) ? schema.$defs.serviceOptionalCi : schema.$defs.service, `services.${id}`))
286
- add('refuse', 'STACKS_SCHEMA_INVALID', `${declFile}#${issue.path}`, issue.message);
287
- const service = normalizeService(id, entry, { declaringRepo: declaration.repo });
288
- normalized[id] = service;
289
- const at = `${declFile}#services.${id}`;
290
- const ambiguous = (message) => add('refuse', 'STACKS_SERVICE_AMBIGUOUS', at, message);
291
- checkServiceForm(service, id, entry, at, kind, add, ambiguous);
292
- if (checkDisabledService(service, id, at, ciUses, ambiguous, add)) return;
293
- if (checkQualityService(service, id, at, { governing, name, ciUses, ciText, add, ambiguous }, checkServiceCi)) return;
294
- checkEnabledService(service, id, at, { governing, name, ciUses, ciText, add, kind }, ambiguous);
359
+ if (entry?.badgeToken !== undefined && !['codecov', 'sonar'].includes(id))
360
+ add('refuse', 'STACKS_BADGE_TOKEN_SERVICE', `${declFile}#services.${id}.badgeToken`, 'badgeToken is allowed only on codecov or sonar');
361
+ if (!Object.hasOwn(SERVICE_CATALOG, id)) {
362
+ add('refuse', 'STACKS_SERVICE_UNKNOWN', `${declFile}#services.${id}`, `${id} is not a catalogued service (${Object.keys(SERVICE_CATALOG).join(', ')}); a service the catalog does not know is one nobody can read unambiguously`);
363
+ return;
364
+ }
365
+ for (const issue of schemaErrors(entry, ['dependabot', 'audit'].includes(id) ? schema.$defs.serviceOptionalCi : schema.$defs.service, `services.${id}`))
366
+ add('refuse', 'STACKS_SCHEMA_INVALID', `${declFile}#${issue.path}`, issue.message);
367
+ const service = normalizeService(id, entry, { declaringRepo: declaration?.repo });
368
+ normalized[id] = service;
369
+ const at = `${declFile}#services.${id}`;
370
+ const ambiguous = (message) => add('refuse', 'STACKS_SERVICE_AMBIGUOUS', at, message);
371
+ checkServiceForm(service, id, entry, at, kind, add, ambiguous);
372
+ if (checkDisabledService(service, id, at, ciUses, ambiguous, add))
373
+ return;
374
+ if (checkQualityService(service, id, at, { governing, name, ciUses, ciText, add, ambiguous }, checkServiceCi))
375
+ return;
376
+ checkEnabledService(service, id, at, { governing, name, ciUses, ciText, add, kind }, ambiguous);
295
377
  };
296
378
  const checkUndeclared = ({ repo, ciUses, normalized, services, declFile, add }) => {
297
- for (const [id, files] of Object.entries(ciUses)) {
298
- if (['codecov', 'dependabot', 'audit'].includes(id)) continue;
299
- if (!files.length || normalized[id] || (services && Object.hasOwn(services, id))) continue;
300
- const viaSource = resolveStackService(repo, id);
301
- if (viaSource && !services) continue;
302
- const followUp = services ? '' : ` - follow-up: ${FOLLOW_UP.op} mode stacks`;
303
- add(services ? 'refuse' : 'suspect', 'STACKS_SERVICE_UNDECLARED', declFile,
304
- `${files.join(', ')} call ${id}, which the declaration does not state${followUp}`);
305
- }
379
+ for (const [id, files] of Object.entries(ciUses)) {
380
+ if (['codecov', 'dependabot', 'audit'].includes(id))
381
+ continue;
382
+ if (!files.length || normalized[id] || (services && Object.hasOwn(services, id)))
383
+ continue;
384
+ const viaSource = resolveStackService(repo, id);
385
+ if (viaSource && !services)
386
+ continue;
387
+ const followUp = services ? '' : ` - follow-up: ${FOLLOW_UP.op} mode stacks`;
388
+ add(services ? 'refuse' : 'suspect', 'STACKS_SERVICE_UNDECLARED', declFile, `${files.join(', ')} call ${id}, which the declaration does not state${followUp}`);
389
+ }
306
390
  };
307
- const checkQualityGate = (sonar, normalized, declFile, add, repo) => { if (!sonar || sonar.mode === 'disabled') { return; } const gateName = loadSonarGate({ cwd: repo }).gate.name; if (sonar.qualityGate !== gateName) { add(normalized.sonar ? 'refuse' : 'suspect', 'STACKS_QUALITY_GATE_DRIFT', `${declFile}#services.sonar.qualityGate`, `services.sonar.qualityGate is ${sonar.qualityGate ?? 'absent'}; every product names the one gate ${gateName} (knowledge/sonar-gate.yaml owns its thresholds)`); } };
391
+ const checkQualityGate = (sonar, normalized, declFile, add, repo) => { if (!sonar || sonar.mode === 'disabled') {
392
+ return;
393
+ } const gateName = loadSonarGate({ cwd: repo }).gate.name; if (sonar.qualityGate !== gateName) {
394
+ add(normalized.sonar ? 'refuse' : 'suspect', 'STACKS_QUALITY_GATE_DRIFT', `${declFile}#services.sonar.qualityGate`, `services.sonar.qualityGate is ${sonar.qualityGate ?? 'absent'}; every product names the one gate ${gateName} (knowledge/sonar-gate.yaml owns its thresholds)`);
395
+ } };
308
396
  // Sonar settings the repository carries must agree with the declaration, and every repository names the one quality gate.
309
397
  const checkSonarDrift = ({ repo, name, normalized, services, declFile, add }) => {
310
- const sonar = normalized.sonar ?? (services ? null : resolveStackService(repo, 'sonar'));
311
- const props = readProperties(path.join(repo, 'sonar-project.properties'));
312
- if (sonar && sonar.mode !== 'disabled' && Object.keys(props).length) {
313
- const declared = sonar.projects.find((project) => project.repository === name)?.key;
314
- if (declared && props['sonar.projectKey'] && props['sonar.projectKey'] !== declared)
315
- add(normalized.sonar ? 'refuse' : 'suspect', 'STACKS_PROJECT_DRIFT', 'sonar-project.properties', `sonar.projectKey ${props['sonar.projectKey']} but the declaration names ${declared}`);
316
- if (sonar.host.public && props['sonar.host.url'] && trimTrailingSlashes(props['sonar.host.url']) !== trimTrailingSlashes(sonar.host.public))
317
- add('suspect', 'STACKS_HOST_DRIFT', 'sonar-project.properties', `sonar.host.url ${props['sonar.host.url']} but the declaration's public host is ${sonar.host.public}`);
318
- }
319
- // The one quality gate (knowledge/sonar-gate.yaml): a repository names it, never its own thresholds.
320
- checkQualityGate(sonar, normalized, declFile, add, repo);
398
+ const sonar = normalized.sonar ?? (services ? null : resolveStackService(repo, 'sonar'));
399
+ const props = readProperties(path.join(repo, 'sonar-project.properties'));
400
+ if (sonar && sonar.mode !== 'disabled' && Object.keys(props).length) {
401
+ const declared = sonar.projects.find((project) => project.repository === name)?.key;
402
+ if (declared && props['sonar.projectKey'] && props['sonar.projectKey'] !== declared)
403
+ add(normalized.sonar ? 'refuse' : 'suspect', 'STACKS_PROJECT_DRIFT', 'sonar-project.properties', `sonar.projectKey ${props['sonar.projectKey']} but the declaration names ${declared}`);
404
+ if (sonar.host.public && props['sonar.host.url'] && trimTrailingSlashes(props['sonar.host.url']) !== trimTrailingSlashes(sonar.host.public))
405
+ add('suspect', 'STACKS_HOST_DRIFT', 'sonar-project.properties', `sonar.host.url ${props['sonar.host.url']} but the declaration's public host is ${sonar.host.public}`);
406
+ }
407
+ // The one quality gate (knowledge/sonar-gate.yaml): a repository names it, never its own thresholds.
408
+ checkQualityGate(sonar, normalized, declFile, add, repo);
321
409
  };
322
410
  const checkTrackedCustody = ({ repo, root, add }) => {
323
- const tracked = git(lsFiles, repo, ['--', root]);
324
- if (tracked?.status !== 0) return;
325
- for (const file of tracked.stdout.split(/\r?\n/).filter(Boolean)) {
326
- const parts = slash(file).split('/');
327
- const custodyArea = parts.length > 3 && (parts[2] === 'runtime' || parts[2] === 'secrets');
328
- const base = parts.at(-1);
329
- // infra/** is tracked source, but a value file inside it (.env*, *.env, *.key, *.pem, *.tfvars)
330
- // is plaintext custody (inc-5360513a96b3: infra/compose/.env.generated was trackable).
331
- const infraValue = parts.length > 3 && parts[2] === 'infra' && isInfrastructureValueFile(base) && !base.endsWith('.enc') && !base.endsWith('.example');
332
- // An app's sealed member is `<env>/secrets/<member>.enc.yaml` (ruling app-secrets-one-standard, scripts/lib/sealed-member.mjs);
333
- // runtime custody keeps `<name>.<fmt>.enc`.
334
- const appMember = parts.length === 4 && parts[2] === 'secrets' && base.endsWith('.enc.yaml');
335
- if ((custodyArea && !base.endsWith('.enc') && !appMember && !['KEYS.md', '.gitkeep'].includes(base)) || infraValue)
336
- add('refuse', 'STACKS_PLAINTEXT_TRACKED', file, /\.enc\.(?:ya?ml|json|env)$/u.test(base)
337
- ? `${base} is named the wrong way round: a custody file is <name>.<fmt>.enc (rename it to ${base.replace(/\.enc\.(ya?ml|json|env)$/u, '.$1.enc')}) and every read states the format (scripts/api/sops/exec-env.mjs)`
338
- : 'a custody member is tracked in plaintext; untrack it (git rm --cached), rotate the value and commit only its .enc twin');
339
- }
411
+ const tracked = git(lsFiles, repo, ['--', root]);
412
+ if (tracked?.status !== 0)
413
+ return;
414
+ for (const file of tracked.stdout.split(/\r?\n/).filter(Boolean)) {
415
+ const parts = slash(file).split('/');
416
+ const custodyArea = parts.length > 3 && (parts[2] === 'runtime' || parts[2] === 'secrets');
417
+ const base = parts.at(-1) ?? '';
418
+ // infra/** is tracked source, but a value file inside it (.env*, *.env, *.key, *.pem, *.tfvars)
419
+ // is plaintext custody (inc-5360513a96b3: infra/compose/.env.generated was trackable).
420
+ const infraValue = parts.length > 3 && parts[2] === 'infra' && isInfrastructureValueFile(base) && !base.endsWith('.enc') && !base.endsWith('.example');
421
+ // An app's sealed member is `<env>/secrets/<member>.enc.yaml` (ruling app-secrets-one-standard, scripts/lib/sealed-member.mjs);
422
+ // runtime custody keeps `<name>.<fmt>.enc`.
423
+ const appMember = parts.length === 4 && parts[2] === 'secrets' && base.endsWith('.enc.yaml');
424
+ if ((custodyArea && !base.endsWith('.enc') && !appMember && !['KEYS.md', '.gitkeep'].includes(base)) || infraValue)
425
+ add('refuse', 'STACKS_PLAINTEXT_TRACKED', file, /\.enc\.(?:ya?ml|json|env)$/u.test(base)
426
+ ? `${base} is named the wrong way round: a custody file is <name>.<fmt>.enc (rename it to ${base.replace(/\.enc\.(ya?ml|json|env)$/u, '.$1.enc')}) and every read states the format (scripts/api/sops/exec-env.mjs)`
427
+ : 'a custody member is tracked in plaintext; untrack it (git rm --cached), rotate the value and commit only its .enc twin');
428
+ }
340
429
  };
341
430
  const checkEncTwins = ({ repo, root, add }) => {
342
- for (const env of envDirs(repo, root)) {
343
- const filesDir = path.join(repo, root, env.name, 'runtime', 'files');
344
- let members = [];
345
- try { members = fs.readdirSync(filesDir); } catch { continue; }
346
- const bare = members.filter((member) => !member.endsWith('.enc') && !/\.md$/i.test(member) && member !== '.gitkeep'
347
- && !/\.(rotation-pending|bak)$/.test(member) && !members.includes(`${member}.enc`));
348
- if (bare.length) add('suspect', 'STACKS_ENC_TWIN_MISSING', `${root}/${env.name}/runtime/files`,
349
- `${bare.length} plaintext custody member(s) have no .enc twin (${bare.slice(0, 5).join(', ')}${bare.length > 5 ? ', ...' : ''}); encrypt them (stack-secret) so another machine can decrypt them`);
350
- }
431
+ for (const env of envDirs(repo, root)) {
432
+ const filesDir = path.join(repo, root, env.name, 'runtime', 'files');
433
+ let members = [];
434
+ try {
435
+ members = fs.readdirSync(filesDir);
436
+ }
437
+ catch {
438
+ continue;
439
+ }
440
+ const bare = members.filter((member) => !member.endsWith('.enc') && !/\.md$/i.test(member) && member !== '.gitkeep'
441
+ && !/\.(rotation-pending|bak)$/.test(member) && !members.includes(`${member}.enc`));
442
+ if (bare.length)
443
+ add('suspect', 'STACKS_ENC_TWIN_MISSING', `${root}/${env.name}/runtime/files`, `${bare.length} plaintext custody member(s) have no .enc twin (${bare.slice(0, 5).join(', ')}${bare.length > 5 ? ', ...' : ''}); encrypt them (stack-secret) so another machine can decrypt them`);
444
+ }
351
445
  };
352
446
  const checkIgnoreRules = ({ repo, root, add }) => {
353
- // The rules must still deny a value file under infra AFTER the infra re-includes
354
- // (stacks-layout.yaml custody.gitignoreRules). git check-ignore --no-index answers for paths
355
- // that do not exist yet, so the probe proves the rule, not today's files.
356
- for (const env of envDirs(repo, root)) {
357
- const open = INFRA_VALUE_PROBES.map((probe) => `${root}/${env.name}/${probe}`)
358
- .filter((rel) => git(checkIgnore, repo, ['-q', '--no-index', '--', rel])?.status === 1);
359
- if (open.length) add('refuse', 'STACKS_GITIGNORE_VALUE_OPEN', '.gitignore',
360
- `the ignore rules leave plaintext value files under ${root}/${env.name}/infra trackable (${open.join(', ')}); deny them after the infra re-includes and re-include *.enc last (modules/schemas/stacks-layout.yaml custody.gitignoreRules)`);
361
- // The environment runbook is part of the layout contract (stacks-layout.yaml shape.runbook);
362
- // rules that hide it strand it on one machine.
363
- const runbook = `${root}/${env.name}/README.md`;
364
- if (git(checkIgnore, repo, ['-q', '--no-index', '--', runbook])?.status === 0)
365
- add('suspect', 'STACKS_RUNBOOK_IGNORED', runbook, `the ignore rules hide the ${env.name} runbook; re-include it (!${runbook}) so every machine reads it`);
366
- }
447
+ // The rules must still deny a value file under infra AFTER the infra re-includes
448
+ // (stacks-layout.yaml custody.gitignoreRules). git check-ignore --no-index answers for paths
449
+ // that do not exist yet, so the probe proves the rule, not today's files.
450
+ for (const env of envDirs(repo, root)) {
451
+ const open = INFRA_VALUE_PROBES.map((probe) => `${root}/${env.name}/${probe}`)
452
+ .filter((rel) => git(checkIgnore, repo, ['-q', '--no-index', '--', rel])?.status === 1);
453
+ if (open.length)
454
+ add('refuse', 'STACKS_GITIGNORE_VALUE_OPEN', '.gitignore', `the ignore rules leave plaintext value files under ${root}/${env.name}/infra trackable (${open.join(', ')}); deny them after the infra re-includes and re-include *.enc last (modules/schemas/stacks-layout.yaml custody.gitignoreRules)`);
455
+ // The environment runbook is part of the layout contract (stacks-layout.yaml shape.runbook);
456
+ // rules that hide it strand it on one machine.
457
+ const runbook = `${root}/${env.name}/README.md`;
458
+ if (git(checkIgnore, repo, ['-q', '--no-index', '--', runbook])?.status === 0)
459
+ add('suspect', 'STACKS_RUNBOOK_IGNORED', runbook, `the ignore rules hide the ${env.name} runbook; re-include it (!${runbook}) so every machine reads it`);
460
+ }
367
461
  };
368
462
  // Custody layout (modules/schemas/stacks-layout.yaml custody, the product repositories' secrets-guard).
369
463
  // The images folder holds the managed db image of a lite app (slot app.db.image), never custody or an environment.
370
464
  const envDirs = (repo, root) => fs.readdirSync(path.join(repo, root), { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name !== 'images');
371
465
  const checkCustodyLayout = ({ repo, own, add }) => {
372
- if (!own.rooted) return;
373
- const root = STACK_ROOT;
374
- checkTrackedCustody({ repo, root, add });
375
- checkEncTwins({ repo, root, add });
376
- const ignore = readText(path.join(repo, '.gitignore')) ?? '';
377
- if (!new RegExp(String.raw`^/?${escapeRegExp(root)}/\*\*\s*$`, 'm').test(ignore))
378
- add('suspect', 'STACKS_GITIGNORE_OPEN', '.gitignore', `no deny-all rule \`${root}/**\`; custody plaintext is only safe when the tree is denied and *.enc, KEYS.md and the declaration are re-included`);
379
- checkIgnoreRules({ repo, root, add });
466
+ if (!own.rooted)
467
+ return;
468
+ const root = STACK_ROOT;
469
+ checkTrackedCustody({ repo, root, add });
470
+ checkEncTwins({ repo, root, add });
471
+ const ignore = readText(path.join(repo, '.gitignore')) ?? '';
472
+ if (!new RegExp(String.raw `^/?${escapeRegExp(root)}/\*\*\s*$`, 'm').test(ignore))
473
+ add('suspect', 'STACKS_GITIGNORE_OPEN', '.gitignore', `no deny-all rule \`${root}/**\`; custody plaintext is only safe when the tree is denied and *.enc, KEYS.md and the declaration are re-included`);
474
+ checkIgnoreRules({ repo, root, add });
380
475
  };
381
476
  const buildResult = ({ repo, name, newRepo, declaration, governing, normalized, findings }) => {
382
- const pick = (level) => findings.filter((finding) => finding.level === level).map((finding) => `${finding.file}: ${finding.message} [${finding.code}]`);
383
- const refused = pick('refuse');
384
- const needsFollowUp = findings.some((finding) => ['STACKS_DECLARATION_MISSING', 'STACKS_SERVICES_MISSING', 'STACKS_SERVICE_UNDECLARED'].includes(finding.code) && finding.level !== 'refuse');
385
- return {
386
- schema: RESULT_SCHEMA, ok: refused.length === 0, repository: name, repoRoot: slash(repo), newRepo,
387
- declaration: declaration?.file ? slash(declaration.file) : null, root: declaration?.root ?? null,
388
- governedBy: governing ? slash(governing.repo) : null,
389
- services: Object.fromEntries(Object.entries(normalized).map(([id, service]) => [id, summary(service)])),
390
- refused, suspect: pick('suspect'), findings,
391
- ...(needsFollowUp ? { followUp: { ...FOLLOW_UP, ...(isFile(path.join(skillRoot, STACK_DECLARATION_TEMPLATE)) ? { fixture: STACK_DECLARATION_TEMPLATE } : {}) } } : {}),
392
- };
477
+ const pick = (level) => findings.filter((finding) => finding.level === level).map((finding) => `${finding.file}: ${finding.message} [${finding.code}]`);
478
+ const refused = pick('refuse');
479
+ const needsFollowUp = findings.some((finding) => ['STACKS_DECLARATION_MISSING', 'STACKS_SERVICES_MISSING', 'STACKS_SERVICE_UNDECLARED'].includes(finding.code) && finding.level !== 'refuse');
480
+ return {
481
+ schema: RESULT_SCHEMA, ok: refused.length === 0, repository: name, repoRoot: slash(repo), newRepo,
482
+ declaration: declaration?.file ? slash(declaration.file) : null, root: declaration?.root ?? null,
483
+ governedBy: governing ? slash(governing.repo) : null,
484
+ services: Object.fromEntries(Object.entries(normalized).map(([id, service]) => [id, summary(service)])),
485
+ refused, suspect: pick('suspect'), findings,
486
+ ...(needsFollowUp ? { followUp: { ...FOLLOW_UP, ...(isFile(path.join(skillRoot, STACK_DECLARATION_TEMPLATE)) ? { fixture: STACK_DECLARATION_TEMPLATE } : {}) } } : {}),
487
+ };
393
488
  };
394
489
  const declarationView = (own, governing, missingLevel, shown, add) => {
395
- const declaration = own.missing ? governing : own;
396
- if (own.missing && !governing) add(missingLevel, 'STACKS_DECLARATION_MISSING', `.starcistacks/${DECLARATION}`, `no stack declaration (.starcistacks/${DECLARATION}); the services this repository's CI uses are undeclared - follow-up: ${FOLLOW_UP.op} mode stacks`);
397
- if (declaration?.error) add('refuse', 'STACKS_DECLARATION_INVALID', shown(declaration.file), declaration.error);
398
- if (declaration?.doc && declaration.doc.schema !== DECLARATION_SCHEMA) add('refuse', 'STACKS_DECLARATION_INVALID', shown(declaration.file), `schema must be ${DECLARATION_SCHEMA}`);
399
- const doc = declaration?.doc ?? null, services = plain(doc?.services) ? doc.services : null;
400
- const declFile = declaration?.file ? shown(declaration.file) : `.starcistacks/${DECLARATION}`;
401
- if (doc && !Object.hasOwn(doc, 'services')) add(missingLevel, 'STACKS_SERVICES_MISSING', declFile, `the declaration has no services block; declare sonar and every other delivery/quality service the repository uses - follow-up: ${FOLLOW_UP.op} mode stacks`);
402
- return { declaration, services, declFile };
490
+ const declaration = own.missing ? governing : own;
491
+ if (own.missing && !governing)
492
+ add(missingLevel, 'STACKS_DECLARATION_MISSING', `.starcistacks/${DECLARATION}`, `no stack declaration (.starcistacks/${DECLARATION}); the services this repository's CI uses are undeclared - follow-up: ${FOLLOW_UP.op} mode stacks`);
493
+ if (declaration?.error)
494
+ add('refuse', 'STACKS_DECLARATION_INVALID', shown(declaration.file ?? ''), declaration.error);
495
+ if (declaration?.doc && declaration.doc.schema !== DECLARATION_SCHEMA)
496
+ add('refuse', 'STACKS_DECLARATION_INVALID', shown(declaration.file ?? ''), `schema must be ${DECLARATION_SCHEMA}`);
497
+ const doc = declaration?.doc ?? null, services = plain(doc?.services) ? doc.services : null;
498
+ const declFile = declaration?.file ? shown(declaration.file ?? '') : `.starcistacks/${DECLARATION}`;
499
+ if (doc && !Object.hasOwn(doc, 'services'))
500
+ add(missingLevel, 'STACKS_SERVICES_MISSING', declFile, `the declaration has no services block; declare sonar and every other delivery/quality service the repository uses - follow-up: ${FOLLOW_UP.op} mode stacks`);
501
+ return { declaration, services, declFile };
403
502
  };
404
503
  const checkDeclaredServices = ({ services, declaration, governing, name, ciUses, ciText, declFile, normalized, add, kind }) => {
405
- if (!services) return;
406
- const schema = declarationSchema(), ctx = { schema, declFile, declaration, governing, name, ciUses, ciText, normalized, add, kind };
407
- for (const [id, entry] of Object.entries(services)) checkService(id, entry, ctx);
504
+ if (!services)
505
+ return;
506
+ const schema = declarationSchema(), ctx = { schema, declFile, declaration, governing, name, ciUses, ciText, normalized, add, kind };
507
+ for (const [id, entry] of Object.entries(services))
508
+ checkService(id, entry, ctx);
408
509
  };
409
510
  const checkDeclarationIgnore = (own, repo, shown, add) => {
410
- if (!own.missing && own.file) {
411
- const ignored = git(checkIgnore, repo, ['-q', '--', slash(path.relative(repo, own.file))]);
412
- if (ignored?.status === 0) add('suspect', 'STACKS_DECLARATION_IGNORED', shown(own.file), `the ignore rules hide the declaration; re-include it (!${slash(path.relative(repo, own.file))}) so every machine reads the same services`);
413
- }
511
+ if (!own.missing && own.file) {
512
+ const ignored = git(checkIgnore, repo, ['-q', '--', slash(path.relative(repo, own.file))]);
513
+ if (ignored?.status === 0)
514
+ add('suspect', 'STACKS_DECLARATION_IGNORED', shown(own.file), `the ignore rules hide the declaration; re-include it (!${slash(path.relative(repo, own.file))}) so every machine reads the same services`);
515
+ }
414
516
  };
415
517
  /**
416
518
  * The whole check for one repository. `newRepo`: the leg creates this repository, so what it lacks is
417
519
  * refused under the current declaration and custody rules.
418
520
  */
419
521
  export function checkStarciStacks(repoRoot, { newRepo = false, examplesRoot = path.join(skillRoot, 'examples') } = {}) {
420
- const repo = path.resolve(String(repoRoot ?? ''));
421
- const name = repositoryName(repo);
422
- const findings = [];
423
- const add = (level, code, file, message) => findings.push({ level, code, file: slash(file), message });
424
- const missingLevel = newRepo ? 'refuse' : 'suspect', own = findStackDeclaration(repo);
425
- const governing = own.missing ? governingDeclaration(repo) : null;
426
- const shown = (file) => slash(path.relative(repo, file)) || '.';
427
- const { declaration, services, declFile } = declarationView(own, governing, missingLevel, shown, add);
428
- checkCiDeclaration(declaration, declFile, kindOf(repo, examplesRoot), declarationSchema, schemaErrors, add);
429
- // CI evidence: which services the workflows call, and which secret/variable names they read.
430
- const workflows = workflowTexts(repo);
431
- const ciText = workflows.map((workflow) => workflow.text).join('\n');
432
- const ciUses = Object.fromEntries(Object.entries(SERVICE_CATALOG).map(([id, entry]) => [id, workflows.filter((workflow) => entry.ci.some((re) => re.test(workflow.text))).map((workflow) => workflow.file)]));
433
- const normalized = {};
434
- checkDeclaredServices({ services, declaration, governing, name, ciUses, ciText, declFile, normalized, add, kind: kindOf(repo, examplesRoot) });
435
- checkUndeclared({ repo, ciUses, normalized, services, declFile, add });
436
- checkSonarDrift({ repo, name, normalized, services, declFile, add });
437
- checkCustodyLayout({ repo, own, add });
438
- checkDeclarationIgnore(own, repo, shown, add);
439
- for (const item of applicationStackFindings(repo)) add('refuse', item.code, item.path, item.message);
440
- return buildResult({ repo, name, newRepo, declaration, governing, normalized, findings });
522
+ const repo = path.resolve(String(repoRoot ?? ''));
523
+ const name = repositoryName(repo);
524
+ const findings = [];
525
+ const add = (level, code, file, message) => findings.push({ level, code, file: slash(file), message });
526
+ const missingLevel = newRepo ? 'refuse' : 'suspect', own = findStackDeclaration(repo);
527
+ const governing = own.missing ? governingDeclaration(repo) : null;
528
+ const shown = (file) => slash(path.relative(repo, file)) || '.';
529
+ const { declaration, services, declFile } = declarationView(own, governing, missingLevel, shown, add);
530
+ checkCiDeclaration(declaration, declFile, kindOf(repo, examplesRoot), declarationSchema, schemaErrors, add);
531
+ // CI evidence: which services the workflows call, and which secret/variable names they read.
532
+ const workflows = workflowTexts(repo);
533
+ const ciText = workflows.map((workflow) => workflow.text).join('\n');
534
+ const ciUses = Object.fromEntries(Object.entries(SERVICE_CATALOG).map(([id, entry]) => [id, workflows.filter((workflow) => entry.ci.some((re) => re.test(workflow.text))).map((workflow) => workflow.file)]));
535
+ const normalized = {};
536
+ checkDeclaredServices({ services, declaration, governing, name, ciUses, ciText, declFile, normalized, add, kind: kindOf(repo, examplesRoot) });
537
+ checkUndeclared({ repo, ciUses, normalized, services, declFile, add });
538
+ checkSonarDrift({ repo, name, normalized, services, declFile, add });
539
+ checkCustodyLayout({ repo, own, add });
540
+ checkDeclarationIgnore(own, repo, shown, add);
541
+ for (const item of applicationStackFindings(repo))
542
+ add('refuse', item.code, item.path, item.message);
543
+ return buildResult({ repo, name, newRepo, declaration, governing, normalized, findings });
441
544
  }
442
545
  /** A report-safe view: custody as presence, never a value. */
443
546
  function summary(service) {
444
- return { provider: service.provider, mode: service.mode, auth: service.auth, host: service.host,
445
- stack: service.stack ? { owner: service.stack.owner ?? null, repository: service.stack.repository, root: service.stack.root, environment: service.stack.environment, compose: service.stack.compose, container: service.stack.container } : null,
446
- projects: service.projects, credentials: service.credentials.map((credential) => ({ id: credential.id, env: credential.env, key: credential.key,
447
- custody: `${credential.custody.repository}/${credential.custody.path}`, present: credential.custody.encPresent ?? null })),
448
- ci: service.ci, ownerAction: service.ownerAction };
547
+ return { provider: service.provider, mode: service.mode, auth: service.auth, host: service.host,
548
+ stack: service.stack ? { owner: service.stack.owner ?? null, repository: service.stack.repository, root: service.stack.root, environment: service.stack.environment, compose: service.stack.compose, container: service.stack.container } : null,
549
+ projects: service.projects, credentials: service.credentials.map((credential) => ({ id: credential.id, env: credential.env, key: credential.key,
550
+ custody: `${credential.custody.repository}/${credential.custody.path}`, present: credential.custody.encPresent ?? null })),
551
+ ci: service.ci, ownerAction: service.ownerAction };
449
552
  }
450
553
  // ---- the owner-ask guard --------------------------------------------------------------------------------
451
554
  const asksAboutCustody = (body) => /\b(tokens?|secrets?|credentials?|api[ _-]?keys?|passwords?|host|url|access)\b/i.test(body) || /\b(github|repository) (settings?|secrets?|variables?)\b|\bSONAR_[A-Z_]+/i.test(body);
@@ -457,47 +560,59 @@ const asksAboutCustody = (body) => /\b(tokens?|secrets?|credentials?|api[ _-]?ke
457
560
  * when its entry lists this repository.
458
561
  */
459
562
  export function ownerAskConflict({ repo, question } = {}) {
460
- if (!repo || question === undefined || question === null) return null;
461
- const body = typeof question === 'string' ? question : JSON.stringify(question);
462
- for (const id of Object.keys(SERVICE_CATALOG)) {
463
- const service = resolveStackService(repo, id);
464
- if (!service || service.mode === 'disabled' || service.ownerAction !== 'none' || !custodyHeld(service)) continue;
465
- const names = [...service.credentials.map((credential) => credential.env), ...service.ci.secrets.map((item) => item.name), ...service.ci.vars.map((item) => item.name)].filter(Boolean);
466
- const byName = names.find((item) => new RegExp(String.raw`\b${escapeRegExp(item)}\b`).test(body));
467
- const words = [...new Set([id, service.provider, ...SERVICE_CATALOG[id].words].filter(Boolean))];
468
- const byWord = words.find((word) => new RegExp(String.raw`\b${escapeRegExp(word)}\b`, 'i').test(body));
469
- if (!byName && !(byWord && asksAboutCustody(body))) continue;
470
- const custody = service.credentials.map((credential) => `${credential.custody.repository}/${credential.custody.path}`);
471
- const custodyNote = custody.length ? ` and its credentials in custody (${custody.join(', ')})` : ` and auth ${service.auth}`;
472
- const sonarNote = id === 'sonar' ? 'Run starci gate sonar (status, ensure-project, scan) - it reads host, project and token from the declaration and custody. ' : '';
473
- const provisioningNote = service.ci.provisioning ? ` (${service.ci.provisioning})` : '';
474
- return { service: id, provider: service.provider, declaration: slash(service.declaration), matched: byName ?? byWord,
475
- message: `${id} (${service.provider}, ${service.mode}) is declared in ${slash(service.declaration)} with ownerAction none${custodyNote}; the owner is never asked for it. ${sonarNote}CI wiring is ${service.ci.wiring ?? 'undeclared'}${provisioningNote}.` };
476
- }
477
- return null;
563
+ if (!repo || question === undefined || question === null)
564
+ return null;
565
+ const body = typeof question === 'string' ? question : JSON.stringify(question);
566
+ for (const id of Object.keys(SERVICE_CATALOG)) {
567
+ const service = resolveStackService(repo, id);
568
+ if (!service || service.mode === 'disabled' || service.ownerAction !== 'none' || !custodyHeld(service))
569
+ continue;
570
+ const names = [...service.credentials.map((credential) => credential.env), ...service.ci.secrets.map((item) => item.name), ...service.ci.vars.map((item) => item.name)].filter(Boolean);
571
+ const byName = names.find((item) => new RegExp(String.raw `\b${escapeRegExp(item)}\b`).test(body));
572
+ const words = [...new Set([id, service.provider, ...catalogOf(id).words].filter(Boolean))];
573
+ const byWord = words.find((word) => new RegExp(String.raw `\b${escapeRegExp(word)}\b`, 'i').test(body));
574
+ if (!byName && !(byWord && asksAboutCustody(body)))
575
+ continue;
576
+ const custody = service.credentials.map((credential) => `${credential.custody.repository}/${credential.custody.path}`);
577
+ const custodyNote = custody.length ? ` and its credentials in custody (${custody.join(', ')})` : ` and auth ${service.auth}`;
578
+ const sonarNote = id === 'sonar' ? 'Run starci gate sonar (status, ensure-project, scan) - it reads host, project and token from the declaration and custody. ' : '';
579
+ const provisioningNote = service.ci.provisioning ? ` (${service.ci.provisioning})` : '';
580
+ return { service: id, provider: service.provider, declaration: slash(service.declaration), matched: byName ?? byWord,
581
+ message: `${id} (${service.provider}, ${service.mode}) is declared in ${slash(service.declaration)} with ownerAction none${custodyNote}; the owner is never asked for it. ${sonarNote}CI wiring is ${service.ci.wiring ?? 'undeclared'}${provisioningNote}.` };
582
+ }
583
+ return null;
478
584
  }
479
585
  // ---- CLI ------------------------------------------------------------------------------------------------
480
586
  const USAGE = 'Usage: starci gate starcistacks <repo-root> [--new] [--json]\n\nHolds a repository\'s stack declaration services block (sonar, codecov, dependabot, audit, container-registry, analytics, error-tracking) and its custody layout to modules/schemas/application-stacks.schema.yaml and stacks-layout.yaml. Exit 0 clean (suspects allowed), 1 refused, 2 usage.\n';
481
587
  async function checkStarciStacksMain(argv = []) {
482
- const rest = argv;
483
- const args = rest.filter((arg) => !['--json', '--new'].includes(arg));
484
- if (args.includes('--help') || args.includes('-h')) return { exitCode: 0, text: USAGE };
485
- if (args.length !== 1) return { exitCode: 2, text: USAGE };
486
- if (!isDir(args[0])) return { exitCode: 2, text: `${args[0]}: not a directory\n` };
487
- const result = checkStarciStacks(args[0], { newRepo: rest.includes('--new') });
488
- if (rest.includes('--json')) return { exitCode: result.ok ? 0 : 1, text: `${JSON.stringify(result, null, 2)}\n` };
489
- const lines = [...result.refused.map((line) => ` REFUSED ${line}`), ...result.suspect.map((line) => ` SUSPECT ${line}`)];
490
- const fixtureNote = result.followUp?.fixture ? ` from ${result.followUp.fixture}` : '';
491
- if (result.followUp) lines.push(` follow-up: ${result.followUp.op} mode ${result.followUp.params.mode}${fixtureNote}`);
492
- const listing = `${lines.join('\n')}${lines.length ? '\n' : ''}`;
493
- const verdict = `${result.ok ? 'OK' : 'FAIL'}: starcistacks ${result.repository} - ${result.refused.length} refused, ${result.suspect.length} suspect, services: ${Object.keys(result.services).join(', ') || 'none declared'}.\n`;
494
- return { exitCode: result.ok ? 0 : 1, text: `${listing}${verdict}` };
588
+ const rest = argv;
589
+ const args = rest.filter((arg) => !['--json', '--new'].includes(arg));
590
+ if (args.includes('--help') || args.includes('-h'))
591
+ return { exitCode: 0, text: USAGE };
592
+ if (args.length !== 1)
593
+ return { exitCode: 2, text: USAGE };
594
+ const [dir = ''] = args;
595
+ if (!isDir(dir))
596
+ return { exitCode: 2, text: `${dir}: not a directory\n` };
597
+ const result = checkStarciStacks(dir, { newRepo: rest.includes('--new') });
598
+ if (rest.includes('--json'))
599
+ return { exitCode: result.ok ? 0 : 1, text: `${JSON.stringify(result, null, 2)}\n` };
600
+ const lines = [...result.refused.map((line) => ` REFUSED ${line}`), ...result.suspect.map((line) => ` SUSPECT ${line}`)];
601
+ const fixtureNote = result.followUp?.fixture ? ` from ${result.followUp.fixture}` : '';
602
+ if (result.followUp)
603
+ lines.push(` follow-up: ${result.followUp.op} mode ${result.followUp.params.mode}${fixtureNote}`);
604
+ const listing = `${lines.join('\n')}${lines.length ? '\n' : ''}`;
605
+ const verdict = `${result.ok ? 'OK' : 'FAIL'}: starcistacks ${result.repository} - ${result.refused.length} refused, ${result.suspect.length} suspect, services: ${Object.keys(result.services).join(', ') || 'none declared'}.\n`;
606
+ return { exitCode: result.ok ? 0 : 1, text: `${listing}${verdict}` };
495
607
  }
496
608
  if (isMain(import.meta.url)) {
497
- try {
498
- const { exitCode, text: out } = await checkStarciStacksMain(process.argv.slice(2));
499
- process.stdout.write(out); process.exitCode = exitCode;
500
- } catch (error) {
501
- process.stderr.write(`check-starcistacks: ${error?.stack ?? error}\n`); process.exitCode = 2;
502
- }
609
+ try {
610
+ const { exitCode, text: out } = await checkStarciStacksMain(process.argv.slice(2));
611
+ process.stdout.write(out);
612
+ process.exitCode = exitCode;
613
+ }
614
+ catch (error) {
615
+ process.stderr.write(`check-starcistacks: ${error instanceof Error ? (error.stack ?? error) : error}\n`);
616
+ process.exitCode = 2;
617
+ }
503
618
  }