@starci/hfs 4.0.8 → 4.0.9

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 (667) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/README.md +81 -39
  3. package/emit/contracts.mjs +56 -9
  4. package/emit/db-types.mjs +169 -0
  5. package/emit/events.mjs +41 -0
  6. package/emit/operations-worker.mjs +1 -1
  7. package/emit/operations.mjs +2 -2
  8. package/emit/schema-worker.mjs +1 -1
  9. package/lint/bound-sys.cjs +50 -0
  10. package/lint/run.mjs +53 -19
  11. package/package.json +14 -9
  12. package/report/sonar.mjs +8 -8
  13. package/runtime/engine/config.mjs +494 -0
  14. package/runtime/engine/invalid-config.mjs +15 -0
  15. package/runtime/engine/orca-config.mjs +27 -0
  16. package/runtime/knowledge/hfs/canon-pins.yaml +160 -29
  17. package/runtime/knowledge/hfs/peer-integrations.yaml +2 -2
  18. package/runtime/knowledge/hfs/rules.yaml +2059 -0
  19. package/runtime/knowledge/hfs/slots.yaml +912 -120
  20. package/runtime/knowledge/patterns/be/api.yaml +369 -0
  21. package/runtime/knowledge/patterns/be/cli.yaml +297 -0
  22. package/runtime/knowledge/patterns/be/domain.yaml +113 -0
  23. package/runtime/knowledge/patterns/be/event-bus.yaml +285 -0
  24. package/runtime/knowledge/patterns/be/jobs.yaml +261 -0
  25. package/runtime/knowledge/patterns/be/projections.yaml +178 -0
  26. package/runtime/knowledge/patterns/be/queues.yaml +236 -0
  27. package/runtime/knowledge/patterns/be/reactors.yaml +140 -0
  28. package/runtime/knowledge/patterns/be/realtime.yaml +222 -0
  29. package/runtime/knowledge/patterns/be/saga.yaml +248 -0
  30. package/runtime/knowledge/patterns/be/webhooks.yaml +221 -0
  31. package/runtime/knowledge/patterns/fe/folder.yaml +131 -25
  32. package/runtime/knowledge/sonar-gate.yaml +52 -18
  33. package/runtime/modules/kernel/failure-codes.yaml +571 -51
  34. package/runtime/modules/models/registry.yaml +591 -0
  35. package/runtime/modules/models/runtimes.yaml +701 -0
  36. package/runtime/scripts/api/fs/forbidden-root.mjs +51 -0
  37. package/runtime/scripts/api/fs/is-link-like.mjs +25 -0
  38. package/runtime/scripts/api/fs/lib.mjs +10 -2
  39. package/runtime/scripts/api/fs/safe-remove.mjs +12 -132
  40. package/runtime/scripts/api/git/config-get.mjs +10 -0
  41. package/runtime/scripts/api/git/diff.mjs +5 -0
  42. package/runtime/scripts/api/git/lib.mjs +16 -26
  43. package/runtime/scripts/api/git/log.mjs +5 -0
  44. package/runtime/scripts/api/git/ls-files.mjs +6 -0
  45. package/runtime/scripts/api/git/ls-tree.mjs +5 -0
  46. package/runtime/scripts/api/git/merge-base.mjs +9 -0
  47. package/runtime/scripts/api/git/remote.mjs +5 -0
  48. package/runtime/scripts/api/git/rev-parse-query.mjs +5 -0
  49. package/runtime/scripts/api/git/show.mjs +6 -0
  50. package/runtime/scripts/api/sops/decrypt.mjs +9 -5
  51. package/runtime/scripts/api/sops/lib.mjs +35 -0
  52. package/runtime/scripts/api/sops/seal.mjs +18 -0
  53. package/runtime/scripts/hfs/architecture/ast-walks.mjs +220 -0
  54. package/runtime/scripts/hfs/architecture/backend.mjs +16 -23
  55. package/runtime/scripts/hfs/architecture/background-unowned.mjs +16 -34
  56. package/runtime/scripts/hfs/architecture/client-reaches-server.mjs +3 -1
  57. package/runtime/scripts/hfs/architecture/clones.mjs +4 -4
  58. package/runtime/scripts/hfs/architecture/config.mjs +9 -6
  59. package/runtime/scripts/hfs/architecture/connection-map.mjs +20 -8
  60. package/runtime/scripts/hfs/architecture/context-coupling.mjs +88 -0
  61. package/runtime/scripts/hfs/architecture/context-map.mjs +237 -0
  62. package/runtime/scripts/hfs/architecture/context-owner.mjs +82 -0
  63. package/runtime/scripts/hfs/architecture/context-platform-tables.mjs +47 -0
  64. package/runtime/scripts/hfs/architecture/context-transaction.mjs +60 -0
  65. package/runtime/scripts/hfs/architecture/contract-fixture-guard.mjs +4 -10
  66. package/runtime/scripts/hfs/architecture/contracts.mjs +33 -162
  67. package/runtime/scripts/hfs/architecture/dead-exports.mjs +58 -29
  68. package/runtime/scripts/hfs/architecture/doc-language.mjs +1 -1
  69. package/runtime/scripts/hfs/architecture/entrypoint.mjs +2 -1
  70. package/runtime/scripts/hfs/architecture/fe-slot-allows.mjs +1 -1
  71. package/runtime/scripts/hfs/architecture/feature-shape.mjs +5 -2
  72. package/runtime/scripts/hfs/architecture/framework-pinned.mjs +1 -1
  73. package/runtime/scripts/hfs/architecture/frontend.mjs +17 -43
  74. package/runtime/scripts/hfs/architecture/hfs.mjs +24 -39
  75. package/runtime/scripts/hfs/architecture/i18n-keys.mjs +1 -1
  76. package/runtime/scripts/hfs/architecture/index.mjs +17 -4
  77. package/runtime/scripts/hfs/architecture/machine-ast.mjs +44 -11
  78. package/runtime/scripts/hfs/architecture/managed-scripts.mjs +27 -0
  79. package/runtime/scripts/hfs/architecture/module-per-transport.mjs +30 -6
  80. package/runtime/scripts/hfs/architecture/next-data.mjs +22 -63
  81. package/runtime/scripts/hfs/architecture/owners.mjs +35 -15
  82. package/runtime/scripts/hfs/architecture/register-once.mjs +2 -1
  83. package/runtime/scripts/hfs/architecture/registration.mjs +23 -81
  84. package/runtime/scripts/hfs/architecture/required-files.mjs +20 -12
  85. package/runtime/scripts/hfs/architecture/schema-owner.mjs +17 -114
  86. package/runtime/scripts/hfs/architecture/server-action.mjs +24 -0
  87. package/runtime/scripts/hfs/architecture/side-boundary.mjs +16 -0
  88. package/runtime/scripts/hfs/architecture/source-names.mjs +27 -147
  89. package/runtime/scripts/hfs/architecture/sql-owner.mjs +59 -18
  90. package/runtime/scripts/hfs/architecture/sql-returning.mjs +64 -0
  91. package/runtime/scripts/hfs/architecture/supabase-ast.mjs +111 -0
  92. package/runtime/scripts/hfs/architecture/supabase-be.mjs +131 -0
  93. package/runtime/scripts/hfs/architecture/supabase-tables.mjs +42 -0
  94. package/runtime/scripts/hfs/architecture/supabase.mjs +434 -0
  95. package/runtime/scripts/hfs/architecture/surface.mjs +9 -3
  96. package/runtime/scripts/hfs/architecture/test-world-files.mjs +9 -19
  97. package/runtime/scripts/hfs/architecture/tiers.mjs +47 -5
  98. package/runtime/scripts/hfs/architecture/transport-owner.mjs +3 -3
  99. package/runtime/scripts/hfs/architecture/typescript.mjs +12 -26
  100. package/runtime/scripts/hfs/architecture.mjs +4 -4
  101. package/runtime/scripts/hfs/check.mjs +75 -72
  102. package/runtime/scripts/hfs/coverage-scope.mjs +162 -0
  103. package/runtime/scripts/hfs/declaration-shape.mjs +62 -0
  104. package/runtime/scripts/hfs/declaration-slots.mjs +75 -0
  105. package/runtime/scripts/hfs/edition-slots.mjs +66 -0
  106. package/runtime/scripts/hfs/manifest-shape.mjs +109 -32
  107. package/runtime/scripts/hfs/param-names.mjs +18 -0
  108. package/runtime/scripts/hfs/path-findings.mjs +1 -1
  109. package/runtime/scripts/hfs/repo-identity.mjs +9 -6
  110. package/runtime/scripts/hfs/rule-params-shape.mjs +50 -0
  111. package/runtime/scripts/hfs/rules/app-root.mjs +51 -0
  112. package/runtime/scripts/hfs/rules/cli.mjs +37 -0
  113. package/runtime/scripts/hfs/rules/contract-compat.mjs +71 -0
  114. package/runtime/scripts/hfs/rules/contract.mjs +10 -9
  115. package/runtime/scripts/hfs/rules/database-config.mjs +102 -0
  116. package/runtime/scripts/hfs/rules/database-constants.mjs +13 -0
  117. package/runtime/scripts/hfs/rules/database-migrations.mjs +111 -0
  118. package/runtime/scripts/hfs/rules/database-plpgsql.mjs +233 -0
  119. package/runtime/scripts/hfs/rules/database-sql.mjs +336 -0
  120. package/runtime/scripts/hfs/rules/database.mjs +68 -0
  121. package/runtime/scripts/hfs/rules/deps.mjs +1 -1
  122. package/runtime/scripts/hfs/rules/docker.mjs +259 -0
  123. package/runtime/scripts/hfs/rules/edition.mjs +111 -0
  124. package/runtime/scripts/hfs/rules/event-bus.mjs +153 -0
  125. package/runtime/scripts/hfs/rules/fe-contract-documents.mjs +1 -1
  126. package/runtime/scripts/hfs/rules/fe-no-tests.mjs +6 -5
  127. package/runtime/scripts/hfs/rules/frontend-tree.mjs +9 -9
  128. package/runtime/scripts/hfs/rules/integration-specs.mjs +3 -6
  129. package/runtime/scripts/hfs/rules/kinds.mjs +84 -0
  130. package/runtime/scripts/hfs/rules/lint-suppression.mjs +1 -1
  131. package/runtime/scripts/hfs/rules/monorepo.mjs +179 -0
  132. package/runtime/scripts/hfs/rules/peer-integrations.mjs +3 -3
  133. package/runtime/scripts/hfs/rules/pipeline.mjs +11 -21
  134. package/runtime/scripts/hfs/rules/proof-commands.mjs +2 -2
  135. package/runtime/scripts/hfs/rules/read.mjs +1 -1
  136. package/runtime/scripts/hfs/rules/repo-local-checks.mjs +2 -2
  137. package/runtime/scripts/hfs/rules/saga.mjs +170 -0
  138. package/runtime/scripts/hfs/rules/secrets.mjs +3 -3
  139. package/runtime/scripts/hfs/rules/services.mjs +220 -0
  140. package/runtime/scripts/hfs/rules/spec-placement.mjs +1 -1
  141. package/runtime/scripts/hfs/rules/stacks.mjs +3 -3
  142. package/runtime/scripts/hfs/rules/supabase-secrets.mjs +89 -0
  143. package/runtime/scripts/hfs/rules/test-topology.mjs +2 -2
  144. package/runtime/scripts/hfs/secret.mjs +175 -0
  145. package/runtime/scripts/hfs/slots.mjs +78 -108
  146. package/runtime/scripts/hfs/sql/pg-parse.mjs +46 -0
  147. package/runtime/scripts/hfs/test-world-slot.mjs +6 -0
  148. package/runtime/scripts/hfs/tree.mjs +5 -4
  149. package/runtime/scripts/hfs/typescript-programs.mjs +0 -5
  150. package/runtime/scripts/hfs/view.mjs +3 -2
  151. package/runtime/scripts/lib/dockerfile.mjs +124 -0
  152. package/runtime/scripts/lib/env.mjs +10 -0
  153. package/runtime/scripts/lib/event-contract.mjs +103 -0
  154. package/runtime/scripts/lib/git.mjs +36 -0
  155. package/runtime/scripts/lib/graphql-contract.mjs +1 -1
  156. package/runtime/scripts/lib/i18n.mjs +86 -0
  157. package/runtime/scripts/lib/json.mjs +13 -3
  158. package/runtime/scripts/lib/language.mjs +24 -5
  159. package/runtime/scripts/lib/list.mjs +20 -0
  160. package/runtime/scripts/lib/package-at.mjs +37 -0
  161. package/runtime/scripts/lib/path-key.mjs +57 -2
  162. package/runtime/scripts/lib/read-text.mjs +34 -0
  163. package/runtime/scripts/lib/regex.mjs +4 -0
  164. package/runtime/scripts/lib/same-text.mjs +5 -0
  165. package/runtime/scripts/lib/secret-patterns.mjs +4 -3
  166. package/runtime/scripts/lib/sleep-sync.mjs +2 -1
  167. package/runtime/scripts/lib/sops-envelope.mjs +58 -0
  168. package/runtime/scripts/lib/stack-services.mjs +7 -31
  169. package/runtime/scripts/lib/ts-ast.mjs +35 -0
  170. package/runtime/scripts/lib/walk.mjs +4 -3
  171. package/scaffold/add-app.mjs +174 -0
  172. package/scaffold/add-cli-lite.mjs +257 -0
  173. package/scaffold/add-inbox-lite.mjs +314 -0
  174. package/scaffold/add-table.mjs +344 -0
  175. package/scaffold/add.mjs +299 -0
  176. package/scaffold/app.mjs +358 -69
  177. package/scaffold/edition-gate.mjs +25 -0
  178. package/scaffold/image.mjs +19 -0
  179. package/scaffold/lite-exports.mjs +186 -0
  180. package/scaffold/service.mjs +32 -26
  181. package/scaffold/supabase-ports.mjs +26 -0
  182. package/src/main.mjs +327 -0
  183. package/sync/cli.mjs +6 -9
  184. package/sync/format.mjs +1 -1
  185. package/sync/hygiene.mjs +10 -10
  186. package/sync/index.mjs +167 -104
  187. package/sync/managed.mjs +19 -11
  188. package/sync/sonar-key.mjs +5 -0
  189. package/templates/app/ci-workflows/github/workflows/ci.yml +14 -14
  190. package/templates/app/ci-workflows/github/workflows/images.yml +37 -0
  191. package/templates/app/ci-workflows/sonar-steps.yml +9 -0
  192. package/templates/app/ci-workflows-lite/github/workflows/ci.yml +66 -0
  193. package/templates/app/ci-workflows-lite/github/workflows/db-deploy.yml +31 -0
  194. package/templates/app/ci-workflows-lite/github/workflows/images.yml +37 -0
  195. package/templates/app/codegen-lite/scripts/codegen.mjs +32 -0
  196. package/templates/app/docker-ignore/dockerignore +50 -0
  197. package/templates/app/gitignore-lite +35 -0
  198. package/templates/app/hooks/husky/pre-commit +3 -8
  199. package/templates/app/hooks/husky/pre-push +45 -6
  200. package/templates/app/package-scripts/core.json +8 -0
  201. package/templates/app/package-scripts/package.json +3 -10
  202. package/templates/app/package-scripts-lite/package.json +15 -0
  203. package/templates/app/quality-config/codecov.yml +15 -3
  204. package/templates/app/quality-config/sonar-base.properties +4 -0
  205. package/templates/app/quality-config/sonar-project.properties +1 -4
  206. package/templates/app/quality-config-lite/sonar-project.properties +5 -0
  207. package/templates/app/skeleton/.starcistacks/dev/seeds/primary-notes.sql +5 -0
  208. package/templates/app/skeleton/.starciwork/features/system-health/index.yaml +4 -0
  209. package/templates/app/skeleton/.starciwork/{features/index.yaml → index.yaml} +2 -2
  210. package/templates/app/skeleton/README.md +7 -5
  211. package/templates/app/skeleton-lite/.sops.yaml +3 -0
  212. package/templates/app/skeleton-lite/.starcistacks/dev/environment.json +5 -0
  213. package/templates/app/skeleton-lite/.starcistacks/dev/secrets/.gitkeep +1 -0
  214. package/templates/app/skeleton-lite/.starciwork/shell/index.yaml +55 -0
  215. package/templates/app/skeleton-lite/README.md +40 -0
  216. package/templates/app/skeleton-lite/supabase/config.toml +41 -0
  217. package/templates/app/skeleton-lite/supabase/migrations/__timestamp___baseline.sql +56 -0
  218. package/templates/app/skeleton-lite/supabase/seed.sql +0 -0
  219. package/templates/app/task-graph/turbo.json +23 -0
  220. package/templates/app/tool-config-lite/prettierignore +2 -0
  221. package/templates/app/tool-config-lite/prettierrc +1 -0
  222. package/templates/app/upgrade-full/.starcistacks/application-stacks.yaml +38 -0
  223. package/templates/app/upgrade-full/.starcistacks/dev/README.md +10 -0
  224. package/templates/app/upgrade-full/.starcistacks/dev/infra/compose/api.yaml +13 -0
  225. package/templates/app/upgrade-full/.starcistacks/dev/infra/compose/compose.yaml +3 -0
  226. package/templates/app/upgrade-full/.starcistacks/dev/infra/compose/postgres.yaml +12 -0
  227. package/templates/app/upgrade-full/.starcistacks/dev/infra/metadata.json +7 -0
  228. package/templates/be/common/main-listen.ts.partial +14 -0
  229. package/templates/be/common/sql-ident.ts.partial +7 -0
  230. package/templates/be/common/sql-tag.ts.partial +5 -0
  231. package/templates/be/image/api/Dockerfile +38 -0
  232. package/templates/be/image/cli/Dockerfile +38 -0
  233. package/templates/be/image/worker/Dockerfile +36 -0
  234. package/templates/be/inbox/database-client.ts.tpl +50 -0
  235. package/templates/be/inbox/index.ts.tpl +2 -0
  236. package/templates/be/inbox/migration.sql.tpl +29 -0
  237. package/templates/be/inbox/module-definition.ts.tpl +6 -0
  238. package/templates/be/inbox/module.ts.tpl +14 -0
  239. package/templates/be/inbox/options.ts.tpl +2 -0
  240. package/templates/be/inbox/service.ts.tpl +33 -0
  241. package/templates/be/inbox/sql.ts.tpl +16 -0
  242. package/templates/be/inbox/webhook-door.ts.tpl +36 -0
  243. package/templates/be/patterns/api/application.command.http.ts.tpl +10 -0
  244. package/templates/be/patterns/api/application.command.ts.tpl +10 -0
  245. package/templates/be/patterns/api/application.contracts.ts.tpl +11 -0
  246. package/templates/be/patterns/api/application.handler.http.ts.tpl +22 -0
  247. package/templates/be/patterns/api/application.handler.ts.tpl +22 -0
  248. package/templates/be/patterns/api/feature.index.http.ts.tpl +1 -0
  249. package/templates/be/patterns/api/feature.index.ts.tpl +1 -0
  250. package/templates/be/patterns/api/feature.module.ts.tpl +6 -0
  251. package/templates/be/patterns/api/graphql.input.ts.tpl +13 -0
  252. package/templates/be/patterns/api/graphql.mapper.ts.tpl +9 -0
  253. package/templates/be/patterns/api/graphql.module.ts.tpl +7 -0
  254. package/templates/be/patterns/api/graphql.resolver.ts.tpl +27 -0
  255. package/templates/be/patterns/api/graphql.type.ts.tpl +9 -0
  256. package/templates/be/patterns/api/http.controller.ts.tpl +27 -0
  257. package/templates/be/patterns/api/http.mapper.ts.tpl +9 -0
  258. package/templates/be/patterns/api/http.module.ts.tpl +7 -0
  259. package/templates/be/patterns/api/http.request.ts.tpl +10 -0
  260. package/templates/be/patterns/api/http.response.ts.tpl +5 -0
  261. package/templates/be/patterns/cli/group.cli.spec.ts.tpl +15 -0
  262. package/templates/be/patterns/cli/group.cli.ts.tpl +11 -0
  263. package/templates/be/patterns/cli/group.module.ts.tpl +7 -0
  264. package/templates/be/patterns/cli/sub.run.cli.spec.ts.tpl +17 -0
  265. package/templates/be/patterns/cli/sub.run.cli.ts.tpl +15 -0
  266. package/templates/be/patterns/event-bus/platform/errors/event-bus.error.ts.tpl +22 -0
  267. package/templates/be/patterns/event-bus/platform/event-bus.config.ts.tpl +19 -0
  268. package/templates/be/patterns/event-bus/platform/event-bus.contracts.ts.tpl +58 -0
  269. package/templates/be/patterns/event-bus/platform/event-bus.decorators.ts.tpl +46 -0
  270. package/templates/be/patterns/event-bus/platform/event-bus.log-events.ts.tpl +12 -0
  271. package/templates/be/patterns/event-bus/platform/event-bus.module-definition.ts.tpl +10 -0
  272. package/templates/be/patterns/event-bus/platform/event-bus.module.ts.tpl +52 -0
  273. package/templates/be/patterns/event-bus/platform/event-bus.options.ts.tpl +19 -0
  274. package/templates/be/patterns/event-bus/platform/event-bus.port.ts.tpl +28 -0
  275. package/templates/be/patterns/event-bus/platform/event-bus.service.spec.ts.tpl +165 -0
  276. package/templates/be/patterns/event-bus/platform/event-bus.service.ts.tpl +93 -0
  277. package/templates/be/patterns/event-bus/platform/event-envelope.policy.spec.ts.tpl +90 -0
  278. package/templates/be/patterns/event-bus/platform/event-envelope.policy.ts.tpl +58 -0
  279. package/templates/be/patterns/event-bus/platform/event-relay.service.spec.ts.tpl +136 -0
  280. package/templates/be/patterns/event-bus/platform/event-relay.service.ts.tpl +56 -0
  281. package/templates/be/patterns/event-bus/platform/event-runner.service.spec.ts.tpl +272 -0
  282. package/templates/be/patterns/event-bus/platform/event-runner.service.ts.tpl +128 -0
  283. package/templates/be/patterns/event-bus/platform/event-transport.port.ts.tpl +180 -0
  284. package/templates/be/patterns/event-bus/platform/event.policy.spec.ts.tpl +67 -0
  285. package/templates/be/patterns/event-bus/platform/event.policy.ts.tpl +38 -0
  286. package/templates/be/patterns/event-bus/platform/index.ts.tpl +11 -0
  287. package/templates/be/patterns/event-bus/platform/kafka-event-transport.client.spec.ts.tpl +348 -0
  288. package/templates/be/patterns/event-bus/platform/kafka-event-transport.client.ts.tpl +241 -0
  289. package/templates/be/patterns/event-bus/platform/messages/event-bus.messages.ts.tpl +15 -0
  290. package/templates/be/patterns/event-bus/platform/persistence/connection.ts.tpl +8 -0
  291. package/templates/be/patterns/event-bus/platform/persistence/entities/event-outbox.entity.ts.tpl +37 -0
  292. package/templates/be/patterns/event-bus/platform/persistence/event-bus.rows.ts.tpl +11 -0
  293. package/templates/be/patterns/event-bus/platform/persistence/event-bus.sql.ts.tpl +12 -0
  294. package/templates/be/patterns/event-bus/platform/persistence/migrations/1789800009000-create-event-outbox.ts.tpl +26 -0
  295. package/templates/be/patterns/jobs/job.index.ts.tpl +1 -0
  296. package/templates/be/patterns/jobs/job.module.ts.tpl +6 -0
  297. package/templates/be/patterns/jobs/job.processor.ts.tpl +20 -0
  298. package/templates/be/patterns/jobs/job.queue.module.ts.tpl +20 -0
  299. package/templates/be/patterns/jobs/job.step.ts.tpl +19 -0
  300. package/templates/be/patterns/jobs/platform/fenced.processor.spec.ts.tpl +31 -0
  301. package/templates/be/patterns/jobs/platform/fenced.processor.ts.tpl +13 -0
  302. package/templates/be/patterns/jobs/platform/job-claim.service.spec.ts.tpl +150 -0
  303. package/templates/be/patterns/jobs/platform/job-claim.service.ts.tpl +100 -0
  304. package/templates/be/patterns/jobs/platform/job-runner.service.spec.ts.tpl +138 -0
  305. package/templates/be/patterns/jobs/platform/job-runner.service.ts.tpl +69 -0
  306. package/templates/be/patterns/jobs/platform/jobs.config.ts.tpl +11 -0
  307. package/templates/be/patterns/jobs/platform/jobs.contracts.ts.tpl +62 -0
  308. package/templates/be/patterns/jobs/platform/jobs.decorators.ts.tpl +31 -0
  309. package/templates/be/patterns/jobs/platform/jobs.error.ts.tpl +22 -0
  310. package/templates/be/patterns/jobs/platform/jobs.index.ts.tpl +11 -0
  311. package/templates/be/patterns/jobs/platform/jobs.log-events.ts.tpl +9 -0
  312. package/templates/be/patterns/jobs/platform/jobs.messages.ts.tpl +15 -0
  313. package/templates/be/patterns/jobs/platform/jobs.module-definition.ts.tpl +10 -0
  314. package/templates/be/patterns/jobs/platform/jobs.module.ts.tpl +32 -0
  315. package/templates/be/patterns/jobs/platform/jobs.options.ts.tpl +11 -0
  316. package/templates/be/patterns/jobs/platform/jobs.port.ts.tpl +36 -0
  317. package/templates/be/patterns/jobs/platform/persistence.connection.ts.tpl +8 -0
  318. package/templates/be/patterns/jobs/platform/persistence.job.entity.ts.tpl +53 -0
  319. package/templates/be/patterns/jobs/platform/persistence.jobs.rows.ts.tpl +13 -0
  320. package/templates/be/patterns/jobs/platform/persistence.jobs.sql.ts.tpl +33 -0
  321. package/templates/be/patterns/jobs/platform/persistence.migration.ts.tpl +29 -0
  322. package/templates/be/patterns/outbox/platform/index.ts.tpl +2 -0
  323. package/templates/be/patterns/outbox/platform/outbox-relay.policy.spec.ts.tpl +96 -0
  324. package/templates/be/patterns/outbox/platform/outbox-relay.policy.ts.tpl +78 -0
  325. package/templates/be/patterns/outbox/platform/outbox.log-events.ts.tpl +5 -0
  326. package/templates/be/patterns/projections/migration.ts.tpl +18 -0
  327. package/templates/be/patterns/projections/persistence.connection.ts.tpl +8 -0
  328. package/templates/be/patterns/projections/projection.entity.ts.tpl +9 -0
  329. package/templates/be/patterns/projections/projection.index.ts.tpl +3 -0
  330. package/templates/be/patterns/projections/projection.module-definition.ts.tpl +7 -0
  331. package/templates/be/patterns/projections/projection.module.ts.tpl +18 -0
  332. package/templates/be/patterns/projections/projection.options.ts.tpl +2 -0
  333. package/templates/be/patterns/projections/projection.projection.spec.ts.tpl +55 -0
  334. package/templates/be/patterns/projections/projection.projection.ts.tpl +21 -0
  335. package/templates/be/patterns/queues/platform/bullmq-queue-transport.client.spec.ts.tpl +142 -0
  336. package/templates/be/patterns/queues/platform/bullmq-queue-transport.client.ts.tpl +81 -0
  337. package/templates/be/patterns/queues/platform/persistence.connection.ts.tpl +8 -0
  338. package/templates/be/patterns/queues/platform/persistence.migration.ts.tpl +25 -0
  339. package/templates/be/patterns/queues/platform/persistence.queue-outbox.entity.ts.tpl +25 -0
  340. package/templates/be/patterns/queues/platform/persistence.queue.rows.ts.tpl +9 -0
  341. package/templates/be/patterns/queues/platform/persistence.queue.sql.ts.tpl +12 -0
  342. package/templates/be/patterns/queues/platform/queue-outbox.service.spec.ts.tpl +48 -0
  343. package/templates/be/patterns/queues/platform/queue-outbox.service.ts.tpl +17 -0
  344. package/templates/be/patterns/queues/platform/queue-relay.service.spec.ts.tpl +128 -0
  345. package/templates/be/patterns/queues/platform/queue-relay.service.ts.tpl +48 -0
  346. package/templates/be/patterns/queues/platform/queue-transport.port.ts.tpl +115 -0
  347. package/templates/be/patterns/queues/platform/queue-worker.service.spec.ts.tpl +91 -0
  348. package/templates/be/patterns/queues/platform/queue-worker.service.ts.tpl +45 -0
  349. package/templates/be/patterns/queues/platform/queue.config.ts.tpl +15 -0
  350. package/templates/be/patterns/queues/platform/queue.contracts.ts.tpl +31 -0
  351. package/templates/be/patterns/queues/platform/queue.decorators.ts.tpl +45 -0
  352. package/templates/be/patterns/queues/platform/queue.index.ts.tpl +7 -0
  353. package/templates/be/patterns/queues/platform/queue.log-events.ts.tpl +5 -0
  354. package/templates/be/patterns/queues/platform/queue.module-definition.ts.tpl +10 -0
  355. package/templates/be/patterns/queues/platform/queue.module.ts.tpl +62 -0
  356. package/templates/be/patterns/queues/platform/queue.options.ts.tpl +22 -0
  357. package/templates/be/patterns/queues/platform/queue.policy.spec.ts.tpl +19 -0
  358. package/templates/be/patterns/queues/platform/queue.policy.ts.tpl +11 -0
  359. package/templates/be/patterns/queues/platform/queue.port.ts.tpl +14 -0
  360. package/templates/be/patterns/queues/queue.index.ts.tpl +3 -0
  361. package/templates/be/patterns/queues/queue.module.ts.tpl +6 -0
  362. package/templates/be/patterns/queues/queue.queue.ts.tpl +23 -0
  363. package/templates/be/patterns/reactors/application.command.ts.tpl +10 -0
  364. package/templates/be/patterns/reactors/application.contracts.ts.tpl +12 -0
  365. package/templates/be/patterns/reactors/application.handler.ts.tpl +22 -0
  366. package/templates/be/patterns/reactors/consumer.ts.tpl +19 -0
  367. package/templates/be/patterns/reactors/message.module.ts.tpl +20 -0
  368. package/templates/be/patterns/reactors/reactor.index.ts.tpl +1 -0
  369. package/templates/be/patterns/reactors/reactor.module.ts.tpl +6 -0
  370. package/templates/be/patterns/realtime/channel.graphql.module.ts.tpl +6 -0
  371. package/templates/be/patterns/realtime/channel.index.ts.tpl +1 -0
  372. package/templates/be/patterns/realtime/channel.input.ts.tpl +12 -0
  373. package/templates/be/patterns/realtime/channel.subscription.spec.ts.tpl +51 -0
  374. package/templates/be/patterns/realtime/channel.subscription.ts.tpl +32 -0
  375. package/templates/be/patterns/realtime/channel.type.ts.tpl +9 -0
  376. package/templates/be/patterns/saga/saga.compensate.command.ts.tpl +11 -0
  377. package/templates/be/patterns/saga/saga.compensate.contracts.ts.tpl +7 -0
  378. package/templates/be/patterns/saga/saga.compensate.handler.ts.tpl +22 -0
  379. package/templates/be/patterns/saga/saga.compensation.ts.tpl +21 -0
  380. package/templates/be/patterns/saga/saga.complete.command.ts.tpl +11 -0
  381. package/templates/be/patterns/saga/saga.complete.contracts.ts.tpl +7 -0
  382. package/templates/be/patterns/saga/saga.complete.handler.ts.tpl +22 -0
  383. package/templates/be/patterns/saga/saga.done.consumer.ts.tpl +21 -0
  384. package/templates/be/patterns/saga/saga.failed.consumer.ts.tpl +21 -0
  385. package/templates/be/patterns/saga/saga.index.ts.tpl +1 -0
  386. package/templates/be/patterns/saga/saga.message.module.ts.tpl +23 -0
  387. package/templates/be/patterns/saga/saga.module.ts.tpl +20 -0
  388. package/templates/be/patterns/saga/saga.service.spec.ts.tpl +70 -0
  389. package/templates/be/patterns/saga/saga.service.ts.tpl +44 -0
  390. package/templates/be/patterns/saga/saga.state.ts.tpl +14 -0
  391. package/templates/be/patterns/saga/saga.step.ts.tpl +11 -0
  392. package/templates/be/patterns/saga/saga.undo.command.ts.tpl +10 -0
  393. package/templates/be/patterns/saga/saga.undo.contracts.ts.tpl +5 -0
  394. package/templates/be/patterns/saga/saga.undo.handler.ts.tpl +21 -0
  395. package/templates/be/patterns/webhooks/webhook.door.spec.ts.tpl +72 -0
  396. package/templates/be/patterns/webhooks/webhook.door.ts.tpl +36 -0
  397. package/templates/be/patterns/webhooks/webhook.http.module.ts.tpl +6 -0
  398. package/templates/be/patterns/webhooks/webhook.index.ts.tpl +1 -0
  399. package/templates/be/patterns/webhooks/webhook.request.ts.tpl +9 -0
  400. package/templates/be/skeleton/apps/__app__/src/__app__.options.ts +3 -0
  401. package/templates/be/skeleton/apps/__app__/src/app.module.ts +15 -2
  402. package/templates/be/skeleton/apps/__app__/src/main.ts +7 -15
  403. package/templates/be/skeleton/apps/cli/src/app.module.ts +30 -0
  404. package/templates/be/skeleton/apps/cli/src/cli.options.ts +22 -0
  405. package/templates/be/skeleton/apps/cli/src/main.ts +24 -0
  406. package/templates/be/skeleton/src/features/cli/cli.module.ts +7 -0
  407. package/templates/be/skeleton/src/features/cli/index.ts +1 -0
  408. package/templates/be/skeleton/src/features/cli/migrate/migrate.cli.spec.ts +17 -0
  409. package/templates/be/skeleton/src/features/cli/migrate/migrate.cli.ts +15 -0
  410. package/templates/be/skeleton/src/features/cli/migrate/migrate.module.ts +7 -0
  411. package/templates/be/skeleton/src/features/cli/migrate/subs/run.cli.spec.ts +17 -0
  412. package/templates/be/skeleton/src/features/cli/migrate/subs/run.cli.ts +18 -0
  413. package/templates/be/skeleton/src/features/cli/seed/seed.cli.spec.ts +17 -0
  414. package/templates/be/skeleton/src/features/cli/seed/seed.cli.ts +15 -0
  415. package/templates/be/skeleton/src/features/cli/seed/seed.module.ts +7 -0
  416. package/templates/be/skeleton/src/features/cli/seed/subs/run.cli.spec.ts +33 -0
  417. package/templates/be/skeleton/src/features/cli/seed/subs/run.cli.ts +34 -0
  418. package/templates/be/skeleton/src/modules/domain/identity/admission.policy.spec.ts +13 -0
  419. package/templates/be/skeleton/src/modules/domain/identity/auth.guard.spec.ts +40 -0
  420. package/templates/be/skeleton/src/modules/domain/identity/messages/identity.messages.ts +1 -1
  421. package/templates/be/skeleton/src/modules/domain/note/errors/note.error.ts +16 -0
  422. package/templates/be/skeleton/src/modules/domain/note/index.ts +3 -0
  423. package/templates/be/skeleton/src/modules/domain/note/note.contracts.ts +19 -0
  424. package/templates/be/skeleton/src/modules/domain/note/note.decorators.ts +9 -0
  425. package/templates/be/skeleton/src/modules/domain/note/note.module-definition.ts +7 -0
  426. package/templates/be/skeleton/src/modules/domain/note/note.module.ts +19 -0
  427. package/templates/be/skeleton/src/modules/domain/note/note.options.ts +2 -0
  428. package/templates/be/skeleton/src/modules/domain/note/note.service.spec.ts +76 -0
  429. package/templates/be/skeleton/src/modules/domain/note/note.service.ts +37 -0
  430. package/templates/be/skeleton/src/modules/domain/note/persistence/connection.ts +8 -0
  431. package/templates/be/skeleton/src/modules/domain/note/persistence/entities/note.entity.ts +17 -0
  432. package/templates/be/skeleton/src/modules/domain/note/persistence/migrations/1790000000000-init-note.ts +22 -0
  433. package/templates/be/skeleton/src/modules/domain/note/persistence/note.rows.ts +17 -0
  434. package/templates/be/skeleton/src/modules/domain/note/persistence/note.sql.ts +4 -0
  435. package/templates/be/skeleton/src/modules/platform/config/env-source.config.ts +20 -0
  436. package/templates/be/skeleton/src/modules/platform/config/index.ts +1 -1
  437. package/templates/be/skeleton/src/modules/platform/cqrs/cqrs.contracts.ts +18 -0
  438. package/templates/be/skeleton/src/modules/platform/database/connection-source.client.spec.ts +41 -0
  439. package/templates/be/skeleton/src/modules/platform/database/connection-source.client.ts +17 -0
  440. package/templates/be/skeleton/src/modules/platform/database/database.module-definition.ts +13 -0
  441. package/templates/be/skeleton/src/modules/platform/database/database.module.ts +77 -0
  442. package/templates/be/skeleton/src/modules/platform/database/database.options.ts +30 -0
  443. package/templates/be/skeleton/src/modules/platform/database/database.port.ts +65 -0
  444. package/templates/be/skeleton/src/modules/platform/database/database.sql.ts +47 -0
  445. package/templates/be/skeleton/src/modules/platform/database/errors/database.error.ts +16 -0
  446. package/templates/be/skeleton/src/modules/platform/database/index.ts +9 -0
  447. package/templates/be/skeleton/src/modules/platform/database/migrate-connections.client.ts +30 -0
  448. package/templates/be/skeleton/src/modules/platform/database/migration-runner.service.spec.ts +84 -0
  449. package/templates/be/skeleton/src/modules/platform/database/migration-runner.service.ts +23 -0
  450. package/templates/be/skeleton/src/modules/platform/database/primary.config.ts +9 -0
  451. package/templates/be/skeleton/src/modules/platform/database/primary.connection.ts +2 -0
  452. package/templates/be/skeleton/src/modules/platform/database/primary.decorators.ts +10 -0
  453. package/templates/be/skeleton/src/modules/platform/database/seed-connections.client.spec.ts +108 -0
  454. package/templates/be/skeleton/src/modules/platform/database/seed-connections.client.ts +75 -0
  455. package/templates/be/skeleton/src/modules/platform/database/seed-runner.service.spec.ts +88 -0
  456. package/templates/be/skeleton/src/modules/platform/database/seed-runner.service.ts +29 -0
  457. package/templates/be/skeleton/src/modules/platform/errors/errors.filter.spec.ts +48 -0
  458. package/templates/be/skeleton/src/modules/platform/errors/messages/errors.messages.ts +1 -1
  459. package/templates/be/skeleton/src/modules/platform/http-security/errors/http-security.error.ts +12 -0
  460. package/templates/be/skeleton/src/modules/platform/http-security/execution-request.mapper.spec.ts +16 -0
  461. package/templates/be/skeleton/src/modules/platform/http-security/http-security.config.ts +8 -1
  462. package/templates/be/skeleton/src/modules/platform/http-security/http-security.decorators.ts +8 -0
  463. package/templates/be/skeleton/src/modules/platform/http-security/http-security.module.ts +14 -1
  464. package/templates/be/skeleton/src/modules/platform/http-security/http-security.options.ts +12 -0
  465. package/templates/be/skeleton/src/modules/platform/http-security/messages/http-security.messages.ts +10 -2
  466. package/templates/be/skeleton/src/modules/platform/http-security/origin.guard.spec.ts +63 -0
  467. package/templates/be/skeleton/src/modules/platform/http-security/rate-limit.guard.spec.ts +108 -0
  468. package/templates/be/skeleton/src/modules/platform/http-security/request-validation.service.spec.ts +39 -0
  469. package/templates/be/skeleton/src/modules/platform/http-security/request-validation.service.ts +25 -0
  470. package/templates/be/skeleton/src/modules/platform/http-security/webhook-signature.service.spec.ts +172 -0
  471. package/templates/be/skeleton/src/modules/platform/http-security/webhook-signature.service.ts +66 -0
  472. package/templates/be/skeleton/src/modules/platform/logging/logging.log-events.ts +3 -1
  473. package/templates/be/skeleton/src/modules/platform/primitives/outcome.mapper.spec.ts +52 -0
  474. package/templates/be/skeleton-lite/apps/api/Dockerfile +37 -0
  475. package/templates/be/skeleton-lite/apps/api/src/api.options.ts +16 -0
  476. package/templates/be/skeleton-lite/apps/api/src/app.module.ts +81 -0
  477. package/templates/be/skeleton-lite/apps/api/src/main.ts +34 -0
  478. package/templates/be/skeleton-lite/src/modules/domain/identity/admission.policy.ts +14 -0
  479. package/templates/be/skeleton-lite/src/modules/domain/identity/auth.guard.ts +50 -0
  480. package/templates/be/skeleton-lite/src/modules/domain/identity/identity.contracts.ts +26 -0
  481. package/templates/be/skeleton-lite/src/modules/domain/identity/identity.decorators.ts +19 -0
  482. package/templates/be/skeleton-lite/src/modules/domain/identity/identity.module.ts +26 -0
  483. package/templates/be/skeleton-lite/src/modules/domain/identity/identity.options.ts +7 -0
  484. package/templates/be/skeleton-lite/src/modules/domain/identity/index.ts +6 -0
  485. package/templates/be/skeleton-lite/src/modules/integrations/supabase/errors/supabase.error.ts +19 -0
  486. package/templates/be/skeleton-lite/src/modules/integrations/supabase/index.ts +10 -0
  487. package/templates/be/skeleton-lite/src/modules/integrations/supabase/supabase.client.ts +24 -0
  488. package/templates/be/skeleton-lite/src/modules/integrations/supabase/supabase.config.ts +8 -0
  489. package/templates/be/skeleton-lite/src/modules/integrations/supabase/supabase.decorators.ts +17 -0
  490. package/templates/be/skeleton-lite/src/modules/integrations/supabase/supabase.jwks.ts +32 -0
  491. package/templates/be/skeleton-lite/src/modules/integrations/supabase/supabase.options.ts +24 -0
  492. package/templates/be/skeleton-lite/src/modules/platform/cqrs/index.ts +4 -0
  493. package/templates/be/skeleton-lite/src/modules/platform/database/database.module.ts +54 -0
  494. package/templates/be/skeleton-lite/src/modules/platform/database/database.options.ts +23 -0
  495. package/templates/be/skeleton-lite/src/modules/platform/database/database.port.ts +10 -0
  496. package/templates/be/skeleton-lite/src/modules/platform/database/database.sql.ts +49 -0
  497. package/templates/be/skeleton-lite/src/modules/platform/database/index.ts +4 -0
  498. package/templates/be/skeleton-lite/src/modules/platform/database/migration-runner.service.ts +21 -0
  499. package/templates/be/skeleton-lite/src/modules/platform/database/primary.config.ts +11 -0
  500. package/templates/be/skeleton-lite/src/modules/platform/database/seed-runner.service.ts +28 -0
  501. package/templates/be/skeleton-lite/src/modules/platform/http-security/http-security.module.ts +25 -0
  502. package/templates/be/skeleton-lite/src/modules/platform/http-security/index.ts +9 -0
  503. package/templates/be/skeleton-lite/src/modules/platform/i18n/index.ts +4 -0
  504. package/templates/be/skeleton-lite/src/modules/platform/logging/index.ts +5 -0
  505. package/templates/be/table/error.ts.tpl +16 -0
  506. package/templates/be/table/index.ts.tpl +2 -0
  507. package/templates/be/table/migration.sql.tpl +51 -0
  508. package/templates/be/table/module-definition.ts.tpl +7 -0
  509. package/templates/be/table/module.ts.tpl +14 -0
  510. package/templates/be/table/options.ts.tpl +2 -0
  511. package/templates/be/table/rows.ts.tpl +4 -0
  512. package/templates/be/table/service.ts.tpl +23 -0
  513. package/templates/be/table/sql.ts.tpl +4 -0
  514. package/templates/be/tool-config/jest.config.js +3 -1
  515. package/templates/be/upgrade-full/apps/cli/src/app.module.ts +14 -0
  516. package/templates/be/upgrade-full/apps/cli/src/main.ts +26 -0
  517. package/templates/be/upgrade-full/src/features/cli/cli.module.ts +6 -0
  518. package/templates/be/upgrade-full/src/features/cli/migrate/subs/run.cli.ts +23 -0
  519. package/templates/common/gitignore.base +1 -2
  520. package/templates/fe/app/package.json.tpl +1 -0
  521. package/templates/fe/app/tsconfig.json +12 -0
  522. package/templates/fe/common/app-layout.tsx.partial +8 -0
  523. package/templates/fe/common/site-shell.tsx.partial +9 -0
  524. package/templates/fe/image/next/Dockerfile +42 -0
  525. package/templates/fe/skeleton/apps/app/next.config.ts +23 -0
  526. package/templates/fe/skeleton/apps/app/postcss.config.mjs +7 -0
  527. package/templates/fe/skeleton/apps/{__app__ → app}/src/app/[locale]/error.tsx +2 -2
  528. package/templates/fe/skeleton/apps/app/src/app/[locale]/layout.tsx +22 -0
  529. package/templates/fe/skeleton/apps/{__app__ → app}/src/app/[locale]/loading.tsx +2 -2
  530. package/templates/fe/skeleton/apps/{__app__ → app}/src/app/[locale]/not-found.tsx +2 -2
  531. package/templates/fe/skeleton/apps/app/src/app/[locale]/page.tsx +9 -0
  532. package/templates/fe/skeleton/apps/{__app__ → app}/src/app/global-error.tsx +2 -2
  533. package/templates/fe/skeleton/apps/app/src/app/globals.css +10 -0
  534. package/templates/fe/skeleton/apps/app/src/features/layouts/AppLayout/index.tsx +18 -0
  535. package/templates/fe/skeleton/apps/app/src/features/pages/AppErrorPage/component.tsx +19 -0
  536. package/templates/fe/skeleton/apps/app/src/features/pages/AppErrorPage/index.tsx +11 -0
  537. package/templates/fe/skeleton/apps/app/src/features/pages/AppGlobalErrorPage/index.tsx +16 -0
  538. package/templates/fe/skeleton/apps/app/src/features/pages/AppHomePage/component.tsx +10 -0
  539. package/templates/fe/skeleton/apps/app/src/features/pages/AppHomePage/index.tsx +15 -0
  540. package/templates/fe/skeleton/apps/app/src/features/pages/AppLoadingPage/index.tsx +8 -0
  541. package/templates/fe/skeleton/apps/app/src/features/pages/AppNotFoundPage/index.tsx +9 -0
  542. package/templates/fe/skeleton/apps/app/src/modules/i18n/index.ts +4 -0
  543. package/templates/fe/skeleton/apps/app/src/modules/i18n/messages/vi.json +28 -0
  544. package/templates/fe/skeleton/apps/app/src/modules/i18n/request.ts +4 -0
  545. package/templates/fe/skeleton/apps/app/src/modules/routes/index.ts +4 -0
  546. package/templates/fe/skeleton/apps/app/src/proxy.ts +1 -0
  547. package/templates/fe/skeleton/apps/{__app__ → landing}/next.config.ts +4 -2
  548. package/templates/fe/skeleton/apps/landing/postcss.config.mjs +7 -0
  549. package/templates/fe/skeleton/apps/landing/src/app/[locale]/error.tsx +10 -0
  550. package/templates/fe/skeleton/apps/landing/src/app/[locale]/layout.tsx +22 -0
  551. package/templates/fe/skeleton/apps/landing/src/app/[locale]/loading.tsx +6 -0
  552. package/templates/fe/skeleton/apps/landing/src/app/[locale]/not-found.tsx +6 -0
  553. package/templates/fe/skeleton/apps/landing/src/app/[locale]/page.tsx +9 -0
  554. package/templates/fe/skeleton/apps/landing/src/app/global-error.tsx +10 -0
  555. package/templates/fe/skeleton/apps/landing/src/app/globals.css +10 -0
  556. package/templates/fe/skeleton/apps/landing/src/features/layouts/LandingLayout/index.tsx +24 -0
  557. package/templates/fe/skeleton/apps/landing/src/features/pages/LandingErrorPage/component.tsx +18 -0
  558. package/templates/fe/skeleton/apps/landing/src/features/pages/LandingErrorPage/index.tsx +11 -0
  559. package/templates/fe/skeleton/apps/landing/src/features/pages/LandingGlobalErrorPage/index.tsx +16 -0
  560. package/templates/fe/skeleton/apps/landing/src/features/pages/LandingLoadingPage/index.tsx +8 -0
  561. package/templates/fe/skeleton/apps/landing/src/features/pages/LandingNotFoundPage/index.tsx +9 -0
  562. package/templates/fe/skeleton/apps/landing/src/features/pages/LandingPage/component.tsx +24 -0
  563. package/templates/fe/skeleton/apps/landing/src/features/pages/LandingPage/index.tsx +16 -0
  564. package/templates/fe/skeleton/apps/landing/src/modules/config/index.ts +12 -0
  565. package/templates/fe/skeleton/apps/landing/src/modules/i18n/index.ts +7 -0
  566. package/templates/fe/skeleton/apps/landing/src/modules/i18n/messages/vi.json +31 -0
  567. package/templates/fe/skeleton/apps/landing/src/modules/i18n/request.ts +4 -0
  568. package/templates/fe/skeleton/apps/landing/src/modules/routes/index.ts +4 -0
  569. package/templates/fe/skeleton/apps/landing/src/proxy.ts +1 -0
  570. package/templates/fe/skeleton/packages/__project__-i18n/src/index.ts +49 -0
  571. package/templates/fe/skeleton/packages/__project__-i18n/src/proxy.ts +24 -0
  572. package/templates/fe/skeleton/packages/__project__-i18n/src/routing.ts +20 -0
  573. package/templates/fe/skeleton/packages/__project__-ui/src/branches/Providers/index.tsx +23 -0
  574. package/templates/fe/skeleton/packages/__project__-ui/src/composites/ErrorNotice/index.tsx +14 -0
  575. package/templates/fe/skeleton/packages/__project__-ui/src/composites/GlobalErrorNotice/index.tsx +25 -0
  576. package/templates/fe/skeleton/packages/__project__-ui/src/composites/LoadingNotice/index.tsx +9 -0
  577. package/templates/fe/skeleton/packages/__project__-ui/src/composites/LocaleShell/index.tsx +21 -0
  578. package/templates/fe/skeleton/packages/__project__-ui/src/composites/NotFoundNotice/index.tsx +19 -0
  579. package/templates/fe/skeleton/packages/__project__-ui/src/composites/SiteShell/classNames.ts +4 -0
  580. package/templates/fe/skeleton/packages/__project__-ui/src/composites/SiteShell/index.tsx +19 -0
  581. package/templates/fe/skeleton/packages/__project__-ui/src/index.ts +6 -0
  582. package/templates/fe/skeleton-lite/apps/web/next.config.ts +22 -0
  583. package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/error.tsx +10 -0
  584. package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/layout.tsx +37 -0
  585. package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/loading.tsx +6 -0
  586. package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/not-found.tsx +6 -0
  587. package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/page.tsx +9 -0
  588. package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/providers.tsx +26 -0
  589. package/templates/fe/skeleton-lite/apps/web/src/app/auth/callback/route.ts +24 -0
  590. package/templates/fe/skeleton-lite/apps/web/src/app/global-error.tsx +10 -0
  591. package/templates/fe/{skeleton/apps/__app__ → skeleton-lite/apps/web}/src/app/globals.css +1 -1
  592. package/templates/fe/skeleton-lite/apps/web/src/app/health/live/route.ts +5 -0
  593. package/templates/fe/skeleton-lite/apps/web/src/components/blocks/SignInForm/component.tsx +29 -0
  594. package/templates/fe/skeleton-lite/apps/web/src/components/blocks/SignInForm/index.tsx +41 -0
  595. package/templates/fe/skeleton-lite/apps/web/src/components/blocks/SignOutButton/component.tsx +17 -0
  596. package/templates/fe/skeleton-lite/apps/web/src/components/blocks/SignOutButton/index.tsx +38 -0
  597. package/templates/fe/skeleton-lite/apps/web/src/components/composites/ErrorNotice/index.tsx +13 -0
  598. package/templates/fe/skeleton-lite/apps/web/src/components/composites/GlobalErrorNotice/index.tsx +24 -0
  599. package/templates/fe/skeleton-lite/apps/web/src/components/composites/LoadingNotice/index.tsx +7 -0
  600. package/templates/fe/skeleton-lite/apps/web/src/components/composites/NotFoundNotice/index.tsx +18 -0
  601. package/templates/fe/skeleton-lite/apps/web/src/components/composites/SiteShell/classNames.ts +4 -0
  602. package/templates/fe/skeleton-lite/apps/web/src/components/composites/SiteShell/index.tsx +13 -0
  603. package/templates/fe/skeleton-lite/apps/web/src/features/layouts/AppLayout/index.tsx +31 -0
  604. package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppErrorPage/component.tsx +12 -0
  605. package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppErrorPage/index.tsx +10 -0
  606. package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppGlobalErrorPage/index.tsx +15 -0
  607. package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppHomePage/component.tsx +14 -0
  608. package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppHomePage/index.tsx +30 -0
  609. package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppLoadingPage/index.tsx +8 -0
  610. package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppNotFoundPage/index.tsx +9 -0
  611. package/templates/fe/skeleton-lite/apps/web/src/modules/brand/brand.css +20 -0
  612. package/templates/fe/skeleton-lite/apps/web/src/modules/brand/index.ts +2 -0
  613. package/templates/fe/skeleton-lite/apps/web/src/modules/config/index.ts +20 -0
  614. package/templates/fe/skeleton-lite/apps/web/src/modules/db/auth/read-session.ts +25 -0
  615. package/templates/fe/skeleton-lite/apps/web/src/modules/db/auth/write-sign-in.ts +25 -0
  616. package/templates/fe/skeleton-lite/apps/web/src/modules/db/auth/write-sign-out.ts +17 -0
  617. package/templates/fe/skeleton-lite/apps/web/src/modules/db/browser.ts +14 -0
  618. package/templates/fe/skeleton-lite/apps/web/src/modules/db/index.ts +3 -0
  619. package/templates/fe/skeleton-lite/apps/web/src/modules/db/outcome.ts +59 -0
  620. package/templates/fe/skeleton-lite/apps/web/src/modules/db/principal.ts +29 -0
  621. package/templates/fe/skeleton-lite/apps/web/src/modules/db/server.ts +70 -0
  622. package/templates/fe/skeleton-lite/apps/web/src/modules/db/validation/validation.mapper.ts +50 -0
  623. package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/index.ts +8 -0
  624. package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/messages/vi.json +48 -0
  625. package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/request.ts +16 -0
  626. package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/routing.ts +14 -0
  627. package/templates/fe/skeleton-lite/apps/web/src/modules/routes/index.ts +7 -0
  628. package/templates/fe/skeleton-lite/apps/web/src/modules/routes/safe-next-path.ts +26 -0
  629. package/templates/fe/skeleton-lite/apps/web/src/proxy.ts +43 -0
  630. package/templates/fe/table/read.ts.tpl +16 -0
  631. package/templates/fe/table/write.ts.tpl +16 -0
  632. package/upgrade/check.mjs +137 -0
  633. package/upgrade/index.mjs +524 -0
  634. package/bin/hfs.mjs +0 -233
  635. package/runtime/scripts/api/fs/rmdir-link.mjs +0 -12
  636. package/runtime/scripts/lib/test-secrets.mjs +0 -116
  637. package/templates/fe/skeleton/apps/__app__/src/app/[locale]/layout.tsx +0 -20
  638. package/templates/fe/skeleton/apps/__app__/src/app/[locale]/page.tsx +0 -9
  639. package/templates/fe/skeleton/apps/__app__/src/components/composites/FailureScreen/index.tsx +0 -28
  640. package/templates/fe/skeleton/apps/__app__/src/features/layouts/LocaleShell/index.tsx +0 -35
  641. package/templates/fe/skeleton/apps/__app__/src/features/pages/ErrorPage/component.tsx +0 -19
  642. package/templates/fe/skeleton/apps/__app__/src/features/pages/ErrorPage/index.tsx +0 -11
  643. package/templates/fe/skeleton/apps/__app__/src/features/pages/GlobalErrorPage/component.tsx +0 -24
  644. package/templates/fe/skeleton/apps/__app__/src/features/pages/GlobalErrorPage/index.tsx +0 -14
  645. package/templates/fe/skeleton/apps/__app__/src/features/pages/HomePage/component.tsx +0 -21
  646. package/templates/fe/skeleton/apps/__app__/src/features/pages/HomePage/index.tsx +0 -15
  647. package/templates/fe/skeleton/apps/__app__/src/features/pages/LoadingPage/component.tsx +0 -23
  648. package/templates/fe/skeleton/apps/__app__/src/features/pages/LoadingPage/index.tsx +0 -8
  649. package/templates/fe/skeleton/apps/__app__/src/features/pages/NotFoundPage/component.tsx +0 -28
  650. package/templates/fe/skeleton/apps/__app__/src/features/pages/NotFoundPage/index.tsx +0 -8
  651. package/templates/fe/skeleton/apps/__app__/src/modules/i18n/config.ts +0 -8
  652. package/templates/fe/skeleton/apps/__app__/src/modules/i18n/index.ts +0 -2
  653. package/templates/fe/skeleton/apps/__app__/src/modules/i18n/messages/vi.json +0 -25
  654. package/templates/fe/skeleton/apps/__app__/src/modules/i18n/request.ts +0 -19
  655. package/templates/fe/skeleton/apps/__app__/src/modules/i18n/routing.ts +0 -9
  656. package/templates/fe/skeleton/apps/__app__/src/modules/routes/index.ts +0 -4
  657. package/templates/fe/skeleton/apps/__app__/src/proxy.ts +0 -10
  658. /package/templates/be/skeleton/src/features/{system-health → api/system-health}/application/check-liveness.contracts.ts +0 -0
  659. /package/templates/be/skeleton/src/features/{system-health → api/system-health}/application/check-liveness.handler.ts +0 -0
  660. /package/templates/be/skeleton/src/features/{system-health → api/system-health}/application/check-liveness.query.ts +0 -0
  661. /package/templates/be/skeleton/src/features/{system-health → api/system-health}/index.ts +0 -0
  662. /package/templates/be/skeleton/src/features/{system-health → api/system-health}/system-health.module.ts +0 -0
  663. /package/templates/be/skeleton/src/features/{system-health → api/system-health}/transport/http/live.controller.ts +0 -0
  664. /package/templates/be/skeleton/src/features/{system-health → api/system-health}/transport/http/system-health-http.module.ts +0 -0
  665. /package/templates/fe/skeleton/apps/{__app__ → app}/src/app/health/live/route.ts +0 -0
  666. /package/templates/fe/skeleton/apps/{__app__ → app}/src/modules/config/index.ts +0 -0
  667. /package/templates/fe/{skeleton/apps/__app__ → skeleton-lite/apps/web}/postcss.config.mjs +0 -0
@@ -7,7 +7,7 @@
7
7
  # catalog READ this file through scripts/hfs/slots.mjs; none of them hardcodes a path.
8
8
  # Shape: modules/schemas/hfs-slots.schema.yaml. The repository side is hfs.json: modules/schemas/hfs-repo.schema.yaml.
9
9
  schema: starci/hfs-slots@2
10
- version: 2.0.0
10
+ version: 2.1.0
11
11
 
12
12
  versioning:
13
13
  # MAJOR.MINOR.PATCH of this manifest. A repository pins only the MAJOR in hfs.json ("hfs": 2).
@@ -24,6 +24,9 @@ versioning:
24
24
  a slot is never edited in place; it gets retiredIn (a major) and a successor slot id. After that major a
25
25
  path matching a retired slot is HFS_SLOT_RETIRED (error).
26
26
  pins: knowledge/hfs/canon-pins.yaml carries the exact versions of every @starci/* package and framework this MAJOR supports.
27
+ # 2.1.0 is such a minor: it ADDS the edition mechanism (the `editions` list, the per-slot `editions`, `litePresence`, `lite`
28
+ # overlays and `provider`, and `sides.<side>.reads` of an app-root tree) and the lite slots; an app without `edition` in its
29
+ # hfs.json is edition full and every answer for it is unchanged.
27
30
 
28
31
  # Field meaning, per slot.
29
32
  # id stable name, never changes meaning inside a major.
@@ -38,6 +41,13 @@ versioning:
38
41
  # tier node of the import direction matrix (tiers below); none = takes no part in import checks;
39
42
  # inherit = the tier of the enclosing owner.
40
43
  # owner true when every directory matching the slot is an owner: the unit cross-owner imports and cycles use.
44
+ # anonymousActions on a directory slot: the Server Action files (relative to it) that authenticate or end a session and so run for an anonymous principal (FE_AUTH_SESSION_TRUST exempts them from refusing it; they still parse their input).
45
+ # entries on a directory slot that sits inside an owner (not an owner itself): the files, besides the owner's index.ts, an importer
46
+ # outside the owner may enter through (a client-safe entry of a module whose barrel is server-only). Each is a relative
47
+ # path the slot allows. Everything else of the owner is entered through index.ts only (ARCH_OWNER_EXPORT_BYPASS).
48
+ # outcomeHome true on a file slot: the one file that declares the repository's Outcome union. A repository holds exactly one such
49
+ # file (modules/api/outcome.ts, the api package's outcome.ts, or modules/db/outcome.ts of a lite app),
50
+ # and no other file declares an Outcome union (one-outcome-union, FE_TRANSPORT_OWNER).
41
51
  # requires files or directories that must exist inside each instance; <var> is replaced by the bound name,
42
52
  # a leading / means repository root; a trailing / means directory.
43
53
  # requiredInstances variable -> names that must exist as instances (config, logging ... of platform).
@@ -45,42 +55,89 @@ versioning:
45
55
  # requiredWhen connections: required only when hfs.json declares a database connection.
46
56
  # allows / forbids names inside the instance; the loader exposes them, slot-check enforces them.
47
57
  # tests unit-beside | e2e | none.
48
- # composedBy the app kinds allowed to compose a transport slot (an app of another kind imports none of its modules).
58
+ # coverage required | none, on every tracked slot of profile be. A required slot's subtree is measured at 100 per file on every
59
+ # ruleParams.be.logicRoles file (lines, branches, functions, statements); a none slot's subtree is never measured and
60
+ # is excluded from Sonar's coverage. scripts/hfs/coverage-scope.mjs derives the jest per-file thresholds, Sonar's
61
+ # sonar.coverage.exclusions and the Codecov paths and components from this field and nowhere else (R204).
62
+ # Orthogonal to the lite `editions`: an edition never changes what a slot measures.
63
+ # composedBy the app kinds allowed to compose a transport slot (an app of another kind imports none of its modules); on an
64
+ # owner slot (be.cli) the app kinds that list its root module, every folder of the root holding one static module.
49
65
  # budget size budgets (lines per file, files per slot, exports).
66
+ # trigger the trigger kind (triggerKinds) of a feature-tier slot: api, webhooks, realtime, saga, reactors, jobs or cli. A feature of one kind
67
+ # never imports a feature of another (BE_KIND_ISOLATION); the two meet only through the event bus.
68
+ # pattern the design pattern whose declaration (hfs.json sides.<side>.patterns) enables this opt-in slot; a path in the slot of an
69
+ # undeclared pattern is HFS_SLOT_NOT_ENABLED, and a declared pattern's proof scenarios are ruleParams.be.patternScenarios.
50
70
  # appKind the app kind of hfs.json apps[].kind whose directory this slot describes.
51
- # managedBy the template directory hfs sync renders this slot's files from. The slot path then lists literal files
71
+ # managedBy the template directory starci app sync renders this slot's files from. The slot path then lists literal files
52
72
  # only, and each is rendered from templates/<profile or common>/<managedBy>/<path, leading dots dropped>;
73
+ # liteManagedBy the template directory starci app sync renders under edition lite (default: managedBy). A slot with only
74
+ # liteManagedBy is managed only in lite; its effective lite.path still lists literal files;
53
75
  # the managed file list lives here and nowhere in code. A difference is HFS_MANAGED_FILE_DRIFT (a
54
76
  # back end's tsconfig.json: HFS_TS_STRICT when a flag or an option was added).
55
77
  # rules rule ids (the catalog lands with knowledge/hfs/rules.yaml).
78
+ # editions the editions (below) the slot exists in; default all of them. A slot outside the declared edition owns
79
+ # no path - under lite, what full keeps absent is removed, and what lite forbids is a finding.
80
+ # litePresence the presence under edition lite (default: presence): a bare value, or {be, fe} for a both-sides slot.
81
+ # A slot forbidden in lite is external there and must name its goesTo.
82
+ # lite the fields the slot carries under lite only (path, requires, allows, forbids, minInstances,
83
+ # requiredInstances), each shaped as the same field in the slot body; every other field is the edition's.
84
+ # Under lite `tests` is none on every slot: a lite app has no test world.
85
+ # provider the hfs.json connections[].provider that enables this opt-in slot (supabase); never an app kind.
56
86
  # since / retiredIn / successor lifecycle.
57
87
  presenceValues: [required, optional, opt-in, forbidden]
58
88
  trackedValues: [tracked, ignored, external]
59
89
  testValues: [unit-beside, e2e, none]
60
90
 
91
+ # The editions a repository declares in hfs.json `edition` (absent means full). Lite is this same manifest filtered: a slot
92
+ # whose `editions` lacks lite does not exist for a lite app, `litePresence` replaces presence and a `lite` map replaces the
93
+ # fields it names. Upgrade lite -> full adds only (the forbidden test world becomes legal again); downgrade is not supported.
94
+ editions: [full, lite]
95
+
61
96
  # The two sides of an app: the folder of each is its profile name (be/, fe/). `reads` lists the only paths of the OTHER side a side
62
- # may read (hfs.json sides.<side>.reads names a subset); nothing else crosses sides.
97
+ # may read (hfs.json sides.<side>.reads names a subset), or a tree an app-root slot owns (supabase/types/); nothing else crosses sides.
63
98
  sides:
64
- be: {reads: []}
65
- fe: {reads: [be/contracts/]} # the back end's committed contract snapshots, the input of the front end's codegen
99
+ be: {reads: [supabase/types/]} # the generated Database types of a Supabase connection (server code types its PostgREST calls)
100
+ fe: {reads: [be/contracts/, supabase/types/]} # be/contracts/: the committed contract snapshots, the input of the front end's codegen; supabase/types/: the same generated types
101
+
102
+ # The trigger kinds of the back end's features: what starts the work of a feature. A feature slot names its kind in `trigger`; no kind imports another
103
+ # (BE_KIND_ISOLATION), every cross-kind call is an event on the bus, and only be/apps/* compose features.
104
+ triggerKinds: [api, webhooks, realtime, saga, reactors, jobs, cli]
66
105
 
67
106
  appKinds:
68
- be: [api, worker, migrate, cli]
107
+ be: [api, worker, cli]
69
108
  fe: [next]
70
109
 
110
+ # RT_SLOT_ID_SHAPE — the slot-id grammar and the suffix vocabulary law, stated once (scripts/checks/check-slot-id-shape.mjs
111
+ # reads this block): an id is dot-separated lowercase kebab segments whose first segment is one of `prefixes`;
112
+ # `<side>.app.<kind>` names a kind of appKinds.<side>; any other deeper id extends a declared slot id (its `parent`)
113
+ # or names `parent` explicitly. The role-suffix vocabulary never shares a word with bannedSuffixes and holds no
114
+ # synonym pair beyond `sameConceptPairs` — the one owner-approved pair (step + saga-step: `.saga-step.ts` stays).
115
+ # Every pair in `refusedPairs` may never both be live suffixes.
116
+ # `glossary` (S9-02): the five names for "handles an incoming thing" are five distinct kinds, not synonyms —
117
+ # consumer answers an event, subscription serves realtime, webhook is its own kind, processor runs a job, handler
118
+ # is CQRS. That is why appKinds may say worker while bannedSuffixes refuses the worker/job file suffixes.
119
+ naming:
120
+ prefixes: [app, be, fe, repo]
121
+ sameConceptPairs: [[step, saga-step]]
122
+ refusedPairs: [[use-case, handler], [listener, subscription], [job, processor], [worker, processor], [dto, input], [repository, mapper]]
123
+ glossary: {consumer: event, subscription: realtime, webhook: webhook, processor: job, handler: cqrs}
124
+
71
125
  # ---------------------------------------------------------------------------------------------------------
72
126
  # Tiers and the dependency direction matrix (owner granularity; type-only imports count; no cycles anywhere)
73
127
  # mayImport lists the tiers a file of that tier may import. Importing inside the same owner is always allowed.
74
128
  # ---------------------------------------------------------------------------------------------------------
75
129
  tiers:
76
130
  be:
77
- app: {mayImport: [feature, domain, platform, integrations, package]}
78
- feature: {mayImport: [domain, platform, integrations, package]} # never another feature
79
- domain: {mayImport: [domain, platform, integrations, package], acyclic: true}
131
+ app: {mayImport: [feature, domain, events, queues, projections, platform, integrations, package]}
132
+ feature: {mayImport: [domain, events, queues, projections, platform, integrations, package]} # never another feature
133
+ domain: {mayImport: [domain, events, queues, platform, integrations, package], acyclic: true} # a domain service writes the outbox: it publishes events and enqueues queue jobs inside its own transaction
134
+ events: {mayImport: [events, platform, package], acyclic: true} # modules/events/<service>: typed event classes (pattern event-bus)
135
+ queues: {mayImport: [queues, events, platform, package], acyclic: true} # modules/queues/<queue>: typed producers and schedulers (pattern queue)
136
+ projections: {mayImport: [projections, domain, events, platform, integrations, package], acyclic: true} # modules/projections/<name>: read models (pattern projection)
80
137
  integrations: {mayImport: [platform, package]}
81
138
  platform: {mayImport: [platform, package], acyclic: true}
82
- e2e: {mayImport: [e2e, app, feature, domain, platform, integrations, fixtures, package]}
83
- fixtures: {mayImport: [domain, platform, integrations, package]} # public contracts only
139
+ e2e: {mayImport: [e2e, app, feature, domain, events, queues, projections, platform, integrations, fixtures, package]}
140
+ fixtures: {mayImport: [domain, events, queues, projections, platform, integrations, package]} # public contracts only
84
141
  package: {mayImport: [package]}
85
142
  fe:
86
143
  route: {mayImport: [feature, foundation, modules, transport, package]} # app/** adapters
@@ -97,14 +154,17 @@ tiers:
97
154
  crossOwner: every import across owners targets the owner's public entry (index.ts or index.tsx); never export *.
98
155
  crossApp: apps never import each other; shared code is a packages/<pkg> slot.
99
156
 
100
- # Parameters the lint factories and checks read per profile (through ruleParams(profile) in scripts/hfs/slots.mjs).
157
+ # Parameters the lint factories and checks read per profile (through ruleParams(profile) in scripts/hfs/slots.mjs, which
158
+ # merges common under the profile: a side restates a common key only to override it).
101
159
  ruleParams:
102
- be:
103
- # HFS_SIZE_GROWTH: a file above soft may not grow against its parent commit; a new file stays within soft.
160
+ common:
161
+ # HFS_SIZE_GROWTH: a file above soft may not grow against its parent commit; a new file stays within soft. Slot
162
+ # budgets (component.tsx 300, hooks 200, modules 400) are stricter than this cap where they apply.
104
163
  fileLines: {soft: 500, hardGrowth: true}
105
- # R21 HFS_DUPLICATE_CODE: the ONE threshold of duplicate code (the architecture machine reads it here; no other file states it):
106
- # a token-normalised block of at least `lines` source lines and `tokens` tokens that appears twice.
164
+ # R21 HFS_DUPLICATE_CODE: the one threshold of duplicate code of both sides (the architecture machine reads it here;
165
+ # no other file states it): a token-normalised block of at least `lines` source lines and `tokens` tokens that appears twice.
107
166
  duplicateBlock: {lines: 8, tokens: 60}
167
+ be:
108
168
  # R90 BE_INFRA_OWNER: raw infrastructure library (a module specifier or a global reference) -> the platform or
109
169
  # integration capabilities allowed to import it; [] means nowhere in a back end. A specifier also covers its subpaths.
110
170
  infraOwners:
@@ -120,14 +180,15 @@ ruleParams:
120
180
  "@nestjs/cache-manager": [integrations/cache, integrations/redis] # the CACHE_MANAGER token: caching goes through the cache capability (retired local rule must-use-cache-service)
121
181
  redis: [integrations/cache, integrations/redis]
122
182
  ioredis: [integrations/cache, integrations/redis]
123
- bullmq: [platform/messaging]
124
- "@nestjs/bullmq": [platform/messaging]
125
- kafkajs: [platform/messaging]
126
- "@nestjs/schedule": [platform/scheduling, platform/retry, platform/messaging, platform/http]
127
- setInterval: [platform/scheduling, platform/retry, platform/messaging, platform/http]
128
- setTimeout: [platform/scheduling, platform/retry, platform/messaging, platform/http]
129
- "node:timers": [platform/scheduling, platform/retry, platform/messaging, platform/http]
130
- "node:timers/promises": [platform/scheduling, platform/retry, platform/messaging, platform/http]
183
+ bullmq: [platform/queue] # pattern queue: BullMQ jobs and job schedulers (the relay of the outbox into queues)
184
+ "@nestjs/bullmq": [platform/queue]
185
+ kafkajs: [platform/event-bus] # pattern event-bus: Kafka is the only event transport
186
+ "@nestjs/schedule": [] # no @Cron / @Interval / @Timeout anywhere: a schedule is a BullMQ job scheduler of a queue
187
+ eventemitter2: []
188
+ setInterval: [platform/retry, platform/event-bus, platform/queue, platform/http]
189
+ setTimeout: [platform/retry, platform/event-bus, platform/queue, platform/http]
190
+ "node:timers": [platform/retry, platform/event-bus, platform/queue, platform/http]
191
+ "node:timers/promises": [platform/retry, platform/event-bus, platform/queue, platform/http]
131
192
  winston: [platform/logging]
132
193
  dayjs: [platform/clock, platform/primitives]
133
194
  moment: [platform/clock, platform/primitives]
@@ -135,6 +196,22 @@ ruleParams:
135
196
  "@nestjs/config": []
136
197
  dotenv: []
137
198
  "@nestjs/event-emitter": []
199
+ "@nestjs/platform-socket.io": [platform/realtime]
200
+ socket.io: [platform/realtime]
201
+ graphql-subscriptions: [platform/realtime]
202
+ graphql-ws: [platform/realtime]
203
+ # R87 BE_CQRS_SHAPE no-event-bus: the in-process event vocabulary the rule refuses. `imports` maps a module
204
+ # specifier to the member names that are an in-process event; `[]` flags the package itself (its emitter packages
205
+ # are the `[]` owners of infraOwners above). `classes` maps a module specifier to the class names that are an
206
+ # in-process bus when constructed or extended.
207
+ eventBus:
208
+ imports:
209
+ "@nestjs/cqrs": [EventBus, EventsHandler, IEventHandler]
210
+ "@nestjs/event-emitter": []
211
+ classes:
212
+ events: [EventEmitter]
213
+ "node:events": [EventEmitter]
214
+ rxjs: [Subject, BehaviorSubject, ReplaySubject, AsyncSubject]
138
215
  # R48 BE_SPEC_QUALITY spec-infra-double-from-kit: in a `<name>.service.spec.ts` a provider `{ provide: TOKEN, useValue: X }` takes X from
139
216
  # the kit named here (the package root of `kit`). The token name is normalised to UPPER_SNAKE (`EntityManager` -> ENTITY_MANAGER), the
140
217
  # FIRST entry whose `token` regex matches decides, and `fallback` decides for every other token. `forms` say what X may be:
@@ -147,23 +224,90 @@ ruleParams:
147
224
  - {token: "(?:^|_)ENTITY_MANAGER$", double: mockEntityManager, forms: [call]}
148
225
  - {token: "(?:^|_)TRANSACTION(?:_RUNNER)?$", double: fakeTransaction, forms: [call]}
149
226
  - {token: "(?:^|_)CLOCK$", double: FakeClock, forms: [new]}
150
- - {token: "(?:^|_)OUTBOX(?:_|$)", double: recordingOutbox, forms: [call]}
227
+ - {token: "(?:^|_)EVENT_BUS$", double: recordingEventBus, forms: [call]}
228
+ - {token: "(?:^|_)QUEUE_OUTBOX$", double: recordingQueueOutbox, forms: [call]}
229
+ - {token: "(?:^|_)INBOX(?:_|$)", double: fakeInbox, forms: [call]}
151
230
  - {token: "(?:^|_)CACHE(?:_|$)", double: fakeCache, forms: [call]}
152
231
  - {token: "(?:^|_)(?:LOCK|LEASE|FENCE|HOLD)(?:_|$)", double: fakeLock, forms: [call]}
153
232
  - {token: "(?:^|_)(?:IDS|ID_GENERATOR|ID_FACTORY)$", double: fakeIds, forms: [call]}
154
233
  fallback: {double: mock, forms: [call, primitive, array]}
234
+ # R85 BE_RAW_INJECT injected-param-name: the names of the ports every class injects the same way (BE-CONVENTION 1.6: the parameter name
235
+ # is the camelCase of the port). An entry matches a constructor parameter that carries an injector and is annotated with a type
236
+ # declared outside the owner (a package, platform/* or integrations/*): `type` is the exact declared name, `typeSuffix` the end of
237
+ # it. The parameter is named one of `names`, or ends in `nameSuffix` when the entry has one (`orderEntityManager` for a class that
238
+ # injects two connections, `paymentOptions` beside `options`). A type no entry names is free to be named for what it is.
239
+ paramNames:
240
+ - {type: Clock, names: [clock]}
241
+ - {type: Logger, names: [logger]}
242
+ - {type: Cache, names: [cache]}
243
+ - {type: CommandBus, names: [commandBus]}
244
+ - {type: QueryBus, names: [queryBus]}
245
+ - {type: EntityManager, names: [entityManager], nameSuffix: EntityManager}
246
+ - {typeSuffix: Options, names: [options], nameSuffix: Options}
155
247
  # R89 BE_SOURCE_FORM: the closed role-suffix vocabulary of a source file name (<kebab-name>.<suffix>.ts; BE-CONVENTION 1.15)
156
248
  # and the suffixes that are never a role. index.ts, main.ts, app.module.ts and the migrations of be.persistence are the
157
249
  # only names outside it.
158
- suffixes: [module, module-definition, decorators, options, config, connection, service, command, query, handler, contracts, port, resolver, controller, gateway, consumer, job, cli, input, type, args, request, response, mapper, policy, entity, sql, rows, error, messages, log-events, cache-keys, guard, interceptor, filter, client, spec, integration-spec, e2e-spec, contract-spec, global-setup, builder]
250
+ suffixes: [module, module-definition, decorators, options, config, connection, service, command, query, handler, compensation, saga-state, saga-step, saga, contracts, port, resolver, controller, gateway, webhook, subscription, consumer, cli, event, queue, projection, projection-entity, processor, step, input, type, args, request, response, mapper, policy, entity, sql, rows, error, messages, log-events, cache-keys, guard, interceptor, filter, client, spec, integration-spec, e2e-spec, contract-spec, global-setup, builder]
159
251
  # R47 BE_CONTRACT_UNGUARDED: the world's shape vocabulary. `helper` names the function a contract spec imports to erase the values of a JSON
160
252
  # payload (`shapeOf`); a contract spec proves a fake's fixtures by asserting helper(real) equals helper(fixture).
161
253
  contractShape: {helper: shapeOf}
162
- bannedSuffixes: [use-case, repository, fixture, factory, store, worker, scheduler, cron, listener, dto, util, utils, helper, helpers, rules, types, constants, int-spec, harness-spec, test]
163
- fe:
164
- # Slot budgets (component.tsx 300, hooks 200, modules 400) are stricter than this cap where they apply.
165
- fileLines: {soft: 500, hardGrowth: true}
166
- duplicateBlock: {lines: 8, tokens: 60}
254
+ # THE list of unit-tested roles of a back end whose spec is REQUIRED (the ONE source every consumer reads: the eslint unit-test rules, hfs
255
+ # spec-placement and the cli spec check, the architecture machine's unit-spec checks and the unit-run gate). A role's subject is
256
+ # `<name>.<role>.ts` (inside `slot` when one is named) and its unit spec `<name>.<spec>.ts` beside it. What the unit run MEASURES is not
257
+ # listed here: it is `logicRoles` over the slots whose `coverage` is required (scripts/hfs/coverage-scope.mjs). `exempt` lists the rules
258
+ # the role is exempt from: a cli command is an action runner, not a thin transport (R149). A lane that adds a spec-required role adds
259
+ # its entry here and nowhere else.
260
+ unitRoles:
261
+ - {role: service, spec: service.spec}
262
+ - {role: cli, spec: cli.spec, slot: be.cli, exempt: [BE_TRANSPORT_SHAPE]}
263
+ # R204 HFS_COVERAGE_SCOPE_DRIFT. THE roles that carry logic: a file `<name>.<role>.ts` of one of these roles
264
+ # inside a slot whose `coverage` is required is measured at 100 per file on lines, branches, functions and statements (the three
265
+ # generated consumers: jest thresholds, sonar.coverage.exclusions, codecov.yml). A role that is not listed here (module, module-definition,
266
+ # options, config, command, query, contracts, port, event, queue, entity, sql, rows, messages, log-events, error, decorators, cache-keys,
267
+ # projection-entity, connection, and every file with no role: index.ts, main.ts, migrations) is declaration or composition and is never measured.
268
+ # A unit spec beside such a file (`<name>.<role>.spec.ts`) is legal but not required: the file owes 100, not a spec of its own.
269
+ logicRoles: [service, policy, projection, guard, mapper, client, filter, interceptor, processor, step, saga, saga-step, compensation, consumer, webhook]
270
+ # R203 BE_FEATURE_THIN. THE roles a feature-tier file is judged thin by: a file `<name>.<role>.ts` of one of these roles inside a slot of tier
271
+ # feature maps parameters and makes ONE delegating call into modules/ (a service, the command or query bus, the event bus, a queue producer): no
272
+ # branch, loop, ternary, try or throw, no computation; orchestration (step order, compensation, claim, fencing, retry) lives in platform/*.
273
+ thinRoles: [handler, resolver, controller, consumer, processor, step, saga, saga-step, compensation, webhook, gateway, subscription, cli, mapper, service]
274
+ # BE_PATTERN_SPEC_MISSING: a declared pattern (hfs.json sides.be.patterns) is proven on the test world. Each id below needs a test titled
275
+ # "<pattern>/<id>: ..." in an e2e or integration spec (be/src/tests/e2e, be/src/tests/integration).
276
+ patternScenarios:
277
+ event-bus: [atomic-with-transaction, relay-only-after-commit, redelivery-then-dead-letter, duplicate-delivery-is-a-noop]
278
+ queue: [enqueue-atomic-with-transaction, relay-job-id-is-outbox-id, scheduler-fires-once]
279
+ webhooks: [bad-signature-is-refused, replay-is-refused, accepted-delivery-publishes-once]
280
+ realtime: [subscriber-receives-push, foreign-topic-is-isolated, disconnect-releases-subscription]
281
+ fenced-job: [claim-bumps-token, zombie-fenced-out, redispatch-isolated]
282
+ projection: [recompute-is-idempotent, replay-rebuilds]
283
+ # BE_KIND_DECLARATION: the patterns an app that uses a trigger kind declares in hfs.json sides.be.patterns; the platform capabilities of those
284
+ # patterns (the slots of tier platform that name the pattern) must exist.
285
+ kindPatterns:
286
+ api: []
287
+ webhooks: []
288
+ realtime: []
289
+ saga: [saga]
290
+ reactors: [event-bus]
291
+ jobs: [fenced-job, queue]
292
+ cli: []
293
+ # starci app add <noun> <name>: the pattern topic (knowledge/patterns/be/<topic>.yaml) whose files tree is generated, the path variable of the
294
+ # instance, what it registers in hfs.json, the options it needs (--<option>), the other nouns generated with it under the same name (a job is
295
+ # the consumer of its queue, so `also: [queue]`) and the defaults of the other variables ("@name" = the name).
296
+ addKinds:
297
+ job: {topic: jobs, variable: job, trigger: jobs, patterns: [fenced-job, queue], also: [queue], defaults: {step: "@name"}}
298
+ reactor: {topic: reactors, variable: reactor, trigger: reactors, patterns: [event-bus], needs: [event, from, service], defaults: {action: "@name"}}
299
+ queue: {topic: queues, variable: queue, patterns: [queue]}
300
+ webhook: {topic: webhooks, variable: provider, trigger: webhooks, patterns: [webhooks], needs: [service], defaults: {event: "@name"}}
301
+ realtime: {topic: realtime, variable: channel, trigger: realtime, patterns: [realtime], needs: [service]}
302
+ saga: {topic: saga, variable: saga, trigger: saga, patterns: [saga], needs: [owner, from, failed, done, service], defaults: {step: "@name"}}
303
+ projection: {topic: projections, variable: name, patterns: [projection], needs: [connection]}
304
+ api: {topic: api, variable: feature, trigger: api, patterns: [], needs: [service], defaults: {action: "@name"}}
305
+ cli: {topic: cli, variable: group, trigger: cli, patterns: [], needs: [service], wire: {file: src/features/cli/cli.module.ts, import: "./@@group@@/@@group@@.module", symbol: "@@Group@@Module"}}
306
+ bannedSuffixes: [use-case, repository, fixture, factory, store, worker, scheduler, cron, job, listener, dto, util, utils, helper, helpers, rules, types, constants, int-spec, harness-spec, test]
307
+ # Edition lite (ruleParams(manifest, 'be', 'lite') merges these keys over the base): the schema authority is the Supabase
308
+ # migration tree (supabase/migrations/*.sql, forward-only), so a lite back end holds no TypeORM migration or entity under
309
+ # be/ (R34/R35 read schemaAuthority; be.persistence's lite overlay allows neither).
310
+ lite: {schemaAuthority: supabase, unitRoles: []} # lite has no test world: no service or cli command owes a unit spec
167
311
 
168
312
  slots:
169
313
 
@@ -186,14 +330,28 @@ slots:
186
330
  rules: [HFS_README_*]
187
331
  - id: app.package-manifest
188
332
  profiles: [app]
189
- # The one package.json of the app: every dependency of both sides and every script; npm workspaces only for fe/packages/*.
333
+ # The one package.json of the app, at the app root: be's dependencies and every tool, and every script; npm workspaces are
334
+ # exactly fe/apps/* and fe/packages/* (HFS_MONO_WORKSPACES), each with its own package.json. be has no package.json of its own.
190
335
  path: package.json
191
336
  presence: required
192
337
  tracked: tracked
193
338
  tier: none
194
339
  tests: none
195
340
  managedBy: package-scripts # the `scripts` block only; the rest of the file is the repository's
196
- rules: [HFS_PACKAGE_MANAGER_MIXED, HFS_DEP_VERSION_SKEW, HFS_CANON_PIN_DRIFT, HFS_MANAGED_FILE_DRIFT]
341
+ liteManagedBy: package-scripts-lite
342
+ rules: [HFS_PACKAGE_MANAGER_MIXED, HFS_DEP_VERSION_SKEW, HFS_CANON_PIN_DRIFT, HFS_MANAGED_FILE_DRIFT, HFS_MONO_WORKSPACES, HFS_MONO_WORKSPACE_DEP]
343
+ - id: app.task-graph
344
+ profiles: [app]
345
+ # The turbo task graph of the app (build, dev, lint, typecheck over the fe workspaces; build depends on ^build and caches
346
+ # .next and dist): rendered by starci app sync from templates/app/task-graph/turbo.json, judged for drift by starci app check. turbo needs
347
+ # it beside the root package.json and lockfile.
348
+ path: turbo.json
349
+ presence: required
350
+ tracked: tracked
351
+ tier: none
352
+ tests: none
353
+ managedBy: task-graph
354
+ rules: [HFS_MANAGED_FILE_DRIFT, HFS_MONO_WORKSPACES]
197
355
  - id: app.lockfile
198
356
  profiles: [app]
199
357
  path: package-lock.json
@@ -209,7 +367,7 @@ slots:
209
367
  tracked: tracked
210
368
  tier: none
211
369
  tests: none
212
- rules: [HFS_GITIGNORE_BLOCK_DRIFT] # the marked block of .gitignore is rendered by hfs sync (templates/app/gitignore)
370
+ rules: [HFS_GITIGNORE_BLOCK_DRIFT] # the marked block of .gitignore is rendered by starci app sync (templates/app/gitignore)
213
371
  - id: app.tool-config
214
372
  profiles: [app]
215
373
  path: "{.editorconfig,.nvmrc}"
@@ -227,6 +385,7 @@ slots:
227
385
  tier: none
228
386
  tests: none
229
387
  managedBy: tool-config
388
+ liteManagedBy: tool-config-lite # the same files plus the generated supabase/types, which the Supabase CLI writes unformatted
230
389
  rules: [HFS_MANAGED_FILE_DRIFT, HFS_TOOL_CONFIG_LOCAL]
231
390
  - id: app.tool-config-optional
232
391
  profiles: [app]
@@ -251,7 +410,9 @@ slots:
251
410
  tracked: tracked
252
411
  tier: none
253
412
  tests: none
413
+ lite: {path: sonar-project.properties}
254
414
  managedBy: quality-config
415
+ liteManagedBy: quality-config-lite
255
416
  rules: [HFS_SONAR_CONFIG, HFS_MANAGED_FILE_DRIFT]
256
417
  - id: app.hooks
257
418
  profiles: [app]
@@ -270,16 +431,63 @@ slots:
270
431
  tier: none
271
432
  tests: none
272
433
  managedBy: ci-workflows
434
+ liteManagedBy: ci-workflows-lite
273
435
  rules: [HFS_CI_E2E_AUTOMATIC, HFS_CI_MISSING_CANON, HFS_MANAGED_FILE_DRIFT]
274
436
  - id: app.ci-e2e
275
437
  profiles: [app]
276
438
  path: ".github/workflows/e2e.yml"
277
439
  presence: optional # on: workflow_dispatch only
440
+ litePresence: forbidden # a lite app has no e2e run
278
441
  tracked: tracked
279
442
  tier: none
280
443
  tests: none
444
+ goesTo: "the full edition: a lite app has no e2e workflow (no tests, nothing to run)"
281
445
  managedBy: ci-workflows
282
446
  rules: [HFS_CI_E2E_AUTOMATIC, HFS_MANAGED_FILE_DRIFT]
447
+ - id: app.ci-images
448
+ profiles: [app]
449
+ # Builds every image of the app (one matrix entry per declared app, path-filtered), never pushes: rendered by starci app sync.
450
+ path: ".github/workflows/images.yml"
451
+ presence: required
452
+ tracked: tracked
453
+ tier: none
454
+ tests: none
455
+ managedBy: ci-workflows
456
+ liteManagedBy: ci-workflows-lite
457
+ rules: [HFS_MANAGED_FILE_DRIFT]
458
+ - id: app.ci-db
459
+ profiles: [app]
460
+ # The database deploy workflow: workflow_dispatch only (like app.ci-e2e); starci app sync renders it under the managed ci group.
461
+ path: ".github/workflows/db-deploy.yml"
462
+ presence: optional
463
+ tracked: tracked
464
+ tier: none
465
+ tests: none
466
+ liteManagedBy: ci-workflows-lite
467
+ rules: [HFS_MANAGED_FILE_DRIFT]
468
+ since: 2.1.0
469
+ - id: app.dockerignore
470
+ profiles: [app]
471
+ # The one build-context filter of every image (the context is the app root): rendered by starci app sync; it keeps secrets and build output out.
472
+ path: .dockerignore
473
+ presence: required
474
+ tracked: tracked
475
+ tier: none
476
+ tests: none
477
+ managedBy: docker-ignore
478
+ rules: [HFS_MANAGED_FILE_DRIFT]
479
+ - id: app.browser
480
+ profiles: [app]
481
+ # The browser journey of the product (Playwright, owner ruling: one minimal journey per example app, run manually): the config, its
482
+ # journeys and the script that provisions the stack it runs against. It is the product's, never the front end's (FE_NO_TESTS keeps
483
+ # refusing any test or test tooling inside fe/). Declared by `browser: true` in hfs.json; the managed script `test:browser` exists only then.
484
+ path: "browser/"
485
+ presence: opt-in
486
+ tracked: tracked
487
+ tier: none
488
+ tests: none
489
+ requires: [playwright.config.ts, journeys/]
490
+ allows: [playwright.config.ts, stack.mjs, "journeys/*.ts"]
283
491
  - id: app.github-meta
284
492
  profiles: [app]
285
493
  path: ".github/{CODEOWNERS,pull_request_template.md,ISSUE_TEMPLATE/**,dependabot.yml}"
@@ -290,13 +498,16 @@ slots:
290
498
  - id: app.scripts
291
499
  profiles: [app]
292
500
  # The app's operational scripts only (`*.mjs`, `*.cjs`, `*.ps1`, `*.sh`), or nothing: an empty folder is `scripts/.gitkeep`, which
293
- # `hfs scaffold app` writes. A spec or a test (BE_SPEC_PLACEMENT, FE_NO_TESTS), a check or lint source (`check-*`, `eslint-local-rules*`,
501
+ # `starci app scaffold` writes. A spec or a test (BE_SPEC_PLACEMENT, FE_NO_TESTS), a check or lint source (`check-*`, `eslint-local-rules*`,
294
502
  # a local eslint plugin: HFS_REPO_LOCAL_CHECK) and anything that re-implements a canon check is not an operational script and has no place here.
295
503
  path: "scripts/{.gitkeep,*.mjs,*.cjs,*.ps1,*.sh}"
296
504
  presence: optional
297
505
  tracked: tracked
298
506
  tier: none
299
507
  tests: none
508
+ lite:
509
+ path: scripts/codegen.mjs
510
+ liteManagedBy: codegen-lite
300
511
  rules: [HFS_SCRIPT_ONE_OFF, BE_SPEC_PLACEMENT, HFS_REPO_LOCAL_CHECK] # one-off codemods and fix-* scripts are agent output, not tooling
301
512
  - id: app.sides
302
513
  profiles: [app]
@@ -314,13 +525,29 @@ slots:
314
525
  tracked: tracked
315
526
  tier: none
316
527
  tests: none
317
- requires: [.gitignore, workspace.yaml, features/index.yaml]
318
- allows: [workspace.yaml, "brand/**", shell/index.yaml, "features/<feature>/index.yaml",
528
+ requires: [.gitignore, workspace.yaml, index.yaml]
529
+ allows: [workspace.yaml, index.yaml, "brand/**", shell/index.yaml, "features/<feature>/index.yaml",
319
530
  "features/<feature>/<family>/<name>/index.yaml", "features/<feature>/br/<rule>/ac/<name>/index.yaml",
320
531
  "features/<feature>/ui/<name>/assets/<approved-file>", "features/<feature>/uat/<name>/{index,accounts,fixtures}.yaml",
321
532
  "features/<feature>/uat/<name>/{seed,cleanup}.sql", "_resources/{identities,environments,fixtures}/<slug>/resource.yaml"]
322
533
  forbids: [evidence/, logs/, reports/, captures/, runs/, "kernel-*/", "*.sqlite*", _derived/, "work/node records"]
534
+ # Lite keeps the product catalog, feature overviews, grammar and brand drawing records; UAT records belong to the forbidden slot below.
535
+ lite:
536
+ requires: [.gitignore, workspace.yaml, index.yaml]
537
+ allows: [workspace.yaml, index.yaml, "brand/**", shell/index.yaml, "features/<feature>/index.yaml", "features/<feature>/ui/<name>/index.yaml", "features/<feature>/ui/<name>/assets/<approved-file>"]
323
538
  rules: [HFS_AGENT_DATA_TRACKED, HFS_WORK_NODE_RETIRED, HFS_IDENTITY_CUSTODY]
539
+ - id: app.starciwork.uat
540
+ profiles: [app]
541
+ # The UAT records of the full edition: a lite app has no UAT (no tests, no record of one), so the trees are forbidden paths
542
+ # under lite only. In full this slot does not exist and .starciwork answers for them.
543
+ editions: [lite]
544
+ path: ".starciwork/{_resources/identities,_resources/fixtures,features/<feature>/uat,features/<feature>/br}/"
545
+ presence: forbidden
546
+ tracked: external
547
+ tier: none
548
+ tests: none
549
+ goesTo: "the full edition: a lite app has no UAT records (no identities or fixtures resources, no br/ac evidence, no uat directories)"
550
+ since: 2.1.0
324
551
  - id: app.starcistacks
325
552
  profiles: [app]
326
553
  # The stack declarations and the sealed custody of the app, at the app root beside be/, fe/ and .starciwork (never under a side).
@@ -331,8 +558,14 @@ slots:
331
558
  tests: none
332
559
  requires: [application-stacks.yaml]
333
560
  allows: [application-stacks.yaml, "<env>/README.md", "<env>/environment.json", "<env>/infra/{compose,k8s,terraform}/**", "<env>/infra/metadata.json",
334
- "<env>/runtime/{config,env}/**", "<env>/runtime/env/KEYS.md", "<env>/secrets/<slug>.enc", "<env>/seeds/**"]
561
+ "<env>/runtime/{config,env}/**", "<env>/runtime/env/KEYS.md", "<env>/secrets/<slug>.enc", "<env>/secrets/.gitkeep", "<env>/seeds/**"]
335
562
  forbids: ["<env>/runtime/files/**", "**/*.enc outside <env>/secrets/", DESIGN.md, deployment.json, "k8s/ at root"]
563
+ # Lite runs one managed host: the reduced shape keeps the stack declaration, the environment descriptor and the sealed
564
+ # secrets; infra/, runtime/ and seeds/ are the full edition's.
565
+ lite:
566
+ requires: []
567
+ allows: [application-stacks.yaml, "<env>/README.md", "<env>/environment.json", "<env>/secrets/<slug>.enc", "<env>/secrets/.gitkeep"]
568
+ forbids: ["<env>/runtime/files/**", "**/*.enc outside <env>/secrets/", DESIGN.md, deployment.json, "k8s/ at root"]
336
569
  rules: [HFS_STACKS_SHAPE, HFS_STACKS_IN_SIDE, HFS_PLAINTEXT_SECRET, HFS_IDENTITY_CUSTODY]
337
570
  - id: app.sops
338
571
  profiles: [app]
@@ -359,7 +592,7 @@ slots:
359
592
  goesTo: "${STARCI_CACHE_HOME:-%LOCALAPPDATA%/StarCi/cache}/<repo>/<tool>/ (the canon presets set eslint --cache-location, jest cacheDirectory, turbo cacheDir, sonar.working.directory)"
360
593
  - id: app.agent-output
361
594
  profiles: [app]
362
- path: "{report*.json,*.tmp.json,%*%,nul,.qwen*,.artifacts/,.tmp-*,*-lint.json,lf.json,lt.json,*.log,design-plans/,.gitmounts/,.dat,vi-flat.txt}"
595
+ path: "{report*.json,*.tmp.json,%*%,nul,.artifacts/,.tmp-*,*-lint.json,lf.json,lt.json,*.log,design-plans/,.gitmounts/,.dat,vi-flat.txt}"
363
596
  presence: forbidden
364
597
  tracked: external
365
598
  tier: none
@@ -373,7 +606,7 @@ slots:
373
606
  tracked: external
374
607
  tier: none
375
608
  tests: none
376
- goesTo: "D:/starci-lanes/<project>/<lane>/ (outside every repository) for a lane; a Kernel workflow's worktree is created and owned by Orca outside the app checkout, never tracked, and removed by the runtime's host-side controller once the workflow's finish marked it release-pending"
609
+ goesTo: "<lanes root>/<project>/<lane>/ (outside every repository) for a lane; a Kernel workflow's worktree is created and owned by Orca outside the app checkout, never tracked, and removed by the runtime's host-side controller once the workflow's finish marked it release-pending"
377
610
  - id: app.plaintext-env
378
611
  profiles: [app]
379
612
  path: "{.env,.env.*,.secrets/,**/*.pem,**/*.key}"
@@ -384,10 +617,92 @@ slots:
384
617
  goesTo: ".starcistacks/<env>/secrets/<slug>.enc (at the app root); decrypted values only in memory or under %LOCALAPPDATA%/StarCi/secrets/<project>/<env>/ via *_FILE"
385
618
  rules: [HFS_PLAINTEXT_SECRET]
386
619
 
620
+ # ----- app root: the Supabase project of an app whose connection declares provider: supabase (every lite app) -------
621
+ - id: app.supabase
622
+ profiles: [app]
623
+ # The one Supabase project directory, at the app root beside be/, fe/ and .starcistacks (never under a side).
624
+ path: "supabase/"
625
+ presence: opt-in # enabled by a connection with provider: supabase; required under edition lite
626
+ provider: supabase
627
+ litePresence: required
628
+ tracked: tracked
629
+ tier: none
630
+ tests: none
631
+ since: 2.1.0
632
+ - id: app.supabase.config
633
+ profiles: [app]
634
+ # The project configuration: auth keys are env(NAME) references, never literals (L08 judges its policy keys).
635
+ path: "supabase/config.toml"
636
+ presence: opt-in
637
+ provider: supabase
638
+ litePresence: required
639
+ tracked: tracked
640
+ tier: none
641
+ tests: none
642
+ since: 2.1.0
643
+ - id: app.supabase.migrations
644
+ profiles: [app]
645
+ # The schema authority of an app on Supabase: forward-only SQL migrations named <timestamp14>_<kebab-name>.sql,
646
+ # one file per change, immutable once on origin/main (L02-L08 judge them).
647
+ path: "supabase/migrations/<stamp>_<name>.sql"
648
+ presence: opt-in
649
+ provider: supabase
650
+ litePresence: required
651
+ lite: {minInstances: 1}
652
+ tracked: tracked
653
+ tier: none
654
+ tests: none
655
+ since: 2.1.0
656
+ - id: app.supabase.seed
657
+ profiles: [app]
658
+ path: "supabase/seed.sql"
659
+ presence: optional # synthetic dev data only; a lite app seeds nothing it cannot regenerate
660
+ tracked: tracked
661
+ tier: none
662
+ tests: none
663
+ since: 2.1.0
664
+ - id: app.supabase.types
665
+ profiles: [app]
666
+ # The generated Database types (database.types.ts) contract:emit writes from the migrations; both sides read it
667
+ # (sides.<side>.reads supabase/types/); drift against regeneration is L09.
668
+ path: "supabase/types/database.types.ts"
669
+ presence: opt-in
670
+ provider: supabase
671
+ litePresence: required
672
+ tracked: tracked
673
+ tier: none
674
+ tests: none
675
+ since: 2.1.0
676
+ - id: app.supabase.templates
677
+ profiles: [app]
678
+ path: "supabase/templates/*.html"
679
+ presence: optional # the auth e-mail templates Supabase serves
680
+ tracked: tracked
681
+ tier: none
682
+ tests: none
683
+ since: 2.1.0
684
+ - id: app.supabase.local-state
685
+ profiles: [app]
686
+ path: "supabase/{.temp,.branches}/"
687
+ presence: optional
688
+ tracked: ignored # the local CLI's working state: exists, never committed
689
+ tier: none
690
+ tests: none
691
+ since: 2.1.0
692
+ - id: app.supabase.functions
693
+ profiles: [app]
694
+ path: "supabase/functions/"
695
+ presence: forbidden # a Deno runtime is a second language runtime outside the lint canon and the slot machine (lite v1)
696
+ tracked: external
697
+ tier: none
698
+ tests: none
699
+ goesTo: "a be feature: webhooks/<provider> for a third-party callback, api for an endpoint, a cli command on a schedule (pg_cron / pg_net)"
700
+ since: 2.1.0
701
+
387
702
  # ----- side root (both sides): what the old standalone repository root held, less the app-root files ----------
388
703
  - id: repo.side-root-forbidden
389
704
  profiles: [be, fe]
390
- path: "{package.json,package-lock.json,hfs.json,README.md,.gitignore,.gitattributes,.husky/,.github/,.starciwork/,.starcistacks/,.sops.yaml,sonar-project.properties,codecov.yml,.prettierrc,.prettierignore,scripts/}"
705
+ path: "{package.json,package-lock.json,hfs.json,README.md,.gitignore,.gitattributes,.husky/,.github/,.starciwork/,.starcistacks/,supabase/,.sops.yaml,sonar-project.properties,codecov.yml,.prettierrc,.prettierignore,scripts/}"
391
706
  presence: forbidden
392
707
  tracked: external
393
708
  tier: none
@@ -395,7 +710,7 @@ slots:
395
710
  goesTo: "the app root: one package.json, lockfile, hfs.json, README, git and CI files, hooks, formatter, Sonar and Codecov configuration, scripts/, .starciwork, .starcistacks and .sops.yaml per app"
396
711
  - id: be.tool-config
397
712
  profiles: [be]
398
- # Managed files (hfs sync renders them, hfs check compares them): the whole tool configuration of a back end. Each is
713
+ # Managed files (starci app sync renders them, starci app check compares them): the whole tool configuration of a back end. Each is
399
714
  # a reference to a canon package, never a configuration: tsconfig.json extends @starci/tsconfig/be.json and adds the
400
715
  # three path aliases and excludes the world, integration, e2e and contract trees (fixtures stay in the root program: unit
401
716
  # specs import them); tsconfig.build.json puts an overlay preset after it and adds only the path-relative options a
@@ -409,15 +724,21 @@ slots:
409
724
  tracked: tracked
410
725
  tier: none
411
726
  tests: none
727
+ # Lite: no test tsconfig (no test tree) and no jest configuration; both are forbidden paths there (repo.tests-forbidden).
728
+ lite: {path: "{tsconfig.json,tsconfig.build.json,eslint.config.mjs}"}
729
+ coverage: none
412
730
  managedBy: tool-config
413
731
  rules: [HFS_MANAGED_FILE_DRIFT, HFS_TOOL_CONFIG_LOCAL, HFS_TS_STRICT]
414
732
  - id: be.nest-cli
415
733
  profiles: [be]
734
+ # The Nest monorepo of the back end: `monorepo: true` and one project per declared be app (apps/<app>, even for one service).
416
735
  path: nest-cli.json
417
736
  presence: required
418
737
  tracked: tracked
419
738
  tier: none
420
739
  tests: none
740
+ coverage: none
741
+ rules: [HFS_MONO_NEST_PROJECTS]
421
742
  - id: be.tool-config-local
422
743
  profiles: [be]
423
744
  path: "{.eslintrc,.eslintrc.*,.eslintignore,eslint.config.js,eslint.config.cjs,eslint.config.ts,eslint.config.mts,eslint.config.cts,.prettierrc.*,prettier.config.*,jest.config.ts,jest.config.mjs,jest.config.cjs,jest.config.json,jest.config.e2e.js}"
@@ -429,7 +750,7 @@ slots:
429
750
  rules: [HFS_TOOL_CONFIG_LOCAL]
430
751
  - id: fe.tool-config
431
752
  profiles: [fe]
432
- # Managed files (hfs sync renders them, hfs check compares them): the tool configuration of a front end. Each is a
753
+ # Managed files (starci app sync renders them, starci app check compares them): the tool configuration of a front end. Each is a
433
754
  # reference to a canon package, never a configuration: tsconfig.json extends @starci/tsconfig/next.json and only
434
755
  # names the preset and nothing else (each app's own tsconfig.json, in fe.app.next, carries its aliases and include; there is
435
756
  # no test tsconfig: a front end has no tests, FE_NO_TESTS); eslint.config.mjs is the one-line starciFeConfig call;
@@ -442,22 +763,14 @@ slots:
442
763
  tests: none
443
764
  managedBy: tool-config
444
765
  rules: [HFS_MANAGED_FILE_DRIFT, HFS_TOOL_CONFIG_LOCAL, HFS_RULE_OFF_WITHOUT_REPLACEMENT, HFS_TS_STRICT]
445
- - id: fe.tool-config-repo
446
- profiles: [fe]
447
- # The one tool file a preset cannot render because it carries the repository's own facts: turbo.json (its task graph).
448
- path: turbo.json
449
- presence: optional
450
- tracked: tracked
451
- tier: none
452
- tests: none
453
766
  - id: fe.tool-config-local
454
767
  profiles: [fe]
455
- path: "{.eslintrc,.eslintrc.*,.eslintignore,eslint.config.js,eslint.config.cjs,eslint.config.ts,eslint.config.mts,eslint.config.cts,.stylelintrc,.stylelintrc.*,.stylelintignore,stylelint.config.js,stylelint.config.cjs,stylelint.config.ts,stylelint.config.json,prettier.config.*,.prettierrc.*,lint-staged.config.*,.lintstagedrc*}"
768
+ path: "{.eslintrc,.eslintrc.*,.eslintignore,eslint.config.js,eslint.config.cjs,eslint.config.ts,eslint.config.mts,eslint.config.cts,.stylelintrc,.stylelintrc.*,.stylelintignore,stylelint.config.js,stylelint.config.cjs,stylelint.config.ts,stylelint.config.json,prettier.config.*,.prettierrc.*,lint-staged.config.*,.lintstagedrc*,turbo.json}"
456
769
  presence: forbidden
457
770
  tracked: external
458
771
  tier: none
459
772
  tests: none
460
- goesTo: "nowhere: a front end has exactly the managed tool configuration (fe.tool-config); a change to a rule or a flag is proposed in the .claude runtime"
773
+ goesTo: "nowhere: a front end has exactly the managed tool configuration (fe.tool-config); the task graph is the app root's turbo.json (app.task-graph); a change to a rule or a flag is proposed in the .claude runtime"
461
774
  rules: [HFS_TOOL_CONFIG_LOCAL]
462
775
  - id: repo.docs
463
776
  profiles: [be, fe]
@@ -466,6 +779,7 @@ slots:
466
779
  tracked: tracked
467
780
  tier: none
468
781
  tests: none
782
+ coverage: none
469
783
  rules: [HFS_ROOT_MARKDOWN_FORBIDDEN]
470
784
  - id: repo.docs-media
471
785
  profiles: [be, fe]
@@ -474,6 +788,7 @@ slots:
474
788
  tracked: tracked
475
789
  tier: none
476
790
  tests: none
791
+ coverage: none
477
792
  budget: {bytes: 512000}
478
793
  - id: repo.packages
479
794
  profiles: [be, fe]
@@ -484,21 +799,21 @@ slots:
484
799
  owner: true
485
800
  requires: [package.json, src/index.ts, tsconfig.json]
486
801
  tests: unit-beside
802
+ coverage: none
487
803
  rules: [FE_PACKAGE_SHAPE, HFS_UNUSED_EXPORT]
488
804
  - id: repo.app-image
489
805
  profiles: [be, fe]
806
+ # One image per app: every declared app has its own Dockerfile (apps/<app>/Dockerfile), built from the APP ROOT (the folder with
807
+ # the one package.json). It is app-owned (starci app scaffold writes it once from templates/<side>/image) and judged by the docker rules.
808
+ # A Dockerfile anywhere else is owned by no slot.
490
809
  path: "apps/<app>/Dockerfile"
491
- presence: optional
492
- tracked: tracked
493
- tier: none
494
- tests: none
495
- - id: repo.dockerignore
496
- profiles: [be, fe]
497
- path: .dockerignore
498
- presence: optional
810
+ presence: required
811
+ litePresence: {fe: optional} # lite: a Next app may be built by its host (Netlify, Vercel); a be image stays required
499
812
  tracked: tracked
500
813
  tier: none
501
814
  tests: none
815
+ coverage: none
816
+ rules: [HFS_DOCKER_BUILD_CONTEXT, HFS_DOCKER_STAGES, HFS_DOCKER_ENTRY, HFS_DOCKER_BASE_PIN, HFS_DOCKER_SECRETS]
502
817
 
503
818
  # ----- never tracked: build output the tools need in place ---------------------------------------------
504
819
  - id: repo.build-output
@@ -528,7 +843,7 @@ slots:
528
843
  goesTo: "${STARCI_CACHE_HOME:-%LOCALAPPDATA%/StarCi/cache}/<repo>/<tool>/ (the canon presets set eslint --cache-location, jest cacheDirectory, turbo cacheDir, sonar.working.directory)"
529
844
  - id: repo.agent-output
530
845
  profiles: [be, fe]
531
- path: "{report*.json,*.tmp.json,%*%,nul,.qwen*,.artifacts/,.tmp-*,*-lint.json,lf.json,lt.json,*.log,design-plans/,.gitmounts/,.dat,vi-flat.txt}"
846
+ path: "{report*.json,*.tmp.json,%*%,nul,.artifacts/,.tmp-*,*-lint.json,lf.json,lt.json,*.log,design-plans/,.gitmounts/,.dat,vi-flat.txt}"
532
847
  presence: forbidden
533
848
  tracked: external
534
849
  tier: none
@@ -542,7 +857,7 @@ slots:
542
857
  tracked: external
543
858
  tier: none
544
859
  tests: none
545
- goesTo: "D:/starci-lanes/<project>/<lane>/ (outside every repository)"
860
+ goesTo: "<lanes root>/<project>/<lane>/ (outside every repository)"
546
861
  - id: repo.plaintext-env
547
862
  profiles: [be, fe]
548
863
  path: "{.env,.env.*,.secrets/,**/*.pem,**/*.key}"
@@ -552,6 +867,21 @@ slots:
552
867
  tests: none
553
868
  goesTo: ".starcistacks/<env>/secrets/<slug>.enc (at the app root); decrypted values only in memory or under %LOCALAPPDATA%/StarCi/secrets/<project>/<env>/ via *_FILE"
554
869
  rules: [HFS_PLAINTEXT_SECRET]
870
+ - id: repo.tests-forbidden
871
+ profiles: [be, fe]
872
+ # Edition lite has no test world, on either side: a spec file, a test directory or a test-tool configuration anywhere in
873
+ # a side is a forbidden path (a finding, not an ignored file). In full this slot does not exist and each test file is
874
+ # judged by its own slot (the be tests tree, FE_NO_TESTS on the fe side).
875
+ editions: [lite]
876
+ # No nested braces: braceVariants expands each inner group per outer option. `**/e2e/` also matches a root e2e/ but
877
+ # be.root-e2e wins that on specificity; both are forbidden.
878
+ path: "{**/*.spec.*,**/*.test.*,**/*-spec.*,src/tests/,**/__tests__/,**/e2e/,**/test-support/,**/__mocks__/,**/jest.*,**/vitest.*,**/playwright.*,**/cypress.config.*,**/tsconfig.e2e.json,**/e2e.yml,**/e2e.yaml}"
879
+ presence: forbidden
880
+ tracked: external
881
+ tier: none
882
+ tests: none
883
+ goesTo: "the full edition: a lite repository holds no spec, test directory or jest/vitest/playwright configuration"
884
+ since: 2.1.0
555
885
  - id: be.root-e2e
556
886
  profiles: [be]
557
887
  path: "e2e/"
@@ -569,7 +899,8 @@ slots:
569
899
  tracked: tracked
570
900
  tier: none
571
901
  tests: none
572
- why: emitted by `npm run contract:emit` (`hfs emit-contracts`, never written by hand); CI fails when the emitted file differs (a contract, not a build artefact)
902
+ coverage: none
903
+ why: emitted by `npm run contract:emit` (`starci app emit`, never written by hand); CI fails when the emitted file differs (a contract, not a build artefact)
573
904
  rules: [HFS_CONTRACT_SNAPSHOT_DRIFT]
574
905
  - id: be.contract.openapi
575
906
  profiles: [be]
@@ -578,8 +909,33 @@ slots:
578
909
  tracked: tracked
579
910
  tier: none
580
911
  tests: none
581
- why: emitted by `npm run contract:emit` (`hfs emit-contracts`, never written by hand) from the app's typed operation table `apps/<app>/src/operations.ts` (canon BE-OPERATIONS-1); CI fails when the emitted file differs
912
+ coverage: none
913
+ why: emitted by `npm run contract:emit` (`starci app emit`, never written by hand) from the app's typed operation table `apps/<app>/src/operations.ts` (canon BE-OPERATIONS-1); CI fails when the emitted file differs
582
914
  rules: [HFS_CONTRACT_SNAPSHOT_DRIFT]
915
+ - id: be.contract.events
916
+ profiles: [be]
917
+ path: "contracts/<app>/events.json"
918
+ presence: opt-in
919
+ litePresence: forbidden # lite has no event bus, so no async contract to emit
920
+ tracked: tracked
921
+ tier: none
922
+ tests: none
923
+ coverage: none
924
+ why: the async contract of a service (`starci/event-contract@1`), emitted by `npm run contract:emit` (`starci app emit`, never written by hand) from the typed event classes of `src/modules/events/<service>/`; the consumers of its events rely on it (R166 HFS_EVENT_CONTRACT)
925
+ goesTo: "the full edition: a lite app has no event bus (no Kafka, no outbox), so no async contract exists to emit"
926
+ rules: [HFS_EVENT_CONTRACT]
927
+ - id: be.contract.events.pin
928
+ profiles: [be]
929
+ path: "contracts/<app>/events.pin.json"
930
+ presence: opt-in
931
+ litePresence: forbidden
932
+ tracked: tracked
933
+ tier: none
934
+ tests: none
935
+ coverage: none
936
+ why: the previously published event contract of a service, the same format as its events.json (copied over when the contract is published, never edited); the current events.json may only extend it additively, a breaking change is a new `<event>.v2` key (R177 BE_CONTRACT_BREAKING)
937
+ goesTo: "the full edition: a lite app has no event bus, so no published event contract to pin"
938
+ rules: [BE_CONTRACT_BREAKING]
583
939
 
584
940
  # ----- BE: apps (one slot per app kind; a new kind = a new slot, minor bump) ---------------------------
585
941
  - id: be.app.api
@@ -594,50 +950,53 @@ slots:
594
950
  requires: [main.ts, app.module.ts]
595
951
  allows: [main.ts, app.module.ts, "<app>.options.ts", operations.ts]
596
952
  tests: none # an app is proven by the e2e world (useTestWorld({ apps })), never by a unit spec
953
+ coverage: none
597
954
  budget: {app.module.ts: 250, main.ts: 80}
598
- rules: [BE_APP_COMPOSITION_ONLY, BE_APP_BUSINESS_ROLE, BE_DEFAULT_DENY, BE_ERROR_MASKED, BE_FEATURE_NOT_COMPOSED]
955
+ rules: [BE_APP_COMPOSITION_ONLY, BE_APP_BUSINESS_ROLE, BE_DEFAULT_DENY, BE_ERROR_MASKED, BE_FEATURE_NOT_COMPOSED, BE_CONTEXT_OWNER]
599
956
  - id: be.app.worker
600
957
  profiles: [be]
601
958
  path: "apps/<app>/src/"
602
- appKind: worker # composes transport/schedule and transport/message modules only
959
+ appKind: worker # composes transport/message modules and job processors only (an api app may compose them too: a worker app is for separate scaling)
603
960
  presence: opt-in
961
+ litePresence: forbidden # lite has no queue or event bus to consume; background work is a cli command on a schedule
604
962
  tracked: tracked
605
963
  tier: app
606
964
  owner: true
607
965
  requires: [main.ts, app.module.ts]
608
966
  allows: [main.ts, app.module.ts, "<app>.options.ts"]
609
967
  tests: none # an app is proven by the e2e world (useTestWorld({ apps })), never by a unit spec
610
- rules: [BE_APP_COMPOSITION_ONLY, BE_BACKGROUND_UNOWNED]
611
- - id: be.app.migrate
612
- profiles: [be]
613
- path: "apps/<app>/src/"
614
- appKind: migrate # the ONLY process that runs migrations (one run per connection, before api/worker start)
615
- presence: required
616
- requiredWhen: connections # required as soon as hfs.json declares a connection
617
- tracked: tracked
618
- tier: app
619
- owner: true
620
- requires: [main.ts]
621
- allows: [main.ts, "<app>.options.ts"]
622
- tests: none # an app is proven by the e2e world (useTestWorld({ apps })), never by a unit spec
623
- rules: [BE_SCHEMA_AUTHORITY, BE_ENTRYPOINT_ONLY_IN_APPS]
968
+ goesTo: "the full edition: a lite app has no worker (scheduled work is a cli command invoked by pg_cron / pg_net or a platform cron)"
969
+ coverage: none
970
+ rules: [BE_APP_COMPOSITION_ONLY, BE_BACKGROUND_UNOWNED, BE_CONTEXT_OWNER]
624
971
  - id: be.app.cli
625
972
  profiles: [be]
973
+ # The ONE app of every one-off action (migrate, seed, sync, backup, any operator command): be/apps/cli, on nest-commander.
974
+ # main.ts boots with CommandFactory.run (BE_CLI_BOOTSTRAP); app.module.ts composes the platform and the cli feature root
975
+ # (src/features/cli/cli.module.ts); cli.options.ts carries what it needs from the environment, the connections it migrates
976
+ # among them (the test world migrates through the same list). It is required as soon as the back end declares a connection
977
+ # or tracks a command, with its image apps/cli/Dockerfile (BE_CLI_REQUIRED): one image, run as `cli <group> <command>`.
626
978
  path: "apps/<app>/src/"
627
979
  appKind: cli
628
980
  presence: opt-in
981
+ litePresence: optional # lite: schema migration is the Supabase CLI's job; a cli app exists only when the app declares one
629
982
  tracked: tracked
630
983
  tier: app
631
984
  owner: true
632
985
  requires: [main.ts, app.module.ts]
633
986
  allows: [main.ts, app.module.ts, "<app>.options.ts"]
634
987
  tests: none # an app is proven by the e2e world (useTestWorld({ apps })), never by a unit spec
988
+ coverage: none
989
+ rules: [BE_CLI_REQUIRED, BE_CLI_BOOTSTRAP, BE_CLI_OWNER, BE_SCHEMA_AUTHORITY]
635
990
 
636
991
  # ----- BE: features ------------------------------------------------------------------------------------
992
+ # src/features/ is grouped by TRIGGER KIND (owner decision 2026-10-02): features/api/<feature>/ (a request: application/ and
993
+ # transport/<protocol>/, composed by the api apps), features/cli/ (a one-off action: the cli feature root, compiled only into
994
+ # apps/cli). Every kind imports only downward into modules/*; no kind imports another.
637
995
  - id: be.feature
638
996
  profiles: [be]
639
- path: "src/features/<feature>/"
997
+ path: "src/features/api/<feature>/"
640
998
  presence: required
999
+ trigger: api # a request-driven feature (graphql, http); the other trigger kinds have their own roots below
641
1000
  tracked: tracked
642
1001
  tier: feature
643
1002
  owner: true
@@ -645,96 +1004,250 @@ slots:
645
1004
  requires: [index.ts, "<feature>.module.ts", application/]
646
1005
  allows: [index.ts, "<feature>.module.ts", application/, transport/, messages/] # nothing else at feature root
647
1006
  tests: none
1007
+ coverage: none
648
1008
  budget: {files: 250, indexExports: 60} # a feature above 250 source files must be split by product capability
649
1009
  rules: [BE_FEATURE_SHAPE, BE_FEATURE_IMPORTS_FEATURE, BE_PUBLIC_SURFACE, BE_FEATURE_NOT_COMPOSED]
650
1010
  - id: be.feature.application
651
1011
  profiles: [be]
652
- path: "src/features/<feature>/application/"
1012
+ path: "src/features/{api/,jobs/,reactors/,saga/}<feature>/application/" # inside a trigger kind (jobs, reactors, saga) the feature folder is one level down
1013
+ lite: {path: "src/features/api/<feature>/application/"} # lite has only the api kind; the other roots are forbidden slots
653
1014
  presence: required
654
1015
  tracked: tracked
655
1016
  tier: feature
656
1017
  allows: ["<action>.{command,query,handler,contracts}.ts"]
657
1018
  forbids: [graphql/, http/, message/, schedule/, transport/] # no protocol names under application/
658
1019
  tests: none
1020
+ coverage: none
659
1021
  rules: [BE_APPLICATION_IMPORTS_TRANSPORT, BE_APPLICATION_TRANSPORT_FRAMEWORK, BE_ENTITY_IN_CONTRACT, BE_CQRS_SHAPE]
1022
+ - id: be.feature.saga
1023
+ profiles: [be]
1024
+ path: "src/features/saga/<saga>/"
1025
+ pattern: saga # enabled by `patterns: ["saga"]` in hfs.json sides.be (R169 to R173); an undeclared saga folder is HFS_SLOT_NOT_ENABLED
1026
+ presence: opt-in # a saga: the orchestrator of a business flow that crosses services, started and advanced only by events (knowledge/patterns/be/saga.yaml)
1027
+ litePresence: forbidden
1028
+ trigger: saga
1029
+ tracked: tracked
1030
+ tier: feature
1031
+ owner: true
1032
+ requires: [index.ts, "<saga>.module.ts", "<saga>.saga.service.ts", "<saga>.saga-state.ts", steps/, compensations/, transport/]
1033
+ allows: [index.ts, "<saga>.module.ts", "<saga>.saga.service.ts", "<saga>.saga.service.spec.ts", "<saga>.saga-state.ts", "<saga>.saga.log-events.ts", application/, steps/, compensations/, transport/] # the orchestrator is a `*.service.ts` because a handler injects only services; unit-tested beside it
1034
+ tests: unit-beside # <saga>.saga.service.spec.ts beside the orchestrator; the state machine and its fence are platform/saga's service; each compensation path has an e2e spec
1035
+ goesTo: "the full edition: a lite app has no saga (no event bus to advance it)"
1036
+ coverage: none
1037
+ rules: [BE_KIND_ISOLATION, BE_FEATURE_IMPORTS_FEATURE, BE_FEATURE_NOT_COMPOSED, BE_SAGA_STEP_COMPENSATION, BE_SAGA_STATE_VERSIONED, BE_SAGA_EVENT_CONTRACT, BE_SAGA_E2E_MISSING]
1038
+ - id: be.feature.saga.steps
1039
+ profiles: [be]
1040
+ path: "src/features/saga/<saga>/steps/"
1041
+ pattern: saga
1042
+ presence: opt-in
1043
+ litePresence: forbidden
1044
+ tracked: tracked
1045
+ tier: feature
1046
+ allows: ["<step>.saga-step.ts"] # one step of the flow: its name and the event it puts on the wire (the contract); its work is done by the domain, not by the step
1047
+ tests: none
1048
+ goesTo: "the full edition: a lite app has no saga"
1049
+ coverage: none
1050
+ rules: [BE_SAGA_STEP_COMPENSATION, BE_SAGA_EVENT_CONTRACT]
1051
+ - id: be.feature.saga.compensations
1052
+ profiles: [be]
1053
+ path: "src/features/saga/<saga>/compensations/"
1054
+ pattern: saga
1055
+ presence: opt-in
1056
+ litePresence: forbidden
1057
+ tracked: tracked
1058
+ tier: feature
1059
+ allows: ["<step>.compensation.ts"] # REQUIRED for every step: the command that undoes it, naming the failure event that triggers it
1060
+ tests: none
1061
+ goesTo: "the full edition: a lite app has no saga"
1062
+ coverage: none
1063
+ rules: [BE_SAGA_STEP_COMPENSATION, BE_SAGA_EVENT_CONTRACT, BE_SAGA_E2E_MISSING]
660
1064
  - id: be.feature.application.support
661
1065
  profiles: [be]
662
- path: "src/features/<feature>/application/support/"
1066
+ path: "src/features/{api/,jobs/,reactors/,saga/}<feature>/application/support/"
1067
+ lite: {path: "src/features/api/<feature>/application/support/"}
663
1068
  presence: optional # helpers and types local to ONE feature; same tier as application, never a shared home
664
1069
  tracked: tracked
665
1070
  tier: feature # feature tier: another feature cannot import it (features never import features)
666
1071
  allows: ["<name>.<role>.ts"] # <role> from ruleParams.be.suffixes; no <name>.types.ts
667
1072
  forbids: [graphql/, http/, message/, schedule/, transport/]
668
1073
  tests: none
1074
+ coverage: none
669
1075
  rules: [BE_APPLICATION_IMPORTS_TRANSPORT, BE_APPLICATION_TRANSPORT_FRAMEWORK, BE_FEATURE_IMPORTS_FEATURE]
670
1076
  since: 1.0.0
671
1077
  - id: be.transport.http
672
1078
  profiles: [be]
673
- path: "src/features/<feature>/transport/http/"
1079
+ path: "src/features/api/<feature>/transport/http/"
674
1080
  presence: optional
675
1081
  tracked: tracked
676
1082
  tier: feature
677
1083
  requires: ["<feature>-http.module.ts"]
678
1084
  allows: ["<action>.controller.ts", "dto/<action>.{request,response}.ts", "<action>.mapper.ts"]
679
1085
  tests: none
1086
+ coverage: none
680
1087
  composedBy: [api]
681
1088
  rules: [BE_DEFAULT_DENY, BE_INPUT_BOUNDED, BE_TRANSPORT_SHAPE]
682
1089
  - id: be.transport.graphql
683
1090
  profiles: [be]
684
- path: "src/features/<feature>/transport/graphql/"
1091
+ path: "src/features/api/<feature>/transport/graphql/"
685
1092
  presence: optional
686
1093
  tracked: tracked
687
1094
  tier: feature
688
1095
  requires: ["<feature>-graphql.module.ts"]
689
1096
  allows: ["<action>.resolver.ts", "dto/<action>.{input,type,args}.ts", "<action>.mapper.ts"]
690
1097
  tests: none
1098
+ coverage: none
691
1099
  composedBy: [api]
692
1100
  rules: [BE_DEFAULT_DENY, BE_INPUT_BOUNDED, BE_TRANSPORT_SHAPE]
693
1101
  - id: be.transport.message
694
1102
  profiles: [be]
695
- path: "src/features/<feature>/transport/message/"
696
- presence: opt-in # queue worker: consumed only by a worker app
1103
+ path: "src/features/{api/,reactors/,saga/}<feature>/transport/message/"
1104
+ presence: opt-in # queue consumer: composed by the service that owns it (an api app) or by a worker app scaled apart
1105
+ litePresence: forbidden # a consumer reads the event bus, which lite does not have
1106
+ pattern: event-bus # a consumer reads the event bus; declaring the pattern enables the slot
697
1107
  tracked: tracked
698
1108
  tier: feature
699
1109
  requires: ["<feature>-message.module.ts"]
700
1110
  allows: ["<event>.consumer.ts"] # BE-CONVENTION 1.6: transport/message holds only consumers; a .message.ts is outside the closed suffix list
701
1111
  tests: none
702
- composedBy: [worker]
703
- rules: [BE_BACKGROUND_UNOWNED]
704
- - id: be.transport.schedule
1112
+ coverage: none
1113
+ composedBy: [api, worker]
1114
+ goesTo: "the full edition: a lite app has no event bus, so no message transport (a third-party callback is a webhooks feature)"
1115
+ rules: [BE_BACKGROUND_UNOWNED, BE_SAGA_CONSUMER_DEDUPE]
1116
+ # ----- BE: feature kinds that are not an api: a webhook door and a realtime door. Each kind is a folder of src/features/ named by
1117
+ # the kind and holds one folder per provider or channel; a kind imports only modules/* (never another kind) and only an
1118
+ # api-kind app composes it. Their doors answer to the same door laws as every transport (default deny, bounded input,
1119
+ # no envelope), plus the kind laws of knowledge/patterns/be/webhooks.yaml and realtime.yaml.
1120
+ - id: be.feature.webhooks
705
1121
  profiles: [be]
706
- path: "src/features/<feature>/transport/schedule/"
707
- presence: opt-in # cron job, sweep or outbox publisher: consumed only by a worker app
1122
+ path: "src/features/webhooks/<provider>/"
1123
+ presence: opt-in # a provider webhook intake: verifies the signature and replay window, then hands the delivery to ONE domain intake method
1124
+ pattern: webhooks # declaring the pattern (hfs.json sides.be.patterns) enables the kind (knowledge/patterns/be/webhooks.yaml)
1125
+ trigger: webhooks
708
1126
  tracked: tracked
709
1127
  tier: feature
710
- requires: ["<feature>-schedule.module.ts"]
711
- allows: ["<job>.job.ts"]
1128
+ owner: true
1129
+ requires: [index.ts, transport/]
1130
+ allows: [index.ts, transport/]
712
1131
  tests: none
713
- composedBy: [worker]
714
- rules: [BE_BACKGROUND_UNOWNED]
715
- - id: be.transport.websocket
1132
+ coverage: none
1133
+ rules: [BE_KIND_ISOLATION, BE_FEATURE_IMPORTS_FEATURE, BE_FEATURE_NOT_COMPOSED]
1134
+ since: 1.0.0
1135
+ - id: be.feature.webhooks.http
1136
+ profiles: [be]
1137
+ path: "src/features/webhooks/<provider>/transport/http/"
1138
+ presence: opt-in # the one door of the provider: its module, the signed webhook and the door's own spec
1139
+ pattern: webhooks
1140
+ tracked: tracked
1141
+ tier: feature
1142
+ requires: ["<provider>-http.module.ts", "<provider>.webhook.ts", "<provider>.webhook.spec.ts"]
1143
+ allows: ["<provider>-http.module.ts", "<provider>.webhook.ts", "<provider>.webhook.spec.ts", dto/]
1144
+ tests: unit-beside # the door's own spec (<provider>.webhook.spec.ts) beside the door: the one non-service unit spec of this kind
1145
+ lite: # a lite app has no test world: the door and its module only
1146
+ requires: ["<provider>-http.module.ts", "<provider>.webhook.ts"]
1147
+ allows: ["<provider>-http.module.ts", "<provider>.webhook.ts", dto/]
1148
+ coverage: none
1149
+ composedBy: [api]
1150
+ rules: [BE_WEBHOOK_SHAPE, BE_WEBHOOK_UNVERIFIED, BE_DEFAULT_DENY, BE_INPUT_BOUNDED]
1151
+ since: 1.0.0
1152
+ - id: be.feature.webhooks.dto
716
1153
  profiles: [be]
717
- path: "src/features/<feature>/transport/websocket/"
1154
+ path: "src/features/webhooks/<provider>/transport/http/dto/"
718
1155
  presence: opt-in
1156
+ pattern: webhooks
719
1157
  tracked: tracked
720
1158
  tier: feature
721
- requires: ["<feature>-websocket.module.ts"]
722
- allows: ["<channel>.gateway.ts", "dto/*.ts"]
1159
+ allows: ["<event>.request.ts"] # the typed provider payload, validated and bounded (BE_INPUT_BOUNDED)
723
1160
  tests: none
1161
+ coverage: none
724
1162
  composedBy: [api]
1163
+ rules: [BE_INPUT_BOUNDED]
725
1164
  since: 1.0.0
726
- - id: be.feature.transport.cli
1165
+ - id: be.feature.realtime
727
1166
  profiles: [be]
728
- path: "src/features/<feature>/transport/cli/"
729
- presence: opt-in # a command line entry: dispatches one command or query, composed by an app of kind cli
1167
+ path: "src/features/realtime/<channel>/"
1168
+ presence: opt-in # a push channel: reads and pushes only (a socket gateway or a GraphQL subscription), no domain write
1169
+ litePresence: forbidden # under lite realtime is Supabase Realtime (the client subscribes to channels; nothing serves them in be/)
1170
+ pattern: realtime # declaring the pattern (hfs.json sides.be.patterns) enables the kind (knowledge/patterns/be/realtime.yaml)
1171
+ trigger: realtime
730
1172
  tracked: tracked
731
1173
  tier: feature
732
- requires: ["<feature>-cli.module.ts"]
733
- allows: ["<name>.cli.ts", "dto/*.ts"] # .cli.ts: .command.ts is a CQRS message (HFS delta 3)
1174
+ owner: true
1175
+ requires: [index.ts, transport/]
1176
+ allows: [index.ts, transport/]
734
1177
  tests: none
735
- composedBy: [cli]
736
- rules: [BE_APPLICATION_IMPORTS_TRANSPORT, BE_FEATURE_NOT_COMPOSED, BE_ENTRYPOINT_ONLY_IN_APPS]
1178
+ goesTo: "the full edition: under lite realtime is Supabase Realtime (RLS-governed), not a be kind"
1179
+ coverage: none
1180
+ rules: [BE_KIND_ISOLATION, BE_FEATURE_IMPORTS_FEATURE, BE_FEATURE_NOT_COMPOSED]
1181
+ since: 1.0.0
1182
+ - id: be.feature.realtime.graphql
1183
+ profiles: [be]
1184
+ path: "src/features/realtime/<channel>/transport/graphql/"
1185
+ presence: opt-in # a GraphQL subscription over graphql-ws
1186
+ litePresence: forbidden
1187
+ pattern: realtime
1188
+ tracked: tracked
1189
+ tier: feature
1190
+ goesTo: "the full edition: under lite realtime is Supabase Realtime, not a be kind"
1191
+ requires: ["<channel>-graphql.module.ts", "<channel>.subscription.ts", "<channel>.subscription.spec.ts"]
1192
+ allows: ["<channel>-graphql.module.ts", "<channel>.subscription.ts", "<channel>.subscription.spec.ts", dto/]
1193
+ tests: unit-beside # the door's own spec beside the door (a subscription has no service of its own)
1194
+ coverage: none
1195
+ composedBy: [api]
1196
+ rules: [BE_REALTIME_SHAPE, BE_REALTIME_WRITES, BE_REALTIME_TOPIC_SCOPE, BE_DEFAULT_DENY]
1197
+ since: 1.0.0
1198
+ - id: be.feature.realtime.dto
1199
+ profiles: [be]
1200
+ path: "src/features/realtime/<channel>/transport/graphql/dto/"
1201
+ presence: opt-in
1202
+ litePresence: forbidden
1203
+ pattern: realtime
1204
+ tracked: tracked
1205
+ tier: feature
1206
+ goesTo: "the full edition: under lite realtime is Supabase Realtime, not a be kind"
1207
+ allows: ["<name>.{input,type,args}.ts"]
1208
+ tests: none
1209
+ coverage: none
1210
+ composedBy: [api]
1211
+ rules: [BE_INPUT_BOUNDED]
1212
+ since: 1.0.0
1213
+ - id: be.feature.realtime.websocket
1214
+ profiles: [be]
1215
+ path: "src/features/realtime/<channel>/transport/websocket/"
1216
+ presence: opt-in # a socket gateway
1217
+ litePresence: forbidden
1218
+ pattern: realtime
1219
+ tracked: tracked
1220
+ tier: feature
1221
+ goesTo: "the full edition: under lite realtime is Supabase Realtime, not a be kind"
1222
+ requires: ["<channel>-websocket.module.ts", "<channel>.gateway.ts", "<channel>.gateway.spec.ts"]
1223
+ allows: ["<channel>-websocket.module.ts", "<channel>.gateway.ts", "<channel>.gateway.spec.ts"]
1224
+ tests: unit-beside # the door's own spec beside the door (a gateway has no service of its own)
1225
+ coverage: none
1226
+ composedBy: [api]
1227
+ rules: [BE_REALTIME_SHAPE, BE_REALTIME_WRITES, BE_REALTIME_TOPIC_SCOPE, BE_DEFAULT_DENY]
737
1228
  since: 1.0.0
1229
+ - id: be.cli
1230
+ profiles: [be]
1231
+ # The cli feature root: every one-off action of the back end, compiled only into apps/cli. A group is a nest-commander
1232
+ # @Command (<group>/<group>.cli.ts) whose sub-commands are @SubCommand classes extending CommandRunner
1233
+ # (<group>/subs/<name>.cli.ts), each with its unit spec beside it (<name>.cli.spec.ts): a command is an action runner, not a
1234
+ # thin transport (BE_CLI_COMMAND_SHAPE). `.cli.ts` because `.command.ts` is the CQRS message (HFS delta 3). Every module of
1235
+ # the root is a static @Module (a feature holds no module definition): cli.module.ts imports each group module, a group
1236
+ # module provides its commands, and a command takes its inputs from a platform capability (the connections and their opener
1237
+ # from platform/database) or from a command-line @Option.
1238
+ path: "src/features/cli/"
1239
+ presence: optional
1240
+ trigger: cli # the one cli feature root: a one-off action never imports a feature of another kind (BE_KIND_ISOLATION)
1241
+ tracked: tracked
1242
+ tier: feature
1243
+ owner: true
1244
+ requires: [index.ts, cli.module.ts]
1245
+ # A composed root: an app of kind cli lists cli.module.ts, never a group module (BE_MODULE_SHAPE).
1246
+ composedBy: [cli]
1247
+ allows: [index.ts, cli.module.ts, "<group>/<group>.cli.ts", "<group>/<group>.cli.spec.ts", "<group>/<group>.module.ts", "<group>/subs/<name>.cli.ts", "<group>/subs/<name>.cli.spec.ts"]
1248
+ tests: unit-beside
1249
+ coverage: none
1250
+ rules: [BE_CLI_COMMAND_SHAPE, BE_CLI_OWNER, BE_CLI_REQUIRED, BE_MODULE_SHAPE, BE_SCHEMA_AUTHORITY]
738
1251
 
739
1252
  # ----- BE: modules (three tiers, closed set) -----------------------------------------------------------
740
1253
  - id: be.domain
@@ -748,8 +1261,9 @@ slots:
748
1261
  allows: ["<capability>.module.ts", "<capability>.config.ts", "<capability>.options.ts", errors/, messages/, persistence/, policies/, "*.service.ts", "*.service.spec.ts", "*.contracts.ts"]
749
1262
  forbids: [testing/, exceptions/, utils/, helpers/, shared/]
750
1263
  tests: unit-beside # <name>.service.spec.ts beside each <name>.service.ts, nothing else
1264
+ coverage: required
751
1265
  budget: {indexExports: 60}
752
- rules: [BE_TIER_DIRECTION, ARCH_OWNER_CYCLE, BE_PUBLIC_SURFACE, BE_ERROR_HOME, BE_CONFIG_OWNER]
1266
+ rules: [BE_TIER_DIRECTION, ARCH_OWNER_CYCLE, BE_PUBLIC_SURFACE, BE_ERROR_HOME, BE_CONFIG_OWNER, BE_CONTEXT_COUPLING, BE_CONTEXT_TRANSACTION]
753
1267
  - id: be.errors
754
1268
  profiles: [be]
755
1269
  path: "src/modules/{domain,integrations}/<capability>/errors/"
@@ -758,6 +1272,7 @@ slots:
758
1272
  tier: inherit
759
1273
  allows: ["<capability>.error.ts", "<name>.error.ts"] # one family per capability, extends platform/errors DomainError
760
1274
  tests: none
1275
+ coverage: none
761
1276
  rules: [BE_ERROR_HOME]
762
1277
  - id: be.domain.messages
763
1278
  profiles: [be]
@@ -767,29 +1282,41 @@ slots:
767
1282
  tier: inherit
768
1283
  allows: ["<capability>.messages.ts"] # one keyed catalog per capability, vi and en, read through the platform/i18n MessageCatalog port
769
1284
  tests: none
1285
+ coverage: none
770
1286
  rules: [BE_USER_COPY_LITERAL]
771
1287
  since: 1.0.0
772
1288
  - id: be.feature.messages
773
1289
  profiles: [be]
774
- path: "src/features/<feature>/messages/"
1290
+ path: "src/features/api/<feature>/messages/"
775
1291
  presence: optional
776
1292
  tracked: tracked
777
1293
  tier: feature
778
1294
  allows: ["<feature>.messages.ts"] # one keyed catalog per feature, vi and en, read through the platform/i18n MessageCatalog port
779
1295
  tests: none
1296
+ coverage: none
780
1297
  rules: [BE_USER_COPY_LITERAL]
781
1298
  since: 1.0.0
782
1299
  - id: be.persistence
783
1300
  profiles: [be]
784
- path: "src/modules/{domain,platform}/<capability>/persistence/"
1301
+ path: "src/modules/{domain,platform,projections}/<capability>/persistence/"
1302
+ # R178: the platform capabilities whose tables exist on EVERY connection that uses them (the event-bus outbox, the inbox of every service, the job table); their arrays may be
1303
+ # registered on several declared connections. Any other capability registers its arrays on exactly one (BE_SCHEMA_OWNER).
1304
+ perConnection: [event-bus, inbox, jobs]
785
1305
  presence: optional
786
1306
  tracked: tracked
787
1307
  tier: inherit
788
1308
  requires: [connection.ts] # the capability's <c>Entities / <c>Migrations arrays (the owner index re-exports the two arrays by name); no CONNECTION alias: the connection is the one the apps register the arrays on
789
1309
  allows: ["entities/<table>.entity.ts", "migrations/<epochMs13>-<kebab-name>.ts", "<name>.sql.ts", "<name>.rows.ts", connection.ts]
790
1310
  forbids: [index.ts, "<name>.repository.ts"]
791
- tests: none
792
- rules: [BE_SCHEMA_OWNER, BE_SCHEMA_AUTHORITY, BE_SQL_OUTSIDE_PERSISTENCE]
1311
+ # Lite keeps the shared-EntityManager persistence (raw SqlText + rows types over the generated Database row types) but the
1312
+ # schema authority is supabase/migrations (ruleParams.be.lite.schemaAuthority): no TypeORM entity and no be migration.
1313
+ lite:
1314
+ requires: [] # no TypeORM arrays to re-export: the persistence of a lite capability is its sql and rows files
1315
+ allows: ["<name>.sql.ts", "<name>.rows.ts", connection.ts]
1316
+ forbids: [index.ts, "<name>.repository.ts", entities/, migrations/]
1317
+ tests: none
1318
+ coverage: none
1319
+ rules: [BE_SCHEMA_OWNER, BE_SCHEMA_AUTHORITY, BE_SQL_OUTSIDE_PERSISTENCE, BE_SQL_TABLE_OWNER, BE_SQL_RETURNING_SHAPE, BE_CONTEXT_COUPLING, BE_CONTEXT_PLATFORM_TABLES]
793
1320
  - id: be.platform
794
1321
  profiles: [be]
795
1322
  path: "src/modules/platform/<capability>/"
@@ -808,8 +1335,160 @@ slots:
808
1335
  requires: [index.ts]
809
1336
  forbids: [common/, utils/, shared/, testing/] # primitives live in platform/primitives
810
1337
  tests: unit-beside # <name>.service.spec.ts beside each <name>.service.ts, nothing else
1338
+ coverage: required
811
1339
  budget: {indexExports: 60}
812
1340
  rules: [BE_TIER_DIRECTION, ARCH_OWNER_CYCLE, BE_MODULE_SHAPE]
1341
+ # ----- BE: pattern slots (opt-in; enabled by hfs.json sides.be.patterns). A path under one is HFS_SLOT_NOT_ENABLED unless its pattern is declared.
1342
+ - id: be.platform.event-bus
1343
+ profiles: [be]
1344
+ path: "src/modules/platform/event-bus/"
1345
+ presence: opt-in
1346
+ litePresence: forbidden # Kafka is exactly the footprint lite removes
1347
+ pattern: event-bus # the ONE durable event transport: transactional outbox, relay, Kafka, inbox, retry and dead letter (knowledge/patterns/be/event-bus.yaml)
1348
+ tracked: tracked
1349
+ tier: platform
1350
+ owner: true
1351
+ goesTo: "the full edition: a lite app has no event bus (no Kafka, no outbox)"
1352
+ requires: [index.ts, event-bus.module.ts, event-bus.port.ts, event-bus.contracts.ts, event-bus.service.ts, event-bus.service.spec.ts]
1353
+ tests: unit-beside
1354
+ coverage: required
1355
+ budget: {indexExports: 60}
1356
+ rules: [BE_TIER_DIRECTION, ARCH_OWNER_CYCLE, BE_MODULE_SHAPE, BE_INFRA_OWNER]
1357
+ - id: be.platform.queue
1358
+ profiles: [be]
1359
+ path: "src/modules/platform/queue/"
1360
+ presence: opt-in
1361
+ litePresence: forbidden # BullMQ/Redis is exactly the footprint lite removes
1362
+ pattern: queue # the ONE BullMQ owner: the outbox relay into queues (jobId = outbox id) and job schedulers (knowledge/patterns/be/queues.yaml)
1363
+ tracked: tracked
1364
+ tier: platform
1365
+ owner: true
1366
+ goesTo: "the full edition: a lite app has no job queue (a scheduled action is a cli command; pg_cron / pg_net fires it)"
1367
+ requires: [index.ts, queue.module.ts, queue.port.ts, queue.contracts.ts, queue-outbox.service.ts, queue-outbox.service.spec.ts]
1368
+ tests: unit-beside
1369
+ coverage: required
1370
+ budget: {indexExports: 60}
1371
+ rules: [BE_TIER_DIRECTION, ARCH_OWNER_CYCLE, BE_MODULE_SHAPE, BE_INFRA_OWNER]
1372
+ - id: be.events
1373
+ profiles: [be]
1374
+ path: "src/modules/events/<service>/"
1375
+ presence: opt-in
1376
+ litePresence: forbidden # no event bus, so no typed events
1377
+ pattern: event-bus # typed event classes of one service; each equals an entry of be/contracts/<service>/events.json
1378
+ tracked: tracked
1379
+ tier: events
1380
+ owner: true
1381
+ goesTo: "the full edition: a lite app has no event bus, so no modules/events"
1382
+ requires: [index.ts]
1383
+ allows: ["<event>.event.ts", index.ts]
1384
+ tests: none
1385
+ coverage: none
1386
+ rules: [BE_EVENT_CLASS_CONTRACT, BE_TIER_DIRECTION]
1387
+ - id: be.queues
1388
+ profiles: [be]
1389
+ path: "src/modules/queues/<queue>/"
1390
+ presence: opt-in
1391
+ litePresence: forbidden # no queue, so no typed producers
1392
+ pattern: queue # a typed producer of one queue: enqueueX(payload, tx) writes the outbox; its schedulers live beside it
1393
+ tracked: tracked
1394
+ tier: queues
1395
+ owner: true
1396
+ goesTo: "the full edition: a lite app has no job queue, so no modules/queues"
1397
+ requires: [index.ts, "<queue>.queue.ts"] # the module exists when a domain module imports the producer; a scheduler-only queue has none
1398
+ allows: ["<queue>.queue.ts", "<queue>.module.ts", "<queue>.contracts.ts", index.ts]
1399
+ tests: none
1400
+ coverage: none
1401
+ rules: [BE_OUTBOX_WRITE_TX, BE_TIER_DIRECTION]
1402
+ - id: be.feature.reactors
1403
+ profiles: [be]
1404
+ path: "src/features/reactors/<reactor>/"
1405
+ presence: opt-in
1406
+ litePresence: forbidden # no event bus, so nothing reacts to a domain event
1407
+ pattern: event-bus # a reactor consumes events of another context and keeps a local copy or triggers a projection
1408
+ trigger: reactors
1409
+ tracked: tracked
1410
+ tier: feature
1411
+ owner: true
1412
+ requires: [index.ts, "<reactor>.module.ts", transport/]
1413
+ allows: [index.ts, "<reactor>.module.ts", application/, transport/]
1414
+ tests: none
1415
+ goesTo: "the full edition: a lite app has no event bus, so no reactors (a third-party callback is a webhooks feature)"
1416
+ coverage: none
1417
+ rules: [BE_KIND_ISOLATION, BE_FEATURE_IMPORTS_FEATURE, BE_FEATURE_NOT_COMPOSED]
1418
+ - id: be.feature.jobs
1419
+ profiles: [be]
1420
+ path: "src/features/jobs/<job>/"
1421
+ presence: opt-in
1422
+ litePresence: forbidden # no fenced-job platform, so no job tree
1423
+ pattern: fenced-job # a background job: a processor with a fenced claim, steps and the application it dispatches to
1424
+ trigger: jobs
1425
+ tracked: tracked
1426
+ tier: feature
1427
+ owner: true
1428
+ requires: [index.ts, "<job>.module.ts", steps/, transport/]
1429
+ allows: [index.ts, "<job>.module.ts", application/, steps/, transport/]
1430
+ tests: none
1431
+ goesTo: "the full edition: a lite app has no job queue (a scheduled action is a cli command; pg_cron / pg_net fires it)"
1432
+ coverage: none
1433
+ rules: [BE_KIND_ISOLATION, BE_FEATURE_IMPORTS_FEATURE, BE_FEATURE_NOT_COMPOSED]
1434
+ - id: be.feature.jobs.queue
1435
+ profiles: [be]
1436
+ path: "src/features/jobs/<job>/transport/queue/"
1437
+ presence: opt-in # the queue transport of the job: its module and the processor the job runner hands the fenced claim to
1438
+ litePresence: forbidden
1439
+ pattern: fenced-job
1440
+ tracked: tracked
1441
+ tier: feature
1442
+ requires: ["<job>-queue.module.ts", "<job>.processor.ts"]
1443
+ allows: ["<job>-queue.module.ts", "<job>.processor.ts"]
1444
+ tests: none
1445
+ coverage: none
1446
+ composedBy: [api, worker]
1447
+ goesTo: "the full edition: a lite app has no job queue"
1448
+ rules: [BE_BACKGROUND_UNOWNED]
1449
+ - id: be.feature.jobs.steps
1450
+ profiles: [be]
1451
+ path: "src/features/jobs/<job>/steps/"
1452
+ presence: opt-in
1453
+ litePresence: forbidden
1454
+ pattern: fenced-job # one external effect per step; it takes the claimed job and writes through the guarded methods
1455
+ tracked: tracked
1456
+ tier: feature
1457
+ allows: ["<step>.step.ts"]
1458
+ tests: none
1459
+ goesTo: "the full edition: a lite app has no job queue"
1460
+ coverage: none
1461
+ rules: [BE_JOB_SHAPE, BE_JOB_RUN_KEY]
1462
+ - id: be.platform.jobs
1463
+ profiles: [be]
1464
+ path: "src/modules/platform/jobs/"
1465
+ presence: opt-in
1466
+ litePresence: forbidden # no fenced-job primitive without a queue
1467
+ pattern: fenced-job # the ONE fencing primitive: the job entity, the claim that bumps the token, guarded writes (knowledge/patterns/be/jobs.yaml)
1468
+ tracked: tracked
1469
+ tier: platform
1470
+ owner: true
1471
+ goesTo: "the full edition: a lite app has no job queue (a scheduled action is a cli command)"
1472
+ requires: [index.ts, jobs.module.ts, jobs.port.ts, jobs.contracts.ts, job-claim.service.ts, job-claim.service.spec.ts, fenced.processor.ts]
1473
+ tests: unit-beside
1474
+ coverage: required
1475
+ budget: {indexExports: 60}
1476
+ rules: [BE_TIER_DIRECTION, ARCH_OWNER_CYCLE, BE_MODULE_SHAPE, BE_JOB_WRITE_OUTSIDE_OWNER, BE_JOB_FENCE_REQUIRED]
1477
+ - id: be.projections
1478
+ profiles: [be]
1479
+ path: "src/modules/projections/<name>/"
1480
+ presence: opt-in
1481
+ litePresence: forbidden # a read model replayed off an event stream needs the event bus lite does not have
1482
+ pattern: projection # a read model: only its projection writes its table; recompute is idempotent and replayable; readers use get*
1483
+ tracked: tracked
1484
+ tier: projections
1485
+ owner: true
1486
+ goesTo: "the full edition: a lite app has no event stream to project; a read shape is a view or a read-* in fe modules/db"
1487
+ requires: [index.ts, "<name>.module.ts", "<name>.projection.ts", "<name>.projection-entity.ts"]
1488
+ allows: [index.ts, "<name>.module.ts", "<name>.module-definition.ts", "<name>.options.ts", "<name>.projection.ts", "<name>.projection-entity.ts", "<name>.contracts.ts", persistence/]
1489
+ tests: unit-beside # a measured slot: <name>.projection.spec.ts beside its projection is legal (the file owes 100 per file; a spec of its own is optional)
1490
+ coverage: required
1491
+ rules: [BE_PROJECTION_WRITE_OWNER, BE_PROJECTION_SHAPE, BE_TIER_DIRECTION, BE_CONTEXT_COUPLING]
813
1492
  - id: be.integrations
814
1493
  profiles: [be]
815
1494
  path: "src/modules/integrations/<provider>/"
@@ -819,6 +1498,7 @@ slots:
819
1498
  owner: true
820
1499
  requires: [index.ts, "<provider>.config.ts"]
821
1500
  tests: unit-beside # <name>.service.spec.ts beside each <name>.service.ts; plus src/tests/integration/<provider>/ (R112)
1501
+ coverage: required
822
1502
  budget: {indexExports: 60}
823
1503
  rules: [BE_TIER_DIRECTION, BE_ERROR_HOME, BE_SECRET_DEFAULT, BE_INTEGRATION_SPEC_MISSING]
824
1504
  - id: be.integrations.model
@@ -828,7 +1508,26 @@ slots:
828
1508
  tracked: tracked
829
1509
  tier: integrations
830
1510
  tests: none
1511
+ coverage: required
831
1512
  why: an ML or LLM model client is an integration; weights and datasets are never in git
1513
+ - id: be.integrations.supabase
1514
+ profiles: [be]
1515
+ # The only be import of @supabase/* (L10): the Auth admin and Storage admin client, plus the JWKS-based verification of
1516
+ # the Supabase access token the guard chain reads (L16). The connection itself is ordinary Postgres (the shared
1517
+ # EntityManager of the declared connection); what lives here are the service calls no database role covers.
1518
+ path: "src/modules/integrations/supabase/"
1519
+ presence: opt-in # enabled by a connection with provider: supabase; required under edition lite
1520
+ provider: supabase
1521
+ litePresence: required
1522
+ tracked: tracked
1523
+ tier: integrations
1524
+ owner: true
1525
+ requires: [index.ts, supabase.config.ts, supabase.options.ts, supabase.decorators.ts, supabase.client.ts, supabase.jwks.ts] # Secrets, no defaults (R44); the options and the named injector (R83); the admin client; the token verification
1526
+ allows: [index.ts, supabase.config.ts, supabase.options.ts, supabase.decorators.ts, supabase.client.ts, supabase.jwks.ts, errors/]
1527
+ tests: unit-beside
1528
+ rules: [BE_TIER_DIRECTION, BE_ERROR_HOME, BE_SECRET_DEFAULT]
1529
+ coverage: required
1530
+ since: 2.1.0
832
1531
 
833
1532
  # ----- BE: tests (owner test layout 2026-09-30). Unit specs are <name>.service.spec.ts beside their service and nothing else; everything else
834
1533
  # lives under src/tests/, one folder per kind, and the folder and the file suffix always agree (a file whose suffix does
@@ -837,13 +1536,16 @@ slots:
837
1536
  profiles: [be]
838
1537
  path: "src/tests/world/"
839
1538
  presence: optional
1539
+ litePresence: forbidden # a lite app has no test world
840
1540
  tracked: tracked
841
1541
  tier: e2e
1542
+ goesTo: "the full edition: a lite app has no test world (no containers, fakes or useTestWorld)"
842
1543
  allows: [global-setup.ts, global-teardown.ts, use-test-world.ts, "fakes/<provider>/server.ts", fakes/] # jest globalSetup/globalTeardown default-export (Jest API); fakes/<provider>/server.ts is the fake server of a provider, a literal file name BE_SOURCE_FORM reads as its role; plus root files with a role suffix (test-world-files, R47)
843
1544
  tests: none
1545
+ coverage: none
844
1546
  why: >-
845
1547
  the ONLY test infrastructure location, shared by integration and e2e: global-setup.ts starts the shared
846
- infrastructure (containers) and runs apps/migrate's exported bootstrap once; use-test-world.ts exports
1548
+ infrastructure (containers) and runs the migrations once over the cli app's connections; use-test-world.ts exports
847
1549
  useTestWorld({ apps: { <name>: { module, listen? } } } | { modules: [...] }) -> world.apps.<name>.api,
848
1550
  world.db.<connection> (the shared EntityManager), world.infra.<service> (latency/cut/restore on a REAL service of the repository's
849
1551
  own stack, the app root's `.starcistacks/<env>`, each behind toxiproxy), world.fake.<provider> (a network-edge fake of an external SaaS started by the world, with
@@ -854,27 +1556,36 @@ slots:
854
1556
  profiles: [be]
855
1557
  path: "src/tests/world/kit/"
856
1558
  presence: optional
1559
+ litePresence: forbidden
857
1560
  tracked: tracked
858
1561
  tier: e2e
1562
+ goesTo: "the full edition: a lite app has no test world"
859
1563
  allows: ["<name>.ts"] # plain kebab names, like platform/primitives (BE-CONVENTION 1.11 names primitives/time.ts): the world's inlined test helpers (poll, free-ports)
860
1564
  tests: none
1565
+ coverage: none
861
1566
  why: the helpers the test world needs (polling, free ports, transports), inlined so the world imports no other repository; a bare <name>.ts entry in allows is what admits a plain name in BE_SOURCE_FORM
862
1567
  - id: be.tests.fixtures
863
1568
  profiles: [be]
864
1569
  path: "src/tests/fixtures/"
865
1570
  presence: optional
1571
+ litePresence: forbidden
866
1572
  tracked: tracked
867
1573
  tier: fixtures
1574
+ goesTo: "the full edition: a lite app has no test fixtures"
868
1575
  allows: [i18n/] # i18n/ is the one folder that may carry localized text
869
1576
  tests: none
1577
+ coverage: none
870
1578
  why: payload fixtures with a role suffix (the doubles come from @starci/jest-preset, not from here); the test data builders live in its builders/ slot; importable by unit specs, integration and e2e; never imports a feature
871
1579
  - id: be.tests.fixtures.builders
872
1580
  profiles: [be]
873
1581
  path: "src/tests/fixtures/builders/*.builder.ts"
874
1582
  presence: optional
1583
+ litePresence: forbidden
875
1584
  tracked: tracked
876
1585
  tier: fixtures
1586
+ goesTo: "the full edition: a lite app has no test fixtures"
877
1587
  tests: none
1588
+ coverage: none
878
1589
  why: >-
879
1590
  the ONLY home of test data builders, one <area>.builder.ts per area, shared by every spec of that area: pure object builders
880
1591
  (commissionRow(overrides), accrueInput(overrides)) for unit specs and persisting builders (orderBuilder(world.db.primary).pending().build(overrides))
@@ -885,36 +1596,48 @@ slots:
885
1596
  profiles: [be]
886
1597
  path: "src/tests/fixtures/i18n/"
887
1598
  presence: optional
1599
+ litePresence: forbidden
888
1600
  tracked: tracked
889
1601
  tier: fixtures
1602
+ goesTo: "the full edition: a lite app has no test fixtures"
890
1603
  allows: ["<name>.<role>.ts"] # data files that deliberately carry localized text (a real Vietnamese string a parser or formatter must accept); no specs here
891
1604
  tests: none
1605
+ coverage: none
892
1606
  why: the ONLY test-fixture location where Vietnamese or another language may appear in source (LANG_NOT_ENGLISH); placement is the marker, there is no comment pragma
893
1607
  - id: be.tests.integration
894
1608
  profiles: [be]
895
1609
  path: "src/tests/integration/<capability>/*.integration-spec.ts"
896
1610
  presence: optional
1611
+ litePresence: forbidden
897
1612
  tracked: tracked
898
1613
  tier: e2e
899
1614
  tests: e2e
1615
+ goesTo: "the full edition: a lite app has no test tree"
1616
+ coverage: none
900
1617
  why: one capability module through useTestWorld({ modules }), no HTTP door of ours (SQL, transactions, concurrency, inbox claims on the real database; and every integration client, `<capability>` being its provider folder, against our real infra, a real peer app or the library fake at the network edge, with its success, its ErrorCode refusal mapping and an outage through the world, R112); run by test:integration
901
1618
  rules: [BE_TEST_TOPOLOGY, BE_INTEGRATION_SPEC_MISSING]
902
1619
  - id: be.tests.e2e
903
1620
  profiles: [be]
904
1621
  path: "src/tests/e2e/<area>/*.e2e-spec.ts"
905
1622
  presence: optional
1623
+ litePresence: forbidden
906
1624
  tracked: tracked
907
1625
  tier: e2e
908
1626
  tests: e2e
1627
+ goesTo: "the full edition: a lite app has no test tree"
1628
+ coverage: none
909
1629
  why: flow e2e through useTestWorld({ apps }); external services are the world's network fakes; run by test:e2e
910
1630
  rules: [HFS_CI_E2E_AUTOMATIC, BE_TEST_TOPOLOGY, BE_E2E_FLOW]
911
1631
  - id: be.tests.contract
912
1632
  profiles: [be]
913
1633
  path: "src/tests/contract/<provider>/*.contract-spec.ts"
914
1634
  presence: optional
1635
+ litePresence: forbidden
915
1636
  tracked: tracked
916
1637
  tier: e2e
917
1638
  tests: e2e
1639
+ goesTo: "the full edition: a lite app has no test tree"
1640
+ coverage: none
918
1641
  why: provider sandbox contracts, skipped without sandbox config; run only by test:contract, never part of test or test:e2e
919
1642
  rules: [BE_TEST_TOPOLOGY]
920
1643
  - id: be.tests.e2e-world-retired
@@ -929,16 +1652,18 @@ slots:
929
1652
  # ----- FE: apps ----------------------------------------------------------------------------------------
930
1653
  - id: fe.app.next
931
1654
  profiles: [fe]
932
- path: "apps/<app>/{next.config.ts,tsconfig.json,postcss.config.mjs}"
1655
+ # Each fe app is an npm workspace: its package.json is @<project>/<app> with its own runtime dependencies and the fixed
1656
+ # workspace scripts (HFS_MONO_FE_WORKSPACE); turbo runs them.
1657
+ path: "apps/<app>/{package.json,next.config.ts,tsconfig.json,postcss.config.mjs}"
933
1658
  appKind: next
934
1659
  presence: required
935
1660
  tracked: tracked
936
1661
  tier: none
937
1662
  minInstances: 1
938
- requires: [next.config.ts, tsconfig.json, postcss.config.mjs, src/app/, src/modules/i18n/,
1663
+ requires: [package.json, next.config.ts, tsconfig.json, postcss.config.mjs, src/app/, src/modules/i18n/,
939
1664
  "src/app/global-error.tsx", "src/app/[locale]/layout.tsx", "src/app/[locale]/error.tsx", "src/app/[locale]/not-found.tsx", "src/app/[locale]/loading.tsx"]
940
1665
  tests: none
941
- rules: [FE_APP_ISOLATION, FE_ERROR_BOUNDARY_MISSING, FE_NEXT_CONVENTIONS]
1666
+ rules: [FE_APP_ISOLATION, FE_ERROR_BOUNDARY_MISSING, FE_NEXT_CONVENTIONS, HFS_MONO_FE_WORKSPACE, HFS_MONO_WORKSPACE_DEP]
942
1667
  - id: fe.app-optional
943
1668
  profiles: [fe]
944
1669
  path: "apps/<app>/public/"
@@ -979,6 +1704,18 @@ slots:
979
1704
  tests: none
980
1705
  goesTo: "apps/<app>/src/proxy.ts: Next >= 16 renamed middleware to proxy and refuses the old name"
981
1706
  rules: [FE_NEXT_CONVENTIONS]
1707
+ - id: fe.route.callback
1708
+ profiles: [fe]
1709
+ # The auth callback of a Supabase session (the PKCE code exchange): the one non-health route handler of an app with a Supabase
1710
+ # connection, in either edition (an upgrade lite -> full leaves it as it is, fe/** is never touched); an app without one has no
1711
+ # route handler in fe/ (L15).
1712
+ path: "apps/<app>/src/app/auth/callback/route.ts"
1713
+ presence: opt-in
1714
+ provider: supabase
1715
+ tracked: tracked
1716
+ tier: route
1717
+ tests: none
1718
+ since: 2.1.0
982
1719
  - id: fe.feature
983
1720
  profiles: [fe]
984
1721
  path: "apps/<app>/src/features/{pages,layouts,overlays}/<name>/"
@@ -1061,6 +1798,7 @@ slots:
1061
1798
  presence: optional
1062
1799
  tracked: tracked
1063
1800
  tier: transport
1801
+ outcomeHome: true
1064
1802
  tests: none
1065
1803
  why: the one Outcome union of a one-app repository; a shared union is fe.package.api.outcome
1066
1804
  rules: [FE_HTTP_STATUS_COLLAPSE]
@@ -1109,10 +1847,47 @@ slots:
1109
1847
  tier: foundation
1110
1848
  tests: none
1111
1849
  why: shared types with no runtime behaviour; a component may import them
1850
+ - id: fe.modules.db
1851
+ profiles: [fe]
1852
+ # The db transport of an app on Supabase (the ONLY place that imports @supabase/ssr or @supabase/supabase-js, L10): the
1853
+ # server client factory (cookies, anon key, RLS applies), the browser client factory, getPrincipal() (React cache,
1854
+ # auth.getClaims()) and toOutcome() mapping {data, error} into the same Outcome vocabulary modules/api uses. Readers
1855
+ # live in <domain>/read-*.ts, Server Actions (schema-parsed input) in <domain>/write-*.ts; the row types come from
1856
+ # supabase/types/database.types.ts (sides.fe.reads). The barrel index.ts is server-only (it re-exports server code), so
1857
+ # browser.ts, the browser client factory a client hook imports, is a second public entry of the owner (`entries`).
1858
+ path: "apps/<app>/src/modules/db/"
1859
+ presence: opt-in # enabled by a connection with provider: supabase; required under edition lite
1860
+ provider: supabase
1861
+ litePresence: required
1862
+ tracked: tracked
1863
+ tier: transport
1864
+ requires: [index.ts, server.ts, browser.ts, principal.ts]
1865
+ allows: [index.ts, server.ts, browser.ts, principal.ts, outcome.ts, "<domain>/read-*.ts", "<domain>/write-*.ts", "<domain>/<domain>.mapper.ts"]
1866
+ entries: [browser.ts]
1867
+ anonymousActions: [auth/write-sign-in.ts, auth/write-sign-out.ts] # the Server Actions that run for an anonymous principal by design (FE_AUTH_SESSION_TRUST)
1868
+ tests: none
1869
+ since: 2.1.0
1870
+ - id: fe.modules.db.outcome
1871
+ profiles: [fe]
1872
+ # A lite app has no modules/api: its db transport is the transport, so the one Outcome union lives here (toOutcome maps
1873
+ # {data, error} into it). Under full the union stays fe.transport.outcome / fe.package.api.outcome.
1874
+ editions: [lite]
1875
+ path: "apps/<app>/src/modules/db/outcome.ts"
1876
+ presence: opt-in
1877
+ provider: supabase
1878
+ litePresence: optional
1879
+ tracked: tracked
1880
+ tier: transport
1881
+ outcomeHome: true
1882
+ tests: none
1883
+ why: the one Outcome union of a lite app; its db owner is the transport
1884
+ rules: [FE_HTTP_STATUS_COLLAPSE]
1885
+ since: 2.1.0
1112
1886
  - id: fe.package.ui
1113
1887
  profiles: [fe]
1114
1888
  path: "packages/<family>-ui/"
1115
1889
  presence: opt-in
1890
+ litePresence: optional # a lite app scaffolds no packages; one appears by need, without an optionalSlots entry
1116
1891
  tracked: tracked
1117
1892
  tier: package
1118
1893
  owner: true
@@ -1127,6 +1902,7 @@ slots:
1127
1902
  profiles: [fe]
1128
1903
  path: "packages/<family>-api/"
1129
1904
  presence: opt-in
1905
+ litePresence: optional
1130
1906
  tracked: tracked
1131
1907
  tier: package
1132
1908
  owner: true
@@ -1149,6 +1925,7 @@ slots:
1149
1925
  presence: optional # enabled with its package (fe.package.api is the opt-in)
1150
1926
  tracked: tracked
1151
1927
  tier: package
1928
+ outcomeHome: true
1152
1929
  tests: none
1153
1930
  why: the repository's one Outcome union
1154
1931
  rules: [FE_HTTP_STATUS_COLLAPSE]
@@ -1156,6 +1933,7 @@ slots:
1156
1933
  profiles: [fe]
1157
1934
  path: "packages/<family>-i18n/"
1158
1935
  presence: opt-in
1936
+ litePresence: optional
1159
1937
  tracked: tracked
1160
1938
  tier: package
1161
1939
  owner: true
@@ -1163,14 +1941,28 @@ slots:
1163
1941
  tests: none
1164
1942
  why: the next-intl stack (routing, navigation, request config) written once, exported as the createAppI18n factory every app calls
1165
1943
  rules: [FE_I18N_PLACEMENT, FE_PACKAGE_SHAPE, HFS_UNUSED_EXPORT]
1944
+ - id: fe.package.db
1945
+ profiles: [fe]
1946
+ # The shared Supabase client factories once a second fe app needs them (server, browser, principal, toOutcome) -
1947
+ # mirroring fe.transport.client vs fe.package.api.client; the one Outcome union is re-exported from
1948
+ # fe.package.api.outcome when the api package exists.
1949
+ path: "packages/<family>-db/"
1950
+ presence: opt-in
1951
+ provider: supabase
1952
+ tracked: tracked
1953
+ tier: package
1954
+ owner: true
1955
+ requires: [package.json, src/index.ts, tsconfig.json]
1956
+ tests: none
1957
+ since: 2.1.0
1166
1958
 
1167
1959
  # Checks that read this manifest (all ship from .claude; none keeps its own path list)
1168
1960
  consumers:
1169
1961
  - scripts/hfs/slots.mjs # the loader every consumer below goes through
1170
- - scripts/hfs/check.mjs # hfs check: HFS_SLOT_* / tracked / external, the side view per side
1962
+ - scripts/hfs/check.mjs # starci app check: HFS_SLOT_* / tracked / external, the side view per side
1171
1963
  - scripts/hfs/architecture.mjs # tiers, direction matrix, cycles, reachability
1172
1964
  - packages/eslint/be starciBeConfig({hfs}) # file globs for rules come from slots
1173
1965
  - packages/eslint/fe starciFeConfig({hfs})
1174
1966
  - packages/stylelint # @starci/stylelint-canon: colour and brand allowances from fe.modules.brand
1175
- - packages/hfs/sync # renders managedBy templates (hfs sync) and judges them (hfs check)
1967
+ - packages/hfs/sync # renders managedBy templates (starci app sync) and judges them (starci app check)
1176
1968
  - modules/kernel/failure-codes.yaml # every rule id listed above has a Vietnamese why