@starci/hfs 3.0.0 → 4.0.1
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.
- package/CHANGELOG.md +22 -0
- package/README.md +25 -21
- package/bin/hfs.mjs +59 -37
- package/lint/run.mjs +70 -39
- package/package.json +2 -2
- package/runtime/engine/admission.mjs +3 -3
- package/runtime/engine/ledger-db.mjs +2 -2
- package/runtime/engine/machine-db.mjs +90 -9
- package/runtime/engine/migrations/machine/0002-worktrees-no-workflow-kind.sql +13 -0
- package/runtime/engine/migrations/runtime/0005-ended-workflow-views.sql +93 -0
- package/runtime/knowledge/hfs/canon-pins.yaml +28 -9
- package/runtime/knowledge/hfs/peer-integrations.yaml +18 -0
- package/runtime/knowledge/hfs/slots.yaml +193 -128
- package/runtime/knowledge/patterns/fe/folder.yaml +36 -36
- package/runtime/modules/kernel/failure-codes.yaml +23 -32
- package/runtime/scripts/checks/architecture/backend.mjs +1 -1
- package/runtime/scripts/checks/architecture/config.mjs +31 -11
- package/runtime/scripts/checks/architecture/contracts.mjs +4 -4
- package/runtime/scripts/checks/architecture/fe-slot-allows.mjs +7 -3
- package/runtime/scripts/checks/architecture/framework-pinned.mjs +5 -47
- package/runtime/scripts/checks/architecture/frontend.mjs +6 -4
- package/runtime/scripts/checks/architecture/hfs.mjs +104 -66
- package/runtime/scripts/checks/architecture/next-data.mjs +3 -2
- package/runtime/scripts/checks/architecture/registration.mjs +1 -1
- package/runtime/scripts/checks/architecture/symbols.mjs +13 -2
- package/runtime/scripts/checks/architecture/test-world-files.mjs +83 -45
- package/runtime/scripts/checks/architecture/typescript.mjs +45 -20
- package/runtime/scripts/checks/typescript-programs.mjs +2 -2
- package/runtime/scripts/lib/hfs-check.mjs +156 -141
- package/runtime/scripts/lib/hfs-path-findings.mjs +13 -2
- package/runtime/scripts/lib/hfs-rules/contract.mjs +15 -42
- package/runtime/scripts/lib/hfs-rules/deps.mjs +4 -2
- package/runtime/scripts/lib/hfs-rules/frontend.mjs +37 -36
- package/runtime/scripts/lib/hfs-rules/peer-integrations.mjs +44 -0
- package/runtime/scripts/lib/hfs-rules/proof-commands.mjs +11 -8
- package/runtime/scripts/lib/hfs-slots.mjs +244 -61
- package/runtime/scripts/lib/hfs-view.mjs +9 -7
- package/runtime/scripts/lib/language.mjs +11 -1
- package/runtime/scripts/lib/safe-remove.mjs +95 -10
- package/scaffold/app.mjs +205 -0
- package/scaffold/service.mjs +26 -16
- package/sync/cli.mjs +1 -1
- package/sync/hygiene.mjs +11 -8
- package/sync/index.mjs +109 -111
- package/sync/managed.mjs +9 -8
- package/sync/sonar-key.mjs +20 -22
- package/templates/{be → app}/ci-workflows/github/workflows/ci.yml +4 -2
- package/templates/app/gitignore +6 -0
- package/templates/app/hooks/husky/pre-commit +25 -0
- package/templates/app/hooks/husky/pre-push +7 -0
- package/templates/app/package-scripts/package.json +22 -0
- package/templates/{be → app}/quality-config/sonar-project.properties +3 -2
- package/templates/app/skeleton/.editorconfig +15 -0
- package/templates/app/skeleton/.gitattributes +2 -0
- package/templates/app/skeleton/.nvmrc +1 -0
- package/templates/app/skeleton/.starciwork/features/index.yaml +7 -0
- package/templates/app/skeleton/.starciwork/workspace.yaml +9 -0
- package/templates/app/skeleton/README.md +36 -0
- package/templates/app/skeleton/scripts/codegen.mjs +4 -0
- package/templates/{fe → app}/tool-config/prettierignore +4 -1
- package/templates/be/skeleton/.sops.yaml +2 -0
- package/templates/be/skeleton/.starcistacks/application-stacks.yaml +10 -0
- package/templates/be/skeleton/apps/__app__/src/__app__.options.ts +3 -0
- package/templates/be/skeleton/apps/__app__/src/app.module.ts +27 -6
- package/templates/be/skeleton/apps/__app__/src/main.ts +4 -1
- package/templates/be/skeleton/src/features/system-health/transport/http/live.controller.ts +2 -0
- package/templates/be/skeleton/src/modules/domain/identity/admission.policy.ts +11 -0
- package/templates/be/skeleton/src/modules/domain/identity/auth.guard.ts +25 -0
- package/templates/be/skeleton/src/modules/domain/identity/errors/identity.error.ts +16 -0
- package/templates/be/skeleton/src/modules/domain/identity/identity.contracts.ts +11 -0
- package/templates/be/skeleton/src/modules/domain/identity/identity.decorators.ts +8 -0
- package/templates/be/skeleton/src/modules/domain/identity/identity.module-definition.ts +7 -0
- package/templates/be/skeleton/src/modules/domain/identity/identity.module.ts +14 -0
- package/templates/be/skeleton/src/modules/domain/identity/identity.options.ts +2 -0
- package/templates/be/skeleton/src/modules/domain/identity/index.ts +6 -0
- package/templates/be/skeleton/src/modules/domain/identity/messages/identity.messages.ts +11 -0
- package/templates/be/skeleton/src/modules/platform/composition/composition.decorators.ts +8 -1
- package/templates/be/skeleton/src/modules/platform/composition/index.ts +1 -1
- package/templates/be/skeleton/src/modules/platform/config/env-source.config.ts +71 -23
- package/templates/be/skeleton/src/modules/platform/config/errors/config.error.ts +14 -16
- package/templates/be/skeleton/src/modules/platform/config/index.ts +1 -1
- package/templates/be/skeleton/src/modules/platform/config/server.config.ts +2 -12
- package/templates/be/skeleton/src/modules/platform/errors/domain.error.ts +16 -7
- package/templates/be/skeleton/src/modules/platform/errors/errors/errors.error.ts +16 -0
- package/templates/be/skeleton/src/modules/platform/errors/errors.contracts.ts +33 -0
- package/templates/be/skeleton/src/modules/platform/errors/errors.decorators.ts +16 -0
- package/templates/be/skeleton/src/modules/platform/errors/errors.filter.ts +32 -0
- package/templates/be/skeleton/src/modules/platform/errors/errors.log-events.ts +2 -2
- package/templates/be/skeleton/src/modules/platform/errors/errors.module-definition.ts +9 -0
- package/templates/be/skeleton/src/modules/platform/errors/errors.module.ts +19 -0
- package/templates/be/skeleton/src/modules/platform/errors/errors.options.ts +7 -0
- package/templates/be/skeleton/src/modules/platform/errors/errors.service.spec.ts +94 -0
- package/templates/be/skeleton/src/modules/platform/errors/errors.service.ts +47 -0
- package/templates/be/skeleton/src/modules/platform/errors/http-status.policy.ts +13 -0
- package/templates/be/skeleton/src/modules/platform/errors/index.ts +4 -1
- package/templates/be/skeleton/src/modules/platform/errors/messages/errors.messages.ts +11 -0
- package/templates/be/skeleton/src/modules/platform/http-security/errors/http-security.error.ts +19 -0
- package/templates/be/skeleton/src/modules/platform/http-security/execution-request.mapper.ts +5 -0
- package/templates/be/skeleton/src/modules/platform/http-security/http-security.config.ts +16 -0
- package/templates/be/skeleton/src/modules/platform/http-security/http-security.decorators.ts +10 -0
- package/templates/be/skeleton/src/modules/platform/http-security/http-security.module-definition.ts +9 -0
- package/templates/be/skeleton/src/modules/platform/http-security/http-security.module.ts +13 -0
- package/templates/be/skeleton/src/modules/platform/http-security/http-security.options.ts +17 -0
- package/templates/be/skeleton/src/modules/platform/http-security/index.ts +7 -0
- package/templates/be/skeleton/src/modules/platform/http-security/messages/http-security.messages.ts +13 -0
- package/templates/be/skeleton/src/modules/platform/http-security/origin.guard.ts +31 -0
- package/templates/be/skeleton/src/modules/platform/http-security/rate-limit.guard.ts +68 -0
- package/templates/be/skeleton/src/modules/platform/i18n/bundle-message-catalog.service.spec.ts +66 -0
- package/templates/be/skeleton/src/modules/platform/i18n/bundle-message-catalog.service.ts +30 -0
- package/templates/be/skeleton/src/modules/platform/i18n/i18n.contracts.ts +18 -0
- package/templates/be/skeleton/src/modules/platform/i18n/i18n.decorators.ts +23 -0
- package/templates/be/skeleton/src/modules/platform/i18n/i18n.module-definition.ts +9 -0
- package/templates/be/skeleton/src/modules/platform/i18n/i18n.module.ts +24 -0
- package/templates/be/skeleton/src/modules/platform/i18n/i18n.options.ts +7 -0
- package/templates/be/skeleton/src/modules/platform/i18n/i18n.port.ts +13 -0
- package/templates/be/skeleton/src/modules/platform/i18n/index.ts +4 -0
- package/templates/be/skeleton/src/modules/platform/i18n/request-locale.service.spec.ts +45 -0
- package/templates/be/skeleton/src/modules/platform/i18n/request-locale.service.ts +19 -0
- package/templates/be/skeleton/src/modules/platform/logging/index.ts +1 -1
- package/templates/be/skeleton/src/modules/platform/logging/json-logger.service.spec.ts +85 -68
- package/templates/be/skeleton/src/modules/platform/logging/json-logger.service.ts +14 -10
- package/templates/be/skeleton/src/modules/platform/logging/logging.decorators.ts +13 -0
- package/templates/be/skeleton/src/modules/platform/logging/logging.log-events.ts +4 -0
- package/templates/be/skeleton/src/modules/platform/logging/logging.port.ts +0 -1
- package/templates/be/skeleton/src/modules/platform/primitives/index.ts +2 -0
- package/templates/be/skeleton/src/modules/platform/primitives/outcome.contracts.ts +25 -0
- package/templates/be/skeleton/src/modules/platform/primitives/outcome.mapper.ts +24 -0
- package/templates/fe/skeleton/apps/__app__/postcss.config.mjs +7 -0
- package/templates/fe/skeleton/apps/__app__/src/app/[locale]/error.tsx +5 -17
- package/templates/fe/skeleton/apps/__app__/src/app/[locale]/layout.tsx +11 -22
- package/templates/fe/skeleton/apps/__app__/src/app/[locale]/loading.tsx +6 -0
- package/templates/fe/skeleton/apps/__app__/src/app/[locale]/not-found.tsx +3 -12
- package/templates/fe/skeleton/apps/__app__/src/app/[locale]/page.tsx +6 -24
- package/templates/fe/skeleton/apps/__app__/src/app/global-error.tsx +4 -18
- package/templates/fe/skeleton/apps/__app__/src/app/globals.css +5 -0
- package/templates/fe/skeleton/apps/__app__/src/components/composites/FailureScreen/index.tsx +28 -0
- package/templates/fe/skeleton/apps/__app__/src/features/layouts/LocaleShell/index.tsx +35 -0
- package/templates/fe/skeleton/apps/__app__/src/features/pages/ErrorPage/component.tsx +21 -0
- package/templates/fe/skeleton/apps/__app__/src/features/pages/ErrorPage/index.tsx +17 -0
- package/templates/fe/skeleton/apps/__app__/src/features/pages/GlobalErrorPage/component.tsx +26 -0
- package/templates/fe/skeleton/apps/__app__/src/features/pages/GlobalErrorPage/index.tsx +15 -0
- package/templates/fe/skeleton/apps/__app__/src/features/pages/HomePage/component.tsx +25 -0
- package/templates/fe/skeleton/apps/__app__/src/features/pages/HomePage/index.tsx +15 -0
- package/templates/fe/skeleton/apps/__app__/src/features/pages/LoadingPage/component.tsx +27 -0
- package/templates/fe/skeleton/apps/__app__/src/features/pages/LoadingPage/index.tsx +8 -0
- package/templates/fe/skeleton/apps/__app__/src/features/pages/NotFoundPage/component.tsx +32 -0
- package/templates/fe/skeleton/apps/__app__/src/features/pages/NotFoundPage/index.tsx +8 -0
- package/templates/fe/skeleton/apps/__app__/src/modules/config/index.ts +12 -0
- package/templates/fe/skeleton/apps/__app__/src/modules/i18n/index.ts +2 -0
- package/templates/fe/skeleton/apps/__app__/src/modules/i18n/messages/vi.json +6 -0
- package/templates/fe/{skeleton-app → skeleton}/apps/__app__/src/modules/i18n/request.ts +1 -0
- package/templates/fe/skeleton/apps/__app__/src/modules/routes/index.ts +4 -0
- package/templates/fe/{skeleton-app → skeleton}/apps/__app__/src/proxy.ts +1 -1
- package/sync/skeleton.mjs +0 -76
- package/templates/be/gitignore +0 -2
- package/templates/be/hooks/husky/pre-commit +0 -13
- package/templates/be/hooks/husky/pre-push +0 -6
- package/templates/be/package-scripts/package.json +0 -19
- package/templates/be/skeleton/scripts/.gitkeep +0 -0
- package/templates/be/skeleton/src/modules/platform/config/config.decorators.ts +0 -9
- package/templates/be/skeleton/src/modules/platform/errors/error.filter.ts +0 -20
- package/templates/be/tool-config/prettierignore +0 -8
- package/templates/fe/ci-workflows/github/workflows/ci.yml +0 -40
- package/templates/fe/gitignore +0 -3
- package/templates/fe/hooks/husky/pre-commit +0 -16
- package/templates/fe/hooks/husky/pre-push +0 -5
- package/templates/fe/package-scripts/package.json +0 -13
- package/templates/fe/parts/api-client.ts +0 -44
- package/templates/fe/parts/api-outcome.ts +0 -7
- package/templates/fe/quality-config/sonar-project.properties +0 -8
- package/templates/fe/skeleton/scripts/.gitkeep +0 -0
- package/templates/fe/skeleton-app/apps/__app__/src/modules/api/client.ts +0 -1
- package/templates/fe/skeleton-app/apps/__app__/src/modules/api/index.ts +0 -3
- package/templates/fe/skeleton-app/apps/__app__/src/modules/api/outcome.ts +0 -1
- package/templates/fe/skeleton-app/apps/__app__/src/modules/i18n/index.ts +0 -4
- package/templates/fe/skeleton-app/apps/__app__/src/modules/i18n/navigation.ts +0 -5
- package/templates/fe/skeleton-shared/apps/__app__/next.config.ts +0 -12
- package/templates/fe/skeleton-shared/apps/__app__/src/modules/api/index.ts +0 -2
- package/templates/fe/skeleton-shared/apps/__app__/src/modules/i18n/index.ts +0 -9
- package/templates/fe/skeleton-shared/apps/__app__/src/modules/i18n/request.ts +0 -5
- package/templates/fe/skeleton-shared/apps/__app__/src/proxy.ts +0 -5
- package/templates/fe/skeleton-shared/packages/__family__-api/package.json +0 -12
- package/templates/fe/skeleton-shared/packages/__family__-api/src/client.ts +0 -1
- package/templates/fe/skeleton-shared/packages/__family__-api/src/index.ts +0 -3
- package/templates/fe/skeleton-shared/packages/__family__-api/src/outcome.ts +0 -1
- package/templates/fe/skeleton-shared/packages/__family__-api/tsconfig.json +0 -5
- package/templates/fe/skeleton-shared/packages/__family__-i18n/package.json +0 -18
- package/templates/fe/skeleton-shared/packages/__family__-i18n/src/app.ts +0 -19
- package/templates/fe/skeleton-shared/packages/__family__-i18n/src/index.ts +0 -2
- package/templates/fe/skeleton-shared/packages/__family__-i18n/src/proxy.ts +0 -12
- package/templates/fe/skeleton-shared/packages/__family__-i18n/src/request.ts +0 -15
- package/templates/fe/skeleton-shared/packages/__family__-i18n/tsconfig.json +0 -5
- package/templates/fe/tool-config/prettierrc +0 -1
- /package/templates/{be → app}/ci-workflows/github/workflows/e2e.yml +0 -0
- /package/templates/{be → app}/starciwork.gitignore +0 -0
- /package/templates/{be → app}/tool-config/prettierrc +0 -0
- /package/templates/fe/{skeleton-app → skeleton}/apps/__app__/next.config.ts +0 -0
- /package/templates/fe/{skeleton-app → skeleton}/apps/__app__/src/modules/i18n/config.ts +0 -0
- /package/templates/fe/{skeleton-app → skeleton}/apps/__app__/src/modules/i18n/routing.ts +0 -0
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
# HFS slot manifest (owner-approved 2026-09-29, decisions 1-10 in the owner decisions record).
|
|
2
|
-
# The one place that says what may exist in a StarCi product repository and where.
|
|
2
|
+
# The one place that says what may exist in a StarCi product repository and where. A product is ONE app repository
|
|
3
|
+
# (hfs 4, manifest major 2): the app root holds the one package.json, lockfile and node_modules, the hfs.json of kind app,
|
|
4
|
+
# the CI, the hooks and .starciwork (slots of profile `app`); `be/` and `fe/` are its two sides, each laid out as the old
|
|
5
|
+
# standalone repository root (slots of profile be or fe, paths relative to the side folder). Every tracked path of every
|
|
3
6
|
# repository must match exactly one slot. Checks, lint factories, the architecture machine, templates and the why
|
|
4
7
|
# catalog READ this file through scripts/lib/hfs-slots.mjs; none of them hardcodes a path.
|
|
5
8
|
# Shape: modules/schemas/hfs-slots.schema.yaml. The repository side is hfs.json: modules/schemas/hfs-repo.schema.yaml.
|
|
6
|
-
schema: starci/hfs-slots@
|
|
7
|
-
version:
|
|
9
|
+
schema: starci/hfs-slots@2
|
|
10
|
+
version: 2.0.0
|
|
8
11
|
|
|
9
12
|
versioning:
|
|
10
|
-
# MAJOR.MINOR.PATCH of this manifest. A repository pins only the MAJOR in hfs.json ("hfs":
|
|
13
|
+
# MAJOR.MINOR.PATCH of this manifest. A repository pins only the MAJOR in hfs.json ("hfs": 2).
|
|
11
14
|
patch: wording, why text, examples; no check result can change.
|
|
12
15
|
minor: >-
|
|
13
16
|
add a slot (always presence optional or opt-in), add an app kind or protocol slot, add a rule at severity warn,
|
|
@@ -24,7 +27,7 @@ versioning:
|
|
|
24
27
|
|
|
25
28
|
# Field meaning, per slot.
|
|
26
29
|
# id stable name, never changes meaning inside a major.
|
|
27
|
-
# profiles be, fe
|
|
30
|
+
# profiles app (the app root; alone), or be, fe or both: the sides the slot exists in (its path is relative to the side folder).
|
|
28
31
|
# path pattern. <name> is one path segment (bound to the variable name), * and ? stay inside a segment,
|
|
29
32
|
# ** spans directories, {a,b} alternates. A pattern ending in / is a directory: it owns everything
|
|
30
33
|
# below it, except what a more specific slot owns. Otherwise it names files.
|
|
@@ -55,6 +58,12 @@ presenceValues: [required, optional, opt-in, forbidden]
|
|
|
55
58
|
trackedValues: [tracked, ignored, external]
|
|
56
59
|
testValues: [unit-beside, e2e, none]
|
|
57
60
|
|
|
61
|
+
# 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.
|
|
63
|
+
sides:
|
|
64
|
+
be: {reads: []}
|
|
65
|
+
fe: {reads: [be/contracts/]} # the back end's committed contract snapshots, the input of the front end's codegen
|
|
66
|
+
|
|
58
67
|
appKinds:
|
|
59
68
|
be: [api, worker, migrate, cli]
|
|
60
69
|
fe: [next]
|
|
@@ -158,25 +167,26 @@ ruleParams:
|
|
|
158
167
|
|
|
159
168
|
slots:
|
|
160
169
|
|
|
161
|
-
# -----
|
|
162
|
-
- id:
|
|
163
|
-
profiles: [
|
|
164
|
-
path:
|
|
170
|
+
# ----- app root (profile app: the one repository of a product) --------------------------------------------
|
|
171
|
+
- id: app.declaration
|
|
172
|
+
profiles: [app]
|
|
173
|
+
path: hfs.json
|
|
165
174
|
presence: required
|
|
166
175
|
tracked: tracked
|
|
167
176
|
tier: none
|
|
168
177
|
tests: none
|
|
169
|
-
rules: [
|
|
170
|
-
- id:
|
|
171
|
-
profiles: [
|
|
172
|
-
path:
|
|
178
|
+
rules: [HFS_ARCH_CONFIG_UNREAD]
|
|
179
|
+
- id: app.readme
|
|
180
|
+
profiles: [app]
|
|
181
|
+
path: README.md
|
|
173
182
|
presence: required
|
|
174
183
|
tracked: tracked
|
|
175
184
|
tier: none
|
|
176
185
|
tests: none
|
|
177
|
-
rules: [
|
|
178
|
-
- id:
|
|
179
|
-
profiles: [
|
|
186
|
+
rules: [HFS_README_*]
|
|
187
|
+
- id: app.package-manifest
|
|
188
|
+
profiles: [app]
|
|
189
|
+
# The one package.json of the app: every dependency of both sides and every script; npm workspaces only for fe/packages/*.
|
|
180
190
|
path: package.json
|
|
181
191
|
presence: required
|
|
182
192
|
tracked: tracked
|
|
@@ -184,53 +194,184 @@ slots:
|
|
|
184
194
|
tests: none
|
|
185
195
|
managedBy: package-scripts # the `scripts` block only; the rest of the file is the repository's
|
|
186
196
|
rules: [HFS_PACKAGE_MANAGER_MIXED, HFS_DEP_VERSION_SKEW, HFS_CANON_PIN_DRIFT, HFS_MANAGED_FILE_DRIFT]
|
|
187
|
-
- id:
|
|
188
|
-
profiles: [
|
|
197
|
+
- id: app.lockfile
|
|
198
|
+
profiles: [app]
|
|
189
199
|
path: package-lock.json
|
|
190
200
|
presence: required
|
|
191
201
|
tracked: tracked
|
|
192
202
|
tier: none
|
|
193
203
|
tests: none
|
|
194
204
|
rules: [HFS_PACKAGE_MANAGER_MIXED, HFS_DEP_VERSION_SKEW]
|
|
195
|
-
- id:
|
|
196
|
-
profiles: [
|
|
197
|
-
path:
|
|
205
|
+
- id: app.git-meta
|
|
206
|
+
profiles: [app]
|
|
207
|
+
path: "{.gitignore,.gitattributes}"
|
|
198
208
|
presence: required
|
|
199
209
|
tracked: tracked
|
|
200
210
|
tier: none
|
|
201
211
|
tests: none
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
212
|
+
rules: [HFS_GITIGNORE_BLOCK_DRIFT] # the marked block of .gitignore is rendered by hfs sync (templates/app/gitignore)
|
|
213
|
+
- id: app.tool-config
|
|
214
|
+
profiles: [app]
|
|
215
|
+
path: "{.editorconfig,.nvmrc}"
|
|
216
|
+
presence: required # plain dotfiles, the repository's own
|
|
217
|
+
tracked: tracked
|
|
218
|
+
tier: none
|
|
219
|
+
tests: none
|
|
220
|
+
- id: app.format-config
|
|
221
|
+
profiles: [app]
|
|
222
|
+
# The one formatter configuration of the app (prettier walks up from every file of both sides to it): .prettierrc names
|
|
223
|
+
# @starci/prettier-config, .prettierignore is the template.
|
|
224
|
+
path: "{.prettierrc,.prettierignore}"
|
|
207
225
|
presence: required
|
|
208
226
|
tracked: tracked
|
|
209
227
|
tier: none
|
|
210
228
|
tests: none
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
229
|
+
managedBy: tool-config
|
|
230
|
+
rules: [HFS_MANAGED_FILE_DRIFT, HFS_TOOL_CONFIG_LOCAL]
|
|
231
|
+
- id: app.tool-config-optional
|
|
232
|
+
profiles: [app]
|
|
233
|
+
path: .npmrc
|
|
234
|
+
presence: optional
|
|
235
|
+
tracked: tracked
|
|
236
|
+
tier: none
|
|
237
|
+
tests: none
|
|
238
|
+
- id: app.tool-config-local
|
|
239
|
+
profiles: [app]
|
|
240
|
+
path: "{tsconfig.json,.eslintrc,.eslintrc.*,.eslintignore,eslint.config.*,.stylelintrc,.stylelintrc.*,.stylelintignore,stylelint.config.*,.prettierrc.*,prettier.config.*,jest.config.*,lint-staged.config.*,.lintstagedrc*}"
|
|
241
|
+
presence: forbidden
|
|
242
|
+
tracked: external
|
|
243
|
+
tier: none
|
|
244
|
+
tests: none
|
|
245
|
+
goesTo: "nowhere at the app root: each side holds exactly its managed tool configuration (be.tool-config, fe.tool-config) and the root only the formatter (app.format-config)"
|
|
246
|
+
rules: [HFS_TOOL_CONFIG_LOCAL]
|
|
247
|
+
- id: app.quality-config
|
|
248
|
+
profiles: [app]
|
|
249
|
+
path: sonar-project.properties
|
|
215
250
|
presence: required
|
|
216
251
|
tracked: tracked
|
|
217
252
|
tier: none
|
|
218
253
|
tests: none
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
254
|
+
managedBy: quality-config
|
|
255
|
+
rules: [HFS_SONAR_CONFIG, HFS_MANAGED_FILE_DRIFT]
|
|
256
|
+
- id: app.hooks
|
|
257
|
+
profiles: [app]
|
|
258
|
+
path: ".husky/{pre-commit,pre-push}"
|
|
259
|
+
presence: required
|
|
224
260
|
tracked: tracked
|
|
225
261
|
tier: none
|
|
226
262
|
tests: none
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
263
|
+
managedBy: hooks
|
|
264
|
+
rules: [HFS_CI_E2E_AUTOMATIC, HFS_MANAGED_FILE_DRIFT]
|
|
265
|
+
- id: app.ci
|
|
266
|
+
profiles: [app]
|
|
267
|
+
path: ".github/workflows/ci.yml"
|
|
268
|
+
presence: required
|
|
269
|
+
tracked: tracked
|
|
270
|
+
tier: none
|
|
271
|
+
tests: none
|
|
272
|
+
managedBy: ci-workflows
|
|
273
|
+
rules: [HFS_CI_E2E_AUTOMATIC, HFS_CI_MISSING_CANON, HFS_MANAGED_FILE_DRIFT]
|
|
274
|
+
- id: app.ci-e2e
|
|
275
|
+
profiles: [app]
|
|
276
|
+
path: ".github/workflows/e2e.yml"
|
|
277
|
+
presence: optional # on: workflow_dispatch only
|
|
278
|
+
tracked: tracked
|
|
279
|
+
tier: none
|
|
280
|
+
tests: none
|
|
281
|
+
managedBy: ci-workflows
|
|
282
|
+
rules: [HFS_CI_E2E_AUTOMATIC, HFS_MANAGED_FILE_DRIFT]
|
|
283
|
+
- id: app.github-meta
|
|
284
|
+
profiles: [app]
|
|
285
|
+
path: ".github/{CODEOWNERS,pull_request_template.md,ISSUE_TEMPLATE/**,dependabot.yml}"
|
|
286
|
+
presence: optional
|
|
287
|
+
tracked: tracked
|
|
288
|
+
tier: none
|
|
289
|
+
tests: none
|
|
290
|
+
- id: app.scripts
|
|
291
|
+
profiles: [app]
|
|
292
|
+
# 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*`,
|
|
294
|
+
# 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
|
+
path: "scripts/{.gitkeep,*.mjs,*.cjs,*.ps1,*.sh}"
|
|
230
296
|
presence: optional
|
|
231
297
|
tracked: tracked
|
|
232
298
|
tier: none
|
|
233
299
|
tests: none
|
|
300
|
+
rules: [HFS_SCRIPT_ONE_OFF, BE_SPEC_PLACEMENT, HFS_REPO_LOCAL_CHECK] # one-off codemods and fix-* scripts are agent output, not tooling
|
|
301
|
+
- id: app.sides
|
|
302
|
+
profiles: [app]
|
|
303
|
+
# The two sides. Everything below be/ and fe/ is judged by the side's own slots (profiles be and fe) with the side folder as its root;
|
|
304
|
+
# this slot answers only for the side folders themselves.
|
|
305
|
+
path: "{be,fe}/"
|
|
306
|
+
presence: required
|
|
307
|
+
tracked: tracked
|
|
308
|
+
tier: none
|
|
309
|
+
tests: none
|
|
310
|
+
- id: app.starciwork
|
|
311
|
+
profiles: [app]
|
|
312
|
+
path: ".starciwork/"
|
|
313
|
+
presence: required
|
|
314
|
+
tracked: tracked
|
|
315
|
+
tier: none
|
|
316
|
+
tests: none
|
|
317
|
+
requires: [.gitignore, workspace.yaml, features/index.yaml]
|
|
318
|
+
allows: [workspace.yaml, "brand/**", shell/index.yaml, "features/<feature>/index.yaml",
|
|
319
|
+
"features/<feature>/<family>/<name>/index.yaml", "features/<feature>/br/<rule>/ac/<name>/index.yaml",
|
|
320
|
+
"features/<feature>/ui/<name>/assets/<approved-file>", "features/<feature>/uat/<name>/{index,accounts,fixtures}.yaml",
|
|
321
|
+
"features/<feature>/uat/<name>/{seed,cleanup}.sql", "_resources/{identities,environments,fixtures}/<slug>/resource.yaml"]
|
|
322
|
+
forbids: [evidence/, logs/, reports/, captures/, runs/, "kernel-*/", "*.sqlite*", _derived/, "work/node records"]
|
|
323
|
+
rules: [HFS_AGENT_DATA_TRACKED, HFS_WORK_NODE_RETIRED, HFS_IDENTITY_CUSTODY]
|
|
324
|
+
- id: app.build-output
|
|
325
|
+
profiles: [app]
|
|
326
|
+
path: "{**/node_modules/,coverage/,reports/,test-results/,**/*.tsbuildinfo}"
|
|
327
|
+
presence: optional
|
|
328
|
+
tracked: ignored
|
|
329
|
+
tier: none
|
|
330
|
+
tests: none
|
|
331
|
+
- id: app.tool-cache
|
|
332
|
+
profiles: [app]
|
|
333
|
+
path: "{.eslintcache,.turbo/,.scannerwork/,.sonar/,.jest-cache/,.cache/,.tools/}"
|
|
334
|
+
presence: forbidden
|
|
335
|
+
tracked: external
|
|
336
|
+
tier: none
|
|
337
|
+
tests: none
|
|
338
|
+
goesTo: "${STARCI_CACHE_HOME:-%LOCALAPPDATA%/StarCi/cache}/<repo>/<tool>/ (the canon presets set eslint --cache-location, jest cacheDirectory, turbo cacheDir, sonar.working.directory)"
|
|
339
|
+
- id: app.agent-output
|
|
340
|
+
profiles: [app]
|
|
341
|
+
path: "{report*.json,*.tmp.json,%*%,nul,.qwen*,.artifacts/,.tmp-*,*-lint.json,lf.json,lt.json,*.log,design-plans/,.gitmounts/,.dat,vi-flat.txt}"
|
|
342
|
+
presence: forbidden
|
|
343
|
+
tracked: external
|
|
344
|
+
tier: none
|
|
345
|
+
tests: none
|
|
346
|
+
goesTo: "agent scratchpad or the StarCi blob store (%LOCALAPPDATA%/StarCi/blobs), cited by {name, sha256}"
|
|
347
|
+
rules: [HFS_UNTRACKED_ROOT_ENTRY, HFS_AGENT_DATA_TRACKED]
|
|
348
|
+
- id: app.worktrees
|
|
349
|
+
profiles: [app]
|
|
350
|
+
path: "{.worktrees/,worktrees/,.starciwork/worktrees/}"
|
|
351
|
+
presence: forbidden
|
|
352
|
+
tracked: external
|
|
353
|
+
tier: none
|
|
354
|
+
tests: none
|
|
355
|
+
goesTo: "D:/starci-lanes/<project>/<lane>/ (outside every repository) for a lane; a runtime op worktree (.starciwork/worktrees/<op>) is git-excluded, never tracked, and removed when its op settles"
|
|
356
|
+
- id: app.plaintext-env
|
|
357
|
+
profiles: [app]
|
|
358
|
+
path: "{.env,.env.*,.secrets/,**/*.pem,**/*.key}"
|
|
359
|
+
presence: forbidden
|
|
360
|
+
tracked: external
|
|
361
|
+
tier: none
|
|
362
|
+
tests: none
|
|
363
|
+
goesTo: "be/.starcistacks/<env>/secrets/<slug>.enc; decrypted values only in memory or under %LOCALAPPDATA%/StarCi/secrets/<project>/<env>/ via *_FILE"
|
|
364
|
+
rules: [HFS_PLAINTEXT_SECRET]
|
|
365
|
+
|
|
366
|
+
# ----- side root (both sides): what the old standalone repository root held, less the app-root files ----------
|
|
367
|
+
- id: repo.side-root-forbidden
|
|
368
|
+
profiles: [be, fe]
|
|
369
|
+
path: "{package.json,package-lock.json,hfs.json,README.md,.gitignore,.gitattributes,.husky/,.github/,.starciwork/,sonar-project.properties,.prettierrc,.prettierignore,scripts/}"
|
|
370
|
+
presence: forbidden
|
|
371
|
+
tracked: external
|
|
372
|
+
tier: none
|
|
373
|
+
tests: none
|
|
374
|
+
goesTo: "the app root: one package.json, lockfile, hfs.json, README, git and CI files, hooks, formatter, Sonar configuration, scripts/ and .starciwork per app"
|
|
234
375
|
- id: be.tool-config
|
|
235
376
|
profiles: [be]
|
|
236
377
|
# Managed files (hfs sync renders them, hfs check compares them): the whole tool configuration of a back end. Each is
|
|
@@ -239,9 +380,10 @@ slots:
|
|
|
239
380
|
# specs import them); tsconfig.build.json puts an overlay preset after it and adds only the path-relative options a
|
|
240
381
|
# preset cannot hold; the tests' own src/tests/tsconfig.json (the nearest config of a world, integration, e2e or contract
|
|
241
382
|
# file, so typed lint and typecheck:tests both find it) extends it with the e2e preset; eslint.config.mjs is the
|
|
242
|
-
# one-line starciBeConfig call; jest.config.js calls the @starci/jest-preset factory
|
|
243
|
-
#
|
|
244
|
-
|
|
383
|
+
# one-line starciBeConfig call; jest.config.js calls the @starci/jest-preset factory (the root scripts run it with
|
|
384
|
+
# --config be/jest.config.js). The formatter configuration is the app's (app.format-config); every preset resolves from the
|
|
385
|
+
# app root node_modules.
|
|
386
|
+
path: "{tsconfig.json,tsconfig.build.json,src/tests/tsconfig.json,eslint.config.mjs,jest.config.js}"
|
|
245
387
|
presence: required
|
|
246
388
|
tracked: tracked
|
|
247
389
|
tier: none
|
|
@@ -270,9 +412,9 @@ slots:
|
|
|
270
412
|
# reference to a canon package, never a configuration: tsconfig.json extends @starci/tsconfig/next.json and only
|
|
271
413
|
# names the preset and nothing else (each app's own tsconfig.json, in fe.app.next, carries its aliases and include; there is
|
|
272
414
|
# no test tsconfig: a front end has no tests, FE_NO_TESTS); eslint.config.mjs is the one-line starciFeConfig call;
|
|
273
|
-
# stylelint.config.mjs is the one-line starciStylelintConfig call
|
|
274
|
-
#
|
|
275
|
-
path: "{tsconfig.json,eslint.config.mjs,stylelint.config.mjs
|
|
415
|
+
# stylelint.config.mjs is the one-line starciStylelintConfig call. The formatter configuration is the app's (app.format-config).
|
|
416
|
+
# A front end has no test configuration at all (FE_NO_TESTS).
|
|
417
|
+
path: "{tsconfig.json,eslint.config.mjs,stylelint.config.mjs}"
|
|
276
418
|
presence: required
|
|
277
419
|
tracked: tracked
|
|
278
420
|
tier: none
|
|
@@ -296,60 +438,6 @@ slots:
|
|
|
296
438
|
tests: none
|
|
297
439
|
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"
|
|
298
440
|
rules: [HFS_TOOL_CONFIG_LOCAL]
|
|
299
|
-
- id: repo.quality-config
|
|
300
|
-
profiles: [be, fe]
|
|
301
|
-
path: sonar-project.properties
|
|
302
|
-
presence: required
|
|
303
|
-
tracked: tracked
|
|
304
|
-
tier: none
|
|
305
|
-
tests: none
|
|
306
|
-
managedBy: quality-config
|
|
307
|
-
rules: [HFS_SONAR_CONFIG, HFS_MANAGED_FILE_DRIFT]
|
|
308
|
-
- id: repo.hooks
|
|
309
|
-
profiles: [be, fe]
|
|
310
|
-
path: ".husky/{pre-commit,pre-push}"
|
|
311
|
-
presence: required
|
|
312
|
-
tracked: tracked
|
|
313
|
-
tier: none
|
|
314
|
-
tests: none
|
|
315
|
-
managedBy: hooks
|
|
316
|
-
rules: [HFS_CI_E2E_AUTOMATIC, HFS_MANAGED_FILE_DRIFT]
|
|
317
|
-
- id: repo.ci
|
|
318
|
-
profiles: [be, fe]
|
|
319
|
-
path: ".github/workflows/ci.yml"
|
|
320
|
-
presence: required
|
|
321
|
-
tracked: tracked
|
|
322
|
-
tier: none
|
|
323
|
-
tests: none
|
|
324
|
-
managedBy: ci-workflows
|
|
325
|
-
rules: [HFS_CI_E2E_AUTOMATIC, HFS_CI_MISSING_CANON, HFS_MANAGED_FILE_DRIFT]
|
|
326
|
-
- id: repo.ci-e2e
|
|
327
|
-
profiles: [be]
|
|
328
|
-
path: ".github/workflows/e2e.yml"
|
|
329
|
-
presence: optional # on: workflow_dispatch only
|
|
330
|
-
tracked: tracked
|
|
331
|
-
tier: none
|
|
332
|
-
tests: none
|
|
333
|
-
managedBy: ci-workflows
|
|
334
|
-
rules: [HFS_CI_E2E_AUTOMATIC, HFS_MANAGED_FILE_DRIFT]
|
|
335
|
-
- id: repo.github-meta
|
|
336
|
-
profiles: [be, fe]
|
|
337
|
-
path: ".github/{CODEOWNERS,pull_request_template.md,ISSUE_TEMPLATE/**,dependabot.yml}"
|
|
338
|
-
presence: optional
|
|
339
|
-
tracked: tracked
|
|
340
|
-
tier: none
|
|
341
|
-
tests: none
|
|
342
|
-
- id: repo.scripts
|
|
343
|
-
profiles: [be, fe]
|
|
344
|
-
# The repository's operational scripts only (`*.mjs`, `*.cjs`, `*.ps1`, `*.sh`), or nothing: an empty folder is `scripts/.gitkeep`, which hfs sync --init writes
|
|
345
|
-
# when the folder is missing. A spec or a test (BE_SPEC_PLACEMENT, FE_NO_TESTS), a check or lint source (`check-*`, `eslint-local-rules*`, a local eslint
|
|
346
|
-
# plugin: HFS_REPO_LOCAL_CHECK) and anything that re-implements a canon check is not an operational script and has no place here.
|
|
347
|
-
path: "scripts/{.gitkeep,*.mjs,*.cjs,*.ps1,*.sh}"
|
|
348
|
-
presence: optional
|
|
349
|
-
tracked: tracked
|
|
350
|
-
tier: none
|
|
351
|
-
tests: none
|
|
352
|
-
rules: [HFS_SCRIPT_ONE_OFF, BE_SPEC_PLACEMENT, HFS_REPO_LOCAL_CHECK] # one-off codemods and fix-* scripts are agent output, not tooling
|
|
353
441
|
- id: repo.docs
|
|
354
442
|
profiles: [be, fe]
|
|
355
443
|
path: "docs/{adr,runbooks,guides}/**/*.md"
|
|
@@ -441,7 +529,7 @@ slots:
|
|
|
441
529
|
tracked: external
|
|
442
530
|
tier: none
|
|
443
531
|
tests: none
|
|
444
|
-
goesTo: "
|
|
532
|
+
goesTo: "be/.starcistacks/<env>/secrets/<slug>.enc; decrypted values only in memory or under %LOCALAPPDATA%/StarCi/secrets/<project>/<env>/ via *_FILE"
|
|
445
533
|
rules: [HFS_PLAINTEXT_SECRET]
|
|
446
534
|
- id: be.root-e2e
|
|
447
535
|
profiles: [be]
|
|
@@ -471,15 +559,6 @@ slots:
|
|
|
471
559
|
tests: none
|
|
472
560
|
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
|
|
473
561
|
rules: [HFS_CONTRACT_SNAPSHOT_DRIFT]
|
|
474
|
-
- id: fe.contract.copy
|
|
475
|
-
profiles: [fe]
|
|
476
|
-
path: "apps/<app>/src/modules/api/contract/<be-app>.{graphql,json}"
|
|
477
|
-
presence: opt-in
|
|
478
|
-
tracked: tracked
|
|
479
|
-
tier: none
|
|
480
|
-
tests: none
|
|
481
|
-
why: byte copy of the BE snapshot, refreshed by `npm run contract:pull`; the land gate compares hashes
|
|
482
|
-
rules: [HFS_CONTRACT_SNAPSHOT_DRIFT]
|
|
483
562
|
|
|
484
563
|
# ----- BE: apps (one slot per app kind; a new kind = a new slot, minor bump) ---------------------------
|
|
485
564
|
- id: be.app.api
|
|
@@ -739,7 +818,7 @@ slots:
|
|
|
739
818
|
presence: optional
|
|
740
819
|
tracked: tracked
|
|
741
820
|
tier: e2e
|
|
742
|
-
allows: [global-setup.ts, global-teardown.ts, use-test-world.ts, fakes/] # jest globalSetup/globalTeardown default-export (Jest API); plus root files with a role suffix (test-world-files, R47)
|
|
821
|
+
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)
|
|
743
822
|
tests: none
|
|
744
823
|
why: >-
|
|
745
824
|
the ONLY test infrastructure location, shared by integration and e2e: global-setup.ts starts the shared
|
|
@@ -827,20 +906,6 @@ slots:
|
|
|
827
906
|
goesTo: "src/tests/world/ (the only test infrastructure location; e2e/<area>/ holds only *.e2e-spec.ts)"
|
|
828
907
|
|
|
829
908
|
# ----- BE: work and stacks -----------------------------------------------------------------------------
|
|
830
|
-
- id: be.starciwork
|
|
831
|
-
profiles: [be]
|
|
832
|
-
path: ".starciwork/"
|
|
833
|
-
presence: required
|
|
834
|
-
tracked: tracked
|
|
835
|
-
tier: none
|
|
836
|
-
tests: none
|
|
837
|
-
requires: [.gitignore, workspace.yaml, features/index.yaml]
|
|
838
|
-
allows: [workspace.yaml, "brand/**", shell/index.yaml, "features/<feature>/index.yaml",
|
|
839
|
-
"features/<feature>/<family>/<name>/index.yaml", "features/<feature>/br/<rule>/ac/<name>/index.yaml",
|
|
840
|
-
"features/<feature>/ui/<name>/assets/<approved-file>", "features/<feature>/uat/<name>/{index,accounts,fixtures}.yaml",
|
|
841
|
-
"features/<feature>/uat/<name>/{seed,cleanup}.sql", "_resources/{identities,environments,fixtures}/<slug>/resource.yaml"]
|
|
842
|
-
forbids: [evidence/, logs/, reports/, captures/, runs/, "kernel-*/", "*.sqlite*", _derived/, "work/node records"]
|
|
843
|
-
rules: [HFS_AGENT_DATA_TRACKED, HFS_WORK_NODE_RETIRED, HFS_IDENTITY_CUSTODY]
|
|
844
909
|
- id: be.starcistacks
|
|
845
910
|
profiles: [be]
|
|
846
911
|
path: ".starcistacks/"
|
|
@@ -864,13 +929,13 @@ slots:
|
|
|
864
929
|
# ----- FE: apps ----------------------------------------------------------------------------------------
|
|
865
930
|
- id: fe.app.next
|
|
866
931
|
profiles: [fe]
|
|
867
|
-
path: "apps/<app>/{
|
|
932
|
+
path: "apps/<app>/{next.config.ts,tsconfig.json,postcss.config.mjs}"
|
|
868
933
|
appKind: next
|
|
869
934
|
presence: required
|
|
870
935
|
tracked: tracked
|
|
871
936
|
tier: none
|
|
872
937
|
minInstances: 1
|
|
873
|
-
requires: [
|
|
938
|
+
requires: [next.config.ts, tsconfig.json, postcss.config.mjs, src/app/, src/modules/i18n/,
|
|
874
939
|
"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"]
|
|
875
940
|
tests: none
|
|
876
941
|
rules: [FE_APP_ISOLATION, FE_ERROR_BOUNDARY_MISSING, FE_NEXT_CONVENTIONS]
|
|
@@ -977,7 +1042,7 @@ slots:
|
|
|
977
1042
|
# client.ts/outcome.ts (fe.transport.client/outcome) in a one-app repository, or the api package's
|
|
978
1043
|
# (fe.package.api.client/outcome) when the repository shares it (FE_TRANSPORT_OWNER, machine).
|
|
979
1044
|
requires: [index.ts]
|
|
980
|
-
allows: [index.ts, client.ts, outcome.ts, "<domain>/read-*.ts", "<domain>/*.graphql", "<domain>/<domain>.mapper.ts",
|
|
1045
|
+
allows: [index.ts, client.ts, outcome.ts, "<domain>/read-*.ts", "<domain>/*.graphql", "<domain>/<domain>.mapper.ts", __generated__/]
|
|
981
1046
|
roles: {entry: index.ts, reader: "read-*.ts"} # a reader is a server module (FE_CLIENT_REACHES_SERVER)
|
|
982
1047
|
tests: none
|
|
983
1048
|
rules: [FE_TRANSPORT_OWNER, FE_HTTP_STATUS_COLLAPSE, FE_WIRE_GENERATED]
|
|
@@ -1102,10 +1167,10 @@ slots:
|
|
|
1102
1167
|
# Checks that read this manifest (all ship from .claude; none keeps its own path list)
|
|
1103
1168
|
consumers:
|
|
1104
1169
|
- scripts/lib/hfs-slots.mjs # the loader every consumer below goes through
|
|
1105
|
-
- scripts/
|
|
1170
|
+
- scripts/lib/hfs-check.mjs # hfs check: HFS_SLOT_* / tracked / external, the side view per side
|
|
1106
1171
|
- scripts/checks/architecture.mjs # tiers, direction matrix, cycles, reachability
|
|
1107
1172
|
- packages/eslint/be starciBeConfig({hfs}) # file globs for rules come from slots
|
|
1108
1173
|
- packages/eslint/fe starciFeConfig({hfs})
|
|
1109
|
-
- packages/stylelint-canon
|
|
1174
|
+
- packages/stylelint # @starci/stylelint-canon: colour and brand allowances from fe.modules.brand
|
|
1110
1175
|
- packages/hfs/sync # renders managedBy templates (hfs sync) and judges them (hfs check)
|
|
1111
1176
|
- modules/kernel/failure-codes.yaml # every rule id listed above has a Vietnamese why
|
|
@@ -17,7 +17,7 @@ provenance:
|
|
|
17
17
|
- packages/grammar/src/**
|
|
18
18
|
limitations: |
|
|
19
19
|
Folder rules place code by responsibility. They do not prove rendered behavior or accessibility. Inspect the
|
|
20
|
-
selected
|
|
20
|
+
selected app's actual source, manifests, aliases and lint configuration. Verify installed Grammar
|
|
21
21
|
exports at @starci/grammar/common.
|
|
22
22
|
rules:
|
|
23
23
|
- id: FE-FOLDER-1
|
|
@@ -25,9 +25,9 @@ rules:
|
|
|
25
25
|
kind: mandatory
|
|
26
26
|
hfsRules: [R01, R02, R59, R64]
|
|
27
27
|
requirement: |
|
|
28
|
-
|
|
29
|
-
all product source lives under apps/<app>/src.
|
|
30
|
-
(apps/<app>/src) uses app/ for framework adapters; features/{pages,layouts,overlays} for route-facing product
|
|
28
|
+
The front end of every app (`fe/`) is an fe/apps/<app>/ monorepo even with one app, named by its role (for example
|
|
29
|
+
web), and all product source lives under fe/apps/<app>/src. An fe/src/ does not exist. Each Next source root
|
|
30
|
+
(fe/apps/<app>/src) uses app/ for framework adapters; features/{pages,layouts,overlays} for route-facing product
|
|
31
31
|
composition; components/{blocks,composites,branches,leaves} for product visuals; hooks/<domain> for React hooks;
|
|
32
32
|
and modules/<capability> for cohesive reusable technical and domain capabilities. Every app has the modules api,
|
|
33
33
|
config, i18n and routes, and one brand.css under modules/brand when it sets brand tokens. No other empty role
|
|
@@ -63,47 +63,46 @@ rules:
|
|
|
63
63
|
- instrumentation-client.mjs
|
|
64
64
|
- next-env.d.ts
|
|
65
65
|
frameworkPinnedRootExports:
|
|
66
|
-
# Export names the framework mandates in a pinned root file, keyed by file stem
|
|
67
|
-
#
|
|
68
|
-
# these files when they sit directly in a Next source root; everywhere else the name-shape rule is unchanged.
|
|
66
|
+
# Export names the framework mandates in a pinned root file, keyed by file stem: a naming review keeps exactly these
|
|
67
|
+
# names in exactly these files when they sit directly in a Next source root.
|
|
69
68
|
middleware: [config, middleware, default]
|
|
70
69
|
proxy: [config, proxy, default]
|
|
71
70
|
instrumentation: [register, onRequestError]
|
|
72
71
|
instrumentation-client: [onRouterTransitionStart]
|
|
73
72
|
rationale: |
|
|
74
|
-
One fixed source root per app keeps every
|
|
73
|
+
One fixed source root per app keeps every front end the same shape while preventing app helpers, component-local
|
|
75
74
|
hooks and feature-local transport buckets from becoming undeclared owners.
|
|
76
75
|
cases:
|
|
77
76
|
- id: case-1
|
|
78
77
|
when: "A routed authentication scenario"
|
|
79
|
-
write: "apps/<app>/src/features/pages/AuthenticationPage/index.tsx"
|
|
78
|
+
write: "fe/apps/<app>/src/features/pages/AuthenticationPage/index.tsx"
|
|
80
79
|
- id: case-2
|
|
81
80
|
when: "A reusable API client, reader or session capability"
|
|
82
81
|
write: >
|
|
83
|
-
apps/<app>/src/modules/api/ or apps/<app>/src/modules/session/ with one explicit public index.ts entry
|
|
82
|
+
fe/apps/<app>/src/modules/api/ or fe/apps/<app>/src/modules/session/ with one explicit public index.ts entry
|
|
84
83
|
- id: case-3
|
|
85
84
|
when: "A reusable product visual without scenario ownership"
|
|
86
|
-
write: "apps/<app>/src/components/{blocks,composites,branches,leaves}/<Name>/index.tsx"
|
|
85
|
+
write: "fe/apps/<app>/src/components/{blocks,composites,branches,leaves}/<Name>/index.tsx"
|
|
87
86
|
- id: case-4
|
|
88
87
|
when: "A React hook"
|
|
89
|
-
write: "apps/<app>/src/hooks/<domain>/useAutoScroll.ts; built-in React hook calls may remain in visuals"
|
|
88
|
+
write: "fe/apps/<app>/src/hooks/<domain>/useAutoScroll.ts; built-in React hook calls may remain in visuals"
|
|
90
89
|
- id: case-5
|
|
91
90
|
kind: exception
|
|
92
91
|
when: "Next.js only loads the file from the source root (proxy, instrumentation, instrumentation-client, next-env.d.ts)"
|
|
93
92
|
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
|
|
93
|
+
fe/apps/<app>/src/proxy.ts beside fe/apps/<app>/src/app/ as a thin adapter; locale routing logic lives in
|
|
94
|
+
fe/apps/<app>/src/modules/i18n/ and the adapter imports it; the names the framework mandates there
|
|
96
95
|
(frameworkPinnedRootExports, for example export const config = { matcher }) keep their framework spelling
|
|
97
96
|
- 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
|
|
97
|
+
when: "Any other file or folder directly under the source root (fe/apps/<app>/src/i18n/request.ts, fe/apps/<app>/src/config.ts), and an fe/src/"
|
|
99
98
|
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
|
|
99
|
+
Move it to its owner, for example fe/apps/<app>/src/modules/i18n/request.ts, and point framework configuration
|
|
100
|
+
(fe/apps/<app>/next.config.ts) at the new path
|
|
102
101
|
- id: case-7
|
|
103
102
|
when: "The per-slot data-status recipe"
|
|
104
103
|
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)
|
|
104
|
+
fe/apps/<app>/src/components/composites/SlotView/index.tsx, fe/apps/<app>/src/modules/slot/index.ts (Slot, toSlot,
|
|
105
|
+
SlotLabels) and fe/apps/<app>/src/hooks/slot/useSlotLabels.ts, one copy each (examples/shape-slot)
|
|
107
106
|
verification:
|
|
108
107
|
automated:
|
|
109
108
|
- HFS_SLOT_UNDECLARED
|
|
@@ -126,12 +125,12 @@ rules:
|
|
|
126
125
|
requirement: |
|
|
127
126
|
A split unit (a connected block, and a feature page, layout or overlay) uses index.tsx for the connected X and
|
|
128
127
|
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
|
|
130
|
-
|
|
128
|
+
(a shape that is a domain status is typed with that status, never a second name for it). Only that index.tsx imports
|
|
129
|
+
component.tsx. Pure blocks, composites, branches and leaves use index.tsx only and never gain an
|
|
131
130
|
empty twin. classNames.ts is present only when the unit owns class strings. Budgets: component.tsx 300 lines,
|
|
132
131
|
connected index.tsx 200 lines, at most 6 data hooks and 6 useState per connected unit.
|
|
133
132
|
rationale: |
|
|
134
|
-
Stable basenames make class
|
|
133
|
+
Stable basenames make class names and the connected and presentational split discoverable without forwarding
|
|
135
134
|
twins, and budgets keep a unit reviewable.
|
|
136
135
|
cases:
|
|
137
136
|
- id: case-1
|
|
@@ -210,7 +209,7 @@ rules:
|
|
|
210
209
|
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
210
|
file is a finding. Server-side readers (fetch and map, no React) live in modules/api/<domain>/read-*.ts and are
|
|
212
211
|
wrapped in React cache() when several blocks call them in one request. Generic client plumbing, the Outcome type,
|
|
213
|
-
the
|
|
212
|
+
the generated wire types (from be/contracts/, through the root codegen) live in modules/api; environment reading lives in modules/config; every
|
|
214
213
|
href builder lives in modules/routes; locale routing lives in modules/i18n; colour values live in modules/brand.
|
|
215
214
|
A helper name defined twice across hook files is a finding.
|
|
216
215
|
rationale: |
|
|
@@ -219,19 +218,19 @@ rules:
|
|
|
219
218
|
cases:
|
|
220
219
|
- id: case-1
|
|
221
220
|
when: "Authentication request lifecycle"
|
|
222
|
-
write: "apps/<app>/src/hooks/authentication/useAuthentication.ts"
|
|
221
|
+
write: "fe/apps/<app>/src/hooks/authentication/useAuthentication.ts"
|
|
223
222
|
- id: case-2
|
|
224
223
|
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"
|
|
224
|
+
write: "fe/apps/<app>/src/hooks/sales/sales.shared.ts, for example useSalesAccessToken's token reader"
|
|
226
225
|
- id: case-3
|
|
227
226
|
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"
|
|
227
|
+
write: "fe/apps/<app>/src/modules/api/sales/read-sales-summary.ts, exported through modules/api/index.ts"
|
|
229
228
|
- id: case-4
|
|
230
229
|
when: "The API client"
|
|
231
|
-
write: "apps/<app>/src/modules/api/client.ts and apps/<app>/src/modules/api/outcome.ts"
|
|
230
|
+
write: "fe/apps/<app>/src/modules/api/client.ts and fe/apps/<app>/src/modules/api/outcome.ts"
|
|
232
231
|
- id: case-5
|
|
233
232
|
when: "Intrinsic custom helper"
|
|
234
|
-
write: "apps/<app>/src/hooks/ui/useAutoScroll.ts; direct useRef and useState calls may remain in their visual owner"
|
|
233
|
+
write: "fe/apps/<app>/src/hooks/ui/useAutoScroll.ts; direct useRef and useState calls may remain in their visual owner"
|
|
235
234
|
verification:
|
|
236
235
|
automated:
|
|
237
236
|
- FE_HOOKS_ARE_HOOKS
|
|
@@ -248,21 +247,22 @@ rules:
|
|
|
248
247
|
kind: mandatory
|
|
249
248
|
hfsRules: [R63]
|
|
250
249
|
requirement: |
|
|
251
|
-
A shared package is packages/<pkg>/ with package.json (explicit exports), src/index.ts
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
250
|
+
A shared front-end package is fe/packages/<pkg>/ with package.json (explicit exports), src/index.ts and tsconfig.json,
|
|
251
|
+
built to dist, and no spec (the front end has no tests, FE_NO_TESTS). The design system is the runtime's own
|
|
252
|
+
`@starci/grammar` package (`.claude/packages/grammar`, installed from the npm registry): its components live under
|
|
253
|
+
packages/grammar/src/core/<tier>/<Name>/ of the runtime with collocated class strings and specs, and its family
|
|
254
|
+
entries and built-output proof stay at its package roots. See packages.yaml for the shared UI package shape.
|
|
255
255
|
rationale: |
|
|
256
256
|
Package layout separates primitives from app composition and keeps public entry verification against dist.
|
|
257
257
|
cases:
|
|
258
258
|
- id: case-1
|
|
259
|
-
when: "A Grammar component"
|
|
259
|
+
when: "A Grammar component (runtime package)"
|
|
260
260
|
write: "packages/grammar/src/core/<primitive|composite|branch|composition>/<Name>/index.tsx"
|
|
261
261
|
- id: case-2
|
|
262
|
-
when: "A Grammar component's class strings and spec"
|
|
262
|
+
when: "A Grammar component's class strings and spec (runtime package)"
|
|
263
263
|
write: "sibling classNames.ts and index.spec.tsx"
|
|
264
264
|
- id: case-3
|
|
265
|
-
when: "
|
|
265
|
+
when: "Grammar built-output proof (runtime package)"
|
|
266
266
|
write: "common/index.test.mjs, core/index.test.mjs, package-boundary.test.mjs (node:test against dist/)"
|
|
267
267
|
verification:
|
|
268
268
|
automated:
|
|
@@ -296,7 +296,7 @@ rules:
|
|
|
296
296
|
- id: case-4
|
|
297
297
|
when: "A deployment constant"
|
|
298
298
|
write: >
|
|
299
|
-
Never. process.env.NEXT_PUBLIC_* is read only in apps/<app>/src/modules/config and reached through that module,
|
|
299
|
+
Never. process.env.NEXT_PUBLIC_* is read only in fe/apps/<app>/src/modules/config and reached through that module,
|
|
300
300
|
with no localhost fallback; a missing value in production throws when the module loads.
|
|
301
301
|
verification:
|
|
302
302
|
automated:
|