@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
@@ -2,10 +2,10 @@ import { composeEntries } from '../lib/compose-values.mjs';
2
2
  import fs from 'node:fs';
3
3
  import path from 'node:path';
4
4
  import { lsFiles } from '../api/git/ls-files.mjs';
5
- import {parseYaml} from '../../engine/yaml.mjs';
5
+ import { parseYaml } from '../../engine/yaml.mjs';
6
6
  import { validateAgainstSchema } from '../lib/json-schema.mjs';
7
7
  import { PROVISION_OPERATION_SCHEMA_REF, provisionOperationSchema } from '../lib/provision-operation-schema.mjs';
8
- import {slash} from '../lib/path-key.mjs';
8
+ import { slash } from '../lib/path-key.mjs';
9
9
  import { envelopeOf } from './secret-document.mjs';
10
10
  import { isInside, walkFiles } from '../lib/walk.mjs';
11
11
  import { composeIncludePaths, swarmInventoryIssues, swarmModelIssues } from '../lib/swarm-stack.mjs';
@@ -16,441 +16,601 @@ import { releaseBackupIssues, scheduleIssues } from './rules/stacks-release.mjs'
16
16
  import { stacksDeploymentContexts } from './rules/stacks-runtime.mjs';
17
17
  import { readJson } from './rules/read.mjs';
18
18
  import { applicationStackFindings, liteCustodyDirectory } from './starcistacks-runtime.mjs';
19
-
20
- const RESULT='starci/application-stacks-check@1';
21
- const STACKS_DIR='.starcistacks';
22
- const MAX_INPUT_BYTES=4*1024*1024;
23
- const SCHEMA_ID='starci/application-stacks@1';
24
- const RUNBOOK_COMMANDS=['prepare','doctor','up','status','logs','down','verification'];
25
- const PLAINTEXT_SECRET_PATTERN=/\.(key|pem|p12|pfx)$/i;
26
- const inside=(root,target)=>isInside(path.resolve(root),path.resolve(target));
27
- const object=value=>value&&typeof value==='object'&&!Array.isArray(value);
28
- const sensitive=/pass(word)?|secret|token|credential|private[_-]?key|api[_-]?key/i;
29
- const nonempty=value=>typeof value==='string'&&Boolean(value.trim());
30
- const manifestSchema=parseYaml(fs.readFileSync(new URL('../../modules/schemas/application-stacks.schema.yaml',import.meta.url),'utf8'));
31
- const HARDENING_CODES=new Set([
32
- 'STACKS_SECRET_NOT_EXTERNAL','STACKS_SECRET_UNVERSIONED','STACKS_SECRET_ENV_LITERAL','STACKS_PORT_PUBLISHED',
33
- 'STACKS_SECOND_INGRESS','STACKS_DNS_UNDECLARED','STACKS_HOST_UNROUTED','STACKS_TLS_UNDECLARED',
34
- 'STACKS_PLACEMENT_MISSING','STACKS_HEALTHCHECK_MISSING','STACKS_UPDATE_POLICY_MISSING','STACKS_REALTIME_SCALED',
35
- 'STACKS_RELEASE_UNDECLARED','BACKUP_UNDECLARED','BACKUP_PROOF_MISSING','STACKS_MODEL_UNAVAILABLE',
36
- 'HFS_SCHEDULE_UNDECLARED','STACKS_INGRESS_UNDECLARED','STACKS_INGRESS_UNKNOWN',
37
- 'STACKS_LITE_SELFHOSTED_DB','STACKS_SIDECAR_VOLUME_UNDECLARED','STACKS_SIDECAR_DB_UNSUPPORTED','STACKS_LITE_REDIS_PERSISTENCE',
19
+ const RESULT = 'starci/application-stacks-check@1';
20
+ const STACKS_DIR = '.starcistacks';
21
+ const MAX_INPUT_BYTES = 4 * 1024 * 1024;
22
+ const SCHEMA_ID = 'starci/application-stacks@1';
23
+ const RUNBOOK_COMMANDS = ['prepare', 'doctor', 'up', 'status', 'logs', 'down', 'verification'];
24
+ const PLAINTEXT_SECRET_PATTERN = /\.(key|pem|p12|pfx)$/i;
25
+ const inside = (root, target) => isInside(path.resolve(root), path.resolve(target));
26
+ const object = (value) => value && typeof value === 'object' && !Array.isArray(value);
27
+ const sensitive = /pass(word)?|secret|token|credential|private[_-]?key|api[_-]?key/i;
28
+ const nonempty = (value) => typeof value === 'string' && Boolean(value.trim());
29
+ const manifestSchema = parseYaml(fs.readFileSync(new URL('../../modules/schemas/application-stacks.schema.yaml', import.meta.url), 'utf8'));
30
+ const HARDENING_CODES = new Set([
31
+ 'STACKS_SECRET_NOT_EXTERNAL', 'STACKS_SECRET_UNVERSIONED', 'STACKS_SECRET_ENV_LITERAL', 'STACKS_PORT_PUBLISHED',
32
+ 'STACKS_SECOND_INGRESS', 'STACKS_DNS_UNDECLARED', 'STACKS_HOST_UNROUTED', 'STACKS_TLS_UNDECLARED',
33
+ 'STACKS_PLACEMENT_MISSING', 'STACKS_HEALTHCHECK_MISSING', 'STACKS_UPDATE_POLICY_MISSING', 'STACKS_REALTIME_SCALED',
34
+ 'STACKS_RELEASE_UNDECLARED', 'BACKUP_UNDECLARED', 'BACKUP_PROOF_MISSING', 'STACKS_MODEL_UNAVAILABLE',
35
+ 'HFS_SCHEDULE_UNDECLARED', 'STACKS_INGRESS_UNDECLARED', 'STACKS_INGRESS_UNKNOWN',
36
+ 'STACKS_LITE_SELFHOSTED_DB', 'STACKS_SIDECAR_VOLUME_UNDECLARED', 'STACKS_SIDECAR_DB_UNSUPPORTED', 'STACKS_LITE_REDIS_PERSISTENCE',
38
37
  ]);
39
-
40
- function regular(file,{root=null}={}){
41
- try{
42
- const stat=fs.lstatSync(file);if(!stat.isFile()||stat.isSymbolicLink())return false;
43
- return !root||inside(root,fs.realpathSync(file));
44
- }catch{return false;}
45
- }
46
- function directory(file){try{return fs.lstatSync(file).isDirectory()&&!fs.lstatSync(file).isSymbolicLink();}catch{return false;}}
47
- function safeAncestors(root,target){
48
- if(!inside(root,target))return false;
49
- let cursor=path.resolve(target),base=path.resolve(root);
50
- while(cursor!==base){
51
- if(fs.existsSync(cursor)&&fs.lstatSync(cursor).isSymbolicLink())return false;
52
- cursor=path.dirname(cursor);
53
- }
54
- return true;
55
- }
56
- function resolvedPath(base,relative){
57
- if(!nonempty(relative)||path.isAbsolute(relative)||String(relative).split(/[\\/]/).includes('..'))return null;
58
- const target=path.resolve(base,String(relative));return inside(base,target)&&safeAncestors(base,target)?target:null;
59
- }
60
-
61
- function validateSchema(value){
62
- const errors=[],isObject=value=>value!==null&&typeof value==='object'&&!Array.isArray(value),resolve=ref=>ref.slice(2).split('/').reduce((node,key)=>node?.[key],manifestSchema);
63
- const typeOk=(node,type)=>{
64
- if(type==='object')return isObject(node);
65
- if(type==='array')return Array.isArray(node);
66
- if(type==='integer')return Number.isInteger(node);
67
- if(type)return typeof node===type;
68
- return true;
69
- };
70
- const checkString=(node,shape,at)=>{
71
- if(shape.minLength&&node.length<shape.minLength)errors.push({path:at,message:'string too short'});
72
- if(shape.pattern&&!new RegExp(shape.pattern,'u').test(node))errors.push({path:at,message:'pattern mismatch'});
73
- };
74
- const checkNumber=(node,shape,at)=>{
75
- if(shape.minimum!==undefined&&node<shape.minimum)errors.push({path:at,message:'number below minimum'});
76
- if(shape.maximum!==undefined&&node>shape.maximum)errors.push({path:at,message:'number above maximum'});
77
- };
78
- const checkObject=(node,shape,at)=>{
79
- if(shape.minProperties&&Object.keys(node).length<shape.minProperties)errors.push({path:at,message:'object has too few properties'});
80
- for(const key of shape.required??[])if(!Object.hasOwn(node,key))errors.push({path:at,message:`missing ${key}`});
81
- for(const [key,child] of Object.entries(node)){
82
- if(Object.hasOwn(shape.properties??{},key))check(child,shape.properties[key],`${at}/${key}`);
83
- else if(shape.additionalProperties===false)errors.push({path:`${at}/${key}`,message:'unknown field'});
84
- else if(isObject(shape.additionalProperties))check(child,shape.additionalProperties,`${at}/${key}`);
38
+ function regular(file, { root = null } = {}) {
39
+ try {
40
+ const stat = fs.lstatSync(file);
41
+ if (!stat.isFile() || stat.isSymbolicLink())
42
+ return false;
43
+ return !root || inside(root, fs.realpathSync(file));
85
44
  }
86
- };
87
- const checkAlternatives=(node,shape,at,check)=>{
88
- const matched=shape.anyOf.some((option)=>{
89
- const start=errors.length;check(node,option,at);
90
- if(errors.length===start)return true;
91
- errors.splice(start);return false;
92
- });
93
- if(!matched)errors.push({path:at,message:'matches none of the allowed shapes'});
94
- };
95
- const checkArray=(node,shape,at,check)=>{
96
- if(shape.minItems&&node.length<shape.minItems)errors.push({path:at,message:'array too short'});
97
- node.forEach((item,index)=>shape.items&&check(item,shape.items,`${at}/${index}`));
98
- };
99
- // The operations block refers to its own schema file: its owner's composed schema validates it, as the provision target owner does.
100
- const checkOperations=(node,at)=>{
101
- for(const error of validateAgainstSchema(node,provisionOperationSchema()))errors.push({path:at,message:error});
102
- };
103
- const check=(node,shape,at)=>{
104
- if(shape?.$ref===PROVISION_OPERATION_SCHEMA_REF)return checkOperations(node,at);
105
- if(shape?.$ref&&!shape.$ref.startsWith('#/')){errors.push({path:at,message:`unresolved schema reference ${shape.$ref}`});return;}
106
- if(shape?.$ref)return check(node,resolve(shape.$ref),at);
107
- if(Array.isArray(shape?.anyOf)){
108
- checkAlternatives(node,shape,at,check);
109
- return;
45
+ catch {
46
+ return false;
110
47
  }
111
- const typed=typeOk(node,shape?.type);
112
- if(!typed){errors.push({path:at,message:`expected ${shape.type}`});return;}
113
- if(Object.hasOwn(shape??{},'const')&&node!==shape.const)errors.push({path:at,message:'const mismatch'});
114
- if(Array.isArray(shape?.enum)&&!shape.enum.includes(node))errors.push({path:at,message:'value outside enum'});
115
- if(typeof node==='string')checkString(node,shape,at);
116
- if(typeof node==='number')checkNumber(node,shape,at);
117
- if(Array.isArray(node)){
118
- checkArray(node,shape,at,check);
119
- return;
48
+ }
49
+ function directory(file) { try {
50
+ return fs.lstatSync(file).isDirectory() && !fs.lstatSync(file).isSymbolicLink();
51
+ }
52
+ catch {
53
+ return false;
54
+ } }
55
+ function safeAncestors(root, target) {
56
+ if (!inside(root, target))
57
+ return false;
58
+ let cursor = path.resolve(target), base = path.resolve(root);
59
+ while (cursor !== base) {
60
+ if (fs.existsSync(cursor) && fs.lstatSync(cursor).isSymbolicLink())
61
+ return false;
62
+ cursor = path.dirname(cursor);
120
63
  }
121
- if(!isObject(node))return;
122
- checkObject(node,shape,at);
123
- };check(value,manifestSchema,'$');return errors;
64
+ return true;
65
+ }
66
+ function resolvedPath(base, relative) {
67
+ if (!nonempty(relative) || path.isAbsolute(relative) || String(relative).split(/[\\/]/).includes('..'))
68
+ return null;
69
+ const target = path.resolve(base, String(relative));
70
+ return inside(base, target) && safeAncestors(base, target) ? target : null;
124
71
  }
125
- const unresolved=value=>typeof value==='string'&&/(^|[^$])\$\{/.test(value);
126
- const serviceUnresolved=(service)=>{
127
- const build=object(service?.build)?service.build:{context:service?.build};
128
- const values=[service?.image,build.context,build.dockerfile,...(Array.isArray(service?.ports)?service.ports:[])];
129
- for(const volume of Array.isArray(service?.volumes)?service.volumes:[])values.push(object(volume)?volume.source:String(volume).split(':')[0]);
130
- for(const [key,value] of composeEntries(service?.environment, { isMapping: object }))if(!sensitive.test(key))values.push(value);
131
- return values.some(unresolved);
72
+ function validateSchema(value) {
73
+ const errors = [], isObject = (value) => value !== null && typeof value === 'object' && !Array.isArray(value), resolve = (ref) => ref.slice(2).split('/').reduce((node, key) => node?.[key], manifestSchema);
74
+ const typeOk = (node, type) => {
75
+ if (type === 'object')
76
+ return isObject(node);
77
+ if (type === 'array')
78
+ return Array.isArray(node);
79
+ if (type === 'integer')
80
+ return Number.isInteger(node);
81
+ if (type)
82
+ return typeof node === type;
83
+ return true;
84
+ };
85
+ const checkString = (node, shape, at) => {
86
+ if (shape.minLength && node.length < shape.minLength)
87
+ errors.push({ path: at, message: 'string too short' });
88
+ if (shape.pattern && !new RegExp(shape.pattern, 'u').test(node))
89
+ errors.push({ path: at, message: 'pattern mismatch' });
90
+ };
91
+ const checkNumber = (node, shape, at) => {
92
+ if (shape.minimum !== undefined && node < shape.minimum)
93
+ errors.push({ path: at, message: 'number below minimum' });
94
+ if (shape.maximum !== undefined && node > shape.maximum)
95
+ errors.push({ path: at, message: 'number above maximum' });
96
+ };
97
+ const checkObject = (node, shape, at) => {
98
+ if (shape.minProperties && Object.keys(node).length < shape.minProperties)
99
+ errors.push({ path: at, message: 'object has too few properties' });
100
+ for (const key of shape.required ?? [])
101
+ if (!Object.hasOwn(node, key))
102
+ errors.push({ path: at, message: `missing ${key}` });
103
+ for (const [key, child] of Object.entries(node)) {
104
+ if (Object.hasOwn(shape.properties ?? {}, key))
105
+ check(child, shape.properties[key], `${at}/${key}`);
106
+ else if (shape.additionalProperties === false)
107
+ errors.push({ path: `${at}/${key}`, message: 'unknown field' });
108
+ else if (isObject(shape.additionalProperties))
109
+ check(child, shape.additionalProperties, `${at}/${key}`);
110
+ }
111
+ };
112
+ const checkAlternatives = (node, shape, at, check) => {
113
+ const matched = shape.anyOf.some((option) => {
114
+ const start = errors.length;
115
+ check(node, option, at);
116
+ if (errors.length === start)
117
+ return true;
118
+ errors.splice(start);
119
+ return false;
120
+ });
121
+ if (!matched)
122
+ errors.push({ path: at, message: 'matches none of the allowed shapes' });
123
+ };
124
+ const checkArray = (node, shape, at, check) => {
125
+ if (shape.minItems && node.length < shape.minItems)
126
+ errors.push({ path: at, message: 'array too short' });
127
+ node.forEach((item, index) => shape.items && check(item, shape.items, `${at}/${index}`));
128
+ };
129
+ // The operations block refers to its own schema file: its owner's composed schema validates it, as the provision target owner does.
130
+ const checkOperations = (node, at) => {
131
+ for (const error of validateAgainstSchema(node, provisionOperationSchema()))
132
+ errors.push({ path: at, message: error });
133
+ };
134
+ const check = (node, shape, at) => {
135
+ if (shape?.$ref === PROVISION_OPERATION_SCHEMA_REF)
136
+ return checkOperations(node, at);
137
+ if (shape?.$ref && !shape.$ref.startsWith('#/')) {
138
+ errors.push({ path: at, message: `unresolved schema reference ${shape.$ref}` });
139
+ return;
140
+ }
141
+ if (shape?.$ref)
142
+ return check(node, resolve(shape.$ref), at);
143
+ if (Array.isArray(shape?.anyOf)) {
144
+ checkAlternatives(node, shape, at, check);
145
+ return;
146
+ }
147
+ const typed = typeOk(node, shape?.type);
148
+ if (!typed) {
149
+ errors.push({ path: at, message: `expected ${shape.type}` });
150
+ return;
151
+ }
152
+ if (Object.hasOwn(shape ?? {}, 'const') && node !== shape.const)
153
+ errors.push({ path: at, message: 'const mismatch' });
154
+ if (Array.isArray(shape?.enum) && !shape.enum.includes(node))
155
+ errors.push({ path: at, message: 'value outside enum' });
156
+ if (typeof node === 'string')
157
+ checkString(node, shape, at);
158
+ if (typeof node === 'number')
159
+ checkNumber(node, shape, at);
160
+ if (Array.isArray(node)) {
161
+ checkArray(node, shape, at, check);
162
+ return;
163
+ }
164
+ if (!isObject(node))
165
+ return;
166
+ checkObject(node, shape, at);
167
+ };
168
+ check(value, manifestSchema, '$');
169
+ return errors;
170
+ }
171
+ const unresolved = (value) => typeof value === 'string' && /(^|[^$])\$\{/.test(value);
172
+ const serviceUnresolved = (service) => {
173
+ const build = object(service?.build) ? service.build : { context: service?.build };
174
+ const values = [service?.image, build.context, build.dockerfile, ...(Array.isArray(service?.ports) ? service.ports : [])];
175
+ for (const volume of Array.isArray(service?.volumes) ? service.volumes : [])
176
+ values.push(object(volume) ? volume.source : String(volume).split(':')[0]);
177
+ for (const [key, value] of composeEntries(service?.environment, { isMapping: object }))
178
+ if (!sensitive.test(key))
179
+ values.push(value);
180
+ return values.some(unresolved);
132
181
  };
133
- function unresolvedCompose(model){
134
- for(const service of Object.values(object(model?.services)?model.services:{})){
135
- if(serviceUnresolved(service))return true;
136
- }
137
- return Object.values(object(model?.secrets)?model.secrets:{}).some(item=>unresolved(item?.file));
182
+ function unresolvedCompose(model) {
183
+ for (const service of Object.values(object(model?.services) ? model.services : {})) {
184
+ if (serviceUnresolved(service))
185
+ return true;
186
+ }
187
+ return Object.values(object(model?.secrets) ? model.secrets : {}).some((item) => unresolved(item?.file));
138
188
  }
139
- function sopsEnvelope(file){
140
- // Recognizes both the YAML/JSON envelope (a "sops" object) and the flattened dotenv envelope SOPS
141
- // emits for a .env member (sops_lastmodified=, sops_mac=, sops_age__list_0__map_enc=, ...).
142
- try{
143
- if(fs.statSync(file).size>MAX_INPUT_BYTES)return false;
144
- const text=fs.readFileSync(file,'utf8');
145
- return (/(?:(?:^|\n)sops:|["']sops["']\s*:|(?:^|\n)sops_[a-z0-9_]+=)/.test(text))&&/ENC\[AES256_GCM,data:/.test(text);
146
- }catch{return false;}
189
+ function sopsEnvelope(file) {
190
+ // Recognizes both the YAML/JSON envelope (a "sops" object) and the flattened dotenv envelope SOPS
191
+ // emits for a .env member (sops_lastmodified=, sops_mac=, sops_age__list_0__map_enc=, ...).
192
+ try {
193
+ if (fs.statSync(file).size > MAX_INPUT_BYTES)
194
+ return false;
195
+ const text = fs.readFileSync(file, 'utf8');
196
+ return (/(?:(?:^|\n)sops:|["']sops["']\s*:|(?:^|\n)sops_[a-z0-9_]+=)/.test(text)) && /ENC\[AES256_GCM,data:/.test(text);
197
+ }
198
+ catch {
199
+ return false;
200
+ }
147
201
  }
148
-
149
- function gitTrackedSet(root){
150
- try{
151
- const result=lsFiles(['--',STACKS_DIR],{dir:root,timeout:5000});
152
- if(result.error||result.status!==0||typeof result.stdout!=='string')return null;
153
- return new Set(result.stdout.split(/\r?\n/).filter(Boolean).map(slash));
154
- }catch{return null;}
202
+ function gitTrackedSet(root) {
203
+ try {
204
+ const result = lsFiles(['--', STACKS_DIR], { dir: root, timeout: 5000 });
205
+ if (result.error || result.status !== 0 || typeof result.stdout !== 'string')
206
+ return null;
207
+ return new Set(result.stdout.split(/\r?\n/).filter(Boolean).map(slash));
208
+ }
209
+ catch {
210
+ return null;
211
+ }
155
212
  }
156
- function composeLike(file){
157
- try{
158
- if(fs.statSync(file).size>MAX_INPUT_BYTES)return false;
159
- const doc=parseYaml(fs.readFileSync(file,'utf8'));
160
- return object(doc)&&(object(doc.services)||Array.isArray(doc.include));
161
- }catch{return false;}
213
+ function composeLike(file) {
214
+ try {
215
+ if (fs.statSync(file).size > MAX_INPUT_BYTES)
216
+ return false;
217
+ const doc = parseYaml(fs.readFileSync(file, 'utf8'));
218
+ return object(doc) && (object(doc.services) || Array.isArray(doc.include));
219
+ }
220
+ catch {
221
+ return false;
222
+ }
162
223
  }
163
- function reachableComposeFiles(envDir,entries){
164
- const reachable=new Set(),queue=[...entries];
165
- while(queue.length){
166
- const relative=queue.shift(),target=resolvedPath(envDir,relative);
167
- if(!target||reachable.has(target))continue;
168
- reachable.add(target);
169
- if(!regular(target,{root:envDir}))continue;
170
- let doc=null;try{doc=parseYaml(fs.readFileSync(target,'utf8'));}catch{continue;}
171
- for(const include of composeIncludePaths(doc??{}).paths)
172
- queue.push(path.isAbsolute(include)?include:slash(path.join(path.relative(envDir,path.dirname(target)),include)));
173
- }
174
- return reachable;
224
+ function reachableComposeFiles(envDir, entries) {
225
+ const reachable = new Set(), queue = [...entries];
226
+ while (queue.length) {
227
+ const relative = queue.shift(), target = resolvedPath(envDir, relative);
228
+ if (!target || reachable.has(target))
229
+ continue;
230
+ reachable.add(target);
231
+ if (!regular(target, { root: envDir }))
232
+ continue;
233
+ let doc = null;
234
+ try {
235
+ doc = parseYaml(fs.readFileSync(target, 'utf8'));
236
+ }
237
+ catch {
238
+ continue;
239
+ }
240
+ for (const include of composeIncludePaths(doc ?? {}).paths)
241
+ queue.push(path.isAbsolute(include) ? include : slash(path.join(path.relative(envDir, path.dirname(target)), include)));
242
+ }
243
+ return reachable;
175
244
  }
176
- function scanForUndeclaredCompose(root,envDir,reachable,add,at){
177
- const infra=path.join(envDir,'infra');
178
- if(!directory(infra))return;
179
- const stack=[infra];
180
- while(stack.length){
181
- const dir=stack.pop();
182
- let entries=[];try{entries=fs.readdirSync(dir,{withFileTypes:true});}catch{continue;}
183
- for(const entry of entries){
184
- const full=path.join(dir,entry.name);
185
- if(entry.isSymbolicLink())continue;
186
- if(entry.isDirectory()){stack.push(full);continue;}
187
- if(!/\.ya?ml$/i.test(entry.name)||reachable.has(full)||!composeLike(full))continue;
188
- add('STACKS_UNDECLARED_COMPOSE',at,`${slash(path.relative(root,full))} is a Compose-shaped file under infra/ that is not declared or reachable from composeFiles; the composeFiles list must be the complete list for this environment`);
245
+ function scanForUndeclaredCompose(root, envDir, reachable, add, at) {
246
+ const infra = path.join(envDir, 'infra');
247
+ if (!directory(infra))
248
+ return;
249
+ const stack = [infra];
250
+ while (stack.length) {
251
+ const dir = stack.pop();
252
+ let entries = [];
253
+ try {
254
+ entries = fs.readdirSync(dir, { withFileTypes: true });
255
+ }
256
+ catch {
257
+ continue;
258
+ }
259
+ for (const entry of entries) {
260
+ const full = path.join(dir, entry.name);
261
+ if (entry.isSymbolicLink())
262
+ continue;
263
+ if (entry.isDirectory()) {
264
+ stack.push(full);
265
+ continue;
266
+ }
267
+ if (!/\.ya?ml$/i.test(entry.name) || reachable.has(full) || !composeLike(full))
268
+ continue;
269
+ add('STACKS_UNDECLARED_COMPOSE', at, `${slash(path.relative(root, full))} is a Compose-shaped file under infra/ that is not declared or reachable from composeFiles; the composeFiles list must be the complete list for this environment`);
270
+ }
189
271
  }
190
- }
191
272
  }
192
-
193
- const loadInputs=({manifestFile,deploymentModelFile,errors,add})=>{
194
- const input={manifest:{},model:{}};
195
- try{if(!errors.some(item=>['manifest-unavailable','input-too-large'].includes(item.code)&&item.path===`${STACKS_DIR}/application-stacks.yaml`))input.manifest=parseYaml(fs.readFileSync(manifestFile,'utf8'));}
196
- catch{add('manifest-invalid',`${STACKS_DIR}/application-stacks.yaml`,'manifest YAML could not be parsed');}
197
- try{if(!errors.some(item=>['deployment-model-unavailable','input-too-large'].includes(item.code)&&item.path==='deploymentModelFile')){
198
- const text=fs.readFileSync(deploymentModelFile,'utf8');try{input.model=JSON.parse(text);}catch{input.model=parseYaml(text);}
199
- }}catch{add('deployment-model-invalid','deploymentModelFile','rendered deployment JSON or YAML could not be parsed');}
200
- if(!object(input.manifest))input.manifest={};
201
- if(!object(input.model))input.model={};
202
- return input;
273
+ const loadInputs = ({ manifestFile, deploymentModelFile, errors, add }) => {
274
+ const input = { manifest: {}, model: {} };
275
+ try {
276
+ if (!errors.some(item => ['manifest-unavailable', 'input-too-large'].includes(item.code) && item.path === `${STACKS_DIR}/application-stacks.yaml`))
277
+ input.manifest = parseYaml(fs.readFileSync(manifestFile, 'utf8'));
278
+ }
279
+ catch {
280
+ add('manifest-invalid', `${STACKS_DIR}/application-stacks.yaml`, 'manifest YAML could not be parsed');
281
+ }
282
+ try {
283
+ if (!errors.some(item => ['deployment-model-unavailable', 'input-too-large'].includes(item.code) && item.path === 'deploymentModelFile')) {
284
+ const text = fs.readFileSync(deploymentModelFile, 'utf8');
285
+ try {
286
+ input.model = JSON.parse(text);
287
+ }
288
+ catch {
289
+ input.model = parseYaml(text);
290
+ }
291
+ }
292
+ }
293
+ catch {
294
+ add('deployment-model-invalid', 'deploymentModelFile', 'rendered deployment JSON or YAML could not be parsed');
295
+ }
296
+ if (!object(input.manifest))
297
+ input.manifest = {};
298
+ if (!object(input.model))
299
+ input.model = {};
300
+ return input;
203
301
  };
204
- const checkStackEntry=(entry,environmentNames,add,{stacksRoot,lite})=>{
205
- if(!entry.isDirectory()||entry.isSymbolicLink())return;
206
- if(entry.name==='tmp'){add('STACKS_SCRATCH_IN_CANONICAL',`${STACKS_DIR}/tmp`,'scratch belongs in the OS temp directory, not a tracked .starcistacks/tmp');return;}
207
- if(entry.name==='k8s')return;
208
- if(lite&&entry.name==='images')return; // the managed db image (slot app.db.image, ruling lite-db-image-in-stacks), no environment
209
- if(lite&&liteCustodyDirectory(path.join(stacksRoot,entry.name)))return;
210
- if(!environmentNames.has(entry.name))add('STACKS_UNDECLARED_ENVIRONMENT',`${STACKS_DIR}/${entry.name}`,`${entry.name} is not an environment the declaration admits; either the declaration admits it or it is not an environment`);
302
+ const checkStackEntry = (entry, environmentNames, add, { stacksRoot, lite }) => {
303
+ if (!entry.isDirectory() || entry.isSymbolicLink())
304
+ return;
305
+ if (entry.name === 'tmp') {
306
+ add('STACKS_SCRATCH_IN_CANONICAL', `${STACKS_DIR}/tmp`, 'scratch belongs in the OS temp directory, not a tracked .starcistacks/tmp');
307
+ return;
308
+ }
309
+ if (entry.name === 'k8s')
310
+ return;
311
+ if (lite && entry.name === 'images')
312
+ return; // the managed db image (slot app.db.image, ruling lite-db-image-in-stacks), no environment
313
+ if (lite && liteCustodyDirectory(path.join(stacksRoot, entry.name)))
314
+ return;
315
+ if (!environmentNames.has(entry.name))
316
+ add('STACKS_UNDECLARED_ENVIRONMENT', `${STACKS_DIR}/${entry.name}`, `${entry.name} is not an environment the declaration admits; either the declaration admits it or it is not an environment`);
211
317
  };
212
- const checkDeclaredEnvironmentDirs=(stacksRoot,environmentNames,add)=>{
213
- for(const name of environmentNames)if(!directory(path.join(stacksRoot,name)))add('environment-directory-missing',`${STACKS_DIR}/${name}`,`declared environment ${name} has no directory`);
318
+ const checkDeclaredEnvironmentDirs = (stacksRoot, environmentNames, add) => {
319
+ for (const name of environmentNames)
320
+ if (!directory(path.join(stacksRoot, name)))
321
+ add('environment-directory-missing', `${STACKS_DIR}/${name}`, `declared environment ${name} has no directory`);
214
322
  };
215
- const checkStacksTree=({stacksRoot,manifest,environmentNames,add,lite})=>{
216
- if(!directory(stacksRoot))return;
217
- let entries=[];try{entries=fs.readdirSync(stacksRoot,{withFileTypes:true});}catch{}
218
- for(const entry of entries)checkStackEntry(entry,environmentNames,add,{stacksRoot,lite});
219
- checkDeclaredEnvironmentDirs(stacksRoot,environmentNames,add);
220
- const k8sDir=path.join(stacksRoot,'k8s'),k8sExists=directory(k8sDir);
221
- if(manifest.k8s?.status==='supported'&&!k8sExists)add('k8s-directory-missing',`${STACKS_DIR}/k8s`,'k8s.status is supported but .starcistacks/k8s is missing');
222
- if(manifest.k8s?.status==='deferred'&&k8sExists)add('k8s-directory-unexpected',`${STACKS_DIR}/k8s`,'k8s.status is deferred, so a .starcistacks/k8s directory contradicts the declaration');
323
+ const checkStacksTree = ({ stacksRoot, manifest, environmentNames, add, lite }) => {
324
+ if (!directory(stacksRoot))
325
+ return;
326
+ let entries = [];
327
+ try {
328
+ entries = fs.readdirSync(stacksRoot, { withFileTypes: true });
329
+ }
330
+ catch { }
331
+ for (const entry of entries)
332
+ checkStackEntry(entry, environmentNames, add, { stacksRoot, lite });
333
+ checkDeclaredEnvironmentDirs(stacksRoot, environmentNames, add);
334
+ const k8sDir = path.join(stacksRoot, 'k8s'), k8sExists = directory(k8sDir);
335
+ if (manifest.k8s?.status === 'supported' && !k8sExists)
336
+ add('k8s-directory-missing', `${STACKS_DIR}/k8s`, 'k8s.status is supported but .starcistacks/k8s is missing');
337
+ if (manifest.k8s?.status === 'deferred' && k8sExists)
338
+ add('k8s-directory-unexpected', `${STACKS_DIR}/k8s`, 'k8s.status is deferred, so a .starcistacks/k8s directory contradicts the declaration');
223
339
  };
224
- const checkComposeFiles=({spec,environment,envDir,root,add})=>{
225
- const composeFiles=Array.isArray(spec.composeFiles)?spec.composeFiles:[];
226
- if(!composeFiles.length)add('compose-files-empty',`environments.${environment}.composeFiles`,'declare at least one environment-relative Compose source file');
227
- const safeComposeFiles=[];
228
- for(const [index,relative] of composeFiles.entries()){
229
- const target=resolvedPath(envDir,relative);
230
- if(!target||!regular(target,{root:envDir}))add('compose-path-unsafe',`environments.${environment}.composeFiles[${index}]`,'Compose source must be a regular non-symlink file below the environment directory');
231
- else safeComposeFiles.push(relative);
232
- }
233
- if(directory(envDir)){
234
- if(nonempty(spec.renderedModel))safeComposeFiles.push(spec.renderedModel);
235
- const reachable=reachableComposeFiles(envDir,safeComposeFiles);
236
- scanForUndeclaredCompose(root,envDir,reachable,add,`environments.${environment}.composeFiles`);
237
- }
340
+ const checkComposeFiles = ({ spec, environment, envDir, root, add }) => {
341
+ const composeFiles = Array.isArray(spec.composeFiles) ? spec.composeFiles : [];
342
+ if (!composeFiles.length)
343
+ add('compose-files-empty', `environments.${environment}.composeFiles`, 'declare at least one environment-relative Compose source file');
344
+ const safeComposeFiles = [];
345
+ for (const [index, relative] of composeFiles.entries()) {
346
+ const target = resolvedPath(envDir, relative);
347
+ if (!target || !regular(target, { root: envDir }))
348
+ add('compose-path-unsafe', `environments.${environment}.composeFiles[${index}]`, 'Compose source must be a regular non-symlink file below the environment directory');
349
+ else
350
+ safeComposeFiles.push(relative);
351
+ }
352
+ if (directory(envDir)) {
353
+ if (nonempty(spec.renderedModel))
354
+ safeComposeFiles.push(spec.renderedModel);
355
+ const reachable = reachableComposeFiles(envDir, safeComposeFiles);
356
+ scanForUndeclaredCompose(root, envDir, reachable, add, `environments.${environment}.composeFiles`);
357
+ }
238
358
  };
239
- const checkRunbook=({spec,environment,envDir,add})=>{
240
- if(!nonempty(spec.runbook))add('runbook-path-invalid',`environments.${environment}.runbook`,'runbook must name a file inside the environment directory');
241
- else{
242
- const runbookFile=resolvedPath(envDir,spec.runbook);
243
- const oversized=(()=>{try{return fs.statSync(runbookFile).size>MAX_INPUT_BYTES;}catch{return true;}})();
244
- if(!runbookFile||!regular(runbookFile,{root:envDir})||oversized)
245
- add('runbook-path-invalid',`environments.${environment}.runbook`,'runbook must resolve to an existing regular non-symlink file below the environment directory');
246
- else{
247
- let text='';try{text=fs.readFileSync(runbookFile,'utf8');}catch{}
248
- for(const command of RUNBOOK_COMMANDS){
249
- const rowPattern=new RegExp(String.raw`^\s*\|\s*${command}\s*\|`,'im');
250
- if(!rowPattern.test(text))add('runbook-command-missing',`environments.${environment}.runbook#${command}`,`runbook must document the ${command} command`);
251
- }
359
+ const checkRunbook = ({ spec, environment, envDir, add }) => {
360
+ if (!nonempty(spec.runbook))
361
+ add('runbook-path-invalid', `environments.${environment}.runbook`, 'runbook must name a file inside the environment directory');
362
+ else {
363
+ const runbookFile = resolvedPath(envDir, spec.runbook);
364
+ const oversized = (() => { try {
365
+ return fs.statSync(runbookFile).size > MAX_INPUT_BYTES;
366
+ }
367
+ catch {
368
+ return true;
369
+ } })();
370
+ if (!runbookFile || !regular(runbookFile, { root: envDir }) || oversized)
371
+ add('runbook-path-invalid', `environments.${environment}.runbook`, 'runbook must resolve to an existing regular non-symlink file below the environment directory');
372
+ else {
373
+ let text = '';
374
+ try {
375
+ text = fs.readFileSync(runbookFile, 'utf8');
376
+ }
377
+ catch { }
378
+ for (const command of RUNBOOK_COMMANDS) {
379
+ const rowPattern = new RegExp(String.raw `^\s*\|\s*${command}\s*\|`, 'im');
380
+ if (!rowPattern.test(text))
381
+ add('runbook-command-missing', `environments.${environment}.runbook#${command}`, `runbook must document the ${command} command`);
382
+ }
383
+ }
252
384
  }
253
- }
254
385
  };
255
- const checkSecret=(secret,at,{envDir,environment,root,tracked,secretNames,add})=>{
256
- if(!object(secret))return;
257
- if(secretNames.has(secret.name))add('secret-duplicate',`${at}.name`,'secret names must be unique');else secretNames.add(secret.name);
258
- const plain=nonempty(secret.where)?resolvedPath(envDir,secret.where):null;
259
- // The member of `where` is `<where>.enc.yaml`, or a legacy `<where>.enc` (scripts/lib/sealed-member.mjs); an entry naming `key`
260
- // declares that one key of the member, which its envelope must hold.
261
- const member=nonempty(secret.where)?[`${secret.where}.enc.yaml`,`${secret.where}.enc`].map(name=>resolvedPath(envDir,name)).find(file=>file&&regular(file,{root:envDir})):null;
262
- const enc=member??(nonempty(secret.where)?resolvedPath(envDir,`${secret.where}.enc.yaml`):null);
263
- if(!enc||!regular(enc,{root:envDir})||!sopsEnvelope(enc))
264
- add('encrypted-ref-invalid',`${at}.where`,'secret must resolve to <where>.enc.yaml (or a legacy <where>.enc) as a recognizable SOPS envelope, an existing regular file below the environment directory');
265
- else if(nonempty(secret.key)&&!envelopeOf(fs.readFileSync(enc,'utf8')).keys.includes(secret.key))
266
- add('encrypted-ref-invalid',`${at}.key`,`the member at ${secret.where} holds no ${secret.key}`);
267
- else if(Array.isArray(secret.keys)){
268
- // A grouped member declares its keys in one entry; each must be in the envelope, and one entry declares one key or a group, not both.
269
- if(nonempty(secret.key))add('encrypted-ref-invalid',`${at}.keys`,'an entry declares one key (key) or a group (keys), not both');
270
- const held=envelopeOf(fs.readFileSync(enc,'utf8')).keys;
271
- for(const key of secret.keys.filter(key=>!held.includes(key)))add('encrypted-ref-invalid',`${at}.keys`,`the member at ${secret.where} holds no ${key}`);
272
- }
273
- if(nonempty(secret.where)&&!plain)
274
- add('materialized-path-invalid',`${at}.where`,'secret plaintext path and its existing ancestors must remain below the environment directory without symlinks');
275
- if(!String(secret.recipientPolicy??'').trim()||!String(secret.keyCustody??'').trim())
276
- add('secret-policy-missing',at,'secret recipient and key custody policies are required');
277
- if(tracked&&plain&&tracked.has(slash(path.relative(root,plain))))
278
- add('STACKS_PLAINTEXT_SECRET',`${at}.where`,'a decrypted secret member must never be a tracked file; only its .enc member is committed');
386
+ const checkSecret = (secret, at, { envDir, root, tracked, secretNames, add }) => {
387
+ if (!object(secret))
388
+ return;
389
+ if (secretNames.has(secret.name))
390
+ add('secret-duplicate', `${at}.name`, 'secret names must be unique');
391
+ else
392
+ secretNames.add(secret.name);
393
+ const plain = nonempty(secret.where) ? resolvedPath(envDir, secret.where) : null;
394
+ // The member of `where` is `<where>.enc.yaml`, or a legacy `<where>.enc` (scripts/lib/sealed-member.mjs); an entry naming `key`
395
+ // declares that one key of the member, which its envelope must hold.
396
+ const member = nonempty(secret.where) ? [`${secret.where}.enc.yaml`, `${secret.where}.enc`].map(name => resolvedPath(envDir, name)).find(file => file && regular(file, { root: envDir })) : null;
397
+ const enc = member ?? (nonempty(secret.where) ? resolvedPath(envDir, `${secret.where}.enc.yaml`) : null);
398
+ if (!enc || !regular(enc, { root: envDir }) || !sopsEnvelope(enc))
399
+ add('encrypted-ref-invalid', `${at}.where`, 'secret must resolve to <where>.enc.yaml (or a legacy <where>.enc) as a recognizable SOPS envelope, an existing regular file below the environment directory');
400
+ else if (nonempty(secret.key) && !envelopeOf(fs.readFileSync(enc, 'utf8')).keys.includes(secret.key))
401
+ add('encrypted-ref-invalid', `${at}.key`, `the member at ${secret.where} holds no ${secret.key}`);
402
+ else if (Array.isArray(secret.keys)) {
403
+ // A grouped member declares its keys in one entry; each must be in the envelope, and one entry declares one key or a group, not both.
404
+ if (nonempty(secret.key))
405
+ add('encrypted-ref-invalid', `${at}.keys`, 'an entry declares one key (key) or a group (keys), not both');
406
+ const held = envelopeOf(fs.readFileSync(enc, 'utf8')).keys;
407
+ for (const key of secret.keys.filter((key) => !held.includes(key)))
408
+ add('encrypted-ref-invalid', `${at}.keys`, `the member at ${secret.where} holds no ${key}`);
409
+ }
410
+ if (nonempty(secret.where) && !plain)
411
+ add('materialized-path-invalid', `${at}.where`, 'secret plaintext path and its existing ancestors must remain below the environment directory without symlinks');
412
+ if (!String(secret.recipientPolicy ?? '').trim() || !String(secret.keyCustody ?? '').trim())
413
+ add('secret-policy-missing', at, 'secret recipient and key custody policies are required');
414
+ if (tracked && plain && tracked.has(slash(path.relative(root, plain))))
415
+ add('STACKS_PLAINTEXT_SECRET', `${at}.where`, 'a decrypted secret member must never be a tracked file; only its .enc member is committed');
279
416
  };
280
- const checkSecrets=({spec,environment,envDir,root,add})=>{
281
- const declaredSecrets=Array.isArray(spec.secrets)?spec.secrets:[],secretNames=new Set();
282
- const tracked=directory(envDir)?gitTrackedSet(root):null;
283
- for(const [index,secret] of declaredSecrets.entries())
284
- checkSecret(secret,`environments.${environment}.secrets[${index}]`,{envDir,environment,root,tracked,secretNames,add});
285
- if(tracked){
286
- const envPrefix=`${slash(path.relative(root,envDir))}/`;
287
- for(const relTracked of tracked){
288
- if(!relTracked.startsWith(envPrefix))continue;
289
- const base=path.basename(relTracked);
290
- if(PLAINTEXT_SECRET_PATTERN.test(base)||base==='id_rsa')
291
- add('STACKS_PLAINTEXT_SECRET',relTracked,'a tracked file below the environment directory matches a plaintext secret pattern; only <name>.enc may be committed');
417
+ const checkSecrets = ({ spec, environment, envDir, root, add }) => {
418
+ const declaredSecrets = Array.isArray(spec.secrets) ? spec.secrets : [], secretNames = new Set();
419
+ const tracked = directory(envDir) ? gitTrackedSet(root) : null;
420
+ for (const [index, secret] of declaredSecrets.entries())
421
+ checkSecret(secret, `environments.${environment}.secrets[${index}]`, { envDir, root, tracked, secretNames, add });
422
+ if (tracked) {
423
+ const envPrefix = `${slash(path.relative(root, envDir))}/`;
424
+ for (const relTracked of tracked) {
425
+ if (!relTracked.startsWith(envPrefix))
426
+ continue;
427
+ const base = path.basename(relTracked);
428
+ if (PLAINTEXT_SECRET_PATTERN.test(base) || base === 'id_rsa')
429
+ add('STACKS_PLAINTEXT_SECRET', relTracked, 'a tracked file below the environment directory matches a plaintext secret pattern; only <name>.enc may be committed');
430
+ }
292
431
  }
293
- }
294
432
  };
295
- const checkEnvironmentSpec=({spec,environment,envDir,ids,root,add})=>{
296
- if(spec.status!=='supported')add('environment-unsupported',`environments.${environment}`,'environment must be explicitly supported');
297
- if(!['docker-compose','docker-swarm'].includes(spec.runtime))add('runtime-invalid',`environments.${environment}.runtime`,'runtime must be docker-compose or docker-swarm');
298
- checkComposeFiles({spec,environment,envDir,root,add});
299
- const bindings=object(spec.components)?spec.components:{};
300
- for(const id of Object.keys(bindings))if(!ids.has(id))
301
- add('binding-component-unknown',`environments.${environment}.components.${id}`,'environment binds a component id that is absent from the top-level inventory');
302
- checkRunbook({spec,environment,envDir,add});
303
- checkSecrets({spec,environment,envDir,root,add});
433
+ const checkEnvironmentSpec = ({ spec, environment, envDir, ids, root, add }) => {
434
+ if (spec.status !== 'supported')
435
+ add('environment-unsupported', `environments.${environment}`, 'environment must be explicitly supported');
436
+ if (!['docker-compose', 'docker-swarm'].includes(spec.runtime))
437
+ add('runtime-invalid', `environments.${environment}.runtime`, 'runtime must be docker-compose or docker-swarm');
438
+ checkComposeFiles({ spec, environment, envDir, root, add });
439
+ const bindings = object(spec.components) ? spec.components : {};
440
+ for (const id of Object.keys(bindings))
441
+ if (!ids.has(id))
442
+ add('binding-component-unknown', `environments.${environment}.components.${id}`, 'environment binds a component id that is absent from the top-level inventory');
443
+ checkRunbook({ spec, environment, envDir, add });
444
+ checkSecrets({ spec, environment, envDir, root, add });
304
445
  };
305
- const checkServiceIds=(services,ids,add)=>{
306
- for(const service of Object.keys(services))if(!ids.has(service))
307
- add('compose-service-unclassified',`services.${service}`,'rendered service name is not a declared component id');
446
+ const checkServiceIds = (services, ids, add) => {
447
+ for (const service of Object.keys(services))
448
+ if (!ids.has(service))
449
+ add('compose-service-unclassified', `services.${service}`, 'rendered service name is not a declared component id');
308
450
  };
309
- const checkServiceEnvironment=(services,add)=>{
310
- for(const [service,definition] of Object.entries(services))for(const [key] of composeEntries(definition?.environment, { isMapping: object }))
311
- if(sensitive.test(key)&&!/_FILE$/i.test(key))add('plaintext-sensitive-environment',`services.${service}.environment.${key}`,'sensitive configuration is present as a direct environment value; value redacted');
451
+ const checkServiceEnvironment = (services, add) => {
452
+ for (const [service, definition] of Object.entries(services))
453
+ for (const [key] of composeEntries(definition?.environment, { isMapping: object }))
454
+ if (sensitive.test(key) && !/_FILE$/i.test(key))
455
+ add('plaintext-sensitive-environment', `services.${service}.environment.${key}`, 'sensitive configuration is present as a direct environment value; value redacted');
312
456
  };
313
- const dependencyComponent=(id,component,apps)=>component?.stateful===true||component?.database!==undefined
314
- ||!apps.has(component?.app)&&!apps.has(id)&&!['service','job'].includes(component?.role);
315
- const checkDependencyContainers=({services,spec,manifest,repo,add})=>{
316
- const apps=new Set([...(Array.isArray(repo?.apps)?repo.apps:[]),
317
- ...Object.values(object(repo?.sides)?repo.sides:{}).flatMap(side=>Array.isArray(side?.apps)?side.apps:[])].map(app=>app.name));
318
- for(const id of Object.keys(object(spec?.components)?spec.components:{})){
319
- const component=manifest.components?.[id];
320
- if(nonempty(component?.app)&&!apps.has(component.app))
321
- add('HFS_STACKS_SHAPE',`components.${id}.app`,'an application component must name an app in the HFS declaration');
322
- if(!dependencyComponent(id,component,apps))continue;
323
- const service=services[id];
324
- if(!object(service)||!nonempty(service.image)&&!nonempty(service.build)&&!nonempty(service.build?.context))
325
- add('HFS_STACKS_SHAPE',`services.${id}`,'a selected dependency must run in a Docker Linux container with a rendered image or build context');
326
- else if(service.platform!==undefined&&(typeof service.platform!=='string'||!/^linux(?:\/[^/]+){0,2}$/.test(service.platform)))
327
- add('HFS_STACKS_SHAPE',`services.${id}.platform`,'a selected dependency must use a Docker Linux container platform');
328
- }
457
+ const dependencyComponent = (id, component, apps) => component?.stateful === true || component?.database !== undefined
458
+ || !apps.has(component?.app) && !apps.has(id) && !['service', 'job'].includes(component?.role);
459
+ const checkDependencyContainers = ({ services, spec, manifest, repo, add }) => {
460
+ const apps = new Set([...(Array.isArray(repo?.apps) ? repo.apps : []),
461
+ ...Object.values(object(repo?.sides) ? repo.sides : {}).flatMap((side) => Array.isArray(side?.apps) ? side.apps : [])].map((app) => app.name));
462
+ for (const id of Object.keys(object(spec?.components) ? spec.components : {})) {
463
+ const component = manifest.components?.[id];
464
+ if (nonempty(component?.app) && !apps.has(component.app))
465
+ add('HFS_STACKS_SHAPE', `components.${id}.app`, 'an application component must name an app in the HFS declaration');
466
+ if (!dependencyComponent(id, component, apps))
467
+ continue;
468
+ const service = services[id];
469
+ if (!object(service) || !nonempty(service.image) && !nonempty(service.build) && !nonempty(service.build?.context))
470
+ add('HFS_STACKS_SHAPE', `services.${id}`, 'a selected dependency must run in a Docker Linux container with a rendered image or build context');
471
+ else if (service.platform !== undefined && (typeof service.platform !== 'string' || !/^linux(?:\/[^/]+){0,2}$/.test(service.platform)))
472
+ add('HFS_STACKS_SHAPE', `services.${id}.platform`, 'a selected dependency must use a Docker Linux container platform');
473
+ }
329
474
  };
330
- const checkModel=({model,ids,spec,manifest,lite,repo,add})=>{
331
- const services=object(model.services)?model.services:{};
332
- checkServiceIds(services,ids,add);
333
- if(unresolvedCompose(model))add('compose-placeholder-unresolved','composeModel','rendered Compose still contains an unresolved interpolation placeholder in a host-resolved field');
334
- checkServiceEnvironment(services,add);
335
- checkDependencyContainers({services,spec,manifest,repo,add});
336
- if(object(spec)&&spec.runtime==='docker-swarm')for(const issue of swarmModelIssues({model,components:manifest.components,bindings:spec.components,lite}))
337
- add('HFS_STACKS_SHAPE',issue.at,issue.message);
475
+ const checkModel = ({ model, ids, spec, manifest, lite, repo, add }) => {
476
+ const services = object(model.services) ? model.services : {};
477
+ checkServiceIds(services, ids, add);
478
+ if (unresolvedCompose(model))
479
+ add('compose-placeholder-unresolved', 'composeModel', 'rendered Compose still contains an unresolved interpolation placeholder in a host-resolved field');
480
+ checkServiceEnvironment(services, add);
481
+ checkDependencyContainers({ services, spec, manifest, repo, add });
482
+ if (object(spec) && spec.runtime === 'docker-swarm')
483
+ for (const issue of swarmModelIssues({ model, components: manifest.components, bindings: spec.components, lite }))
484
+ add('HFS_STACKS_SHAPE', issue.at, issue.message);
338
485
  };
339
-
340
- const checkInputPaths=({environment,stacksRoot,manifestFile,deploymentModelFile,add})=>{
341
- if(!nonempty(environment))add('environment-invalid','environment','environment must name a declared environment');
342
- if(!regular(manifestFile,{root:stacksRoot}))add('manifest-unavailable',`${STACKS_DIR}/application-stacks.yaml`,`manifest must be a regular file inside ${STACKS_DIR}`);
343
- if(!regular(deploymentModelFile))add('deployment-model-unavailable','deploymentModelFile','rendered deployment evidence must be a regular, non-symlink file');
344
- for(const [file,at] of [[manifestFile,`${STACKS_DIR}/application-stacks.yaml`],[deploymentModelFile,'deploymentModelFile']])
345
- try{if(fs.statSync(file).size>MAX_INPUT_BYTES)add('input-too-large',at,'input exceeds the 4 MiB static-check limit');}catch{}
486
+ const checkInputPaths = ({ environment, stacksRoot, manifestFile, deploymentModelFile, add }) => {
487
+ if (!nonempty(environment))
488
+ add('environment-invalid', 'environment', 'environment must name a declared environment');
489
+ if (!regular(manifestFile, { root: stacksRoot }))
490
+ add('manifest-unavailable', `${STACKS_DIR}/application-stacks.yaml`, `manifest must be a regular file inside ${STACKS_DIR}`);
491
+ if (!regular(deploymentModelFile))
492
+ add('deployment-model-unavailable', 'deploymentModelFile', 'rendered deployment evidence must be a regular, non-symlink file');
493
+ for (const [file, at] of [[manifestFile, `${STACKS_DIR}/application-stacks.yaml`], [deploymentModelFile, 'deploymentModelFile']])
494
+ try {
495
+ if (fs.statSync(file).size > MAX_INPUT_BYTES)
496
+ add('input-too-large', at, 'input exceeds the 4 MiB static-check limit');
497
+ }
498
+ catch { }
346
499
  };
347
- const componentIdsOf=(manifest,add)=>{
348
- const catalog=object(manifest.components)?manifest.components:{},ids=new Set(Object.keys(catalog));
349
- if(!ids.size)add('components-empty','components','declare at least one applicable application component');
350
- for(const id of ids)if(!/^[a-z][a-z0-9-]*$/.test(id))add('component-invalid',`components.${id}`,'component ids must be lowercase kebab-case');
351
- return ids;
500
+ const componentIdsOf = (manifest, add) => {
501
+ const catalog = object(manifest.components) ? manifest.components : {}, ids = new Set(Object.keys(catalog));
502
+ if (!ids.size)
503
+ add('components-empty', 'components', 'declare at least one applicable application component');
504
+ for (const id of ids)
505
+ if (!/^[a-z][a-z0-9-]*$/.test(id))
506
+ add('component-invalid', `components.${id}`, 'component ids must be lowercase kebab-case');
507
+ return ids;
352
508
  };
353
- const environmentMapOf=(manifest,add)=>{
354
- const environments=object(manifest.environments)?manifest.environments:{},environmentNames=new Set(Object.keys(environments));
355
- if(!environmentNames.size)add('environments-empty','environments','declare at least one supported environment');
356
- return {environments,environmentNames};
509
+ const environmentMapOf = (manifest, add) => {
510
+ const environments = object(manifest.environments) ? manifest.environments : {}, environmentNames = new Set(Object.keys(environments));
511
+ if (!environmentNames.size)
512
+ add('environments-empty', 'environments', 'declare at least one supported environment');
513
+ return { environments, environmentNames };
357
514
  };
358
- const checkKubernetesDeclaration=(manifest,add)=>{
359
- if(!['deferred','supported'].includes(manifest.k8s?.status)||!String(manifest.k8s?.reason??'').trim())
360
- add('k8s-status-invalid','k8s','Kubernetes must be explicitly deferred or supported with a reason');
515
+ const checkKubernetesDeclaration = (manifest, add) => {
516
+ if (!['deferred', 'supported'].includes(manifest.k8s?.status) || !String(manifest.k8s?.reason ?? '').trim())
517
+ add('k8s-status-invalid', 'k8s', 'Kubernetes must be explicitly deferred or supported with a reason');
361
518
  };
362
- const checkSelectedEnvironment=({environment,environmentNames,environments,stacksRoot,ids,root,model,manifest,lite,repo,add})=>{
363
- if(nonempty(environment)&&!environmentNames.has(String(environment)))
364
- add('environment-unknown','environment',`environment must be one the declaration admits: ${[...environmentNames].join(', ')}`);
365
- const spec=environments[String(environment)],envDir=path.join(stacksRoot,String(environment??''));
366
- if(object(spec))checkEnvironmentSpec({spec,environment,envDir,ids,root,add});
367
- checkModel({model,ids,spec,manifest,lite,repo,add});
519
+ const checkSelectedEnvironment = ({ environment, environmentNames, environments, stacksRoot, ids, root, model, manifest, lite, repo, add }) => {
520
+ if (nonempty(environment) && !environmentNames.has(String(environment)))
521
+ add('environment-unknown', 'environment', `environment must be one the declaration admits: ${[...environmentNames].join(', ')}`);
522
+ const spec = environments[String(environment)], envDir = path.join(stacksRoot, String(environment ?? ''));
523
+ if (object(spec))
524
+ checkEnvironmentSpec({ spec, environment, envDir, ids, root, add });
525
+ checkModel({ model, ids, spec, manifest, lite, repo, add });
368
526
  };
369
- const checkStacksManifest=(manifest,model,{stacksRoot,environment,root,repo,add})=>{
370
- for(const issue of validateSchema(manifest))add('schema-shape-invalid',issue.path,issue.message);
371
- if(manifest.schema!==SCHEMA_ID)add('schema-invalid','schema',`expected ${SCHEMA_ID}`);
372
- const ids=componentIdsOf(manifest,add),{environments,environmentNames}=environmentMapOf(manifest,add);
373
- const lite=repo.edition==='lite';
374
- checkStacksTree({stacksRoot,manifest,environmentNames,add,lite});
375
- checkKubernetesDeclaration(manifest,add);
376
- for(const issue of swarmInventoryIssues({components:manifest.components,lite}))add('HFS_STACKS_SHAPE',issue.at,issue.message);
377
- checkSelectedEnvironment({environment,environmentNames,environments,stacksRoot,ids,root,model,manifest,lite,repo,add});
527
+ const checkStacksManifest = (manifest, model, { stacksRoot, environment, root, repo, add }) => {
528
+ for (const issue of validateSchema(manifest))
529
+ add('schema-shape-invalid', issue.path, issue.message);
530
+ if (manifest.schema !== SCHEMA_ID)
531
+ add('schema-invalid', 'schema', `expected ${SCHEMA_ID}`);
532
+ const ids = componentIdsOf(manifest, add), { environments, environmentNames } = environmentMapOf(manifest, add);
533
+ const lite = repo.edition === 'lite';
534
+ checkStacksTree({ stacksRoot, manifest, environmentNames, add, lite });
535
+ checkKubernetesDeclaration(manifest, add);
536
+ for (const issue of swarmInventoryIssues({ components: manifest.components, lite }))
537
+ add('HFS_STACKS_SHAPE', issue.at, issue.message);
538
+ checkSelectedEnvironment({ environment, environmentNames, environments, stacksRoot, ids, root, model, manifest, lite, repo, add });
378
539
  };
379
-
380
- function deploymentAvailable(model,errors){
381
- const invalid=errors.some(item=>item.path==='deploymentModelFile');
382
- return !invalid&&object(model.services)&&Object.keys(model.services).length>0&&model.include===undefined;
540
+ function deploymentAvailable(model, errors) {
541
+ const invalid = errors.some(item => item.path === 'deploymentModelFile');
542
+ return !invalid && object(model.services) && Object.keys(model.services).length > 0 && model.include === undefined;
383
543
  }
384
-
385
- function diskEvidenceFiles(root){
386
- const excluded=new Set(['node_modules','.git','.claude','.aws','.codex']);
387
- return walkFiles(root,{ignoreReadErrors:true,exclude:(name,_file,entry)=>entry.isSymbolicLink()||excluded.has(name)})
388
- .map(file=>slash(path.relative(root,file)));
544
+ function diskEvidenceFiles(root) {
545
+ const excluded = new Set(['node_modules', '.git', '.claude', '.aws', '.codex']);
546
+ return walkFiles(root, { ignoreReadErrors: true, exclude: (name, _file, entry) => entry.isSymbolicLink() || excluded.has(name) })
547
+ .map((file) => slash(path.relative(root, file)));
389
548
  }
390
-
391
- function checkSwarmHardening({manifest,model,environment,root,files,repo,errors,add}){
392
- const spec=manifest.environments?.[environment];
393
- if(spec?.runtime!=='docker-swarm')return;
394
- if(!deploymentAvailable(model,errors)){
395
- add('STACKS_MODEL_UNAVAILABLE',`environments.${environment}`,'a rendered deployment model with nonempty services is required to judge Swarm safety');
396
- return;
397
- }
398
- const context={doc:manifest,environment,spec,model,repoRoot:root,files,repo};
399
- for(const finder of [swarmHardeningIssues,ingressIssues,liteBackingIssues,releaseBackupIssues,scheduleIssues])
400
- for(const issue of finder(context))add(issue.code,issue.at,issue.message);
549
+ function checkSwarmHardening({ manifest, model, environment, root, files, repo, errors, add }) {
550
+ const spec = manifest.environments?.[environment];
551
+ if (spec?.runtime !== 'docker-swarm')
552
+ return;
553
+ if (!deploymentAvailable(model, errors)) {
554
+ add('STACKS_MODEL_UNAVAILABLE', `environments.${environment}`, 'a rendered deployment model with nonempty services is required to judge Swarm safety');
555
+ return;
556
+ }
557
+ const context = { doc: manifest, environment, spec, model, repoRoot: root, files, repo };
558
+ for (const finder of [swarmHardeningIssues, ingressIssues, liteBackingIssues, releaseBackupIssues, scheduleIssues])
559
+ for (const issue of finder(context))
560
+ add(issue.code, issue.at, issue.message);
401
561
  }
402
-
403
- export function checkApplicationStacks({repoRoot,environment,deploymentModelFile,files,repo}={}){
404
- const root=path.resolve(String(repoRoot??'')),stacksRoot=path.join(root,STACKS_DIR),manifestFile=path.join(stacksRoot,'application-stacks.yaml'),errors=[];
405
- const diskDeclaration=readJson(root,'hfs.json'),declaration={edition:'full',...(object(diskDeclaration)?diskDeclaration:{}),...(object(repo)?repo:{})};
406
- const add=(code,at,message)=>errors.push({code,path:at,message});
407
- if(fs.existsSync(path.join(root,'hfs.json'))&&!object(diskDeclaration))
408
- add('HFS_STACKS_SHAPE','hfs.json','the HFS declaration must be a readable JSON object before application identities can be checked');
409
- checkInputPaths({environment,stacksRoot,manifestFile,deploymentModelFile,add});
410
- const {manifest,model}=loadInputs({manifestFile,deploymentModelFile,errors,add});
411
- checkStacksManifest(manifest,model,{stacksRoot,environment,root,repo:declaration,add});
412
- checkSwarmHardening({manifest,model,environment,root,files:files??diskEvidenceFiles(root),repo:declaration,errors,add});
413
- for(const item of applicationStackFindings(root))add(item.code,item.path,item.message);
414
-
415
- return {schema:RESULT,ok:errors.length===0,environment,manifest:manifestFile,deploymentModel:path.resolve(String(deploymentModelFile??'')),errors,
416
- limitations:['static conformance only; Docker was not invoked, host application processes were not started, and remote APIs were not called','the deployment model must be rendered by Docker Compose or Docker Stack tooling; provenance is supplied by the caller and is not independently authenticated','component classification, runbook completeness and secret custody are author assertions checked for consistency and structure, not independently discovered facts','SOPS envelope recognition is structural, not cryptographic verification or decryption','tracked-plaintext detection uses git ls-files when the repository is reachable and is skipped, not assumed clean, when it is not','runbook prepare, doctor and verification commands are declared and documented but not executed']};
562
+ export function checkApplicationStacks({ repoRoot, environment, deploymentModelFile, files, repo } = {}) {
563
+ const root = path.resolve(String(repoRoot ?? '')), stacksRoot = path.join(root, STACKS_DIR), manifestFile = path.join(stacksRoot, 'application-stacks.yaml'), errors = [];
564
+ const diskDeclaration = readJson(root, 'hfs.json'), declaration = { edition: 'full', ...(object(diskDeclaration) ? diskDeclaration : {}), ...(object(repo) ? repo : {}) };
565
+ const add = (code, at, message) => { errors.push({ code, path: at, message }); };
566
+ if (fs.existsSync(path.join(root, 'hfs.json')) && !object(diskDeclaration))
567
+ add('HFS_STACKS_SHAPE', 'hfs.json', 'the HFS declaration must be a readable JSON object before application identities can be checked');
568
+ checkInputPaths({ environment, stacksRoot, manifestFile, deploymentModelFile: deploymentModelFile, add });
569
+ const { manifest, model } = loadInputs({ manifestFile, deploymentModelFile: deploymentModelFile, errors, add });
570
+ checkStacksManifest(manifest, model, { stacksRoot, environment, root, repo: declaration, add });
571
+ checkSwarmHardening({ manifest, model, environment, root, files: files ?? diskEvidenceFiles(root), repo: declaration, errors, add });
572
+ for (const item of applicationStackFindings(root))
573
+ add(item.code, item.path, item.message);
574
+ return { schema: RESULT, ok: errors.length === 0, environment, manifest: manifestFile, deploymentModel: path.resolve(String(deploymentModelFile ?? '')), errors,
575
+ limitations: ['static conformance only; Docker was not invoked, host application processes were not started, and remote APIs were not called', 'the deployment model must be rendered by Docker Compose or Docker Stack tooling; provenance is supplied by the caller and is not independently authenticated', 'component classification, runbook completeness and secret custody are author assertions checked for consistency and structure, not independently discovered facts', 'SOPS envelope recognition is structural, not cryptographic verification or decryption', 'tracked-plaintext detection uses git ls-files when the repository is reachable and is skipped, not assumed clean, when it is not', 'runbook prepare, doctor and verification commands are declared and documented but not executed'] };
417
576
  }
418
-
419
- const declarationFindingCodes=new Set(['STACKS_RELEASE_UNDECLARED','BACKUP_UNDECLARED','BACKUP_PROOF_MISSING','HFS_SCHEDULE_UNDECLARED']);
420
-
421
- function trackedGateFinding(entry,error){
422
- let file=entry.file;
423
- const code=HARDENING_CODES.has(error.code)?error.code:'HFS_STACKS_SHAPE';
424
- const modelPath=/^(?:services(?:\.|$)|secrets(?:\.|$)|composeModel$|deploymentModelFile$)/.test(error.path);
425
- if(declarationFindingCodes.has(error.code)||!modelPath&&code==='HFS_STACKS_SHAPE')file=`${STACKS_DIR}/application-stacks.yaml`;
426
- if(error.code==='HFS_SCHEDULE_UNDECLARED'){
427
- const source=error.path.replace(/:\d+$/,'');
428
- if(/\.(?:sql|[cm]?[jt]s)$/.test(source))file=source;
429
- }
430
- return {code,level:'error',path:file,message:`${file}: ${error.path}: ${error.message}`};
577
+ const declarationFindingCodes = new Set(['STACKS_RELEASE_UNDECLARED', 'BACKUP_UNDECLARED', 'BACKUP_PROOF_MISSING', 'HFS_SCHEDULE_UNDECLARED']);
578
+ function trackedGateFinding(entry, error) {
579
+ let file = entry.file;
580
+ const code = HARDENING_CODES.has(error.code) ? error.code : 'HFS_STACKS_SHAPE';
581
+ const modelPath = /^(?:services(?:\.|$)|secrets(?:\.|$)|composeModel$|deploymentModelFile$)/.test(error.path);
582
+ if (declarationFindingCodes.has(error.code) || !modelPath && code === 'HFS_STACKS_SHAPE')
583
+ file = `${STACKS_DIR}/application-stacks.yaml`;
584
+ if (error.code === 'HFS_SCHEDULE_UNDECLARED') {
585
+ const source = error.path.replace(/:\d+$/, '');
586
+ if (/\.(?:sql|[cm]?[jt]s)$/.test(source))
587
+ file = source;
588
+ }
589
+ return { code, level: 'error', path: file, message: `${file}: ${error.path}: ${error.message}` };
431
590
  }
432
-
433
- const findingKey=finding=>`${finding.code}\n${slash(finding.path)}\n${finding.message}`;
434
-
435
- function trackedGateErrors(entry,context,files){
436
- if(entry.unavailable)return [{code:'STACKS_MODEL_UNAVAILABLE',level:'error',path:entry.file,
437
- message:`${entry.file}: environments.${entry.environment}: a tracked rendered deployment model is required to judge Swarm safety`}];
438
- const result=checkApplicationStacks({repoRoot:context.repoRoot,environment:entry.environment,
439
- deploymentModelFile:path.resolve(context.repoRoot,entry.file),files,repo:context.repo});
440
- return result.errors.map(error=>trackedGateFinding(entry,error));
591
+ const findingKey = (finding) => `${finding.code}\n${slash(finding.path)}\n${finding.message}`;
592
+ function trackedGateErrors(entry, context, files) {
593
+ if (entry.unavailable)
594
+ return [{ code: 'STACKS_MODEL_UNAVAILABLE', level: 'error', path: entry.file,
595
+ message: `${entry.file}: environments.${entry.environment}: a tracked rendered deployment model is required to judge Swarm safety` }];
596
+ const result = checkApplicationStacks({ repoRoot: context.repoRoot, environment: entry.environment,
597
+ deploymentModelFile: path.resolve(context.repoRoot, entry.file), files, repo: context.repo });
598
+ return result.errors.map(error => trackedGateFinding(entry, error));
441
599
  }
442
-
443
600
  /** Run the public gate on the app's tracked rendered Swarm evidence, without duplicating finder results. */
444
- export function trackedStacksGateFindings(context={}){
445
- const {deployments,files}=stacksDeploymentContexts(context),findings=[];
446
- const existing=context.existingFindings??[],seen=new Set(existing.map(findingKey));
447
- for(const entry of deployments){
448
- for(const finding of trackedGateErrors(entry,context,files)){
449
- if(context.inScope&&!context.inScope(finding.path))continue;
450
- const key=findingKey(finding);
451
- if(seen.has(key))continue;
452
- seen.add(key);findings.push(finding);
601
+ export function trackedStacksGateFindings(context = {}) {
602
+ const { deployments, files } = stacksDeploymentContexts(context), findings = [];
603
+ const existing = context.existingFindings ?? [], seen = new Set(existing.map(findingKey));
604
+ for (const entry of deployments) {
605
+ for (const finding of trackedGateErrors(entry, context, files)) {
606
+ if (context.inScope && !context.inScope(finding.path))
607
+ continue;
608
+ const key = findingKey(finding);
609
+ if (seen.has(key))
610
+ continue;
611
+ seen.add(key);
612
+ findings.push(finding);
613
+ }
453
614
  }
454
- }
455
- return findings;
615
+ return findings;
456
616
  }