@starci/hfs 4.2.17 → 4.4.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 (564) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +7 -0
  3. package/db/migrate.mjs +361 -0
  4. package/db/post-cutover.mjs +136 -0
  5. package/emit/db-types.mjs +10 -1
  6. package/lint/run.mjs +1 -1
  7. package/package.json +4 -1
  8. package/release/gate.mjs +298 -0
  9. package/release/index.mjs +103 -0
  10. package/release/push.mjs +110 -0
  11. package/runtime/knowledge/hfs/canon-pins.yaml +11 -11
  12. package/runtime/knowledge/hfs/fe-stack.yaml +22 -1
  13. package/runtime/knowledge/hfs/rules.yaml +3437 -160
  14. package/runtime/knowledge/hfs/slots.yaml +430 -92
  15. package/runtime/knowledge/patterns/be/api.yaml +92 -2
  16. package/runtime/knowledge/patterns/be/cli.yaml +37 -9
  17. package/runtime/knowledge/patterns/be/event-bus.yaml +3 -3
  18. package/runtime/knowledge/patterns/be/jobs.yaml +20 -2
  19. package/runtime/knowledge/patterns/be/projections.yaml +4 -2
  20. package/runtime/knowledge/patterns/be/queues.yaml +11 -3
  21. package/runtime/knowledge/patterns/be/reactors.yaml +20 -1
  22. package/runtime/knowledge/patterns/be/realtime.yaml +25 -3
  23. package/runtime/knowledge/patterns/be/saga.yaml +20 -3
  24. package/runtime/knowledge/patterns/be/webhooks.yaml +210 -36
  25. package/runtime/knowledge/patterns/fe/folder.yaml +4 -0
  26. package/runtime/knowledge/sonar-gate.yaml +1 -1
  27. package/runtime/modules/kernel/failure-codes.yaml +3128 -47
  28. package/runtime/modules/models/runtimes.yaml +1 -1
  29. package/runtime/modules/schemas/application-stacks.schema.yaml +663 -0
  30. package/runtime/packages/cli/src/catalog.generated.mjs +14104 -0
  31. package/runtime/packages/cli/src/validate-args.mjs +213 -0
  32. package/runtime/scripts/api/git/add.mjs +5 -0
  33. package/runtime/scripts/api/git/cat-file.mjs +5 -0
  34. package/runtime/scripts/api/git/init.mjs +7 -0
  35. package/runtime/scripts/api/git/lib.mjs +3 -0
  36. package/runtime/scripts/api/git/tag.mjs +5 -0
  37. package/runtime/scripts/api/node/lib.mjs +20 -0
  38. package/runtime/scripts/api/node/run-node.mjs +6 -0
  39. package/runtime/scripts/api/sops/lib.mjs +41 -0
  40. package/runtime/scripts/api/sops/resolve-sops.mjs +7 -0
  41. package/runtime/scripts/api/sops/seal.mjs +1 -1
  42. package/runtime/scripts/api/sops/selected-age-recipient.mjs +7 -0
  43. package/runtime/scripts/api/supabase/database-dump.mjs +5 -0
  44. package/runtime/scripts/api/supabase/database-query.mjs +5 -0
  45. package/runtime/scripts/api/supabase/lib.mjs +44 -0
  46. package/runtime/scripts/api/supabase/migration-repair.mjs +5 -0
  47. package/runtime/scripts/hfs/app-wiring.mjs +201 -0
  48. package/runtime/scripts/hfs/architecture/backend-app-files.mjs +103 -0
  49. package/runtime/scripts/hfs/architecture/backend.mjs +22 -10
  50. package/runtime/scripts/hfs/architecture/background-unowned.mjs +157 -41
  51. package/runtime/scripts/hfs/architecture/canon-clones.mjs +240 -0
  52. package/runtime/scripts/hfs/architecture/canon-jsx-clones.mjs +143 -0
  53. package/runtime/scripts/hfs/architecture/client-reaches-server.mjs +6 -3
  54. package/runtime/scripts/hfs/architecture/clones.mjs +17 -8
  55. package/runtime/scripts/hfs/architecture/connection-map.mjs +14 -12
  56. package/runtime/scripts/hfs/architecture/context-map.mjs +25 -14
  57. package/runtime/scripts/hfs/architecture/context-owner.mjs +1 -2
  58. package/runtime/scripts/hfs/architecture/dead-exports-bindings.mjs +149 -0
  59. package/runtime/scripts/hfs/architecture/dead-exports.mjs +23 -50
  60. package/runtime/scripts/hfs/architecture/default-deny.mjs +8 -6
  61. package/runtime/scripts/hfs/architecture/frontend-block-render.mjs +104 -0
  62. package/runtime/scripts/hfs/architecture/frontend-world-render.mjs +30 -11
  63. package/runtime/scripts/hfs/architecture/frontend-world.mjs +1 -1
  64. package/runtime/scripts/hfs/architecture/function-spec-providers.mjs +153 -0
  65. package/runtime/scripts/hfs/architecture/hfs.mjs +3 -3
  66. package/runtime/scripts/hfs/architecture/i18n-keys.mjs +16 -3
  67. package/runtime/scripts/hfs/architecture/machine-ast.mjs +17 -0
  68. package/runtime/scripts/hfs/architecture/module-per-transport.mjs +49 -25
  69. package/runtime/scripts/hfs/architecture/next-data-inference.mjs +176 -0
  70. package/runtime/scripts/hfs/architecture/next-data-symbols.mjs +26 -0
  71. package/runtime/scripts/hfs/architecture/next-data.mjs +31 -23
  72. package/runtime/scripts/hfs/architecture/reachability.mjs +14 -0
  73. package/runtime/scripts/hfs/architecture/register-once.mjs +15 -7
  74. package/runtime/scripts/hfs/architecture/required-files.mjs +2 -1
  75. package/runtime/scripts/hfs/architecture/source-names.mjs +48 -3
  76. package/runtime/scripts/hfs/architecture/staged-consumers.mjs +37 -0
  77. package/runtime/scripts/hfs/architecture/supabase-ast.mjs +214 -17
  78. package/runtime/scripts/hfs/architecture/supabase-be.mjs +1 -6
  79. package/runtime/scripts/hfs/architecture/supabase.mjs +59 -21
  80. package/runtime/scripts/hfs/architecture/surface.mjs +2 -1
  81. package/runtime/scripts/hfs/architecture/tiers.mjs +11 -4
  82. package/runtime/scripts/hfs/architecture/typescript.mjs +1 -8
  83. package/runtime/scripts/hfs/architecture/unit-spec-providers.mjs +9 -13
  84. package/runtime/scripts/hfs/check-results.mjs +28 -0
  85. package/runtime/scripts/hfs/check.mjs +63 -72
  86. package/runtime/scripts/hfs/coverage-from.mjs +70 -0
  87. package/runtime/scripts/hfs/db-baseline-verb.mjs +276 -0
  88. package/runtime/scripts/hfs/db-plan-verb.mjs +254 -0
  89. package/runtime/scripts/hfs/db-restamp-verb.mjs +60 -0
  90. package/runtime/scripts/hfs/declaration-shape.mjs +39 -3
  91. package/runtime/scripts/hfs/doc-commands.mjs +367 -0
  92. package/runtime/scripts/hfs/edition-slots.mjs +2 -2
  93. package/runtime/scripts/hfs/example-secrets.mjs +158 -0
  94. package/runtime/scripts/hfs/manifest-shape.mjs +91 -6
  95. package/runtime/scripts/hfs/path-findings.mjs +32 -2
  96. package/runtime/scripts/hfs/refusal-codes.mjs +6 -0
  97. package/runtime/scripts/hfs/rule-catalog.mjs +156 -11
  98. package/runtime/scripts/hfs/rule-params-shape.mjs +22 -10
  99. package/runtime/scripts/hfs/rules/a15-findings.mjs +88 -0
  100. package/runtime/scripts/hfs/rules/app-env-required.mjs +20 -0
  101. package/runtime/scripts/hfs/rules/app-root.mjs +2 -2
  102. package/runtime/scripts/hfs/rules/assets.mjs +79 -0
  103. package/runtime/scripts/hfs/rules/boot-proof.mjs +16 -0
  104. package/runtime/scripts/hfs/rules/canon-copy-drift.mjs +59 -0
  105. package/runtime/scripts/hfs/rules/contract.mjs +66 -5
  106. package/runtime/scripts/hfs/rules/coverage-below.mjs +230 -0
  107. package/runtime/scripts/hfs/rules/database-access.mjs +343 -0
  108. package/runtime/scripts/hfs/rules/database-baseline.mjs +142 -0
  109. package/runtime/scripts/hfs/rules/database-config.mjs +250 -2
  110. package/runtime/scripts/hfs/rules/database-constants.mjs +9 -0
  111. package/runtime/scripts/hfs/rules/database-migrations.mjs +317 -37
  112. package/runtime/scripts/hfs/rules/database-plpgsql.mjs +119 -9
  113. package/runtime/scripts/hfs/rules/database-queue-sources.mjs +176 -0
  114. package/runtime/scripts/hfs/rules/database-queues.mjs +361 -0
  115. package/runtime/scripts/hfs/rules/database-sql.mjs +91 -9
  116. package/runtime/scripts/hfs/rules/database.mjs +11 -2
  117. package/runtime/scripts/hfs/rules/deploy-scan.mjs +192 -0
  118. package/runtime/scripts/hfs/rules/deploy-workflow.mjs +219 -0
  119. package/runtime/scripts/hfs/rules/doc-fresh.mjs +286 -0
  120. package/runtime/scripts/hfs/rules/docker-health.mjs +249 -0
  121. package/runtime/scripts/hfs/rules/docker.mjs +24 -7
  122. package/runtime/scripts/hfs/rules/edition.mjs +11 -10
  123. package/runtime/scripts/hfs/rules/env-parity.mjs +373 -0
  124. package/runtime/scripts/hfs/rules/env-value-shape.mjs +113 -0
  125. package/runtime/scripts/hfs/rules/event-bus.mjs +3 -2
  126. package/runtime/scripts/hfs/rules/event-tables.mjs +100 -0
  127. package/runtime/scripts/hfs/rules/fe-contract-documents.mjs +115 -15
  128. package/runtime/scripts/hfs/rules/fe-stack.mjs +57 -0
  129. package/runtime/scripts/hfs/rules/feature-files.mjs +28 -0
  130. package/runtime/scripts/hfs/rules/frontend-tree.mjs +54 -35
  131. package/runtime/scripts/hfs/rules/integration-specs.mjs +3 -2
  132. package/runtime/scripts/hfs/rules/lexicon.mjs +62 -0
  133. package/runtime/scripts/hfs/rules/lite-proof-lane.mjs +89 -0
  134. package/runtime/scripts/hfs/rules/monorepo.mjs +14 -1
  135. package/runtime/scripts/hfs/rules/pipeline.mjs +8 -1
  136. package/runtime/scripts/hfs/rules/proof-commands.mjs +2 -0
  137. package/runtime/scripts/hfs/rules/release-provenance.mjs +211 -0
  138. package/runtime/scripts/hfs/rules/saga-delivery.mjs +66 -0
  139. package/runtime/scripts/hfs/rules/saga.mjs +8 -16
  140. package/runtime/scripts/hfs/rules/script-hygiene.mjs +151 -0
  141. package/runtime/scripts/hfs/rules/secret-custody-only.mjs +65 -0
  142. package/runtime/scripts/hfs/rules/secrets.mjs +26 -0
  143. package/runtime/scripts/hfs/rules/services.mjs +4 -4
  144. package/runtime/scripts/hfs/rules/stack-tooling.mjs +93 -0
  145. package/runtime/scripts/hfs/rules/stacks-deploy.mjs +76 -0
  146. package/runtime/scripts/hfs/rules/stacks-ingress.mjs +87 -0
  147. package/runtime/scripts/hfs/rules/stacks-lite-backing.mjs +111 -0
  148. package/runtime/scripts/hfs/rules/stacks-release.mjs +366 -0
  149. package/runtime/scripts/hfs/rules/stacks-runtime.mjs +168 -0
  150. package/runtime/scripts/hfs/rules/stacks-swarm.mjs +417 -0
  151. package/runtime/scripts/hfs/rules/stacks.mjs +13 -8
  152. package/runtime/scripts/hfs/rules/supabase-secrets.mjs +1 -1
  153. package/runtime/scripts/hfs/rules/supply-chain.mjs +167 -0
  154. package/runtime/scripts/hfs/rules/unused-symbol-flow.mjs +101 -0
  155. package/runtime/scripts/hfs/rules/unused-symbol-ledger.mjs +77 -0
  156. package/runtime/scripts/hfs/rules/unused-symbol-translators.mjs +165 -0
  157. package/runtime/scripts/hfs/rules/unused-symbol-values.mjs +103 -0
  158. package/runtime/scripts/hfs/rules/unused-symbol.mjs +320 -0
  159. package/runtime/scripts/hfs/runtime-rules/source-ast.mjs +185 -0
  160. package/runtime/scripts/hfs/runtime-rules/workflow-source.mjs +19 -0
  161. package/runtime/scripts/hfs/sealed-members.mjs +101 -0
  162. package/runtime/scripts/hfs/secret-bootstrap-crypto.mjs +200 -0
  163. package/runtime/scripts/hfs/secret-document.mjs +97 -0
  164. package/runtime/scripts/hfs/secret-edit.mjs +170 -0
  165. package/runtime/scripts/hfs/secret-errors.mjs +20 -0
  166. package/runtime/scripts/hfs/secret-files.mjs +124 -0
  167. package/runtime/scripts/hfs/secret-init.mjs +300 -0
  168. package/runtime/scripts/hfs/secret-output.mjs +100 -0
  169. package/runtime/scripts/hfs/secret-sops.mjs +130 -0
  170. package/runtime/scripts/hfs/secret-store.mjs +163 -0
  171. package/runtime/scripts/hfs/secret.mjs +162 -193
  172. package/runtime/scripts/hfs/slot-imports.mjs +18 -3
  173. package/runtime/scripts/hfs/slot-required.mjs +13 -0
  174. package/runtime/scripts/hfs/slot-semantic-problems.mjs +2 -1
  175. package/runtime/scripts/hfs/slots.mjs +1 -0
  176. package/runtime/scripts/hfs/stack-config-kit.mjs +34 -0
  177. package/runtime/scripts/hfs/stack-deploy-config.mjs +313 -0
  178. package/runtime/scripts/hfs/stack-ingress-config.mjs +237 -0
  179. package/runtime/scripts/hfs/stacks-gate.mjs +414 -0
  180. package/runtime/scripts/hfs/starcistacks-runtime.mjs +61 -0
  181. package/runtime/scripts/hfs/tree-findings.mjs +22 -0
  182. package/runtime/scripts/lib/command-tokens.mjs +126 -0
  183. package/runtime/scripts/lib/compose-values.mjs +11 -0
  184. package/runtime/scripts/lib/deployment-findings.mjs +7 -0
  185. package/runtime/scripts/lib/json-clone.mjs +61 -0
  186. package/runtime/scripts/lib/list.mjs +4 -0
  187. package/runtime/scripts/lib/roots.mjs +59 -0
  188. package/runtime/scripts/lib/runtime-example.mjs +13 -0
  189. package/runtime/scripts/lib/secret-patterns.mjs +60 -5
  190. package/runtime/scripts/lib/sql-ast.mjs +32 -0
  191. package/runtime/scripts/lib/stack-services.mjs +1 -1
  192. package/runtime/scripts/lib/swarm-stack.mjs +126 -0
  193. package/runtime/scripts/lib/trim-end.mjs +8 -0
  194. package/runtime/scripts/lib/ts-analysis.mjs +23 -0
  195. package/runtime/scripts/lib/ts-ast.mjs +27 -2
  196. package/runtime/scripts/lib/workflow-events.mjs +8 -0
  197. package/scaffold/add-app.mjs +77 -59
  198. package/scaffold/add-cli-lite.mjs +24 -17
  199. package/scaffold/add-compose.mjs +79 -0
  200. package/scaffold/add-database-lite.mjs +14 -0
  201. package/scaffold/add-fe-overlay.mjs +122 -0
  202. package/scaffold/add-fe-page.mjs +269 -0
  203. package/scaffold/add-inbox-lite.mjs +87 -124
  204. package/scaffold/add-plan.mjs +183 -0
  205. package/scaffold/add-table.mjs +132 -70
  206. package/scaffold/add.mjs +186 -204
  207. package/scaffold/app.mjs +128 -43
  208. package/scaffold/edition-gate.mjs +3 -3
  209. package/scaffold/image.mjs +2 -2
  210. package/scaffold/lite-exports.mjs +2 -1
  211. package/scaffold/service.mjs +2 -2
  212. package/src/main.mjs +238 -119
  213. package/stack/age-install.mjs +37 -0
  214. package/stack/backup.mjs +100 -0
  215. package/stack/build-args.mjs +42 -0
  216. package/stack/build.mjs +173 -0
  217. package/stack/cli.mjs +130 -0
  218. package/stack/config.mjs +57 -0
  219. package/stack/context.mjs +57 -0
  220. package/stack/converge.mjs +107 -0
  221. package/stack/deploy.mjs +301 -0
  222. package/stack/digest.mjs +16 -0
  223. package/stack/docker-transport.mjs +53 -0
  224. package/stack/edge-alias.mjs +118 -0
  225. package/stack/errors.mjs +21 -0
  226. package/stack/image-scan.mjs +152 -0
  227. package/stack/ingress-create.mjs +114 -0
  228. package/stack/ingress-publish.mjs +273 -0
  229. package/stack/ingress.mjs +706 -0
  230. package/stack/jobs.mjs +51 -0
  231. package/stack/live-restore.mjs +177 -0
  232. package/stack/prepare.mjs +266 -0
  233. package/stack/private-file.mjs +22 -0
  234. package/stack/provision.mjs +90 -0
  235. package/stack/prune.mjs +46 -0
  236. package/stack/release-store.mjs +87 -0
  237. package/stack/remote.mjs +205 -0
  238. package/stack/render.mjs +149 -0
  239. package/stack/retire.mjs +47 -0
  240. package/stack/sealed.mjs +149 -0
  241. package/stack/task.mjs +42 -0
  242. package/stack/verify.mjs +99 -0
  243. package/sync/ci-commands.mjs +13 -0
  244. package/sync/index.mjs +127 -64
  245. package/sync/stack-images.mjs +127 -0
  246. package/templates/app/ci-workflows/github/workflows/ci.yml +5 -1
  247. package/templates/app/ci-workflows/github/workflows/images.yml +15 -3
  248. package/templates/app/ci-workflows/github/workflows/stack-deploy.yml +148 -0
  249. package/templates/app/ci-workflows-lite/github/workflows/ci.yml +7 -3
  250. package/templates/app/ci-workflows-lite/github/workflows/db-deploy.yml +38 -6
  251. package/templates/app/ci-workflows-lite/github/workflows/images.yml +14 -2
  252. package/templates/app/ci-workflows-lite/github/workflows/stack-deploy.yml +148 -0
  253. package/templates/app/codegen-graphql.mjs.partial +13 -2
  254. package/templates/app/codegen-lite/scripts/codegen.mjs +6 -1
  255. package/templates/app/codegen-prelude.mjs.partial +8 -0
  256. package/templates/app/gitignore +4 -0
  257. package/templates/app/gitignore-lite +4 -0
  258. package/templates/app/hooks/husky/pre-commit +7 -12
  259. package/templates/app/package-scripts/package.json +1 -0
  260. package/templates/app/package-scripts/supabase.json +7 -0
  261. package/templates/app/package-scripts-lite/package.json +1 -7
  262. package/templates/app/skeleton/.editorconfig +1 -0
  263. package/templates/app/skeleton/.gitattributes +7 -1
  264. package/templates/app/skeleton/.github/dependabot.yml +17 -0
  265. package/templates/app/skeleton/.nvmrc +1 -1
  266. package/templates/app/skeleton/.sops.yaml +1 -1
  267. package/templates/app/skeleton/.starcistacks/application-stacks.yaml +134 -1
  268. package/templates/app/skeleton/.starcistacks/dev/README.md +16 -0
  269. package/templates/app/skeleton/.starcistacks/dev/environment.json +5 -0
  270. package/templates/app/skeleton/.starcistacks/dev/infra/compose/stack.yaml +71 -0
  271. package/templates/app/skeleton/.starcistacks/dev/runtime/env/KEYS.md +14 -0
  272. package/templates/app/skeleton/.starcistacks/production/README.md +37 -0
  273. package/templates/app/skeleton/.starcistacks/production/environment.json +5 -0
  274. package/templates/app/skeleton/.starcistacks/production/infra/swarm/Caddyfile +15 -0
  275. package/templates/app/skeleton/.starcistacks/production/infra/swarm/owner-actions.md +11 -0
  276. package/templates/app/skeleton/.starcistacks/production/infra/swarm/recovery/restore-proof.md +7 -0
  277. package/templates/app/skeleton/.starcistacks/production/infra/swarm/releases/current.yaml +6 -0
  278. package/templates/app/skeleton/.starcistacks/production/infra/swarm/releases/images.lock.yaml +9 -0
  279. package/templates/app/skeleton/.starcistacks/production/infra/swarm/releases/previous.yaml +6 -0
  280. package/templates/app/skeleton/.starcistacks/production/infra/swarm/stack.yaml +142 -0
  281. package/templates/app/skeleton/.starcistacks/production/runtime/env/KEYS.md +14 -0
  282. package/templates/app/skeleton/README.md +6 -6
  283. package/templates/app/skeleton/scripts/codegen.mjs +1 -2
  284. package/templates/app/skeleton-lite/.editorconfig +16 -0
  285. package/templates/app/skeleton-lite/.gitattributes +8 -0
  286. package/templates/app/skeleton-lite/.github/dependabot.yml +17 -0
  287. package/templates/app/skeleton-lite/.nvmrc +1 -0
  288. package/templates/app/skeleton-lite/.sops.yaml +3 -2
  289. package/templates/app/skeleton-lite/.starcistacks/application-stacks.yaml +193 -0
  290. package/templates/app/skeleton-lite/.starcistacks/dev/README.md +20 -0
  291. package/templates/app/skeleton-lite/.starcistacks/dev/environment.json +2 -1
  292. package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/Caddyfile +16 -0
  293. package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/owner-actions.md +11 -0
  294. package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/recovery/restore-proof.md +7 -0
  295. package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/releases/current.yaml +6 -0
  296. package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/releases/images.lock.yaml +8 -0
  297. package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/releases/previous.yaml +6 -0
  298. package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/stack.yaml +120 -0
  299. package/templates/app/skeleton-lite/.starcistacks/dev/runtime/env/KEYS.md +18 -0
  300. package/templates/app/skeleton-lite/.starcistacks/production/README.md +38 -0
  301. package/templates/app/skeleton-lite/.starcistacks/production/environment.json +6 -0
  302. package/templates/app/skeleton-lite/.starcistacks/production/infra/swarm/Caddyfile +11 -0
  303. package/templates/app/skeleton-lite/.starcistacks/production/infra/swarm/owner-actions.md +11 -0
  304. package/templates/app/skeleton-lite/.starcistacks/production/infra/swarm/recovery/restore-proof.md +7 -0
  305. package/templates/app/skeleton-lite/.starcistacks/production/infra/swarm/releases/current.yaml +6 -0
  306. package/templates/app/skeleton-lite/.starcistacks/production/infra/swarm/releases/images.lock.yaml +8 -0
  307. package/templates/app/skeleton-lite/.starcistacks/production/infra/swarm/releases/previous.yaml +6 -0
  308. package/templates/app/skeleton-lite/.starcistacks/production/infra/swarm/stack.yaml +121 -0
  309. package/templates/app/skeleton-lite/.starcistacks/production/runtime/env/KEYS.md +21 -0
  310. package/templates/app/skeleton-lite/.starciwork/brand/index.yaml +12 -0
  311. package/templates/app/skeleton-lite/.starciwork/workspace.yaml +9 -0
  312. package/templates/app/skeleton-lite/README.md +8 -4
  313. package/templates/app/skeleton-lite/supabase/config.toml +8 -1
  314. package/templates/app/skeleton-lite/supabase/migrations/__timestamp___baseline.sql +15 -6
  315. package/templates/app/starciwork.gitignore-lite +6 -0
  316. package/templates/app/upgrade-full/.starcistacks/dev/README.md +13 -7
  317. package/templates/be/image/worker/Dockerfile +1 -1
  318. package/templates/be/inbox/database-client.spec.ts.tpl +22 -28
  319. package/templates/be/inbox/database-client.ts.tpl +14 -28
  320. package/templates/be/inbox/delivery-recorded.event.ts.tpl +36 -0
  321. package/templates/be/inbox/event-index.ts.tpl +1 -0
  322. package/templates/be/inbox/events.json.tpl +10 -0
  323. package/templates/be/inbox/migration.sql.tpl +1 -0
  324. package/templates/be/inbox/platform/event-bus/event-bus.contracts.ts.tpl +42 -0
  325. package/templates/be/inbox/platform/event-bus/event-bus.decorators.ts.tpl +9 -0
  326. package/templates/be/inbox/platform/event-bus/event-bus.module-definition.ts.tpl +6 -0
  327. package/templates/be/inbox/platform/event-bus/event-bus.module.ts.tpl +19 -0
  328. package/templates/be/inbox/platform/event-bus/event-bus.options.ts.tpl +2 -0
  329. package/templates/be/inbox/platform/event-bus/event-bus.port.ts.tpl +16 -0
  330. package/templates/be/inbox/platform/event-bus/event-bus.service.spec.ts.tpl +45 -0
  331. package/templates/be/inbox/platform/event-bus/event-bus.service.ts.tpl +15 -0
  332. package/templates/be/inbox/platform/event-bus/index.ts.tpl +5 -0
  333. package/templates/be/inbox/platform/event-bus/persistence/event-bus.sql.ts.tpl +5 -0
  334. package/templates/be/inbox/platform/inbox/inbox.decorators.ts.tpl +9 -0
  335. package/templates/be/inbox/platform/inbox/inbox.module-definition.ts.tpl +6 -0
  336. package/templates/be/inbox/platform/inbox/inbox.module.ts.tpl +19 -0
  337. package/templates/be/inbox/platform/inbox/inbox.options.ts.tpl +2 -0
  338. package/templates/be/inbox/platform/inbox/inbox.port.ts.tpl +10 -0
  339. package/templates/be/inbox/platform/inbox/inbox.service.spec.ts.tpl +41 -0
  340. package/templates/be/inbox/platform/inbox/inbox.service.ts.tpl +19 -0
  341. package/templates/be/inbox/platform/inbox/index.ts.tpl +3 -0
  342. package/templates/be/inbox/platform/inbox/persistence/inbox.sql.ts.tpl +7 -0
  343. package/templates/be/inbox/platform/migration.sql.tpl +48 -0
  344. package/templates/be/inbox/service.spec.ts.tpl +49 -20
  345. package/templates/be/inbox/service.ts.tpl +27 -16
  346. package/templates/be/inbox/sql.ts.tpl +2 -2
  347. package/templates/be/inbox/webhook-door.spec.ts.tpl +3 -3
  348. package/templates/be/inbox/webhook-door.ts.tpl +4 -3
  349. package/templates/be/patterns/api/feature.module-definition.ts.tpl +6 -0
  350. package/templates/be/patterns/api/feature.module.ts.tpl +2 -1
  351. package/templates/be/patterns/api/feature.options.ts.tpl +2 -0
  352. package/templates/be/patterns/api/graphql.module-definition.ts.tpl +6 -0
  353. package/templates/be/patterns/api/graphql.module.ts.tpl +3 -2
  354. package/templates/be/patterns/api/graphql.options.ts.tpl +2 -0
  355. package/templates/be/patterns/cli/group.module-definition.ts.tpl +6 -0
  356. package/templates/be/patterns/cli/group.module.ts.tpl +2 -1
  357. package/templates/be/patterns/cli/group.options.ts.tpl +2 -0
  358. package/templates/be/patterns/event-bus/platform/errors/event-bus.error.ts.tpl +0 -3
  359. package/templates/be/patterns/event-bus/platform/messages/event-bus.messages.ts.tpl +0 -2
  360. package/templates/be/patterns/jobs/job.module-definition.ts.tpl +6 -0
  361. package/templates/be/patterns/jobs/job.module.ts.tpl +2 -1
  362. package/templates/be/patterns/jobs/job.options.ts.tpl +2 -0
  363. package/templates/be/patterns/jobs/job.queue.module-definition.ts.tpl +6 -0
  364. package/templates/be/patterns/jobs/job.queue.module.ts.tpl +6 -3
  365. package/templates/be/patterns/jobs/job.queue.options.ts.tpl +2 -0
  366. package/templates/be/patterns/jobs/platform/fenced.processor.spec.ts.tpl +3 -1
  367. package/templates/be/patterns/outbox/platform/outbox-relay.policy.spec.ts.tpl +25 -9
  368. package/templates/be/patterns/queues/platform/queue.config.ts.tpl +1 -1
  369. package/templates/be/patterns/queues/platform/queue.index.ts.tpl +1 -1
  370. package/templates/be/patterns/queues/platform/queue.module-definition.ts.tpl +2 -2
  371. package/templates/be/patterns/queues/platform/queue.module.ts.tpl +11 -0
  372. package/templates/be/patterns/queues/platform/queue.options.ts.tpl +10 -0
  373. package/templates/be/patterns/queues/queue.module-definition.ts.tpl +6 -0
  374. package/templates/be/patterns/queues/queue.module.ts.tpl +2 -1
  375. package/templates/be/patterns/queues/queue.options.ts.tpl +2 -0
  376. package/templates/be/patterns/reactors/message.module-definition.ts.tpl +6 -0
  377. package/templates/be/patterns/reactors/message.module.ts.tpl +6 -3
  378. package/templates/be/patterns/reactors/message.options.ts.tpl +2 -0
  379. package/templates/be/patterns/reactors/reactor.module-definition.ts.tpl +6 -0
  380. package/templates/be/patterns/reactors/reactor.module.ts.tpl +2 -1
  381. package/templates/be/patterns/reactors/reactor.options.ts.tpl +2 -0
  382. package/templates/be/patterns/realtime/channel.gateway.spec.ts.tpl +188 -0
  383. package/templates/be/patterns/realtime/channel.gateway.ts.tpl +22 -0
  384. package/templates/be/patterns/realtime/channel.graphql.module-definition.ts.tpl +6 -0
  385. package/templates/be/patterns/realtime/channel.graphql.module.ts.tpl +2 -1
  386. package/templates/be/patterns/realtime/channel.graphql.options.ts.tpl +2 -0
  387. package/templates/be/patterns/realtime/channel.websocket.module-definition.ts.tpl +6 -0
  388. package/templates/be/patterns/realtime/channel.websocket.module.ts.tpl +110 -0
  389. package/templates/be/patterns/realtime/channel.websocket.options.ts.tpl +7 -0
  390. package/templates/be/patterns/saga/saga.message.module-definition.ts.tpl +6 -0
  391. package/templates/be/patterns/saga/saga.message.module.ts.tpl +6 -3
  392. package/templates/be/patterns/saga/saga.message.options.ts.tpl +2 -0
  393. package/templates/be/patterns/saga/saga.module-definition.ts.tpl +6 -0
  394. package/templates/be/patterns/saga/saga.module.ts.tpl +2 -1
  395. package/templates/be/patterns/saga/saga.options.ts.tpl +2 -0
  396. package/templates/be/patterns/webhooks/webhook.door.spec.ts.tpl +3 -3
  397. package/templates/be/patterns/webhooks/webhook.door.ts.tpl +3 -3
  398. package/templates/be/patterns/webhooks/webhook.http.module-definition.ts.tpl +6 -0
  399. package/templates/be/patterns/webhooks/webhook.http.module.ts.tpl +2 -1
  400. package/templates/be/patterns/webhooks/webhook.http.options.ts.tpl +2 -0
  401. package/templates/be/platform/graphql/localize-errors.mapper.spec.ts +80 -26
  402. package/templates/be/platform/graphql/localize-errors.mapper.ts +2 -5
  403. package/templates/be/skeleton/apps/__app__/src/__app__.env.ts +21 -0
  404. package/templates/be/skeleton/apps/__app__/src/__app__.options.ts +1 -1
  405. package/templates/be/skeleton/apps/__app__/src/app.module.ts +15 -45
  406. package/templates/be/skeleton/apps/__app__/src/compose/api.ts +4 -0
  407. package/templates/be/skeleton/apps/__app__/src/compose/domain.ts +11 -0
  408. package/templates/be/skeleton/apps/__app__/src/compose/platform.ts +44 -0
  409. package/templates/be/skeleton/apps/__app__/src/compose/system.ts +5 -0
  410. package/templates/be/skeleton/apps/__app__/src/compose/webhooks.ts +4 -0
  411. package/templates/be/skeleton/apps/__app__/src/main.ts +18 -10
  412. package/templates/be/skeleton/apps/cli/src/app.module.ts +3 -13
  413. package/templates/be/skeleton/apps/cli/src/cli.env.ts +13 -0
  414. package/templates/be/skeleton/apps/cli/src/cli.options.ts +7 -17
  415. package/templates/be/skeleton/apps/cli/src/compose/cli.ts +5 -0
  416. package/templates/be/skeleton/apps/cli/src/compose/platform.ts +24 -0
  417. package/templates/be/skeleton/apps/cli/src/main.ts +3 -2
  418. package/templates/be/skeleton/src/features/api/system-health/application/check-readiness.contracts.ts +2 -0
  419. package/templates/be/skeleton/src/features/api/system-health/application/check-readiness.handler.ts +22 -0
  420. package/templates/be/skeleton/src/features/api/system-health/application/check-readiness.query.ts +11 -0
  421. package/templates/be/skeleton/src/features/api/system-health/system-health.module-definition.ts +6 -0
  422. package/templates/be/skeleton/src/features/api/system-health/system-health.module.ts +5 -3
  423. package/templates/be/skeleton/src/features/api/system-health/system-health.options.ts +2 -0
  424. package/templates/be/skeleton/src/features/api/system-health/transport/http/ready.controller.ts +19 -0
  425. package/templates/be/skeleton/src/features/api/system-health/transport/http/system-health-http.module-definition.ts +6 -0
  426. package/templates/be/skeleton/src/features/api/system-health/transport/http/system-health-http.module.ts +8 -3
  427. package/templates/be/skeleton/src/features/api/system-health/transport/http/system-health-http.options.ts +2 -0
  428. package/templates/be/skeleton/src/features/cli/cli.module-definition.ts +6 -0
  429. package/templates/be/skeleton/src/features/cli/cli.module.ts +3 -2
  430. package/templates/be/skeleton/src/features/cli/cli.options.ts +2 -0
  431. package/templates/be/skeleton/src/features/cli/migrate/migrate.module-definition.ts +6 -0
  432. package/templates/be/skeleton/src/features/cli/migrate/migrate.module.ts +2 -1
  433. package/templates/be/skeleton/src/features/cli/migrate/migrate.options.ts +2 -0
  434. package/templates/be/skeleton/src/features/cli/seed/seed.module-definition.ts +6 -0
  435. package/templates/be/skeleton/src/features/cli/seed/seed.module.ts +2 -1
  436. package/templates/be/skeleton/src/features/cli/seed/seed.options.ts +2 -0
  437. package/templates/be/skeleton/src/features/cli/seed/subs/run.cli.spec.ts +5 -6
  438. package/templates/be/skeleton/src/modules/domain/identity/admission.policy.spec.ts +56 -4
  439. package/templates/be/skeleton/src/modules/domain/identity/admission.policy.ts +36 -5
  440. package/templates/be/skeleton/src/modules/domain/identity/auth.guard.spec.ts +39 -8
  441. package/templates/be/skeleton/src/modules/domain/identity/auth.guard.ts +14 -8
  442. package/templates/be/skeleton/src/modules/domain/identity/credential.contracts.ts +26 -0
  443. package/templates/be/skeleton/src/modules/domain/identity/credential.guard.spec.ts +119 -0
  444. package/templates/be/skeleton/src/modules/domain/identity/credential.guard.ts +43 -0
  445. package/templates/be/skeleton/src/modules/domain/identity/identity.contracts.ts +16 -0
  446. package/templates/be/skeleton/src/modules/domain/identity/identity.decorators.ts +18 -1
  447. package/templates/be/skeleton/src/modules/domain/identity/identity.module.ts +22 -4
  448. package/templates/be/skeleton/src/modules/domain/identity/identity.options.ts +8 -2
  449. package/templates/be/skeleton/src/modules/domain/identity/index.ts +1 -0
  450. package/templates/be/skeleton/src/modules/platform/config/env-source.config.ts +6 -6
  451. package/templates/be/skeleton/src/modules/platform/database/connection-source.client.spec.ts +11 -4
  452. package/templates/be/skeleton/src/modules/platform/database/database-probe.service.spec.ts +32 -0
  453. package/templates/be/skeleton/src/modules/platform/database/database-probe.service.ts +14 -0
  454. package/templates/be/skeleton/src/modules/platform/database/database.module.ts +7 -1
  455. package/templates/be/skeleton/src/modules/platform/database/index.ts +2 -0
  456. package/templates/be/skeleton/src/modules/platform/database/primary.decorators.ts +8 -0
  457. package/templates/be/skeleton/src/modules/platform/database/seed-connections.client.spec.ts +25 -6
  458. package/templates/be/skeleton/src/modules/platform/errors/errors.filter.spec.ts +16 -6
  459. package/templates/be/skeleton/src/modules/platform/http-security/edge-security.service.spec.ts +130 -0
  460. package/templates/be/skeleton/src/modules/platform/http-security/edge-security.service.ts +58 -0
  461. package/templates/be/skeleton/src/modules/platform/http-security/errors/http-security.error.ts +3 -0
  462. package/templates/be/skeleton/src/modules/platform/http-security/execution-request.mapper.spec.ts +16 -4
  463. package/templates/be/skeleton/src/modules/platform/http-security/http-security.config.ts +16 -10
  464. package/templates/be/skeleton/src/modules/platform/http-security/http-security.module.ts +3 -1
  465. package/templates/be/skeleton/src/modules/platform/http-security/http-security.options.ts +2 -0
  466. package/templates/be/skeleton/src/modules/platform/http-security/index.ts +5 -0
  467. package/templates/be/skeleton/src/modules/platform/http-security/messages/http-security.messages.ts +12 -5
  468. package/templates/be/skeleton/src/modules/platform/http-security/rate-limit.guard.spec.ts +13 -8
  469. package/templates/be/skeleton/src/modules/platform/http-security/readiness.contracts.ts +5 -0
  470. package/templates/be/skeleton/src/modules/platform/http-security/readiness.decorators.ts +7 -0
  471. package/templates/be/skeleton/src/modules/platform/http-security/readiness.service.spec.ts +86 -0
  472. package/templates/be/skeleton/src/modules/platform/http-security/readiness.service.ts +39 -0
  473. package/templates/be/skeleton/src/modules/platform/http-security/webhook-signature.service.spec.ts +139 -131
  474. package/templates/be/skeleton/src/modules/platform/i18n/index.ts +1 -1
  475. package/templates/be/skeleton/src/modules/platform/logging/json-logger.service.spec.ts +91 -67
  476. package/templates/be/skeleton/src/modules/platform/logging/json-logger.service.ts +5 -4
  477. package/templates/be/skeleton/src/modules/platform/logging/logging.log-events.ts +0 -2
  478. package/templates/be/skeleton/src/modules/platform/primitives/outcome.mapper.spec.ts +20 -6
  479. package/templates/be/skeleton/src/modules/platform/primitives/sequence.policy.spec.ts +16 -4
  480. package/templates/be/skeleton/src/modules/platform/retry/index.ts +3 -0
  481. package/templates/be/skeleton/src/modules/platform/retry/retry.decorators.ts +9 -0
  482. package/templates/be/skeleton/src/modules/platform/retry/retry.module-definition.ts +7 -0
  483. package/templates/be/skeleton/src/modules/platform/retry/retry.module.ts +19 -0
  484. package/templates/be/skeleton/src/modules/platform/retry/retry.options.ts +2 -0
  485. package/templates/be/skeleton/src/modules/platform/retry/retry.port.ts +11 -0
  486. package/templates/be/skeleton/src/modules/platform/retry/system-timer.service.spec.ts +36 -0
  487. package/templates/be/skeleton/src/modules/platform/retry/system-timer.service.ts +10 -0
  488. package/templates/be/skeleton/src/tests/contract/http-security/http-security.contract-spec.ts +54 -0
  489. package/templates/be/skeleton-lite/apps/api/src/api.env.ts +25 -0
  490. package/templates/be/skeleton-lite/apps/api/src/api.options.ts +1 -1
  491. package/templates/be/skeleton-lite/apps/api/src/app.module.ts +18 -58
  492. package/templates/be/skeleton-lite/apps/api/src/compose/api.ts +4 -0
  493. package/templates/be/skeleton-lite/apps/api/src/compose/domain.ts +10 -0
  494. package/templates/be/skeleton-lite/apps/api/src/compose/integrations.ts +16 -0
  495. package/templates/be/skeleton-lite/apps/api/src/compose/platform.ts +47 -0
  496. package/templates/be/skeleton-lite/apps/api/src/compose/system.ts +5 -0
  497. package/templates/be/skeleton-lite/apps/api/src/compose/webhooks.ts +4 -0
  498. package/templates/be/skeleton-lite/apps/api/src/main.ts +5 -13
  499. package/templates/be/skeleton-lite/apps/cli/src/cli.env.ts +17 -0
  500. package/templates/be/skeleton-lite/apps/cli/src/compose/platform.ts +12 -0
  501. package/templates/be/skeleton-lite/src/modules/domain/identity/admission.policy.spec.ts +71 -5
  502. package/templates/be/skeleton-lite/src/modules/domain/identity/admission.policy.ts +29 -2
  503. package/templates/be/skeleton-lite/src/modules/domain/identity/auth.guard.spec.ts +68 -15
  504. package/templates/be/skeleton-lite/src/modules/domain/identity/auth.guard.ts +14 -9
  505. package/templates/be/skeleton-lite/src/modules/domain/identity/identity.contracts.ts +9 -2
  506. package/templates/be/skeleton-lite/src/modules/domain/identity/identity.decorators.ts +16 -1
  507. package/templates/be/skeleton-lite/src/modules/domain/identity/identity.module.ts +19 -4
  508. package/templates/be/skeleton-lite/src/modules/domain/identity/identity.options.ts +5 -1
  509. package/templates/be/skeleton-lite/src/modules/domain/identity/index.ts +2 -1
  510. package/templates/be/skeleton-lite/src/modules/integrations/supabase/errors/supabase.error.ts +0 -3
  511. package/templates/be/skeleton-lite/src/modules/integrations/supabase/supabase.client.spec.ts +17 -5
  512. package/templates/be/skeleton-lite/src/modules/platform/database/database.module.ts +24 -13
  513. package/templates/be/skeleton-lite/src/modules/platform/database/database.port.ts +5 -0
  514. package/templates/be/skeleton-lite/src/modules/platform/database/errors/database.error.ts +16 -0
  515. package/templates/be/skeleton-lite/src/modules/platform/database/index.ts +3 -0
  516. package/templates/be/skeleton-lite/src/modules/platform/http-security/http-security.module.ts +5 -2
  517. package/templates/be/skeleton-lite/src/modules/platform/http-security/index.ts +4 -1
  518. package/templates/be/skeleton-lite/src/modules/platform/i18n/index.ts +1 -1
  519. package/templates/be/skeleton-lite/src/modules/platform/logging/logging.log-events.ts +7 -0
  520. package/templates/be/skeleton-lite/src/tests/fixtures/builders/api.builder.ts +29 -0
  521. package/templates/be/skeleton-lite/src/tests/fixtures/builders/cli.builder.ts +16 -0
  522. package/templates/be/table/api/feature.module-definition.ts.tpl +6 -0
  523. package/templates/be/table/api/feature.module.ts.tpl +2 -1
  524. package/templates/be/table/api/feature.options.ts.tpl +2 -0
  525. package/templates/be/table/api/graphql.module-definition.ts.tpl +6 -0
  526. package/templates/be/table/api/graphql.module.ts.tpl +3 -2
  527. package/templates/be/table/api/graphql.options.ts.tpl +2 -0
  528. package/templates/be/table/migration.sql.tpl +1 -0
  529. package/templates/be/upgrade-full/apps/cli/src/app.module.ts +4 -6
  530. package/templates/be/upgrade-full/apps/cli/src/cli.env.ts +7 -0
  531. package/templates/be/upgrade-full/apps/cli/src/compose/cli.ts +5 -0
  532. package/templates/be/upgrade-full/apps/cli/src/compose/platform.ts +9 -0
  533. package/templates/be/upgrade-full/apps/cli/src/main.ts +3 -0
  534. package/templates/be/upgrade-full/src/features/cli/cli.module.ts +3 -2
  535. package/templates/fe/auth/supabase-client.ts.tpl +34 -0
  536. package/templates/fe/skeleton/apps/__app__/src/app/auth/callback/route.ts +11 -0
  537. package/templates/fe/skeleton/apps/__app__/src/components/blocks/SignInForm/index.tsx +6 -1
  538. package/templates/fe/skeleton/apps/__app__/src/components/leaves/Icon/index.tsx +4 -0
  539. package/templates/fe/skeleton/apps/__app__/src/features/overlays/AppGuideOverlay/component.tsx +31 -0
  540. package/templates/fe/skeleton/apps/__app__/src/features/overlays/AppGuideOverlay/index.tsx +24 -0
  541. package/templates/fe/skeleton/apps/__app__/src/hooks/auth/useSession.ts +6 -1
  542. package/templates/fe/skeleton/apps/__app__/src/hooks/auth/useSignOut.ts +3 -4
  543. package/templates/fe/skeleton/apps/__app__/src/hooks/zustand/appGuide/index.ts +1 -0
  544. package/templates/fe/skeleton/apps/__app__/src/hooks/zustand/appGuide/store.ts +15 -0
  545. package/templates/fe/skeleton/apps/__app__/src/modules/auth/pkce-client.ts +18 -0
  546. package/templates/fe/skeleton/apps/__app__/src/modules/auth/server.ts +19 -0
  547. package/templates/fe/skeleton/apps/app/src/features/pages/AppHomePage/index.tsx +7 -1
  548. package/templates/fe/skeleton/apps/app/src/modules/i18n/messages/en.json +57 -0
  549. package/templates/fe/skeleton/apps/app/src/modules/i18n/messages/vi.json +6 -0
  550. package/templates/fe/skeleton/apps/landing/src/modules/i18n/messages/en.json +31 -0
  551. package/templates/fe/skeleton/packages/__project__-i18n/src/routing.ts +1 -1
  552. package/templates/fe/skeleton/packages/__project__-ui/src/index.ts +1 -0
  553. package/templates/fe/skeleton-lite/apps/__app__/src/modules/auth/client.ts +1 -34
  554. package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppHomePage/index.tsx +7 -1
  555. package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/messages/en.json +57 -0
  556. package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/messages/vi.json +30 -24
  557. package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/routing.ts +1 -1
  558. package/templates/fe/transport/graphql/clients/clients/auth.ts +1 -1
  559. package/templates/fe/transport/graphql/clients/links/locale.ts +2 -2
  560. package/upgrade/check.mjs +3 -1
  561. package/upgrade/file-state.mjs +68 -0
  562. package/upgrade/index.mjs +58 -94
  563. package/runtime/scripts/api/git/log.mjs +0 -5
  564. package/templates/app/skeleton-lite/.starciwork/shell/index.yaml +0 -55
@@ -3,25 +3,34 @@ id: be.feature.webhooks
3
3
  title: Provider webhooks
4
4
  purpose: |-
5
5
  Given a delivery a payment, identity or shipping provider POSTs to us, how is it received? A webhook is a feature KIND:
6
- `be/src/features/webhooks/<provider>/` holds one signed door that proves the delivery came from the provider (signature and
7
- replay window), then hands it to exactly ONE domain intake method. That method records the delivery (deduplicated through the
8
- inbox) and publishes the domain event with `eventBus.publish(event, tx)` inside its own transaction, because publishing is
9
- allowed only from `modules/domain`. The door writes nothing and decides nothing; everything after the proof is an event.
6
+ `be/src/features/webhooks/<provider>/` holds one signed door whose first call proves the delivery came from the provider
7
+ (signature and replay window), either separately or inside its ONE domain method that combines verification and intake. The
8
+ intake records the delivery (deduplicated through the inbox) and publishes a typed receipt event with
9
+ `eventBus.publish(event, tx)` inside its own transaction, because publishing is allowed only from `modules/domain`.
10
+ A reactor or job consumes the durable receipt and dispatches the processing command. Neither the door, its HTTP module,
11
+ nor its called domain intake dispatches on a command/query bus or feature handler. A provider may receive a declared fixed `PROVIDER_ACK` body;
12
+ a subscription provider may also use one GET verifier that echoes its challenge result.
10
13
  appliesTo:
11
14
  - backend
12
15
  family: be
13
16
  provenance:
14
- reviewedAt: 2026-10-02
17
+ reviewedAt: 2026-10-14
15
18
  sources:
16
19
  - knowledge/hfs/README.md
17
20
  - knowledge/hfs/slots.yaml
18
21
  - knowledge/hfs/rules.yaml
19
22
  - knowledge/patterns/be/api-auth.yaml
20
23
  - knowledge/patterns/be/idempotency.yaml
24
+ - knowledge/patterns/be/event-bus.yaml
25
+ - knowledge/patterns/be/reactors.yaml
26
+ - knowledge/patterns/be/jobs.yaml
21
27
  - owner decision 2026-10-02 (feature kinds: webhooks, realtime, reactors, saga, jobs, cli; no kind imports another)
28
+ - owner decision 2026-10-14 (literal provider ACK, one optional GET challenge, durable inbox handoff before reactor/job command dispatch)
22
29
  limitations: |-
23
- Static checks prove the door's shape, that the proof is its first statement and that the only thing it can call after it is
24
- one domain service method. They do not prove the HMAC scheme matches the provider's documentation, that the replay window
30
+ Static checks prove the door's shape, that an unconditional verification call is its first statement and that it makes
31
+ one intake call, either after a separate proof or combined with it. They resolve direct command/query-bus and feature-handler receivers in the called
32
+ domain intake; helper calls, dynamic receivers and unavailable method bodies need semantic review. They do not prove that a domain verifier completes proof
33
+ before intake, that the signature scheme matches the provider's documentation, that the replay window
25
34
  is the right length or that the intake is idempotent; the provider fake of the test world and the three
26
35
  `webhooks/*` scenarios prove those against the real stack.
27
36
  rules:
@@ -33,7 +42,8 @@ rules:
33
42
  A provider webhook lives in `be/src/features/webhooks/<provider>/` (slot `be.feature.webhooks`), enabled by declaring the pattern
34
43
  `webhooks` in `hfs.json` `sides.be.patterns`. The folder holds `index.ts` (the module class an app composes), `transport/http/<provider>-http.module.ts`, `transport/http/<provider>.webhook.ts` (the
35
44
  door), `<provider>.webhook.spec.ts` (its spec — the kind's door has no service of its own, so the door carries it) and an optional `transport/http/dto/` of typed
36
- payload classes (`<event>.request.ts`). Nothing else exists there: no application layer, no handler, no consumer. The kind
45
+ payload classes (`<event>.request.ts`). `PROVIDER_ACK`, when needed, is an exported typed literal constant in the door or
46
+ its own HTTP module. Nothing else exists there: no application layer, no handler, no consumer. The kind
37
47
  imports only `modules/*`, never another kind, and only an api-kind app composes `<provider>-http.module.ts`.
38
48
  rationale: |-
39
49
  A webhook is an external caller we do not control, so its door is kept apart from the api features a client uses. One place
@@ -76,24 +86,130 @@ rules:
76
86
  - Confirm the provider endpoint is one door and that a second endpoint of the same provider is a second door only when it has a different proof.
77
87
  relatedExamples: [webhooks]
78
88
  - id: BE-WEBHOOK-2
79
- title: The door verifies first, then makes one domain call
89
+ title: The door verifies first and hands the delivery to one domain intake
80
90
  kind: mandatory
81
- hfsRules: [R182, R183, R41, R42, R203]
91
+ hfsRules: [R182, R183, R41, R42, R203, R263]
82
92
  requirement: |-
83
- `<provider>.webhook.ts` is one `@Controller` with one `@Post` handler declared
84
- `@Public({ reason: PublicReason.SignedWebhook })`, thin (R203 `BE_FEATURE_THIN`). The handler's FIRST statement is the unconditional
85
- `this.<signature>.verify({ provider, rawBody, signature, timestamp })` of the `WebhookSignatureService` of `platform/http-security`
86
- (HMAC-SHA256 over `<timestamp>.<raw body>` with the provider's secret, a constant-time comparison and a replay window on the signed timestamp; it throws before anything else runs). The provider key and its secret and tolerance are options of `platform/http-security` (`options.webhooks[provider]`). The
87
- second and last call is one method of a domain service, the intake. The door injects only that service and the signature
88
- service (injected with `@InjectWebhookSignature()`), holds no branch, loop or `try`, calls nothing else (no parsing, mapping or publishing) and returns nothing: the
89
- provider gets its status code and an empty body. `@RateLimit(RateTier.Strict)` is required by the signed-webhook reason.
93
+ `<provider>.webhook.ts` is one `@Controller` with exactly one `@Post` handler and optionally one `@Get` subscription
94
+ challenge handler. Each handler is declared
95
+ `@Public({ reason: PublicReason.SignedWebhook })`, thin (R203 `BE_FEATURE_THIN`). The handler's FIRST statement is an unconditional
96
+ direct verification call, awaited when async: either `this.<signature>.verify({ provider, rawBody, signature, timestamp })`
97
+ of the `WebhookSignatureService` of `platform/http-security` or a domain verifier. No data access, nested call or other work
98
+ may run in the door before that proof call. The shared service uses HMAC-SHA256 over `<timestamp>.<raw body>`, a constant-time
99
+ comparison and a replay window on the signed timestamp; its provider key, secret and tolerance are `platform/http-security`
100
+ options (`options.webhooks[provider]`). A domain verifier owns the provider's proof when secrets belong to individual
101
+ connections, as for the billing payment webhooks; it refuses an invalid delivery before intake.
102
+ A domain verifier is a method on a service declared in a domain `<name>.service.ts` whose declared class name ends in
103
+ `Verifier` or `verify`, or an exact domain service method declared by the adjacent `<provider>-http.module.ts` as
104
+ `export const webhookVerifiers = [BillingService.prototype.receiveCassoWebhook] as const`. The array names TypeScript
105
+ method symbols, never strings; a local alias or a class from the platform, integrations or a package grants no domain proof.
106
+ A separate verification call is followed by exactly one domain intake call. A domain method that verifies and then performs
107
+ intake may be the first and only call; a separate domain verification call does not count as intake. The door injects only
108
+ domain services and, when used, the signature service (injected with `@InjectWebhookSignature()`), holds no branch, loop or
109
+ `try`, and calls nothing else (no parsing, mapping or publishing). Neither the door nor its HTTP module imports or injects
110
+ `CommandBus`, `QueryBus` from `@nestjs/cqrs`, or a feature command handler. The called domain intake must not dispatch on
111
+ a command or query bus or feature handler either; it records the inbox receipt and outbox event so a reactor or job can dispatch after commit.
112
+ The POST handler returns nothing (an empty body) or exactly `return PROVIDER_ACK`. That exported constant belongs to the
113
+ same webhook feature, is declared with `as const` or a readonly literal type, and contains only literal object values or
114
+ a literal string. Calls, interpolated templates, spreads, input-derived values, inline returned objects and constants of
115
+ another feature are refused. A provider acknowledgement does not weaken the first-statement proof requirement.
116
+ The optional GET starts with a declared domain verifier's challenge call, awaited when async, and returns exactly that
117
+ call's result: `const challenge = this.verifier.challenge(query); return challenge`, or directly returns that call.
118
+ It makes no intake call and computes no other response. `@RateLimit(RateTier.Strict)` is required on both signed-webhook handlers.
119
+ `SignedWebhook` metadata belongs only to this HTTP door or the exact domain verifier named in its module's exported
120
+ `webhookVerifiers`. A public route whose path contains webhook, callback or events belongs in the webhook door too.
90
121
  rationale: |-
91
- The signature is the authentication of a public door. Making the proof the first statement removes every path to the intake
92
- that skips it, and a door with one call and no decision has nothing left to test except the proof and the hand-over.
122
+ The signature is the authentication of a public door. Making verification the first statement removes every door path that
123
+ skips it; a combined domain method must finish that proof before intake. Keeping one intake and no decision leaves the door
124
+ with only the proof and the hand-over to test.
93
125
  cases:
94
126
  - id: case-1
95
127
  when: A delivery whose raw body must be hashed
96
128
  write: 'enable the raw body for the app (`NestFactory.create(..., { rawBody: true })` in the api app) and read `request.rawBody`; never hash a re-serialized `body`.'
129
+ - id: case-2
130
+ when: A payment provider uses a different secret for each billing connection
131
+ write: 'declare BillingService.prototype.receiveCassoWebhook in the adjacent HTTP module webhookVerifiers array; the first and only handler statement awaits that method, which resolves the connection secret, verifies the provider proof and delegates to the intake. The door never reads the connection or payment data itself.'
132
+ - id: case-3
133
+ when: 'A provider requires exactly {"success": true} instead of an empty acknowledgement'
134
+ write: |-
135
+ In the webhook door's own file declare `export const PROVIDER_ACK = { success: true } as const`.
136
+ After the first verification call and the one domain intake call, return `PROVIDER_ACK` with the provider's required
137
+ status code. `return { success: true }`, `return { success: delivery.accepted }`, a spread, a computed constant,
138
+ and an ACK imported from another feature remain findings.
139
+ - id: case-4
140
+ when: A Meta-style provider uses GET subscription verification and POST deliveries
141
+ write: |-
142
+ Worked tree (the webhook and reactor are separate kinds; the app composes their modules):
143
+ be/src/features/webhooks/meta/index.ts
144
+ be/src/features/webhooks/meta/transport/http/meta-http.module.ts
145
+ be/src/features/webhooks/meta/transport/http/meta.webhook.ts
146
+ be/src/features/webhooks/meta/transport/http/meta.webhook.spec.ts
147
+ be/src/features/webhooks/meta/transport/http/dto/meta-challenge.request.ts
148
+ be/src/features/webhooks/meta/transport/http/dto/meta-event.request.ts
149
+ be/src/modules/domain/meta/index.ts
150
+ be/src/modules/domain/meta/meta-verifier.service.ts
151
+ be/src/modules/domain/meta/meta.service.ts
152
+ be/src/modules/events/core/meta-delivery-recorded.event.ts
153
+ be/src/features/reactors/meta-intake/index.ts
154
+ be/src/features/reactors/meta-intake/meta-intake.module.ts
155
+ be/src/features/reactors/meta-intake/application/process-meta-delivery.command.ts
156
+ be/src/features/reactors/meta-intake/application/process-meta-delivery.handler.ts
157
+ be/src/features/reactors/meta-intake/application/process-meta-delivery.contracts.ts
158
+ be/src/features/reactors/meta-intake/transport/message/meta-delivery-recorded.consumer.ts
159
+ be/src/features/reactors/meta-intake/transport/message/meta-intake-message.module.ts
160
+
161
+ meta.webhook.ts:
162
+ import { Body, Controller, Get, Headers, HttpCode, HttpStatus, Post, Query, Req } from "@nestjs/common"
163
+ import type { RawBodyRequest } from "@nestjs/common"
164
+ import type { Request } from "express"
165
+ import { Public, PublicReason } from "@modules/domain/identity"
166
+ import { MetaService, MetaVerifier } from "@modules/domain/meta"
167
+ import { RateLimit, RateTier } from "@modules/platform/http-security"
168
+ import { MetaChallengeRequest } from "./dto/meta-challenge.request"
169
+ import { MetaEventRequest } from "./dto/meta-event.request"
170
+
171
+ export const PROVIDER_ACK = "EVENT_RECEIVED" as const
172
+
173
+ @Controller("webhooks/meta")
174
+ export class MetaWebhook {
175
+ constructor(private readonly verifier: MetaVerifier, private readonly deliveries: MetaService) {}
176
+
177
+ @Get()
178
+ @Public({ reason: PublicReason.SignedWebhook })
179
+ @RateLimit(RateTier.Strict)
180
+ challenge(@Query() query: MetaChallengeRequest): string {
181
+ const challenge = this.verifier.challenge(query)
182
+ return challenge
183
+ }
184
+
185
+ @Post()
186
+ @HttpCode(HttpStatus.OK)
187
+ @Public({ reason: PublicReason.SignedWebhook })
188
+ @RateLimit(RateTier.Strict)
189
+ async receive(
190
+ @Req() request: RawBodyRequest<Request>,
191
+ @Headers("x-hub-signature-256") signature: string | undefined,
192
+ @Body() delivery: MetaEventRequest,
193
+ ): Promise<typeof PROVIDER_ACK> {
194
+ await this.verifier.verify({ rawBody: request.rawBody, signature })
195
+ await this.deliveries.recordMetaDelivery(delivery)
196
+ return PROVIDER_ACK
197
+ }
198
+ }
199
+
200
+ meta-http.module.ts:
201
+ import { Module } from "@nestjs/common"
202
+ import { MetaWebhook } from "./meta.webhook"
203
+
204
+ @Module({ controllers: [MetaWebhook] })
205
+ export class MetaHttpModule {}
206
+
207
+ MetaVerifier is declared in meta-verifier.service.ts. Its challenge method proves the subscription mode and verify
208
+ token before returning the challenge field unchanged; its verify method owns Meta's signature proof and delivery
209
+ freshness policy. MetaService.recordMetaDelivery only records the delivery and its outbox receipt in one transaction
210
+ (case-1 of BE-WEBHOOK-3). The reactor consumes that receipt and dispatches ProcessMetaDeliveryCommand after commit.
211
+ The provider's real GET and POST proof schemes belong in the verifier, rather than being assumed to match the shared
212
+ timestamp/HMAC convention. The api app composes MetaHttpModule; an api or worker app composes MetaIntakeMessageModule.
97
213
  verification:
98
214
  automated:
99
215
  - BE_WEBHOOK_SHAPE
@@ -101,47 +217,81 @@ rules:
101
217
  - BE_DEFAULT_DENY
102
218
  - BE_INPUT_BOUNDED
103
219
  - BE_FEATURE_THIN
220
+ - BE_WEBHOOK_SCOPE
221
+ - BE_WEBHOOK_INBOX_KEY
104
222
  manual:
105
- - Review that the raw body, not a re-serialized one, is verified, and that the replay window matches the provider's tolerance.
223
+ - Review that the raw body, not a re-serialized one, is verified, that the replay window matches the provider's tolerance, and that a domain verifier refuses the delivery before intake or business data access.
224
+ - Trace intake helpers and dynamic dispatch that static resolution cannot inspect; none may dispatch a command before the receipt is durable.
106
225
  relatedExamples: [webhooks]
107
226
  - id: BE-WEBHOOK-3
108
- title: The intake publishes the event inside its own transaction
227
+ title: The intake records the inbox receipt; a reactor or job dispatches after commit
109
228
  kind: mandatory
110
- hfsRules: [R80, R152, R151]
229
+ hfsRules: [R80, R152, R151, R263]
111
230
  requirement: |-
112
- The domain intake method (`PaymentService.acceptNotification`) is the delivery method of R80: its first awaited expression
231
+ The sanctioned path is `verify -> inbox.record -> reactor/job dispatches the command`. `inbox.record` describes the
232
+ domain intake's durable recording responsibility, not a new method on the shared `Inbox` port: the real port has `claim`.
233
+ The domain intake method (`PaymentService.recordPaymentDelivery`) is the delivery method of R80: its first awaited expression
113
234
  claims `(source, eventId)` in the `Inbox` and it returns when the claim answers false, so a redelivery is a no-op. It then
114
- records the delivery and calls `this.eventBus.publish(PaymentConfirmedEvent.create({...}), manager)` inside the same
235
+ records the provider payload or a durable receipt reference and calls
236
+ `this.eventBus.publish(PaymentDeliveryRecordedEvent.create({...}), manager)` inside the same
115
237
  transaction (`entityManager.transaction`). The events are typed classes in `modules/events/<service>/<event>.event.ts` that
116
- equal their `be/contracts/<service>/events.json` entry (`payment.confirmed`, `payment.failed`). The webhook door never
117
- imports `modules/events` or the event bus.
238
+ equal their `be/contracts/<service>/events.json` entry (`payment.delivery-recorded`). The webhook door never imports
239
+ `modules/events` or the event bus. The door, HTTP module and called intake do not reach a command or query bus or feature
240
+ handler. A reactor consumes the receipt event and dispatches its own command, forwarding the delivered `eventId` and
241
+ receipt reference; its domain service claims that separate consumer delivery before processing the receipt. A job may
242
+ instead consume a transactionally enqueued receipt reference under the queue and fenced-job patterns.
243
+ This responsibility follows every intake identified by its first `*Id` parameter, a DTO delivery-id property or raw-body
244
+ signature input. The inbox key is the stable provider delivery id, and a duplicate returns before work. No `process`
245
+ callback, domain processing or command dispatch runs inside the receipt transaction; those run only after commit.
118
246
  rationale: |-
119
247
  Recording the delivery and writing the outbox row in one transaction means a crash cannot lose or duplicate the event, and
120
248
  keeping the publish in `modules/domain` keeps one place that owns the transaction boundary.
121
249
  cases:
122
250
  - id: case-1
123
- when: A confirmed payment arrives
124
- write: 'acceptNotification claims the inbox row, updates the payment row and publishes PaymentConfirmedEvent in one transaction; a second delivery of the same eventId changes nothing.'
251
+ when: A payment provider retries a delivery whose processing belongs in a reactor
252
+ write: |-
253
+ The POST handler verifies first, then calls PaymentService.recordPaymentDelivery(delivery) once. That domain method
254
+ runs entityManager.transaction(async (manager) => { ... }): its first awaited statement claims
255
+ this.inbox.claim(PAYMENT_SOURCE, delivery.eventId, manager), and a false answer returns. With the same manager it
256
+ saves the durable receipt and publishes PaymentDeliveryRecordedEvent carrying that receipt's id. Its method never
257
+ dispatches CommandBus.execute. The outbox relay delivers the committed event to
258
+ features/reactors/payment-intake/transport/message/payment-delivery-recorded.consumer.ts:
259
+
260
+ async handle(delivery: EventDelivery<PaymentDeliveryRecordedEvent>): Promise<void> {
261
+ await this.commandBus.execute(new ProcessPaymentDeliveryCommand({
262
+ request: { eventId: delivery.eventId, receiptId: delivery.event.payload.receiptId },
263
+ }))
264
+ }
265
+
266
+ The command's handler delegates to its domain service; that service claims the reactor delivery id and processes the
267
+ saved receipt in its transaction. A duplicate provider POST records nothing more, and a duplicate relay delivery
268
+ processes nothing twice. The command, handler and consumer live in the reactor's own kind; no feature imports the
269
+ webhook kind. The equivalent job path enqueues a receipt reference in the recording transaction and processes it
270
+ through the job's platform claim and fencing, never synchronously in the POST handler.
125
271
  verification:
126
272
  automated:
127
273
  - BE_INBOX_DEDUPE_MISSING
128
274
  - BE_OUTBOX_WRITE_TX
129
275
  - BE_EVENT_CLASS_CONTRACT
276
+ - BE_WEBHOOK_SCOPE
277
+ - BE_WEBHOOK_INBOX_KEY
130
278
  manual:
131
- - Confirm each provider event kind maps to exactly one published event and that an unknown kind is acknowledged and ignored, not published.
279
+ - Confirm each accepted provider delivery records at most one receipt and outbox event, and the reactor or job alone dispatches processing commands.
132
280
  relatedExamples: [webhooks]
133
281
  - id: BE-WEBHOOK-4
134
282
  title: Three behaviours are proven on the test world
135
283
  kind: mandatory
136
- hfsRules: [R155]
284
+ hfsRules: [R155, R284]
137
285
  requirement: |-
138
- Declaring the pattern `webhooks` requires e2e or integration tests titled `webhooks/<scenario>: ...` for each scenario
286
+ Declaring the pattern `webhooks` requires e2e or integration tests in full and integration tests in lite (R284),
287
+ titled `webhooks/<scenario>: ...` for each scenario
139
288
  `bad-signature-is-refused` (a tampered body is refused and nothing is published), `replay-is-refused` (a stale timestamp is
140
289
  refused and nothing is published) and `accepted-delivery-publishes-once` (a valid delivery publishes the event once and a
141
290
  redelivery publishes nothing more). The provider is the network-edge fake of the test world (`world.fake.<provider>`), which
142
291
  signs with the test secret; a real provider is never called. The unit spec `<provider>.webhook.spec.ts` covers the door
143
- itself with the Nest testing module, a real `WebhookSignatureService` configured with a test secret and a double of the
144
- domain service.
292
+ itself with the Nest testing module and the verifier it uses: a real `WebhookSignatureService` configured with a test secret
293
+ or a real domain verifier configured with the connection's test secret, plus a double of a separate intake service. A method
294
+ that combines verification and intake has its domain service spec proving refusal before intake and the door spec proving delegation.
145
295
  rationale: |-
146
296
  The door has no logic of its own, so the risk is in the wiring: the raw body, the headers, the real signature scheme and the
147
297
  transaction behind the intake. Only a signed request through the real app proves it.
@@ -152,6 +302,7 @@ rules:
152
302
  verification:
153
303
  automated:
154
304
  - BE_PATTERN_SPEC_MISSING
305
+ - BE_LITE_PROOF_LANE_MISSING
155
306
  manual:
156
307
  - Confirm the fake signs exactly as the provider documents and that a contract spec guards its payload fixtures.
157
308
  relatedExamples: [webhooks]
@@ -160,6 +311,14 @@ files:
160
311
  slot: be.feature.webhooks
161
312
  template: webhooks/webhook.index.ts.tpl
162
313
  role: The public surface of the kind folder, the module class an api-kind app composes.
314
+ - path: src/features/webhooks/<provider>/transport/http/<provider>-http.options.ts
315
+ slot: be.feature.webhooks.http
316
+ template: webhooks/webhook.http.options.ts.tpl
317
+ role: The options of the module (the configurable module base needs a type); empty until the module takes some.
318
+ - path: src/features/webhooks/<provider>/transport/http/<provider>-http.module-definition.ts
319
+ slot: be.feature.webhooks.http
320
+ template: webhooks/webhook.http.module-definition.ts.tpl
321
+ role: The configurable-module base of the module, registered as register.
163
322
  - path: src/features/webhooks/<provider>/transport/http/<provider>-http.module.ts
164
323
  slot: be.feature.webhooks.http
165
324
  template: webhooks/webhook.http.module.ts.tpl
@@ -167,7 +326,7 @@ files:
167
326
  - path: src/features/webhooks/<provider>/transport/http/<provider>.webhook.ts
168
327
  slot: be.feature.webhooks.http
169
328
  template: webhooks/webhook.door.ts.tpl
170
- role: The signed door, one controller with one POST handler.
329
+ role: The signed door, one controller with one POST handler and optionally one GET challenge verifier; an own-feature PROVIDER_ACK is a fixed literal.
171
330
  rules: [BE_WEBHOOK_SHAPE, BE_WEBHOOK_UNVERIFIED, BE_DEFAULT_DENY, BE_INPUT_BOUNDED]
172
331
  spec: src/features/webhooks/<provider>/transport/http/<provider>.webhook.spec.ts
173
332
  - path: src/features/webhooks/<provider>/transport/http/<provider>.webhook.spec.ts
@@ -181,12 +340,12 @@ files:
181
340
  template: webhooks/webhook.request.ts.tpl
182
341
  - path: src/modules/platform/http-security/webhook-signature.service.ts
183
342
  slot: be.platform
184
- role: WebhookSignatureService, the one owner of the signature and replay proof.
343
+ role: WebhookSignatureService, the shared signature and replay proof when provider secrets are platform options.
185
344
  spec: src/modules/platform/http-security/webhook-signature.service.spec.ts
186
345
  optional: true
187
346
  - path: src/modules/domain/<capability>/<capability>.service.ts
188
347
  slot: be.domain
189
- role: The intake method, which claims the inbox and publishes the event in its transaction.
348
+ role: The domain verifier or intake method, which may combine per-connection proof with inbox claim, durable receipt recording and outbox publishing in its transaction; no command/query-bus or feature-handler dispatch.
190
349
  rules: [BE_INBOX_DEDUPE_MISSING, BE_OUTBOX_WRITE_TX]
191
350
  optional: true
192
351
  - path: src/modules/events/<service>/<event>.event.ts
@@ -194,9 +353,24 @@ files:
194
353
  role: The typed event the intake publishes.
195
354
  rules: [BE_EVENT_CLASS_CONTRACT]
196
355
  optional: true
356
+ - path: src/features/reactors/<reactor>/transport/message/<event>.consumer.ts
357
+ slot: be.transport.message
358
+ role: Optional reactor consumer of the durable receipt event; forwards its eventId and receipt reference in one command after commit.
359
+ rules: [BE_EVENT_CONSUMER_SHAPE, BE_FEATURE_THIN]
360
+ optional: true
197
361
  - path: src/tests/e2e/<area>/<provider>-webhook.e2e-spec.ts
198
362
  slot: be.tests.e2e
199
363
  role: "Titles webhooks/<scenario>: ... prove the pattern against the provider fake."
200
364
  spec: webhooks/bad-signature-is-refused, webhooks/replay-is-refused, webhooks/accepted-delivery-publishes-once
201
365
  rules: [BE_PATTERN_SPEC_MISSING]
202
366
  optional: true
367
+ sourceMaintenance: >-
368
+ The add webhook verb keeps the default empty acknowledgement and does not add a consumer. In full, the generated POST
369
+ verifies first and calls the supplied domain service's record<Provider>Delivery once. Implement that method as the
370
+ transactional inbox receipt/outbox recording intake above, then add its reactor or job separately.
371
+ In lite, the generated POST verifies first and calls <Provider>InboxService.record<Provider>Delivery. This record-only
372
+ domain intake uses recordWebhookDelivery to insert a pending provider inbox row atomically in Supabase Postgres, unique on
373
+ (provider, delivery_id), without processing it or marking it complete. Add a reactor or job separately to consume the
374
+ committed pending row and dispatch the processing command (reactors.yaml, jobs.yaml). The --service option validates the
375
+ future domain processing owner; the webhook writer does not import or call that owner synchronously and does not
376
+ synthesize a consumer.
@@ -174,6 +174,9 @@ rules:
174
174
  component.tsx. Pure blocks, composites, branches and leaves use index.tsx only and never gain an
175
175
  empty twin. classNames.ts is present only when the unit owns class strings. Budgets: component.tsx 300 lines,
176
176
  connected index.tsx 200 lines, at most 6 data hooks and 6 useState per connected unit.
177
+ A composition-only feature may render an existing public pure component owner directly instead of creating a
178
+ forwarding component.tsx. The route-state overlay host of FE-BOUNDARY-5 renders components/branches/OverlayHost;
179
+ a connected block still requires its exact private Base twin.
177
180
  rationale: |
178
181
  Stable basenames make class names and the connected and presentational split discoverable without forwarding
179
182
  twins, and budgets keep a unit reviewable.
@@ -303,6 +306,7 @@ rules:
303
306
  envelope, the generated wire types (from be/contracts/, through the root codegen) and the axios calls live in
304
307
  modules/api; the session lives in modules/auth; mutation feedback in modules/toast; environment reading in
305
308
  modules/config; every href builder in modules/routes; locale routing in modules/i18n; colour values in modules/brand.
309
+ Only hooks/swr imports the API transport operations; feature owners and other hooks consume those SWR hooks.
306
310
  A helper name defined twice across hook files is a finding.
307
311
  rationale: |
308
312
  A rule that says what a hooks folder is also says where everything else goes, so nobody copies a helper sixteen
@@ -82,7 +82,7 @@ overall:
82
82
  rule: typescript:S3776
83
83
  via: overall.issues
84
84
  # Lite is the same quality policy, coverage included: a lite back end keeps full's unit run and its 100 per file on the measured
85
- # logic files (owner ruling 2026-10-12, R212 case-1). Readers select this block from hfs.json edition; the gate keeps its own
85
+ # logic files (owner ruling 2026-10-12, R212 case-1 (owner:lite-production-swarm), with the Work boundary amended by ruling:R212/case-4). Readers select this block from hfs.json edition; the gate keeps its own
86
86
  # name so a lite project's server gate stays a separate object, and every threshold below equals full's.
87
87
  lite:
88
88
  gate: