@starci/hfs 1.0.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (246) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/README.md +116 -12
  3. package/bin/hfs.mjs +119 -15
  4. package/emit/compiler.mjs +35 -0
  5. package/emit/contracts.mjs +97 -0
  6. package/emit/operations-worker.mjs +24 -0
  7. package/emit/operations.mjs +126 -0
  8. package/emit/schema-worker.mjs +117 -0
  9. package/emit/static-graph.mjs +670 -0
  10. package/emit/type-schema.mjs +145 -0
  11. package/package.json +10 -2
  12. package/report/sonar.mjs +180 -0
  13. package/runtime/engine/admission.mjs +284 -0
  14. package/runtime/engine/digest.mjs +10 -0
  15. package/runtime/engine/ledger-db.mjs +1245 -0
  16. package/runtime/engine/machine-db.mjs +1484 -0
  17. package/runtime/engine/migrations/machine/0001-init.sql +887 -0
  18. package/runtime/engine/migrations/runtime/0001-init.sql +1072 -0
  19. package/runtime/engine/migrations/runtime/0003-usage-unavailable.sql +13 -0
  20. package/runtime/engine/migrations/runtime/0004-attempt-why.sql +43 -0
  21. package/runtime/engine/plain-object.mjs +5 -0
  22. package/runtime/knowledge/hfs/canon-pins.yaml +16 -58
  23. package/runtime/knowledge/hfs/slots.yaml +409 -140
  24. package/runtime/knowledge/patterns/fe/folder.yaml +309 -0
  25. package/runtime/knowledge/sonar-gate.yaml +85 -0
  26. package/runtime/modules/kernel/failure-codes.yaml +1480 -16
  27. package/runtime/scripts/checks/architecture/backend.mjs +350 -0
  28. package/runtime/scripts/checks/architecture/background-unowned.mjs +107 -0
  29. package/runtime/scripts/checks/architecture/client-reaches-server.mjs +94 -0
  30. package/runtime/scripts/checks/architecture/clones.mjs +200 -0
  31. package/runtime/scripts/checks/architecture/config-unread.mjs +35 -0
  32. package/runtime/scripts/checks/architecture/config.mjs +310 -0
  33. package/runtime/scripts/checks/architecture/connection-map.mjs +208 -0
  34. package/runtime/scripts/checks/architecture/constructor-deps.mjs +100 -0
  35. package/runtime/scripts/checks/architecture/contract-fixture-guard.mjs +128 -0
  36. package/runtime/scripts/checks/architecture/contracts.mjs +792 -0
  37. package/runtime/scripts/checks/architecture/cross-app-duplicate.mjs +73 -0
  38. package/runtime/scripts/checks/architecture/dead-exports.mjs +265 -0
  39. package/runtime/scripts/checks/architecture/default-deny.mjs +129 -0
  40. package/runtime/scripts/checks/architecture/doc-language.mjs +39 -0
  41. package/runtime/scripts/checks/architecture/entrypoint.mjs +57 -0
  42. package/runtime/scripts/checks/architecture/error-codes.mjs +45 -0
  43. package/runtime/scripts/checks/architecture/error-masked.mjs +60 -0
  44. package/runtime/scripts/checks/architecture/fe-slot-allows.mjs +38 -0
  45. package/runtime/scripts/checks/architecture/feature-shape.mjs +49 -0
  46. package/runtime/scripts/checks/architecture/framework-pinned.mjs +83 -0
  47. package/runtime/scripts/checks/architecture/frontend.mjs +995 -0
  48. package/runtime/scripts/checks/architecture/hfs-graph.mjs +61 -0
  49. package/runtime/scripts/checks/architecture/hfs.mjs +521 -0
  50. package/runtime/scripts/checks/architecture/hooks-are-hooks.mjs +88 -0
  51. package/runtime/scripts/checks/architecture/i18n-keys.mjs +146 -0
  52. package/runtime/scripts/checks/architecture/index.mjs +316 -0
  53. package/runtime/scripts/checks/architecture/injection-token-exported.mjs +62 -0
  54. package/runtime/scripts/checks/architecture/machine-ast.mjs +138 -0
  55. package/runtime/scripts/checks/architecture/module-per-transport.mjs +130 -0
  56. package/runtime/scripts/checks/architecture/next-data.mjs +775 -0
  57. package/runtime/scripts/checks/architecture/owners.mjs +89 -0
  58. package/runtime/scripts/checks/architecture/package-shape.mjs +63 -0
  59. package/runtime/scripts/checks/architecture/reachability.mjs +233 -0
  60. package/runtime/scripts/checks/architecture/register-once.mjs +141 -0
  61. package/runtime/scripts/checks/architecture/registration.mjs +319 -0
  62. package/runtime/scripts/checks/architecture/required-files.mjs +172 -0
  63. package/runtime/scripts/checks/architecture/route-files-thin.mjs +97 -0
  64. package/runtime/scripts/checks/architecture/schema-owner.mjs +261 -0
  65. package/runtime/scripts/checks/architecture/size-growth.mjs +73 -0
  66. package/runtime/scripts/checks/architecture/source-names.mjs +607 -0
  67. package/runtime/scripts/checks/architecture/sql-owner.mjs +142 -0
  68. package/runtime/scripts/checks/architecture/sql-tokens.mjs +327 -0
  69. package/runtime/scripts/checks/architecture/symbols.mjs +193 -0
  70. package/runtime/scripts/checks/architecture/test-world-files.mjs +163 -0
  71. package/runtime/scripts/checks/architecture/tiers.mjs +130 -0
  72. package/runtime/scripts/checks/architecture/transport-owner.mjs +112 -0
  73. package/runtime/scripts/checks/architecture/typescript.mjs +500 -0
  74. package/runtime/scripts/checks/architecture/unit-spec-providers.mjs +122 -0
  75. package/runtime/scripts/checks/architecture.mjs +41 -0
  76. package/runtime/scripts/checks/common.mjs +37 -0
  77. package/runtime/scripts/checks/typescript-programs.mjs +82 -0
  78. package/runtime/scripts/lib/artifact-hold.mjs +89 -0
  79. package/runtime/scripts/lib/artifact-store.mjs +103 -0
  80. package/runtime/scripts/lib/fs-kind.mjs +10 -0
  81. package/runtime/scripts/lib/git.mjs +53 -0
  82. package/runtime/scripts/lib/hfs-allows.mjs +57 -0
  83. package/runtime/scripts/lib/hfs-check.mjs +254 -28
  84. package/runtime/scripts/lib/hfs-rules/contract.mjs +126 -0
  85. package/runtime/scripts/lib/hfs-rules/deps.mjs +63 -0
  86. package/runtime/scripts/lib/hfs-rules/fe-no-tests.mjs +47 -0
  87. package/runtime/scripts/lib/hfs-rules/frontend.mjs +124 -0
  88. package/runtime/scripts/lib/hfs-rules/lint-suppression.mjs +34 -0
  89. package/runtime/scripts/lib/hfs-rules/pipeline.mjs +51 -0
  90. package/runtime/scripts/lib/hfs-rules/proof-commands.mjs +64 -0
  91. package/runtime/scripts/lib/hfs-rules/read.mjs +28 -0
  92. package/runtime/scripts/lib/hfs-rules/repo-local-checks.mjs +32 -0
  93. package/runtime/scripts/lib/hfs-rules/secrets.mjs +54 -0
  94. package/runtime/scripts/lib/hfs-rules/spec-placement.mjs +31 -0
  95. package/runtime/scripts/lib/hfs-rules/stacks.mjs +54 -0
  96. package/runtime/scripts/lib/hfs-rules/test-topology.mjs +31 -0
  97. package/runtime/scripts/lib/hfs-slots.mjs +95 -41
  98. package/runtime/scripts/lib/hfs-tree.mjs +80 -0
  99. package/runtime/scripts/lib/hfs-view.mjs +68 -0
  100. package/runtime/scripts/lib/json.mjs +22 -0
  101. package/runtime/scripts/lib/language.mjs +107 -0
  102. package/runtime/scripts/lib/path-key.mjs +2 -0
  103. package/runtime/scripts/lib/redact.mjs +148 -0
  104. package/runtime/scripts/lib/repo-identity.mjs +50 -0
  105. package/runtime/scripts/lib/safe-remove.mjs +179 -0
  106. package/runtime/scripts/lib/secret-patterns.mjs +44 -0
  107. package/runtime/scripts/lib/sleep-sync.mjs +17 -0
  108. package/runtime/scripts/lib/stack-declaration.mjs +52 -0
  109. package/runtime/scripts/lib/stack-services.mjs +217 -0
  110. package/runtime/scripts/lib/test-secrets.mjs +120 -0
  111. package/scaffold/service.mjs +333 -0
  112. package/sync/format.mjs +46 -0
  113. package/sync/hygiene.mjs +56 -24
  114. package/sync/index.mjs +126 -41
  115. package/sync/managed.mjs +170 -0
  116. package/sync/skeleton.mjs +32 -10
  117. package/sync/sonar-key.mjs +13 -0
  118. package/sync/ts-strict.mjs +48 -0
  119. package/templates/{common → be/ci-workflows/github/workflows}/ci.yml +13 -13
  120. package/templates/be/{e2e.yml → ci-workflows/github/workflows/e2e.yml} +1 -0
  121. package/templates/be/hooks/husky/pre-commit +13 -0
  122. package/templates/be/hooks/husky/pre-push +7 -0
  123. package/templates/be/package-scripts/package.json +21 -0
  124. package/templates/be/{sonar-project.properties → quality-config/sonar-project.properties} +2 -3
  125. package/templates/be/skeleton/apps/__app__/src/app.module.ts +14 -4
  126. package/templates/be/skeleton/apps/__app__/src/main.ts +9 -6
  127. package/templates/be/skeleton/scripts/.gitkeep +0 -0
  128. package/templates/be/skeleton/src/features/system-health/application/check-liveness.contracts.ts +2 -0
  129. package/templates/be/skeleton/src/features/system-health/application/check-liveness.handler.ts +22 -0
  130. package/templates/be/skeleton/src/features/system-health/application/check-liveness.query.ts +11 -0
  131. package/templates/be/skeleton/src/features/system-health/index.ts +1 -1
  132. package/templates/be/skeleton/src/features/system-health/system-health.module.ts +3 -3
  133. package/templates/be/skeleton/src/features/system-health/transport/http/live.controller.ts +8 -9
  134. package/templates/be/skeleton/src/features/system-health/transport/http/system-health-http.module.ts +3 -6
  135. package/templates/be/skeleton/src/modules/domain/liveness/index.ts +3 -0
  136. package/templates/be/skeleton/src/modules/domain/liveness/liveness.contracts.ts +9 -0
  137. package/templates/be/skeleton/src/modules/domain/liveness/liveness.module-definition.ts +7 -0
  138. package/templates/be/skeleton/src/modules/domain/liveness/liveness.module.ts +14 -0
  139. package/templates/be/skeleton/src/modules/domain/liveness/liveness.options.ts +2 -0
  140. package/templates/be/skeleton/src/modules/domain/liveness/liveness.service.spec.ts +40 -0
  141. package/templates/be/skeleton/src/modules/domain/liveness/liveness.service.ts +26 -0
  142. package/templates/be/skeleton/src/modules/platform/clock/clock.decorators.ts +9 -0
  143. package/templates/be/skeleton/src/modules/platform/clock/clock.module-definition.ts +7 -0
  144. package/templates/be/skeleton/src/modules/platform/clock/clock.module.ts +19 -0
  145. package/templates/be/skeleton/src/modules/platform/clock/clock.options.ts +2 -0
  146. package/templates/be/skeleton/src/modules/platform/clock/clock.port.ts +5 -0
  147. package/templates/be/skeleton/src/modules/platform/clock/index.ts +4 -0
  148. package/templates/be/skeleton/src/modules/platform/clock/system-clock.service.spec.ts +20 -0
  149. package/templates/be/skeleton/src/modules/platform/clock/system-clock.service.ts +9 -0
  150. package/templates/be/skeleton/src/modules/platform/composition/composition.decorators.ts +8 -0
  151. package/templates/be/skeleton/src/modules/platform/composition/index.ts +2 -0
  152. package/templates/be/skeleton/src/modules/platform/config/config.decorators.ts +9 -0
  153. package/templates/be/skeleton/src/modules/platform/config/index.ts +2 -3
  154. package/templates/be/skeleton/src/modules/platform/config/server.config.ts +1 -1
  155. package/templates/be/skeleton/src/modules/platform/config/server.options.ts +0 -3
  156. package/templates/be/skeleton/src/modules/platform/cqrs/cqrs.contracts.ts +5 -0
  157. package/templates/be/skeleton/src/modules/platform/cqrs/cqrs.decorators.ts +9 -0
  158. package/templates/be/skeleton/src/modules/platform/cqrs/cqrs.handler.ts +27 -0
  159. package/templates/be/skeleton/src/modules/platform/cqrs/cqrs.log-events.ts +5 -0
  160. package/templates/be/skeleton/src/modules/platform/cqrs/cqrs.module-definition.ts +7 -0
  161. package/templates/be/skeleton/src/modules/platform/cqrs/cqrs.module.ts +18 -0
  162. package/templates/be/skeleton/src/modules/platform/cqrs/cqrs.options.ts +2 -0
  163. package/templates/be/skeleton/src/modules/platform/cqrs/index.ts +4 -0
  164. package/templates/be/skeleton/src/modules/platform/errors/error.filter.ts +8 -12
  165. package/templates/be/skeleton/src/modules/platform/errors/errors.log-events.ts +5 -0
  166. package/templates/be/skeleton/src/modules/platform/errors/index.ts +1 -1
  167. package/templates/be/skeleton/src/modules/platform/logging/index.ts +4 -4
  168. package/templates/be/skeleton/src/modules/platform/logging/json-logger.service.spec.ts +99 -0
  169. package/templates/be/skeleton/src/modules/platform/logging/json-logger.service.ts +38 -0
  170. package/templates/be/skeleton/src/modules/platform/logging/logging.decorators.ts +9 -0
  171. package/templates/be/skeleton/src/modules/platform/logging/logging.log-events.ts +7 -0
  172. package/templates/be/skeleton/src/modules/platform/logging/logging.module-definition.ts +7 -0
  173. package/templates/be/skeleton/src/modules/platform/logging/logging.module.ts +23 -10
  174. package/templates/be/skeleton/src/modules/platform/logging/logging.options.ts +2 -0
  175. package/templates/be/skeleton/src/modules/platform/logging/logging.port.ts +15 -0
  176. package/templates/be/tool-config/eslint.config.mjs +3 -0
  177. package/templates/be/tool-config/jest.config.js +1 -0
  178. package/templates/be/tool-config/prettierignore +8 -0
  179. package/templates/be/tool-config/prettierrc +1 -0
  180. package/templates/be/tool-config/src/tests/tsconfig.json +5 -0
  181. package/templates/be/tool-config/tsconfig.build.json +5 -0
  182. package/templates/be/tool-config/tsconfig.json +11 -0
  183. package/templates/common/gitignore.base +1 -1
  184. package/templates/fe/ci-workflows/github/workflows/ci.yml +54 -0
  185. package/templates/fe/hooks/husky/pre-commit +16 -0
  186. package/templates/fe/hooks/husky/pre-push +6 -0
  187. package/templates/fe/package-scripts/package.json +17 -0
  188. package/templates/fe/parts/api-client.ts +44 -0
  189. package/templates/fe/parts/api-outcome.ts +7 -0
  190. package/templates/fe/quality-config/sonar-project.properties +8 -0
  191. package/templates/fe/skeleton/apps/__app__/src/app/[locale]/layout.tsx +1 -1
  192. package/templates/fe/skeleton/apps/__app__/src/app/[locale]/not-found.tsx +1 -1
  193. package/templates/fe/skeleton/apps/__app__/src/app/global-error.tsx +1 -1
  194. package/templates/fe/skeleton/scripts/.gitkeep +0 -0
  195. package/templates/fe/skeleton-app/apps/__app__/src/modules/api/client.ts +1 -0
  196. package/templates/fe/skeleton-app/apps/__app__/src/modules/api/index.ts +3 -0
  197. package/templates/fe/skeleton-app/apps/__app__/src/modules/api/outcome.ts +1 -0
  198. package/templates/fe/skeleton-app/apps/__app__/src/modules/i18n/index.ts +4 -0
  199. package/templates/fe/skeleton-shared/apps/__app__/next.config.ts +12 -0
  200. package/templates/fe/skeleton-shared/apps/__app__/src/modules/api/index.ts +2 -0
  201. package/templates/fe/skeleton-shared/apps/__app__/src/modules/i18n/index.ts +9 -0
  202. package/templates/fe/skeleton-shared/apps/__app__/src/modules/i18n/request.ts +5 -0
  203. package/templates/fe/skeleton-shared/apps/__app__/src/proxy.ts +5 -0
  204. package/templates/fe/skeleton-shared/packages/__family__-api/package.json +12 -0
  205. package/templates/fe/skeleton-shared/packages/__family__-api/src/client.ts +1 -0
  206. package/templates/fe/skeleton-shared/packages/__family__-api/src/index.ts +3 -0
  207. package/templates/fe/skeleton-shared/packages/__family__-api/src/outcome.ts +1 -0
  208. package/templates/fe/skeleton-shared/packages/__family__-api/tsconfig.json +5 -0
  209. package/templates/fe/skeleton-shared/packages/__family__-i18n/package.json +18 -0
  210. package/templates/fe/skeleton-shared/packages/__family__-i18n/src/app.ts +19 -0
  211. package/templates/fe/skeleton-shared/packages/__family__-i18n/src/index.ts +2 -0
  212. package/templates/fe/skeleton-shared/packages/__family__-i18n/src/proxy.ts +12 -0
  213. package/templates/fe/skeleton-shared/packages/__family__-i18n/src/request.ts +15 -0
  214. package/templates/fe/skeleton-shared/packages/__family__-i18n/tsconfig.json +5 -0
  215. package/templates/fe/tool-config/eslint.config.mjs +3 -0
  216. package/templates/fe/tool-config/prettierignore +10 -0
  217. package/templates/fe/tool-config/prettierrc +1 -0
  218. package/templates/fe/tool-config/stylelint.config.mjs +3 -0
  219. package/templates/fe/tool-config/tsconfig.json +4 -0
  220. package/templates/be/pre-commit +0 -8
  221. package/templates/be/skeleton/apps/__app__/src/__app__.composition.spec.ts +0 -31
  222. package/templates/be/skeleton/src/features/system-health/transport/http/live.controller.spec.ts +0 -13
  223. package/templates/be/skeleton/src/modules/platform/config/env-source.spec.ts +0 -36
  224. package/templates/be/skeleton/src/modules/platform/config/server.config.spec.ts +0 -19
  225. package/templates/be/skeleton/src/modules/platform/errors/domain-error.spec.ts +0 -15
  226. package/templates/be/skeleton/src/modules/platform/errors/error.filter.spec.ts +0 -38
  227. package/templates/be/skeleton/src/modules/platform/logging/json-logger.spec.ts +0 -33
  228. package/templates/be/skeleton/src/modules/platform/logging/json-logger.ts +0 -35
  229. package/templates/be/skeleton/src/modules/platform/logging/log-id.ts +0 -9
  230. package/templates/be/skeleton/src/modules/platform/logging/logger.port.ts +0 -19
  231. package/templates/common/codecov.yml +0 -13
  232. package/templates/common/pre-push +0 -5
  233. package/templates/fe/e2e.yml +0 -22
  234. package/templates/fe/pre-commit +0 -7
  235. package/templates/fe/skeleton/apps/__app__/src/app/health/live/route.spec.ts +0 -10
  236. package/templates/fe/skeleton/apps/__app__/src/modules/i18n/messages.spec.ts +0 -27
  237. package/templates/fe/skeleton/apps/__app__/src/modules/i18n/routing.spec.ts +0 -10
  238. package/templates/fe/sonar-project.properties +0 -11
  239. /package/templates/be/skeleton/src/modules/platform/config/{env-source.ts → env-source.config.ts} +0 -0
  240. /package/templates/be/skeleton/src/modules/platform/errors/{domain-error.ts → domain.error.ts} +0 -0
  241. /package/templates/fe/{skeleton → skeleton-app}/apps/__app__/next.config.ts +0 -0
  242. /package/templates/fe/{skeleton → skeleton-app}/apps/__app__/src/modules/i18n/config.ts +0 -0
  243. /package/templates/fe/{skeleton → skeleton-app}/apps/__app__/src/modules/i18n/navigation.ts +0 -0
  244. /package/templates/fe/{skeleton → skeleton-app}/apps/__app__/src/modules/i18n/request.ts +0 -0
  245. /package/templates/fe/{skeleton → skeleton-app}/apps/__app__/src/modules/i18n/routing.ts +0 -0
  246. /package/templates/fe/{skeleton → skeleton-app}/apps/__app__/src/proxy.ts +0 -0
@@ -0,0 +1,309 @@
1
+ schema: starci/knowledge-source@1
2
+ id: fe.folder
3
+ title: Folder
4
+ purpose: |
5
+ Given a piece of frontend code, which directory and which file name does it get? Apply the HFS frontend tree
6
+ (knowledge/hfs/README.md section 6, slots fe.*).
7
+ appliesTo:
8
+ - frontend
9
+ family: fe
10
+ provenance:
11
+ reviewedAt: 2026-09-29
12
+ sources:
13
+ - knowledge/hfs/README.md
14
+ - knowledge/hfs/slots.yaml
15
+ - https://nextjs.org/docs/app/getting-started/project-structure
16
+ - https://next-intl.dev/docs/routing
17
+ - packages/grammar/src/**
18
+ limitations: |
19
+ Folder rules place code by responsibility. They do not prove rendered behavior or accessibility. Inspect the
20
+ selected repository's actual source, manifests, aliases and lint and test configuration. Verify installed Grammar
21
+ exports at @starci/grammar/common.
22
+ rules:
23
+ - id: FE-FOLDER-1
24
+ title: Responsibility directories
25
+ kind: mandatory
26
+ hfsRules: [R01, R02, R59, R64]
27
+ requirement: |
28
+ Every frontend repository is an apps/<app>/ monorepo even with one app, named by its role (for example web), and
29
+ all product source lives under apps/<app>/src. A repository-root src/ does not exist. Each Next source root
30
+ (apps/<app>/src) uses app/ for framework adapters; features/{pages,layouts,overlays} for route-facing product
31
+ composition; components/{blocks,composites,branches,leaves} for product visuals; hooks/<domain> for React hooks;
32
+ and modules/<capability> for cohesive reusable technical and domain capabilities. Every app has the modules api,
33
+ config, i18n and routes, and one brand.css under modules/brand when it sets brand tokens. No other empty role
34
+ folder is required. Grammar remains a separate product-agnostic package. The only files that may sit directly in the
35
+ source root (the directory holding app/) are the framework-pinned root files listed in frameworkPinnedRootFiles
36
+ below: Next.js loads them from that exact place. Each is a thin framework adapter whose internal imports enter
37
+ modules or a feature public entry only; every other rule still applies to it, except that the export names the
38
+ framework mandates there (frameworkPinnedRootExports) keep their framework spelling. HFS uses proxy.ts for locale
39
+ routing; a middleware.* file is refused on Next 16 and later by the Next conventions check and the slot
40
+ fe.source-root-pinned, even though the machine list names it as a file Next loads. Any other file at the source
41
+ root, including a helper folder beside a pinned file, has no owner and is refused.
42
+ frameworkPinnedRootFiles:
43
+ # Machine list read by scripts/checks/architecture/framework-pinned.mjs (FE_SOURCE_LAYOUT_INVALID accepts exactly
44
+ # these basenames directly in a Next source root; FE_TIER_DIRECTION keeps them thin). Exact names,
45
+ # case-sensitive, no globs. It lists what the framework loads from the source root. Which of them a repository may
46
+ # keep is decided by the slot fe.source-root-pinned and FE_NEXT_CONVENTIONS, not by this list.
47
+ # Machine list read by scripts/checks/architecture/framework-pinned.mjs (FE_SOURCE_LAYOUT_INVALID
48
+ # accepts exactly these basenames directly in a Next source root;
49
+ # their imports follow the FE_TIER_DIRECTION matrix). Exact names, case-sensitive, no globs. Supervisor rulings for nivo
50
+ # wf-nivo-fe-debt-mug06w7h inc-2e42a24b74e4 and inc-846867b9a34e (proxy is the Next 16 name of
51
+ # middleware, listed so the list survives the upgrade).
52
+ - middleware.ts
53
+ - middleware.js
54
+ - middleware.mjs
55
+ - proxy.ts
56
+ - proxy.js
57
+ - proxy.mjs
58
+ - instrumentation.ts
59
+ - instrumentation.js
60
+ - instrumentation.mjs
61
+ - instrumentation-client.ts
62
+ - instrumentation-client.js
63
+ - instrumentation-client.mjs
64
+ - next-env.d.ts
65
+ frameworkPinnedRootExports:
66
+ # Export names the framework mandates in a pinned root file, keyed by file stem (read by
67
+ # scripts/checks/architecture/framework-pinned.mjs). FE_SOURCE_NAME_SHAPE accepts exactly these names in exactly
68
+ # these files when they sit directly in a Next source root; everywhere else the name-shape rule is unchanged.
69
+ middleware: [config, middleware, default]
70
+ proxy: [config, proxy, default]
71
+ instrumentation: [register, onRequestError]
72
+ instrumentation-client: [onRouterTransitionStart]
73
+ rationale: |
74
+ One fixed source root per app keeps every repository the same shape while preventing app helpers, component-local
75
+ hooks and feature-local transport buckets from becoming undeclared owners.
76
+ cases:
77
+ - id: case-1
78
+ when: "A routed authentication scenario"
79
+ write: "apps/<app>/src/features/pages/AuthenticationPage/index.tsx"
80
+ - id: case-2
81
+ when: "A reusable API client, reader or session capability"
82
+ write: >
83
+ apps/<app>/src/modules/api/ or apps/<app>/src/modules/session/ with one explicit public index.ts entry
84
+ - id: case-3
85
+ when: "A reusable product visual without scenario ownership"
86
+ write: "apps/<app>/src/components/{blocks,composites,branches,leaves}/<Name>/index.tsx"
87
+ - id: case-4
88
+ when: "A React hook"
89
+ write: "apps/<app>/src/hooks/<domain>/useAutoScroll.ts; built-in React hook calls may remain in visuals"
90
+ - id: case-5
91
+ kind: exception
92
+ when: "Next.js only loads the file from the source root (proxy, instrumentation, instrumentation-client, next-env.d.ts)"
93
+ write: >
94
+ apps/<app>/src/proxy.ts beside apps/<app>/src/app/ as a thin adapter; locale routing logic lives in
95
+ apps/<app>/src/modules/i18n/ and the adapter imports it; the names the framework mandates there
96
+ (frameworkPinnedRootExports, for example export const config = { matcher }) keep their framework spelling
97
+ - id: case-6
98
+ when: "Any other file or folder directly under the source root (apps/<app>/src/i18n/request.ts, apps/<app>/src/config.ts), and a repository-root src/"
99
+ write: >
100
+ Move it to its owner, for example apps/<app>/src/modules/i18n/request.ts, and point framework configuration
101
+ (apps/<app>/next.config.ts) at the new path
102
+ - id: case-7
103
+ when: "The per-slot data-status recipe"
104
+ write: >
105
+ apps/<app>/src/components/composites/SlotView/index.tsx, apps/<app>/src/modules/slot/index.ts (Slot, toSlot,
106
+ SlotLabels) and apps/<app>/src/hooks/slot/useSlotLabels.ts, one copy each (examples/shape-slot)
107
+ verification:
108
+ automated:
109
+ - HFS_SLOT_UNDECLARED
110
+ - HFS_SLOT_REQUIRED_MISSING
111
+ - FE_SOURCE_LAYOUT_INVALID
112
+ - FE_NEXT_CONVENTIONS
113
+ - FE_TIER_DIRECTION
114
+ - FE_CUSTOM_HOOK_LOCATION
115
+ - HFS_APPS_REQUIRED
116
+ - HFS_ROOT_SRC_FORBIDDEN_FE
117
+ manual:
118
+ - Confirm the unit's responsibility and public entry, rather than assigning it by consumer count.
119
+ relatedExamples:
120
+ - connected-block
121
+
122
+ - id: FE-FOLDER-2
123
+ title: Discoverable files for one unit
124
+ kind: mandatory
125
+ hfsRules: [R02, R55, R65]
126
+ requirement: |
127
+ A split unit (a connected block, and a feature page, layout or overlay) uses index.tsx for the connected X and
128
+ sibling component.tsx for the pure XBase, its XBaseProps and xDefaultState, plus XState when the shape is its own union
129
+ (a shape that is a domain status is typed with that status, never a second name for it). Only that index.tsx and the
130
+ unit's specs import component.tsx. Pure blocks, composites, branches and leaves use index.tsx only and never gain an
131
+ empty twin. classNames.ts is present only when the unit owns class strings. Budgets: component.tsx 300 lines,
132
+ connected index.tsx 200 lines, at most 6 data hooks and 6 useState per connected unit.
133
+ rationale: |
134
+ Stable basenames make class, tests and the connected and presentational split discoverable without forwarding
135
+ twins, and budgets keep a unit reviewable.
136
+ cases:
137
+ - id: case-1
138
+ when: "Connected block with product-world lifecycle"
139
+ write: "blocks/CatalogBlock/{index.tsx,component.tsx}; every nonempty index render reaches the sibling"
140
+ - id: case-2
141
+ when: "Feature page, layout or overlay"
142
+ write: >
143
+ features/pages/OperatePage/{index.tsx,component.tsx,classNames.ts}, features/layouts/WorkspaceLayout/…,
144
+ features/overlays/SendHandoffOverlay/… (examples/shape-slot)
145
+ - id: case-3
146
+ when: "Single visual unit"
147
+ write: "leaves/ButtonStateSample/{classNames.ts,index.tsx}; no component.tsx twin without a separate world owner"
148
+ - id: case-4
149
+ when: "Custom hook or reusable module"
150
+ write: "hooks/<domain>/useX.ts or modules/<capability>/...; do not create visual twins"
151
+ verification:
152
+ automated:
153
+ - class-names-in-colocated-file
154
+ - no-inline-class-name
155
+ - FE_CONNECTED_BLOCK_RENDER_PAIR
156
+ - FE_SIZE_AND_STATE_BUDGET
157
+ - HFS_SIZE_GROWTH
158
+ - starci-fe/base-import-pair
159
+ manual:
160
+ - Confirm each file owns a real responsibility; do not add a component.tsx twin by habit.
161
+ relatedExamples:
162
+ - connected-block
163
+
164
+ - id: FE-FOLDER-3
165
+ title: Route files adapt to one visual owner and every app has its boundaries
166
+ kind: mandatory
167
+ hfsRules: [R53, R54, R55, R57]
168
+ requirement: |
169
+ Route files under app/[locale]/ (page, layout, template, loading, not-found) are server components. A route may
170
+ resolve framework server inputs (params, searchParams, headers, cookies), then mounts one page-level visual owner.
171
+ A route whose complete responsibility is redirect or notFound mounts none. A route file holds no inline component, no
172
+ drawing decision and no hook, and a redirect from an old URL lives in next.config redirects(), never in a page. Every app has
173
+ app/global-error.tsx, app/[locale]/layout.tsx, app/[locale]/error.tsx and app/[locale]/not-found.tsx, and a loading.tsx
174
+ for each route group that reads data. Each page exports generateMetadata. force-dynamic on a client page is forbidden.
175
+ rationale: |
176
+ Keeping default exports in the App Router layer prevents components from becoming route-coupled, and mandatory
177
+ boundaries turn a render failure into a recoverable screen instead of a blank page.
178
+ cases:
179
+ - id: case-1
180
+ when: "A route"
181
+ write: >
182
+ app/[locale]/authentication/page.tsx: const AuthenticationRoute = async () => <AuthenticationPage />, then export
183
+ default AuthenticationRoute
184
+ - id: case-2
185
+ kind: exception
186
+ when: "Redirect or notFound adapter"
187
+ write: "Resolve framework inputs and redirect or terminate without a visual owner."
188
+ - id: case-3
189
+ when: "Global app files"
190
+ write: >
191
+ app/globals.css, app/global-error.tsx, app/[locale]/error.tsx, app/[locale]/not-found.tsx, app/sitemap.ts,
192
+ app/robots.ts, app/health/{live,ready}/route.ts
193
+ verification:
194
+ automated:
195
+ - FE_ERROR_BOUNDARY_MISSING
196
+ - FE_ROUTE_FILES_THIN
197
+ - FE_CLIENT_BOUNDARY
198
+ - FE_OWNER_REACHABLE
199
+ - FE_TIER_DIRECTION
200
+ manual:
201
+ - Confirm visual routes have one page owner and redirect-only routes have no visual tree.
202
+ relatedExamples: []
203
+
204
+ - id: FE-FOLDER-4
205
+ title: Hooks are hooks; readers and transport live in modules
206
+ kind: mandatory
207
+ hfsRules: [R56, R50, R49]
208
+ requirement: |
209
+ hooks/<domain>/ holds React hooks (use*.ts, one hook per file) and exactly one <domain>.shared.ts for shared
210
+ non-hook helpers of that domain (key builders, token readers). A file in hooks/ that is not a hook and is not the shared
211
+ file is a finding. Server-side readers (fetch and map, no React) live in modules/api/<domain>/read-*.ts and are
212
+ wrapped in React cache() when several blocks call them in one request. Generic client plumbing, the Outcome type,
213
+ the contract copy and generated wire types live in modules/api; environment reading lives in modules/config; every
214
+ href builder lives in modules/routes; locale routing lives in modules/i18n; colour values live in modules/brand.
215
+ A helper name defined twice across hook files is a finding.
216
+ rationale: |
217
+ A rule that says what a hooks folder is also says where everything else goes, so nobody copies a helper sixteen
218
+ times or fills hooks/ with server fetch functions.
219
+ cases:
220
+ - id: case-1
221
+ when: "Authentication request lifecycle"
222
+ write: "apps/<app>/src/hooks/authentication/useAuthentication.ts"
223
+ - id: case-2
224
+ when: "A helper shared by the hooks of one domain"
225
+ write: "apps/<app>/src/hooks/sales/sales.shared.ts, for example useSalesAccessToken's token reader"
226
+ - id: case-3
227
+ when: "A server read used by several blocks"
228
+ write: "apps/<app>/src/modules/api/sales/read-sales-summary.ts, exported through modules/api/index.ts"
229
+ - id: case-4
230
+ when: "The API client"
231
+ write: "apps/<app>/src/modules/api/client.ts and apps/<app>/src/modules/api/outcome.ts"
232
+ - id: case-5
233
+ when: "Intrinsic custom helper"
234
+ write: "apps/<app>/src/hooks/ui/useAutoScroll.ts; direct useRef and useState calls may remain in their visual owner"
235
+ verification:
236
+ automated:
237
+ - FE_HOOKS_ARE_HOOKS
238
+ - FE_CUSTOM_HOOK_LOCATION
239
+ - HFS_DUPLICATE_CODE
240
+ - FE_TIER_DIRECTION
241
+ manual:
242
+ - Confirm each lifecycle has one feature owner and transport mechanics have one module owner.
243
+ relatedExamples:
244
+ - connected-block
245
+
246
+ - id: FE-FOLDER-5
247
+ title: Package unit
248
+ kind: mandatory
249
+ hfsRules: [R63]
250
+ requirement: |
251
+ A shared package is packages/<pkg>/ with package.json (explicit exports), src/index.ts, tsconfig.json and colocated
252
+ specs, built to dist. Grammar components live under packages/grammar/src/core/<tier>/<Name>/ with collocated class
253
+ strings and specs; family entries and built-output proof stay at the package roots. See packages.yaml for the shared
254
+ UI package shape.
255
+ rationale: |
256
+ Package layout separates primitives from app composition and keeps public entry verification against dist.
257
+ cases:
258
+ - id: case-1
259
+ when: "A Grammar component"
260
+ write: "packages/grammar/src/core/<primitive|composite|branch|composition>/<Name>/index.tsx"
261
+ - id: case-2
262
+ when: "A Grammar component's class strings and spec"
263
+ write: "sibling classNames.ts and index.spec.tsx"
264
+ - id: case-3
265
+ when: "Built-output proof"
266
+ write: "common/index.test.mjs, core/index.test.mjs, package-boundary.test.mjs (node:test against dist/)"
267
+ verification:
268
+ automated:
269
+ - FE_PACKAGE_SHAPE
270
+ manual:
271
+ - Verify public app imports use @starci/grammar/common and installed exports, never the package root.
272
+ relatedExamples: []
273
+
274
+ - id: FE-FOLDER-6
275
+ title: Keep units cohesive
276
+ kind: mandatory
277
+ hfsRules: [R49, R21]
278
+ requirement: |
279
+ A unit folder contains files that change for the same responsibility. Do not create generic helpers/ or utils/
280
+ buckets inside components, read deployment configuration in presentation, or split files solely to meet a count.
281
+ Collocated subordinate renders and pure helpers are valid when they remain private to the owner; a separately stable
282
+ responsibility receives its own named owner.
283
+ rationale: |
284
+ Cohesion prevents helper sprawl and duplicated configuration while avoiding one-component-per-folder ceremony that
285
+ has no dependency boundary.
286
+ cases:
287
+ - id: case-1
288
+ when: "A small subordinate render is private to one owner"
289
+ write: "Keep it local with an unexported contract; extract it when it gains a stable public responsibility."
290
+ - id: case-2
291
+ when: "Class strings in component.tsx"
292
+ write: "Never; they go to classNames.ts (lint class-names-in-colocated-file, no-inline-class-name)"
293
+ - id: case-3
294
+ when: "A helpers/ or utils/ folder"
295
+ write: "Do not use it to hide mixed feature, transport or visual responsibilities."
296
+ - id: case-4
297
+ when: "A deployment constant"
298
+ write: >
299
+ Never. process.env.NEXT_PUBLIC_* is read only in apps/<app>/src/modules/config and reached through that module,
300
+ with no localhost fallback; a missing value in production throws when the module loads.
301
+ verification:
302
+ automated:
303
+ - FE_ENV_OWNER
304
+ - class-names-in-colocated-file
305
+ - no-inline-class-name
306
+ - no-helper-folder-in-components
307
+ manual:
308
+ - Confirm extraction is justified by ownership or lifecycle rather than file length or count.
309
+ relatedExamples: []
@@ -0,0 +1,85 @@
1
+ schema: starci/sonar-gate@1
2
+ id: sonar-gate
3
+ title: The Sonar quality gate every product is held to
4
+ purpose: |
5
+ The one place the Sonar thresholds live. A product repository never restates them: its
6
+ .starcistacks/application-stacks.yaml `services.sonar.qualityGate` names the gate below, and
7
+ scripts/checks/sonar-local.mjs (1) makes the local SonarQube's gate of that name carry exactly these
8
+ conditions and selects it for the project, and (2) judges an op's slice - the lines it changed - against
9
+ the same numbers. Code-writing ops cannot settle done while the slice is red
10
+ (scripts/kernel/sonar-settle.mjs). Changing a number here changes every repository on the next scan.
11
+ The gate has two parts: `newCode` (the recent work: duplication, blocker/critical issues, hotspots) and
12
+ `overall` (the whole code: no imported HFS, ESLint or stylelint finding, no duplicated-lines excess, no cognitive-
13
+ complexity issue). The one Sonar mechanism of the HFS canon is packages/hfs/README.md section "Sonar".
14
+ gate:
15
+ name: starci-new-code
16
+ # A project with no new-code baseline judges the whole project as new code (nivo inc-f92febebbb64); a fixed
17
+ # window keeps "new code" the recent work on main, so the server gate is passable on a healthy main.
18
+ newCodePeriod: {type: NUMBER_OF_DAYS, value: 30}
19
+ newCode:
20
+ # Sonar holds NO coverage condition: unit coverage is the runner's job (jest per-file 100 on `*.service.ts`), never an lcov import.
21
+ # SonarQube's ignoreSmallChanges: fewer changed lines than this are not held to duplication.
22
+ ignoreBelowChangedLines: 20
23
+ duplication:
24
+ metric: new_duplicated_lines_density
25
+ maxPercent: 3
26
+ issues:
27
+ # An open issue of these severities on a changed line fails the slice; lesser ones are listed, never blocking.
28
+ blockingSeverities: [BLOCKER, CRITICAL]
29
+ metrics: {BLOCKER: new_blocker_violations, CRITICAL: new_critical_violations}
30
+ max: 0
31
+ hotspots:
32
+ metric: new_security_hotspots_reviewed
33
+ minReviewedPercent: 100
34
+ unreviewedMax: 0
35
+ # The whole code, not only the new code. Every finding of the HFS canon reaches Sonar through ONE pipeline
36
+ # (packages/hfs, contract change hfs-sonar-import) and the gate holds the open-issue count of the whole project at zero:
37
+ # starci-hfs `hfs check --sonar reports/hfs.sonar.json` (repository, managed-file and architecture-machine findings;
38
+ # the rule id is the finding code) -> sonar.externalIssuesReportPaths
39
+ # eslint `eslint -f json -o reports/eslint.json` (the BE and FE canon plugins alike) converted by `hfs report eslint` -> sonar.externalIssuesReportPaths
40
+ # stylelint `stylelint --formatter json` converted by `hfs report stylelint` -> sonar.externalIssuesReportPaths
41
+ # The three imports share ONE placement rule (a finding on a file Sonar does not index is filed on the first source file); Sonar's own
42
+ # ESLint import (sonar.eslint.reportPaths) is not used because it drops such issues.
43
+ # A SonarQube gate condition cannot filter issues by engine, so the condition is the project's open issues (`violations`,
44
+ # overall code): the three engines above are the imports it counts, and every native Sonar issue counts with them. That is
45
+ # stricter than the engines alone and it is intended: a project that meets this gate has no open issue of any origin.
46
+ overall:
47
+ issues:
48
+ metric: violations
49
+ max: 0
50
+ engines: [starci-hfs, eslint, stylelint]
51
+ # HFS_DUPLICATE_CODE (R21): duplicated lines density of the whole project, the Sonar enforcer of the machine's clone check.
52
+ # SonarJS runs its own token-based detection (sonar.cpd.* minimums are not read for TypeScript), so ruleParams.<profile>.duplicateBlock
53
+ # is enforced by the machine and imported as starci-hfs issues, and this density is the Sonar-side backstop.
54
+ duplication:
55
+ metric: duplicated_lines_density
56
+ maxPercent: 3
57
+ # HFS_SIZE_GROWTH (R20): the cognitive complexity of a function is the Sonar rule S3776 (typescript:S3776, Sonar way, default
58
+ # threshold); an open S3776 issue is an open issue of `overall.issues`, so the zero-issue condition is its gate.
59
+ cognitiveComplexity:
60
+ rule: typescript:S3776
61
+ via: overall.issues
62
+ # The Sonar enforcers of the rule catalog (knowledge/hfs/rules.yaml): the failure code each condition above is the gate of.
63
+ enforces:
64
+ - {rule: R20, code: HFS_SIZE_GROWTH, enforcer: cognitive-complexity, condition: cognitiveComplexity}
65
+ - {rule: R21, code: HFS_DUPLICATE_CODE, enforcer: duplicated-lines-density, condition: duplication}
66
+ - {rule: R21, code: HFS_DUPLICATE_SYMBOL, enforcer: duplicated-lines-density, condition: duplication}
67
+ # Ops that write product code: each runs sonar-local on its own change and cannot settle done while the gate is red.
68
+ enforcedOps: [backend.implement, interface.implement, code.refactor]
69
+ reasoning: |
70
+ Chosen against the four projects the local server holds on 2026-09-29 (whole-project duplicated lines
71
+ 1.6 / 2.1 / 1.7 / 0.0 percent), so new code that meets them is at or below what main already carries.
72
+ no coverage condition: the unit project fails below its per-file 100 threshold on `*.service.ts`, so Sonar never
73
+ imports an lcov report and never judges coverage.
74
+ duplication 3: the Sonar way value; the worst project today is 2.1, so 3 rejects a real copy-paste block and
75
+ passes ordinary work.
76
+ blocker/critical 0: the two severities that are defects, not style. main carries old debt of both kinds
77
+ (nivo-backend 3 blocker, 137 critical), so the SLICE verdict reads only changed lines and never main's backlog;
78
+ major and lower are reported on the summary and never fail a slice.
79
+ overall issues 0 and overall duplication 3: the server gate is also the owner's definition of done for the HFS canon:
80
+ a repository that carries any open ESLint, stylelint or HFS finding is not green, whatever the age of the line. The
81
+ op slice keeps judging changed lines (an op cannot fix a repository's whole backlog); the dashboard and CI show the
82
+ whole-project condition. Adopting a repository therefore means clearing its imported findings, which `hfs check`,
83
+ eslint and stylelint already demand at land.
84
+ hotspots 0 unreviewed: a hotspot is a question the author must answer; on changed lines it is answered in the
85
+ same slice.