@starci/eslint-canon-fe 8.3.0 → 8.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/callback-shape.mjs +344 -0
- package/client-boundary.mjs +1 -1
- package/docs/hooks-folder.md +1 -1
- package/env-owner.mjs +120 -9
- package/function-budget.mjs +49 -0
- package/hooks-folder.mjs +149 -18
- package/icon.mjs +67 -0
- package/index.mjs +47 -0
- package/lib/own-hook-origin.mjs +53 -0
- package/lib/program-for.mjs +22 -0
- package/lib/pure-owner-helper.mjs +148 -0
- package/lib/redux-hook-origin.mjs +44 -0
- package/next-conventions.mjs +38 -35
- package/package.json +1 -1
- package/runtime/knowledge/hfs/canon-pins.yaml +11 -11
- package/runtime/knowledge/hfs/fe-stack.yaml +22 -1
- package/runtime/knowledge/hfs/slots.yaml +344 -62
- package/runtime/knowledge/patterns/fe/folder.yaml +1 -0
- package/runtime/knowledge/sonar-gate.yaml +1 -1
- package/runtime/modules/models/runtimes.yaml +1 -1
- package/runtime/packages/hfs/templates/app/package-scripts/package.json +1 -0
- package/runtime/scripts/api/git/lib.mjs +3 -0
- package/runtime/scripts/api/git/merge-base.mjs +9 -0
- package/runtime/scripts/api/git/recorded-lines.mjs +54 -8
- package/runtime/scripts/hfs/architecture/backend-app-files.mjs +1 -8
- package/runtime/scripts/hfs/architecture/backend.mjs +2 -7
- package/runtime/scripts/hfs/architecture/client-reaches-server.mjs +6 -3
- package/runtime/scripts/hfs/architecture/dead-exports-bindings.mjs +149 -0
- package/runtime/scripts/hfs/architecture/dead-exports.mjs +9 -41
- package/runtime/scripts/hfs/architecture/default-deny.mjs +8 -6
- package/runtime/scripts/hfs/architecture/frontend-world.mjs +1 -1
- package/runtime/scripts/hfs/architecture/function-spec-providers.mjs +153 -0
- package/runtime/scripts/hfs/architecture/hfs.mjs +3 -3
- package/runtime/scripts/hfs/architecture/module-per-transport.mjs +28 -13
- package/runtime/scripts/hfs/architecture/next-data-inference.mjs +176 -0
- package/runtime/scripts/hfs/architecture/next-data-symbols.mjs +26 -0
- package/runtime/scripts/hfs/architecture/next-data.mjs +31 -23
- package/runtime/scripts/hfs/architecture/reachability.mjs +14 -0
- package/runtime/scripts/hfs/architecture/register-once.mjs +2 -2
- package/runtime/scripts/hfs/architecture/source-names.mjs +48 -3
- package/runtime/scripts/hfs/architecture/supabase-ast.mjs +211 -11
- package/runtime/scripts/hfs/architecture/supabase-be.mjs +1 -1
- package/runtime/scripts/hfs/architecture/supabase.mjs +59 -21
- package/runtime/scripts/hfs/architecture/surface.mjs +2 -1
- package/runtime/scripts/hfs/architecture/tiers.mjs +3 -3
- package/runtime/scripts/hfs/architecture/unit-spec-providers.mjs +8 -2
- package/runtime/scripts/hfs/declaration-shape.mjs +39 -3
- package/runtime/scripts/hfs/edition-slots.mjs +2 -2
- package/runtime/scripts/hfs/manifest-shape.mjs +67 -7
- package/runtime/scripts/hfs/project-rule.mjs +3 -1
- package/runtime/scripts/hfs/rule-catalog.mjs +156 -11
- package/runtime/scripts/hfs/rule-params-shape.mjs +22 -10
- package/runtime/scripts/hfs/rules/secrets.mjs +13 -0
- package/runtime/scripts/hfs/slot-imports.mjs +18 -5
- package/runtime/scripts/hfs/slots.mjs +1 -0
- package/runtime/scripts/lib/env.mjs +20 -0
- package/runtime/scripts/lib/event-contract.mjs +107 -0
- package/runtime/scripts/lib/json-clone.mjs +61 -0
- package/runtime/scripts/lib/list.mjs +4 -0
- package/runtime/scripts/lib/secret-patterns.mjs +60 -5
- package/runtime/scripts/lib/stack-services.mjs +1 -1
- package/runtime/scripts/lib/ts-analysis.mjs +5 -2
- package/runtime/scripts/lib/ts-ast.mjs +27 -2
- package/served-locale.mjs +80 -9
- package/size-growth.mjs +10 -6
- package/swr-family.mjs +197 -0
- package/translation.mjs +135 -7
- package/transport.mjs +126 -59
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
# catalog READ this file through scripts/hfs/slots.mjs; none of them hardcodes a path.
|
|
8
8
|
# Shape: modules/schemas/hfs-slots.schema.yaml. The repository side is hfs.json: modules/schemas/hfs-repo.schema.yaml.
|
|
9
9
|
schema: starci/hfs-slots@3
|
|
10
|
-
version: 3.
|
|
10
|
+
version: 3.3.0
|
|
11
11
|
|
|
12
12
|
versioning:
|
|
13
13
|
# MAJOR.MINOR.PATCH of this manifest. A repository pins only the MAJOR in hfs.json ("hfs": 3).
|
|
@@ -25,8 +25,8 @@ versioning:
|
|
|
25
25
|
# overlays and `provider`, and `sides.<side>.reads` of an app-root tree) and the lite slots; an app without `edition` in its
|
|
26
26
|
# hfs.json is edition full and every answer for it is unchanged.
|
|
27
27
|
# 2.2.0 adds the declaration-only fe.modules.types.ambient owner.
|
|
28
|
-
# 3.0.0 is a major (owner
|
|
29
|
-
#
|
|
28
|
+
# 3.0.0 is a major (ruling:R212/case-2 (owner:lite-production-swarm) (with the Work boundary amended by ruling:R212/case-4)): lite is the full code structure deployed with Supabase backing services
|
|
29
|
+
# and without e2e, and the front end is one standard for both editions (knowledge/hfs/fe-stack.yaml). The
|
|
30
30
|
# front-end database slots and the Supabase realtime feature slot are removed (Supabase on the front end is auth only, realtime
|
|
31
31
|
# is the socket.io gateway in both editions), fe.route holds pages, layouts and the one health/live route handler, and the slots
|
|
32
32
|
# marked `since: 3.0.0` are added: the Apollo, SWR, RHF, zustand and socket.io hooks, redux, modules/auth, modules/toast, the
|
|
@@ -37,6 +37,7 @@ versioning:
|
|
|
37
37
|
# for fail-fast env parsing only, and `<app>.options.ts` holds env-derived options and port bindings only, with no parsing
|
|
38
38
|
# or environment reads (BE_APP_OPTIONS_COMPOSITION). Secrets use EnvSource.secret(): <NAME>_FILE first, plain <NAME> only
|
|
39
39
|
# when the file key is absent; .starcistacks owns custody. Capability <capability>.config.ts roles stay unchanged.
|
|
40
|
+
# 3.3.0 is a minor (alpha.16): it ADDS the optional app.ci-stack-deploy slot, the managed workflow of the starci app stack verbs.
|
|
40
41
|
|
|
41
42
|
# Field meaning, per slot.
|
|
42
43
|
# id stable name, never changes meaning inside a major.
|
|
@@ -83,16 +84,16 @@ versioning:
|
|
|
83
84
|
# liteManagedBy is managed only in lite; its effective lite.path still lists literal files.
|
|
84
85
|
# the managed file list lives here and nowhere in code. A difference is HFS_MANAGED_FILE_DRIFT (a
|
|
85
86
|
# back end's tsconfig.json: HFS_TS_STRICT when a flag or an option was added).
|
|
86
|
-
# rules rule
|
|
87
|
+
# rules rule codes from knowledge/hfs/rules.yaml.
|
|
87
88
|
# editions the editions (below) the slot exists in; default all of them. A slot outside the declared edition owns
|
|
88
89
|
# no path - under lite, what full keeps absent is removed, and what lite forbids is a finding.
|
|
89
90
|
# litePresence the presence under edition lite (default: presence): a bare value, or {be, fe} for a both-sides slot.
|
|
90
91
|
# A slot forbidden in lite is external there and must name its goesTo. Only infrastructure is forbidden in lite:
|
|
91
|
-
# the
|
|
92
|
-
#
|
|
92
|
+
# database/broker services, the e2e layer and its workflow, and the browser journey. Lite may have contract specs.
|
|
93
|
+
# editionWhy why the slot's presence, shape or adapter differs between lite and full, with the owning ruling citation.
|
|
93
94
|
# lite the fields the slot carries under lite only (path, requires, allows, forbids, minInstances,
|
|
94
95
|
# requiredInstances, why), each shaped as the same field in the slot body; every other field is the edition's.
|
|
95
|
-
# `why` under lite names the
|
|
96
|
+
# `why` under lite names the adapter behind the slot's ports (pgmq, Postgres locks, in-process socket.io).
|
|
96
97
|
# `tests` is never overlaid: a lite slot owes exactly the unit specs its full slot owes.
|
|
97
98
|
# provider the hfs.json connections[].provider that enables this opt-in slot (supabase); never an app kind.
|
|
98
99
|
# since the manifest version that introduced the slot.
|
|
@@ -100,17 +101,18 @@ presenceValues: [required, optional, opt-in, forbidden]
|
|
|
100
101
|
trackedValues: [tracked, ignored, external]
|
|
101
102
|
testValues: [unit-beside, e2e, none]
|
|
102
103
|
|
|
103
|
-
# The editions a repository declares in hfs.json `edition` (absent means full).
|
|
104
|
-
# the full architecture
|
|
104
|
+
# The editions a repository declares in hfs.json `edition` (absent means full). ruling:R212/case-2 (owner:lite-production-swarm) (with the Work boundary amended by ruling:R212/case-4): lite IS
|
|
105
|
+
# the full architecture with Supabase backing services - the same monorepo, slots, CQRS shape and patterns (queue,
|
|
105
106
|
# event bus, saga, reactors, projection, realtime, webhooks, fenced job), declared in hfs.json the same way, and the same unit
|
|
106
107
|
# specs (unitRoles, logicRoles, 100 per file, @starci/jest-preset doubles, builders). What differs is ADAPTERS and DELIVERY, all
|
|
107
|
-
# on Supabase: schema authority in supabase/migrations,
|
|
108
|
-
#
|
|
109
|
-
#
|
|
110
|
-
#
|
|
108
|
+
# on Supabase except realtime: schema authority in supabase/migrations, queue and event-bus ports on Supabase Queues (pgmq),
|
|
109
|
+
# cache and locks on Postgres, schedules on pg_cron (+ pg_net), storage and auth on Supabase. Realtime uses the socket.io gateway
|
|
110
|
+
# in the api process, one instance and no Redis adapter. Integration and provider sandbox contract specs are optional;
|
|
111
|
+
# declared patterns owe their integration proofs (R284). Lite has no e2e world. Production uses declared docker-swarm under
|
|
112
|
+
# .starcistacks/production/infra/swarm/ (owner ruling 2026-10-14); no self-hosted database or broker. The front end is full's (R97).
|
|
111
113
|
# Lite is this same manifest filtered: a slot whose `editions` lacks lite does not exist for a lite app, `litePresence` replaces
|
|
112
114
|
# presence and a `lite` map replaces the fields it names. Upgrade lite -> full adds only (swap the adapters, add the e2e and
|
|
113
|
-
#
|
|
115
|
+
# self-hosted backing services); downgrade is not supported. Lite contracts are allowed (owner decision 2026-10-14).
|
|
114
116
|
editions: [full, lite]
|
|
115
117
|
|
|
116
118
|
# 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
|
|
@@ -162,12 +164,12 @@ tiers:
|
|
|
162
164
|
package: {mayImport: [package]}
|
|
163
165
|
fe:
|
|
164
166
|
route: {mayImport: [feature, foundation, modules, transport, package]} # app/** adapters
|
|
165
|
-
feature: {mayImport: [feature, components, hooks, foundation, modules,
|
|
167
|
+
feature: {mayImport: [feature, components, hooks, foundation, modules, package], mayImportKinds: {feature: {from: [pages, layouts], to: [overlays]}}} # pages and layouts compose overlays and blocks; data goes through hooks/swr
|
|
166
168
|
# A component never reaches data: config, routes, i18n and types are foundation; every other module and the api
|
|
167
169
|
# transport are reached through a hook (owner decision 2026-09-29).
|
|
168
170
|
components: {mayImport: [components, hooks, foundation, package], lowerLayerOnly: true} # blocks, composites, branches, leaves
|
|
169
171
|
# Across domains a hook is imported only through the other domain's public index (hooks/<domain>/index.ts), never deep.
|
|
170
|
-
hooks: {mayImport: [hooks, foundation, modules, transport, package]}
|
|
172
|
+
hooks: {mayImport: [hooks, foundation, modules, transport, package], mayImportSlots: {transport: {from: [fe.hooks.swr]}}} # only hooks/swr imports an operation
|
|
171
173
|
foundation: {mayImport: [foundation, package], acyclic: true} # modules/{config,i18n,routes,types}
|
|
172
174
|
modules: {mayImport: [foundation, modules, transport, package], acyclic: true}
|
|
173
175
|
transport: {mayImport: [foundation, modules, package]} # modules/api: the one fetch
|
|
@@ -178,10 +180,16 @@ crossApp: apps never import each other; shared code is a packages/<pkg> slot.
|
|
|
178
180
|
# Parameters the lint factories and checks read per profile (through ruleParams(profile) in scripts/hfs/slots.mjs, which
|
|
179
181
|
# merges common under the profile: a side restates a common key only to override it).
|
|
180
182
|
ruleParams:
|
|
183
|
+
db:
|
|
184
|
+
# R259: btree_gist supplies UUID equality for the booking interval exclusion constraint.
|
|
185
|
+
# R259: unaccent is a first-party Supabase extension used by Vietnamese-content search functions.
|
|
186
|
+
extensions: [pgmq, pg_cron, pgcrypto, pg_trgm, vector, uuid-ossp, pg_net, supabase_vault, unaccent, btree_gist]
|
|
181
187
|
common:
|
|
182
188
|
# HFS_SIZE_GROWTH: a file above soft may not grow against its parent commit; a new file stays within soft. Slot
|
|
183
189
|
# budgets (component.tsx 300, hooks 200, modules 400) are stricter than this cap where they apply.
|
|
184
190
|
fileLines: {soft: 500, hardGrowth: true}
|
|
191
|
+
functionLines: 80
|
|
192
|
+
featureFiles: 250
|
|
185
193
|
# R21 HFS_DUPLICATE_CODE: the one threshold of duplicate code of both sides (the architecture machine reads it here;
|
|
186
194
|
# no other file states it): a token-normalised block of at least `lines` source lines and `tokens` tokens that appears twice.
|
|
187
195
|
duplicateBlock: {lines: 8, tokens: 60}
|
|
@@ -328,7 +336,7 @@ ruleParams:
|
|
|
328
336
|
# Edition lite (ruleParams(manifest, 'be', 'lite') merges these keys over the base; a map merges one level deep): the schema
|
|
329
337
|
# authority is the Supabase migration tree (supabase/migrations/*.sql, forward-only), so a lite back end holds no TypeORM
|
|
330
338
|
# migration or entity under be/ (R34/R35 read schemaAuthority; be.persistence's lite overlay allows neither). The unit roles,
|
|
331
|
-
# logic roles and spec doubles are full's (owner ruling 2026-10-12). infraOwners: the self-hosted libraries are owned nowhere -
|
|
339
|
+
# logic roles and spec doubles are full's (owner ruling 2026-10-12 (owner:lite-production-swarm), with the Work boundary amended by ruling:R212/case-4). infraOwners: the self-hosted libraries are owned nowhere -
|
|
332
340
|
# platform/queue and platform/event-bus run Supabase Queues (pgmq) through the shared EntityManager, cache and locks are
|
|
333
341
|
# Postgres (advisory locks or an unlogged table), and realtime is the same socket.io gateway as full, run inside the api
|
|
334
342
|
# process with no Redis adapter (owner ruling 2026-10-13).
|
|
@@ -347,6 +355,7 @@ slots:
|
|
|
347
355
|
|
|
348
356
|
# ----- app root (profile app: the one repository of a product) --------------------------------------------
|
|
349
357
|
- id: app.declaration
|
|
358
|
+
since: 3.1.0
|
|
350
359
|
profiles: [app]
|
|
351
360
|
path: hfs.json
|
|
352
361
|
presence: required
|
|
@@ -355,6 +364,7 @@ slots:
|
|
|
355
364
|
tests: none
|
|
356
365
|
rules: [HFS_ARCH_CONFIG_UNREAD]
|
|
357
366
|
- id: app.readme
|
|
367
|
+
since: 3.1.0
|
|
358
368
|
profiles: [app]
|
|
359
369
|
path: README.md
|
|
360
370
|
presence: required
|
|
@@ -363,7 +373,9 @@ slots:
|
|
|
363
373
|
tests: none
|
|
364
374
|
rules: [HFS_README_*]
|
|
365
375
|
- id: app.package-manifest
|
|
376
|
+
since: 3.1.0
|
|
366
377
|
profiles: [app]
|
|
378
|
+
editionWhy: "ruling:R212/case-2 (with the Work boundary amended by ruling:R212/case-4): lite manages Supabase and wiring scripts; full also manages e2e and stack test scripts. Both may run optional contracts."
|
|
367
379
|
# The one package.json of the app, at the app root: be's dependencies and every tool, and every script; npm workspaces are
|
|
368
380
|
# exactly fe/apps/* and fe/packages/* (HFS_MONO_WORKSPACES), each with its own package.json. be has no package.json of its own.
|
|
369
381
|
path: package.json
|
|
@@ -375,6 +387,7 @@ slots:
|
|
|
375
387
|
liteManagedBy: package-scripts-lite
|
|
376
388
|
rules: [HFS_PACKAGE_MANAGER_MIXED, HFS_DEP_VERSION_SKEW, HFS_CANON_PIN_DRIFT, HFS_MANAGED_FILE_DRIFT, HFS_MONO_WORKSPACES, HFS_MONO_WORKSPACE_DEP]
|
|
377
389
|
- id: app.task-graph
|
|
390
|
+
since: 3.1.0
|
|
378
391
|
profiles: [app]
|
|
379
392
|
# The turbo task graph of the app (build, dev, lint, typecheck over the fe workspaces; build depends on ^build and caches
|
|
380
393
|
# .next and dist): rendered by starci app sync from templates/app/task-graph/turbo.json, judged for drift by starci app check. turbo needs
|
|
@@ -387,6 +400,7 @@ slots:
|
|
|
387
400
|
managedBy: task-graph
|
|
388
401
|
rules: [HFS_MANAGED_FILE_DRIFT, HFS_MONO_WORKSPACES]
|
|
389
402
|
- id: app.lockfile
|
|
403
|
+
since: 3.1.0
|
|
390
404
|
profiles: [app]
|
|
391
405
|
path: package-lock.json
|
|
392
406
|
presence: required
|
|
@@ -395,22 +409,42 @@ slots:
|
|
|
395
409
|
tests: none
|
|
396
410
|
rules: [HFS_PACKAGE_MANAGER_MIXED, HFS_DEP_VERSION_SKEW]
|
|
397
411
|
- id: app.git-meta
|
|
412
|
+
since: 3.1.0
|
|
398
413
|
profiles: [app]
|
|
399
|
-
path:
|
|
414
|
+
path: .gitignore
|
|
400
415
|
presence: required
|
|
401
416
|
tracked: tracked
|
|
402
417
|
tier: none
|
|
403
418
|
tests: none
|
|
404
419
|
rules: [HFS_GITIGNORE_BLOCK_DRIFT] # the marked block of .gitignore is rendered by starci app sync (templates/app/gitignore)
|
|
420
|
+
- id: app.gitattributes
|
|
421
|
+
since: 3.2.0
|
|
422
|
+
profiles: [app]
|
|
423
|
+
path: .gitattributes
|
|
424
|
+
presence: required
|
|
425
|
+
tracked: tracked
|
|
426
|
+
tier: none
|
|
427
|
+
tests: none
|
|
428
|
+
managedBy: skeleton
|
|
429
|
+
liteManagedBy: skeleton-lite
|
|
430
|
+
editionWhy: "Lite renders the same managed file from skeleton-lite because the editions share one code structure while their adapters differ (ruling:R212/case-2, with Work amended by ruling:R212/case-4 and hosting by owner:lite-production-swarm)."
|
|
431
|
+
rules: [HFS_MANAGED_FILE_DRIFT]
|
|
405
432
|
- id: app.tool-config
|
|
433
|
+
since: 3.1.0
|
|
406
434
|
profiles: [app]
|
|
407
435
|
path: "{.editorconfig,.nvmrc}"
|
|
408
|
-
presence: required
|
|
436
|
+
presence: required
|
|
409
437
|
tracked: tracked
|
|
410
438
|
tier: none
|
|
411
439
|
tests: none
|
|
440
|
+
managedBy: skeleton
|
|
441
|
+
liteManagedBy: skeleton-lite
|
|
442
|
+
editionWhy: "Lite renders the same managed files from skeleton-lite because the editions share one code structure while their adapters differ (ruling:R212/case-2, with Work amended by ruling:R212/case-4 and hosting by owner:lite-production-swarm)."
|
|
443
|
+
rules: [HFS_MANAGED_FILE_DRIFT]
|
|
412
444
|
- id: app.format-config
|
|
445
|
+
since: 3.1.0
|
|
413
446
|
profiles: [app]
|
|
447
|
+
editionWhy: "ruling:R212/case-2 (with the Work boundary amended by ruling:R212/case-4): lite additionally ignores Supabase-generated row types when formatting; full has its TypeORM schema authority."
|
|
414
448
|
# The one formatter configuration of the app (prettier walks up from every file of both sides to it): .prettierrc names
|
|
415
449
|
# @starci/prettier-config, .prettierignore is the template.
|
|
416
450
|
path: "{.prettierrc,.prettierignore}"
|
|
@@ -422,6 +456,7 @@ slots:
|
|
|
422
456
|
liteManagedBy: tool-config-lite # the same files plus the generated supabase/types, which the Supabase CLI writes unformatted
|
|
423
457
|
rules: [HFS_MANAGED_FILE_DRIFT, HFS_TOOL_CONFIG_LOCAL]
|
|
424
458
|
- id: app.tool-config-optional
|
|
459
|
+
since: 3.1.0
|
|
425
460
|
profiles: [app]
|
|
426
461
|
path: "{.npmrc,.npmignore}" # .npmignore: npm reads the nearest ignore file instead of the root files negations, so an app that ships inside another package keeps its sealed credentials out of the tarball here
|
|
427
462
|
presence: optional
|
|
@@ -429,6 +464,7 @@ slots:
|
|
|
429
464
|
tier: none
|
|
430
465
|
tests: none
|
|
431
466
|
- id: app.tool-config-local
|
|
467
|
+
since: 3.1.0
|
|
432
468
|
profiles: [app]
|
|
433
469
|
path: "{tsconfig.json,.eslintrc,.eslintrc.*,.eslintignore,eslint.config.*,.stylelintrc,.stylelintrc.*,.stylelintignore,stylelint.config.*,.prettierrc.*,prettier.config.*,jest.config.*,lint-staged.config.*,.lintstagedrc*}"
|
|
434
470
|
presence: forbidden
|
|
@@ -438,6 +474,7 @@ slots:
|
|
|
438
474
|
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)"
|
|
439
475
|
rules: [HFS_TOOL_CONFIG_LOCAL]
|
|
440
476
|
- id: app.quality-config
|
|
477
|
+
since: 3.1.0
|
|
441
478
|
profiles: [app]
|
|
442
479
|
path: "{sonar-project.properties,codecov.yml}"
|
|
443
480
|
presence: required
|
|
@@ -447,6 +484,7 @@ slots:
|
|
|
447
484
|
managedBy: quality-config # lite renders the same files: the same coverage scope, Sonar import and Codecov flags as full
|
|
448
485
|
rules: [HFS_SONAR_CONFIG, HFS_MANAGED_FILE_DRIFT]
|
|
449
486
|
- id: app.hooks
|
|
487
|
+
since: 3.1.0
|
|
450
488
|
profiles: [app]
|
|
451
489
|
path: ".husky/{pre-commit,pre-push}"
|
|
452
490
|
presence: required
|
|
@@ -456,7 +494,9 @@ slots:
|
|
|
456
494
|
managedBy: hooks
|
|
457
495
|
rules: [HFS_CI_E2E_AUTOMATIC, HFS_MANAGED_FILE_DRIFT]
|
|
458
496
|
- id: app.ci
|
|
497
|
+
since: 3.1.0
|
|
459
498
|
profiles: [app]
|
|
499
|
+
editionWhy: "ruling:R212/case-2 (with the Work boundary amended by ruling:R212/case-4): lite CI proves boot through R272 wiring and checks Supabase artifacts; full also provides its e2e lane."
|
|
460
500
|
path: ".github/workflows/ci.yml"
|
|
461
501
|
presence: required
|
|
462
502
|
tracked: tracked
|
|
@@ -466,18 +506,22 @@ slots:
|
|
|
466
506
|
liteManagedBy: ci-workflows-lite
|
|
467
507
|
rules: [HFS_CI_E2E_AUTOMATIC, HFS_CI_MISSING_CANON, HFS_MANAGED_FILE_DRIFT]
|
|
468
508
|
- id: app.ci-e2e
|
|
509
|
+
since: 3.1.0
|
|
469
510
|
profiles: [app]
|
|
511
|
+
editionWhy: "ruling:R212/case-2 (with the Work boundary amended by ruling:R212/case-4): lite has no e2e world; R272 supplies its wiring proof, while optional contracts run separately."
|
|
470
512
|
path: ".github/workflows/e2e.yml"
|
|
471
513
|
presence: optional # on: workflow_dispatch only
|
|
472
|
-
litePresence: forbidden #
|
|
514
|
+
litePresence: forbidden # ruling:R212/case-2 (with the Work boundary amended by ruling:R212/case-4): lite has no e2e world; unit specs and R272 wiring run in ci.yml
|
|
473
515
|
tracked: tracked
|
|
474
516
|
tier: none
|
|
475
517
|
tests: none
|
|
476
|
-
goesTo: "the full edition: a lite app has no e2e
|
|
518
|
+
goesTo: "the full edition: a lite app has no e2e world; its unit specs and wiring proof run in .github/workflows/ci.yml; contract specs use test:contract"
|
|
477
519
|
managedBy: ci-workflows
|
|
478
520
|
rules: [HFS_CI_E2E_AUTOMATIC, HFS_MANAGED_FILE_DRIFT]
|
|
479
521
|
- id: app.ci-images
|
|
522
|
+
since: 3.1.0
|
|
480
523
|
profiles: [app]
|
|
524
|
+
editionWhy: "ruling:R212/case-2 (with the Work boundary amended by ruling:R212/case-4): lite renders its Supabase-backed image workflow; full renders images for its separately deployed services."
|
|
481
525
|
# Builds every image of the app (one matrix entry per declared app, path-filtered), never pushes: rendered by starci app sync.
|
|
482
526
|
path: ".github/workflows/images.yml"
|
|
483
527
|
presence: required
|
|
@@ -489,6 +533,10 @@ slots:
|
|
|
489
533
|
rules: [HFS_MANAGED_FILE_DRIFT]
|
|
490
534
|
- id: app.ci-db
|
|
491
535
|
profiles: [app]
|
|
536
|
+
exemplifiedBy:
|
|
537
|
+
- packages/hfs/templates/app/ci-workflows-lite/github/workflows/db-deploy.yml
|
|
538
|
+
- {path: tests/fixtures/hfs-callback-full/.github/workflows/db-deploy.yml, edition: full}
|
|
539
|
+
editionWhy: "ruling:R212/case-2 (with the Work boundary amended by ruling:R212/case-4): lite manages its Supabase database deploy workflow; full owns an optional provider-specific workflow."
|
|
492
540
|
# The database deploy workflow: workflow_dispatch only (like app.ci-e2e); starci app sync renders it under the managed ci group.
|
|
493
541
|
path: ".github/workflows/db-deploy.yml"
|
|
494
542
|
presence: optional
|
|
@@ -498,7 +546,21 @@ slots:
|
|
|
498
546
|
liteManagedBy: ci-workflows-lite
|
|
499
547
|
rules: [HFS_MANAGED_FILE_DRIFT]
|
|
500
548
|
since: 2.1.0
|
|
549
|
+
- id: app.ci-stack-deploy
|
|
550
|
+
since: 3.3.0
|
|
551
|
+
profiles: [app]
|
|
552
|
+
editionWhy: "ruling:R212/case-2 (with the Work boundary amended by ruling:R212/case-4): both editions deploy their images to a Docker Swarm stack through the same starci stack verbs; the workflow lists the images the app declares."
|
|
553
|
+
# The Swarm stack deploy workflow: workflow_dispatch only (like app.ci-db); a thin file that calls `starci app stack`, rendered by starci app sync.
|
|
554
|
+
path: ".github/workflows/stack-deploy.yml"
|
|
555
|
+
presence: optional
|
|
556
|
+
tracked: tracked
|
|
557
|
+
tier: none
|
|
558
|
+
tests: none
|
|
559
|
+
managedBy: ci-workflows
|
|
560
|
+
liteManagedBy: ci-workflows-lite
|
|
561
|
+
rules: [HFS_MANAGED_FILE_DRIFT]
|
|
501
562
|
- id: app.dockerignore
|
|
563
|
+
since: 3.1.0
|
|
502
564
|
profiles: [app]
|
|
503
565
|
# The one build-context filter of every image (the context is the app root): rendered by starci app sync; it keeps secrets and build output out.
|
|
504
566
|
path: .dockerignore
|
|
@@ -509,6 +571,7 @@ slots:
|
|
|
509
571
|
managedBy: docker-ignore
|
|
510
572
|
rules: [HFS_MANAGED_FILE_DRIFT]
|
|
511
573
|
- id: app.deploy
|
|
574
|
+
since: 3.1.0
|
|
512
575
|
profiles: [app]
|
|
513
576
|
# Production delivery tooling for the app's own processes: compose files, Caddyfile, *.env.example and ops shell scripts.
|
|
514
577
|
# Lite still keeps its backing services on Supabase and its encrypted custody in app.starcistacks; R06 scans every file here.
|
|
@@ -519,6 +582,7 @@ slots:
|
|
|
519
582
|
tests: none
|
|
520
583
|
rules: [HFS_PLAINTEXT_SECRET]
|
|
521
584
|
- id: app.ci-deploy
|
|
585
|
+
since: 3.1.0
|
|
522
586
|
profiles: [app]
|
|
523
587
|
# Product-owned delivery workflows; the literal app.ci-db path remains the managed database workflow's more specific slot.
|
|
524
588
|
path: ".github/workflows/<app>-deploy.yml"
|
|
@@ -528,6 +592,7 @@ slots:
|
|
|
528
592
|
tests: none
|
|
529
593
|
rules: [HFS_PLAINTEXT_SECRET]
|
|
530
594
|
- id: app.ci-authorize-deploy-key
|
|
595
|
+
since: 3.1.0
|
|
531
596
|
profiles: [app]
|
|
532
597
|
path: ".github/workflows/authorize-deploy-key.yml"
|
|
533
598
|
presence: optional
|
|
@@ -536,7 +601,9 @@ slots:
|
|
|
536
601
|
tests: none
|
|
537
602
|
rules: [HFS_PLAINTEXT_SECRET]
|
|
538
603
|
- id: app.browser
|
|
604
|
+
since: 3.1.0
|
|
539
605
|
profiles: [app]
|
|
606
|
+
editionWhy: "ruling:R212/case-2 (with the Work boundary amended by ruling:R212/case-4): browser journeys belong to full; lite proves app boot through R272 wiring."
|
|
540
607
|
# The browser journey of the product (Playwright, owner ruling: one minimal journey per example app, run manually): the config, its
|
|
541
608
|
# journeys and the script that provisions the stack it runs against. It is the product's, never the front end's (FE_NO_TESTS keeps
|
|
542
609
|
# refusing any test or test tooling inside fe/). Declared by `browser: true` in hfs.json; the managed script `test:browser` exists only then.
|
|
@@ -550,14 +617,31 @@ slots:
|
|
|
550
617
|
requires: [playwright.config.ts, journeys/]
|
|
551
618
|
allows: [playwright.config.ts, stack.mjs, "journeys/*.ts"]
|
|
552
619
|
- id: app.github-meta
|
|
620
|
+
since: 3.1.0
|
|
553
621
|
profiles: [app]
|
|
554
|
-
path: ".github/{CODEOWNERS,pull_request_template.md,ISSUE_TEMPLATE
|
|
622
|
+
path: ".github/{CODEOWNERS,pull_request_template.md,ISSUE_TEMPLATE/**}"
|
|
555
623
|
presence: optional
|
|
556
624
|
tracked: tracked
|
|
557
625
|
tier: none
|
|
558
626
|
tests: none
|
|
627
|
+
- id: app.dependabot
|
|
628
|
+
since: 3.2.0
|
|
629
|
+
profiles: [app]
|
|
630
|
+
path: .github/dependabot.yml
|
|
631
|
+
presence: required
|
|
632
|
+
tracked: tracked
|
|
633
|
+
tier: none
|
|
634
|
+
tests: none
|
|
635
|
+
managedBy: skeleton
|
|
636
|
+
liteManagedBy: skeleton-lite
|
|
637
|
+
editionWhy: "Lite renders the same managed file from skeleton-lite because the editions share one code structure while their adapters differ (ruling:R212/case-2, with Work amended by ruling:R212/case-4 and hosting by owner:lite-production-swarm)."
|
|
638
|
+
rules: [HFS_MANAGED_FILE_DRIFT, HFS_DEPENDABOT_MISSING]
|
|
559
639
|
- id: app.scripts
|
|
640
|
+
since: 3.1.0
|
|
560
641
|
profiles: [app]
|
|
642
|
+
exemplifiedBy:
|
|
643
|
+
- {path: packages/hfs/templates/app/skeleton/scripts/codegen.mjs, edition: full}
|
|
644
|
+
editionWhy: "Lite manages its Supabase codegen script; full owns operational scripts through the generic path (ruling:R212/case-2 (with the Work boundary amended by ruling:R212/case-4))."
|
|
561
645
|
# The app's operational scripts only (`*.mjs`, `*.cjs`, `*.ps1`, `*.sh`), or nothing: an empty folder is `scripts/.gitkeep`, which
|
|
562
646
|
# `starci app scaffold` writes. A spec or a test (BE_SPEC_PLACEMENT, FE_NO_TESTS), a check or lint source (`check-*`, `eslint-local-rules*`,
|
|
563
647
|
# 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.
|
|
@@ -571,7 +655,11 @@ slots:
|
|
|
571
655
|
liteManagedBy: codegen-lite
|
|
572
656
|
rules: [HFS_SCRIPT_ONE_OFF, BE_SPEC_PLACEMENT, HFS_REPO_LOCAL_CHECK] # one-off codemods and fix-* scripts are agent output, not tooling
|
|
573
657
|
- id: app.scripts.delivery
|
|
658
|
+
since: 3.1.0
|
|
574
659
|
profiles: [app]
|
|
660
|
+
exemplifiedBy:
|
|
661
|
+
- {path: tests/fixtures/hfs-delivery-lite/scripts/build-frontend.mjs, edition: lite}
|
|
662
|
+
editionWhy: "Lite splits product delivery tooling from managed codegen; full owns the same files through app.scripts (ruling:R212/case-2 (with the Work boundary amended by ruling:R212/case-4))."
|
|
575
663
|
editions: [lite]
|
|
576
664
|
# Product-owned environment, secret and build tooling, kept apart from lite's managed codegen script.
|
|
577
665
|
# Full already owns these files through app.scripts; its generic *.mjs pattern must not compete with build-*.mjs.
|
|
@@ -582,6 +670,7 @@ slots:
|
|
|
582
670
|
tests: none
|
|
583
671
|
rules: [HFS_PLAINTEXT_SECRET, HFS_SCRIPT_ONE_OFF, BE_SPEC_PLACEMENT, HFS_REPO_LOCAL_CHECK]
|
|
584
672
|
- id: app.sides
|
|
673
|
+
since: 3.1.0
|
|
585
674
|
profiles: [app]
|
|
586
675
|
# 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;
|
|
587
676
|
# this slot answers only for the side folders themselves.
|
|
@@ -591,7 +680,12 @@ slots:
|
|
|
591
680
|
tier: none
|
|
592
681
|
tests: none
|
|
593
682
|
- id: app.starciwork
|
|
683
|
+
since: 3.1.0
|
|
594
684
|
profiles: [app]
|
|
685
|
+
exemplifiedBy:
|
|
686
|
+
- {path: packages/hfs/templates/app/skeleton/.starciwork/, edition: full}
|
|
687
|
+
- {path: packages/hfs/templates/app/skeleton-lite/.starciwork/, edition: lite}
|
|
688
|
+
editionWhy: "ruling:R212/case-4: lite optionally keeps .gitignore, workspace.yaml and brand records; full keeps the Work catalog and product summaries."
|
|
595
689
|
path: ".starciwork/"
|
|
596
690
|
presence: required
|
|
597
691
|
tracked: tracked
|
|
@@ -601,9 +695,10 @@ slots:
|
|
|
601
695
|
allows: [workspace.yaml, index.yaml, "brand/**", shell/index.yaml, "features/<feature>/index.yaml",
|
|
602
696
|
"features/<feature>/<family>/<name>/index.yaml", "features/<feature>/br/<rule>/ac/<name>/index.yaml",
|
|
603
697
|
"features/<feature>/ui/<name>/assets/<approved-file>", "features/<feature>/uat/<name>/{index,accounts,fixtures}.yaml",
|
|
604
|
-
"features/<feature>/uat/<name>/{seed,cleanup}.sql", "_resources/{identities,environments,fixtures}/<slug>/resource.yaml"
|
|
605
|
-
|
|
606
|
-
|
|
698
|
+
"features/<feature>/uat/<name>/{seed,cleanup}.sql", "_resources/{identities,environments,fixtures}/<slug>/resource.yaml",
|
|
699
|
+
"_derived/{index.yaml,frontier.md,critique.yaml,critique.md}"]
|
|
700
|
+
forbids: [evidence/, logs/, reports/, captures/, runs/, "kernel-*/", "*.sqlite*"]
|
|
701
|
+
# ruling:R212/case-4: lite keeps workspace.yaml for project identity and repo roles, plus brand drawing records,
|
|
607
702
|
# references and assets. No brand means no Work tree is needed; workspace.yaml is legal but optional.
|
|
608
703
|
litePresence: optional
|
|
609
704
|
lite:
|
|
@@ -611,7 +706,12 @@ slots:
|
|
|
611
706
|
allows: [workspace.yaml, "brand/**"]
|
|
612
707
|
rules: [HFS_AGENT_DATA_TRACKED, HFS_IDENTITY_CUSTODY]
|
|
613
708
|
- id: app.starcistacks
|
|
709
|
+
since: 3.1.0
|
|
614
710
|
profiles: [app]
|
|
711
|
+
editionWhy: "owner:lite-production-swarm: lite requires declared docker-swarm production with Supabase backing; full also admits Compose development, Kubernetes and Terraform."
|
|
712
|
+
exemplifiedBy:
|
|
713
|
+
- {path: packages/hfs/templates/app/skeleton/.starcistacks/, edition: full}
|
|
714
|
+
- {path: packages/hfs/templates/app/skeleton-lite/.starcistacks/, edition: lite}
|
|
615
715
|
# The stack declarations and the sealed custody of the app, at the app root beside be/, fe/ and .starciwork (never under a side).
|
|
616
716
|
path: ".starcistacks/"
|
|
617
717
|
presence: required
|
|
@@ -619,18 +719,19 @@ slots:
|
|
|
619
719
|
tier: none
|
|
620
720
|
tests: none
|
|
621
721
|
requires: [application-stacks.yaml]
|
|
622
|
-
allows: [application-stacks.yaml, "<env>/README.md", "<env>/environment.json", "<env>/infra/{compose,k8s,terraform
|
|
722
|
+
allows: [application-stacks.yaml, "<env>/README.md", "<env>/environment.json", "<env>/infra/{compose,swarm,k8s,terraform}/**", "<env>/infra/metadata.json",
|
|
623
723
|
"<env>/runtime/{config,env}/**", "<env>/runtime/env/KEYS.md", "<env>/secrets/<slug>.enc", "<env>/secrets/.gitkeep", "<env>/seeds/**"]
|
|
624
724
|
forbids: ["<env>/runtime/files/**", "**/*.enc outside <env>/secrets/", DESIGN.md, deployment.json, "k8s/ at root"]
|
|
625
|
-
#
|
|
626
|
-
#
|
|
627
|
-
# images duplicate Supabase services: postgres, redis, kafka, keycloak, minio, rabbitmq, mysql and mongo.
|
|
725
|
+
# Owner ruling 2026-10-14: lite declares docker-swarm production, app runtime config, sealed custody and seed data.
|
|
726
|
+
# Supabase supplies the database and broker; production has no Compose or host cron.
|
|
628
727
|
lite:
|
|
629
728
|
requires: [application-stacks.yaml]
|
|
630
|
-
allows: [application-stacks.yaml, "<env>/README.md", "<env>/environment.json", "<env>/infra/swarm/**", "<env>/
|
|
631
|
-
|
|
729
|
+
allows: [application-stacks.yaml, "<env>/README.md", "<env>/environment.json", "<env>/infra/swarm/**", "<env>/infra/metadata.json",
|
|
730
|
+
"<env>/runtime/{config,env}/**", "<env>/runtime/env/KEYS.md", "<env>/secrets/<slug>.enc", "<env>/secrets/.gitkeep", "<env>/seeds/**"]
|
|
731
|
+
forbids: ["<env>/runtime/files/**", "**/*.enc outside <env>/secrets/", DESIGN.md, deployment.json, "k8s/ at root"]
|
|
632
732
|
rules: [HFS_STACKS_SHAPE, HFS_STACKS_IN_SIDE, HFS_PLAINTEXT_SECRET, HFS_IDENTITY_CUSTODY]
|
|
633
733
|
- id: app.sops
|
|
734
|
+
since: 3.1.0
|
|
634
735
|
profiles: [app]
|
|
635
736
|
# The sops custody rule of .starcistacks/<env>/secrets/*.enc, at the app root beside the tree it seals.
|
|
636
737
|
path: .sops.yaml
|
|
@@ -639,6 +740,7 @@ slots:
|
|
|
639
740
|
tier: none
|
|
640
741
|
tests: none
|
|
641
742
|
- id: app.build-output
|
|
743
|
+
since: 3.1.0
|
|
642
744
|
profiles: [app]
|
|
643
745
|
path: "{**/node_modules/,coverage/,reports/,test-results/,**/*.tsbuildinfo}"
|
|
644
746
|
presence: optional
|
|
@@ -646,6 +748,7 @@ slots:
|
|
|
646
748
|
tier: none
|
|
647
749
|
tests: none
|
|
648
750
|
- id: app.tool-cache
|
|
751
|
+
since: 3.1.0
|
|
649
752
|
profiles: [app]
|
|
650
753
|
path: "{.eslintcache,.turbo/,.scannerwork/,.sonar/,.jest-cache/,.cache/,.tools/}"
|
|
651
754
|
presence: forbidden
|
|
@@ -654,6 +757,7 @@ slots:
|
|
|
654
757
|
tests: none
|
|
655
758
|
goesTo: "${STARCI_CACHE_HOME:-%LOCALAPPDATA%/StarCi/cache}/<repo>/<tool>/ (the canon presets set eslint --cache-location, jest cacheDirectory, turbo cacheDir, sonar.working.directory)"
|
|
656
759
|
- id: app.agent-output
|
|
760
|
+
since: 3.1.0
|
|
657
761
|
profiles: [app]
|
|
658
762
|
path: "{report*.json,*.tmp.json,%*%,nul,.artifacts/,.tmp-*,*-lint.json,lf.json,lt.json,*.log,design-plans/,.gitmounts/,.dat,vi-flat.txt}"
|
|
659
763
|
presence: forbidden
|
|
@@ -663,6 +767,7 @@ slots:
|
|
|
663
767
|
goesTo: "agent scratchpad or the StarCi blob store (%LOCALAPPDATA%/StarCi/blobs), cited by {name, sha256}"
|
|
664
768
|
rules: [HFS_UNTRACKED_ROOT_ENTRY, HFS_AGENT_DATA_TRACKED]
|
|
665
769
|
- id: app.worktrees
|
|
770
|
+
since: 3.1.0
|
|
666
771
|
profiles: [app]
|
|
667
772
|
path: "{.worktrees/,worktrees/,.starciwork/worktrees/}"
|
|
668
773
|
presence: forbidden
|
|
@@ -671,6 +776,7 @@ slots:
|
|
|
671
776
|
tests: none
|
|
672
777
|
goesTo: "<lanes root>/<project>/<lane>/ (outside every repository) for a lane; a Kernel workflow's worktree is created and owned by Orca outside the app checkout, never tracked, and removed by the runtime's host-side controller once the workflow's finish marked it release-pending"
|
|
673
778
|
- id: app.plaintext-env
|
|
779
|
+
since: 3.1.0
|
|
674
780
|
profiles: [app]
|
|
675
781
|
path: "{.env,.env.*,secrets.env,.secrets/,**/*.pem,**/*.key}"
|
|
676
782
|
presence: forbidden
|
|
@@ -683,6 +789,10 @@ slots:
|
|
|
683
789
|
# ----- app root: the Supabase project of an app whose connection declares provider: supabase (every lite app) -------
|
|
684
790
|
- id: app.supabase
|
|
685
791
|
profiles: [app]
|
|
792
|
+
exemplifiedBy:
|
|
793
|
+
- packages/hfs/templates/app/skeleton-lite/supabase/
|
|
794
|
+
- {path: tests/fixtures/hfs-callback-full/supabase/, edition: full}
|
|
795
|
+
editionWhy: "ruling:R212/case-2 (owner:lite-production-swarm) (with the Work boundary amended by ruling:R212/case-4): Supabase is required in lite; full enables it only through a declared connection."
|
|
686
796
|
# The one Supabase project directory, at the app root beside be/, fe/ and .starcistacks (never under a side).
|
|
687
797
|
path: "supabase/"
|
|
688
798
|
presence: opt-in # enabled by a connection with provider: supabase; required under edition lite
|
|
@@ -694,6 +804,10 @@ slots:
|
|
|
694
804
|
since: 2.1.0
|
|
695
805
|
- id: app.supabase.config
|
|
696
806
|
profiles: [app]
|
|
807
|
+
exemplifiedBy:
|
|
808
|
+
- packages/hfs/templates/app/skeleton-lite/supabase/config.toml
|
|
809
|
+
- {path: tests/fixtures/hfs-callback-full/supabase/config.toml, edition: full}
|
|
810
|
+
editionWhy: "ruling:R212/case-2 (owner:lite-production-swarm) (with the Work boundary amended by ruling:R212/case-4): lite requires Supabase configuration; full requires it only when Supabase is declared."
|
|
697
811
|
# The project configuration: auth keys are env(NAME) references, never literals (L08 judges its policy keys).
|
|
698
812
|
path: "supabase/config.toml"
|
|
699
813
|
presence: opt-in
|
|
@@ -705,6 +819,10 @@ slots:
|
|
|
705
819
|
since: 2.1.0
|
|
706
820
|
- id: app.supabase.migrations
|
|
707
821
|
profiles: [app]
|
|
822
|
+
exemplifiedBy:
|
|
823
|
+
- packages/hfs/templates/app/skeleton-lite/supabase/migrations/__timestamp___baseline.sql
|
|
824
|
+
- {path: tests/fixtures/hfs-callback-full/supabase/migrations/20261014100000_baseline.sql, edition: full}
|
|
825
|
+
editionWhy: "ruling:R212/case-2 (owner:lite-production-swarm) (with the Work boundary amended by ruling:R212/case-4): lite uses Supabase schema authority and requires a migration; full opts into that provider."
|
|
708
826
|
# The schema authority of an app on Supabase: forward-only SQL migrations named <timestamp14>_<kebab-name>.sql,
|
|
709
827
|
# one file per change, immutable once on origin/main (L02-L08 judge them).
|
|
710
828
|
path: "supabase/migrations/<stamp>_<name>.sql"
|
|
@@ -739,6 +857,10 @@ slots:
|
|
|
739
857
|
since: 2.1.0
|
|
740
858
|
- id: app.supabase.types
|
|
741
859
|
profiles: [app]
|
|
860
|
+
templateNa: "Database row types are generated from the app's Supabase schema by contract:emit; a hand-authored scaffold template would invent the schema contract."
|
|
861
|
+
exemplifiedBy:
|
|
862
|
+
- {path: tests/fixtures/hfs-callback-full/supabase/types/database.types.ts, edition: full}
|
|
863
|
+
editionWhy: "ruling:R212/case-2 (owner:lite-production-swarm) (with the Work boundary amended by ruling:R212/case-4): lite requires generated Supabase row types; full enables them with the provider."
|
|
742
864
|
# The generated Database types (database.types.ts) contract:emit writes from the migrations; both sides read it
|
|
743
865
|
# (sides.<side>.reads supabase/types/); drift against regeneration is L09.
|
|
744
866
|
path: "supabase/types/database.types.ts"
|
|
@@ -777,6 +899,7 @@ slots:
|
|
|
777
899
|
|
|
778
900
|
# ----- side root (both sides): what a standalone repository root holds, less the app-root files ----------
|
|
779
901
|
- id: repo.side-root-forbidden
|
|
902
|
+
since: 3.1.0
|
|
780
903
|
profiles: [be, fe]
|
|
781
904
|
path: "{package.json,package-lock.json,hfs.json,README.md,.gitignore,.gitattributes,.husky/,.github/,.starciwork/,.starcistacks/,supabase/,.sops.yaml,sonar-project.properties,codecov.yml,.prettierrc,.prettierignore,scripts/}"
|
|
782
905
|
presence: forbidden
|
|
@@ -785,6 +908,7 @@ slots:
|
|
|
785
908
|
tests: none
|
|
786
909
|
goesTo: "the app root: one package.json, lockfile, hfs.json, README, git and CI files, hooks, formatter, Sonar and Codecov configuration, scripts/, .starciwork, .starcistacks and .sops.yaml per app"
|
|
787
910
|
- id: be.tool-config
|
|
911
|
+
since: 3.1.0
|
|
788
912
|
profiles: [be]
|
|
789
913
|
# Managed files (starci app sync renders them, starci app check compares them): the whole tool configuration of a back end. Each is
|
|
790
914
|
# a reference to a canon package, never a configuration: tsconfig.json extends @starci/tsconfig/be.json and adds the
|
|
@@ -800,11 +924,12 @@ slots:
|
|
|
800
924
|
tracked: tracked
|
|
801
925
|
tier: none
|
|
802
926
|
tests: none
|
|
803
|
-
# Lite renders the same files (owner ruling 2026-10-12): the unit run and its coverage are full's.
|
|
927
|
+
# Lite renders the same files (owner ruling 2026-10-12 (owner:lite-production-swarm), with the Work boundary amended by ruling:R212/case-4): the unit run and its coverage are full's.
|
|
804
928
|
coverage: none
|
|
805
929
|
managedBy: tool-config
|
|
806
930
|
rules: [HFS_MANAGED_FILE_DRIFT, HFS_TOOL_CONFIG_LOCAL, HFS_TS_STRICT]
|
|
807
931
|
- id: be.nest-cli
|
|
932
|
+
since: 3.1.0
|
|
808
933
|
profiles: [be]
|
|
809
934
|
# The Nest monorepo of the back end: `monorepo: true` and one project per declared be app (apps/<app>, even for one service).
|
|
810
935
|
path: nest-cli.json
|
|
@@ -815,6 +940,7 @@ slots:
|
|
|
815
940
|
coverage: none
|
|
816
941
|
rules: [HFS_MONO_NEST_PROJECTS]
|
|
817
942
|
- id: be.tool-config-local
|
|
943
|
+
since: 3.1.0
|
|
818
944
|
profiles: [be]
|
|
819
945
|
path: "{.eslintrc,.eslintrc.*,.eslintignore,eslint.config.js,eslint.config.cjs,eslint.config.ts,eslint.config.mts,eslint.config.cts,.prettierrc.*,prettier.config.*,jest.config.ts,jest.config.mjs,jest.config.cjs,jest.config.json,jest.config.e2e.js}"
|
|
820
946
|
presence: forbidden
|
|
@@ -824,6 +950,7 @@ slots:
|
|
|
824
950
|
goesTo: "nowhere: a back end has exactly the managed tool configuration (be.tool-config); a change to a rule or a flag is proposed in the .claude runtime"
|
|
825
951
|
rules: [HFS_TOOL_CONFIG_LOCAL]
|
|
826
952
|
- id: fe.tool-config
|
|
953
|
+
since: 3.1.0
|
|
827
954
|
profiles: [fe]
|
|
828
955
|
# Managed files (starci app sync renders them, starci app check compares them): the tool configuration of a front end. Each is a
|
|
829
956
|
# reference to a canon package, never a configuration: tsconfig.json extends @starci/tsconfig/next.json and only
|
|
@@ -839,6 +966,7 @@ slots:
|
|
|
839
966
|
managedBy: tool-config
|
|
840
967
|
rules: [HFS_MANAGED_FILE_DRIFT, HFS_TOOL_CONFIG_LOCAL, HFS_RULE_OFF_WITHOUT_REPLACEMENT, HFS_TS_STRICT]
|
|
841
968
|
- id: fe.tool-config-local
|
|
969
|
+
since: 3.1.0
|
|
842
970
|
profiles: [fe]
|
|
843
971
|
path: "{.eslintrc,.eslintrc.*,.eslintignore,eslint.config.js,eslint.config.cjs,eslint.config.ts,eslint.config.mts,eslint.config.cts,.stylelintrc,.stylelintrc.*,.stylelintignore,stylelint.config.js,stylelint.config.cjs,stylelint.config.ts,stylelint.config.json,prettier.config.*,.prettierrc.*,lint-staged.config.*,.lintstagedrc*,turbo.json}"
|
|
844
972
|
presence: forbidden
|
|
@@ -848,6 +976,7 @@ slots:
|
|
|
848
976
|
goesTo: "nowhere: a front end has exactly the managed tool configuration (fe.tool-config); the task graph is the app root's turbo.json (app.task-graph); a change to a rule or a flag is proposed in the .claude runtime"
|
|
849
977
|
rules: [HFS_TOOL_CONFIG_LOCAL]
|
|
850
978
|
- id: repo.docs
|
|
979
|
+
since: 3.1.0
|
|
851
980
|
profiles: [be, fe]
|
|
852
981
|
path: "docs/{adr,runbooks,guides}/**/*.md"
|
|
853
982
|
presence: opt-in
|
|
@@ -857,6 +986,7 @@ slots:
|
|
|
857
986
|
coverage: none
|
|
858
987
|
rules: [HFS_ROOT_MARKDOWN_FORBIDDEN]
|
|
859
988
|
- id: repo.docs-media
|
|
989
|
+
since: 3.1.0
|
|
860
990
|
profiles: [be, fe]
|
|
861
991
|
path: "docs/**/*.{png,svg,webp}"
|
|
862
992
|
presence: opt-in
|
|
@@ -865,7 +995,23 @@ slots:
|
|
|
865
995
|
tests: none
|
|
866
996
|
coverage: none
|
|
867
997
|
budget: {bytes: 512000}
|
|
998
|
+
- id: repo.i18n-dynamic-keys
|
|
999
|
+
since: 3.3.0
|
|
1000
|
+
profiles: [be, fe]
|
|
1001
|
+
# The side's authored ledger of catalog keys the R280 detector cannot see (fe/i18n-dynamic-keys.yaml, be/i18n-dynamic-keys.yaml:
|
|
1002
|
+
# each side owns its own catalogs). A key it names counts as read; an entry matching no catalog key, or a malformed file, is itself an R280 finding.
|
|
1003
|
+
path: i18n-dynamic-keys.yaml
|
|
1004
|
+
presence: opt-in
|
|
1005
|
+
tracked: tracked
|
|
1006
|
+
tier: none
|
|
1007
|
+
tests: none
|
|
1008
|
+
coverage: none
|
|
1009
|
+
exemplifiedBy:
|
|
1010
|
+
- {path: tests/fixtures/hfs-i18n-ledger/fe/i18n-dynamic-keys.yaml, editions: [lite, full]}
|
|
1011
|
+
- {path: tests/fixtures/hfs-i18n-ledger/be/i18n-dynamic-keys.yaml, editions: [lite, full]}
|
|
1012
|
+
rules: [HFS_UNUSED_SYMBOL]
|
|
868
1013
|
- id: repo.packages
|
|
1014
|
+
since: 3.1.0
|
|
869
1015
|
profiles: [be, fe]
|
|
870
1016
|
path: "packages/<pkg>/"
|
|
871
1017
|
presence: opt-in
|
|
@@ -877,7 +1023,14 @@ slots:
|
|
|
877
1023
|
coverage: none
|
|
878
1024
|
rules: [FE_PACKAGE_SHAPE, HFS_UNUSED_EXPORT]
|
|
879
1025
|
- id: repo.app-image
|
|
1026
|
+
since: 3.1.0
|
|
880
1027
|
profiles: [be, fe]
|
|
1028
|
+
exemplifiedBy:
|
|
1029
|
+
- packages/hfs/templates/be/image/api/Dockerfile
|
|
1030
|
+
- packages/hfs/templates/be/image/cli/Dockerfile
|
|
1031
|
+
- {path: packages/hfs/templates/be/image/worker/Dockerfile, edition: full}
|
|
1032
|
+
- packages/hfs/templates/fe/image/next/Dockerfile
|
|
1033
|
+
editionWhy: "ruling:R212/case-2 (owner:lite-production-swarm) (with the Work boundary amended by ruling:R212/case-4): lite may host Next without its own image; back-end images stay required in both editions."
|
|
881
1034
|
# One image per app: every declared app has its own Dockerfile (apps/<app>/Dockerfile), built from the APP ROOT (the folder with
|
|
882
1035
|
# the one package.json). It is app-owned (starci app scaffold writes it once from templates/<side>/image) and judged by the docker rules.
|
|
883
1036
|
# A Dockerfile anywhere else is owned by no slot.
|
|
@@ -892,6 +1045,7 @@ slots:
|
|
|
892
1045
|
|
|
893
1046
|
# ----- never tracked: build output the tools need in place ---------------------------------------------
|
|
894
1047
|
- id: repo.build-output
|
|
1048
|
+
since: 3.1.0
|
|
895
1049
|
profiles: [be, fe]
|
|
896
1050
|
path: "{**/node_modules/,dist/,apps/*/dist/,packages/*/dist/,.next/,apps/*/.next/,coverage/,reports/,test-results/,**/next-env.d.ts,**/*.tsbuildinfo}"
|
|
897
1051
|
presence: optional
|
|
@@ -899,6 +1053,7 @@ slots:
|
|
|
899
1053
|
tier: none
|
|
900
1054
|
tests: none
|
|
901
1055
|
- id: repo.generated
|
|
1056
|
+
since: 3.1.0
|
|
902
1057
|
profiles: [be, fe]
|
|
903
1058
|
path: "{**/__generated__/,apps/*/src/schema.gql}"
|
|
904
1059
|
presence: optional
|
|
@@ -909,6 +1064,7 @@ slots:
|
|
|
909
1064
|
|
|
910
1065
|
# ----- never in the working tree: caches, scratch, agent output ----------------------------------------
|
|
911
1066
|
- id: repo.tool-cache
|
|
1067
|
+
since: 3.1.0
|
|
912
1068
|
profiles: [be, fe]
|
|
913
1069
|
path: "{.eslintcache,.turbo/,.scannerwork/,.sonar/,.jest-cache/,.cache/,.tools/}"
|
|
914
1070
|
presence: forbidden
|
|
@@ -917,6 +1073,7 @@ slots:
|
|
|
917
1073
|
tests: none
|
|
918
1074
|
goesTo: "${STARCI_CACHE_HOME:-%LOCALAPPDATA%/StarCi/cache}/<repo>/<tool>/ (the canon presets set eslint --cache-location, jest cacheDirectory, turbo cacheDir, sonar.working.directory)"
|
|
919
1075
|
- id: repo.agent-output
|
|
1076
|
+
since: 3.1.0
|
|
920
1077
|
profiles: [be, fe]
|
|
921
1078
|
path: "{report*.json,*.tmp.json,%*%,nul,.artifacts/,.tmp-*,*-lint.json,lf.json,lt.json,*.log,design-plans/,.gitmounts/,.dat,vi-flat.txt}"
|
|
922
1079
|
presence: forbidden
|
|
@@ -926,6 +1083,7 @@ slots:
|
|
|
926
1083
|
goesTo: "agent scratchpad or the StarCi blob store (%LOCALAPPDATA%/StarCi/blobs), cited by {name, sha256}"
|
|
927
1084
|
rules: [HFS_UNTRACKED_ROOT_ENTRY, HFS_AGENT_DATA_TRACKED]
|
|
928
1085
|
- id: repo.worktrees
|
|
1086
|
+
since: 3.1.0
|
|
929
1087
|
profiles: [be, fe]
|
|
930
1088
|
path: "{.worktrees/,worktrees/,.starciwork/worktrees/}"
|
|
931
1089
|
presence: forbidden
|
|
@@ -934,6 +1092,7 @@ slots:
|
|
|
934
1092
|
tests: none
|
|
935
1093
|
goesTo: "<lanes root>/<project>/<lane>/ (outside every repository)"
|
|
936
1094
|
- id: repo.plaintext-env
|
|
1095
|
+
since: 3.1.0
|
|
937
1096
|
profiles: [be, fe]
|
|
938
1097
|
path: "{.env,.env.*,.secrets/,**/*.pem,**/*.key}"
|
|
939
1098
|
presence: forbidden
|
|
@@ -943,6 +1102,7 @@ slots:
|
|
|
943
1102
|
goesTo: ".starcistacks/<env>/secrets/<slug>.enc (at the app root); decrypted values only in memory or under %LOCALAPPDATA%/StarCi/secrets/<project>/<env>/ via *_FILE"
|
|
944
1103
|
rules: [HFS_PLAINTEXT_SECRET]
|
|
945
1104
|
- id: be.root-e2e
|
|
1105
|
+
since: 3.1.0
|
|
946
1106
|
profiles: [be]
|
|
947
1107
|
path: "e2e/"
|
|
948
1108
|
presence: forbidden
|
|
@@ -953,6 +1113,7 @@ slots:
|
|
|
953
1113
|
|
|
954
1114
|
# ----- contract snapshots (the committed machine-emitted files, because they are a cross-repo interface) --
|
|
955
1115
|
- id: be.contract.graphql
|
|
1116
|
+
since: 3.1.0
|
|
956
1117
|
profiles: [be]
|
|
957
1118
|
path: "contracts/<app>/schema.graphql"
|
|
958
1119
|
presence: opt-in
|
|
@@ -963,6 +1124,7 @@ slots:
|
|
|
963
1124
|
why: emitted by `npm run contract:emit` (`starci app emit`, never written by hand); CI fails when the emitted file differs (a contract, not a build artefact)
|
|
964
1125
|
rules: [HFS_CONTRACT_SNAPSHOT_DRIFT]
|
|
965
1126
|
- id: be.contract.openapi
|
|
1127
|
+
since: 3.1.0
|
|
966
1128
|
profiles: [be]
|
|
967
1129
|
path: "contracts/<app>/openapi.json"
|
|
968
1130
|
presence: opt-in
|
|
@@ -973,6 +1135,7 @@ slots:
|
|
|
973
1135
|
why: emitted by `npm run contract:emit` (`starci app emit`, never written by hand) from the app's typed operation table `apps/<app>/src/operations.ts` (canon BE-OPERATIONS-1); CI fails when the emitted file differs
|
|
974
1136
|
rules: [HFS_CONTRACT_SNAPSHOT_DRIFT]
|
|
975
1137
|
- id: be.contract.events
|
|
1138
|
+
since: 3.1.0
|
|
976
1139
|
profiles: [be]
|
|
977
1140
|
path: "contracts/<app>/events.json"
|
|
978
1141
|
presence: opt-in
|
|
@@ -983,6 +1146,7 @@ slots:
|
|
|
983
1146
|
why: the async contract of a service (`starci/event-contract@1`), emitted by `npm run contract:emit` (`starci app emit`, never written by hand) from the typed event classes of `src/modules/events/<service>/`; the consumers of its events rely on it (R166 HFS_EVENT_CONTRACT)
|
|
984
1147
|
rules: [HFS_EVENT_CONTRACT]
|
|
985
1148
|
- id: be.contract.events.pin
|
|
1149
|
+
since: 3.1.0
|
|
986
1150
|
profiles: [be]
|
|
987
1151
|
path: "contracts/<app>/events.pin.json"
|
|
988
1152
|
presence: opt-in
|
|
@@ -1012,6 +1176,7 @@ slots:
|
|
|
1012
1176
|
# area's global ports when it has any); composition only, no business logic or process.env/EnvSource read.
|
|
1013
1177
|
# app.module.ts the composition root: imports the groups and spreads them, binds the app-wide guards and filter.
|
|
1014
1178
|
- id: be.app.api
|
|
1179
|
+
since: 3.1.0
|
|
1015
1180
|
profiles: [be]
|
|
1016
1181
|
path: "apps/<app>/src/"
|
|
1017
1182
|
appKind: api
|
|
@@ -1020,13 +1185,14 @@ slots:
|
|
|
1020
1185
|
tier: app
|
|
1021
1186
|
owner: true
|
|
1022
1187
|
minInstances: 1
|
|
1023
|
-
requires: [main.ts, app.module.ts]
|
|
1188
|
+
requires: [main.ts, app.module.ts, "<app>.env.ts"]
|
|
1024
1189
|
allows: [main.ts, app.module.ts, "<app>.env.ts", "<app>.options.ts", "compose/<area>.ts", operations.ts]
|
|
1025
|
-
tests: none #
|
|
1190
|
+
tests: none # full app boot is proven by useTestWorld({ apps }); lite by starci app check --wiring (R272)
|
|
1026
1191
|
coverage: none
|
|
1027
1192
|
budget: {app.module.ts: 250, main.ts: 80, "<app>.env.ts": 150, "<app>.options.ts": 200, "compose/<area>.ts": 120}
|
|
1028
|
-
rules: [BE_APP_COMPOSITION_ONLY, BE_APP_BUSINESS_ROLE, BE_APP_OPTIONS_COMPOSITION, BE_APP_COMPOSITION_BUDGET, BE_DEFAULT_DENY, BE_ERROR_MASKED, BE_FEATURE_NOT_COMPOSED, BE_CONTEXT_OWNER]
|
|
1193
|
+
rules: [BE_APP_COMPOSITION_ONLY, BE_APP_BUSINESS_ROLE, BE_APP_OPTIONS_COMPOSITION, BE_APP_COMPOSITION_BUDGET, BE_DEFAULT_DENY, BE_ERROR_MASKED, BE_FEATURE_NOT_COMPOSED, BE_CONTEXT_OWNER, BE_APP_ENV_REQUIRED]
|
|
1029
1194
|
- id: be.app.worker
|
|
1195
|
+
since: 3.1.0
|
|
1030
1196
|
profiles: [be]
|
|
1031
1197
|
path: "apps/<app>/src/"
|
|
1032
1198
|
appKind: worker # composes transport/message modules and job processors only (an api app may compose them too: a worker app is for separate scaling)
|
|
@@ -1034,14 +1200,16 @@ slots:
|
|
|
1034
1200
|
tracked: tracked
|
|
1035
1201
|
tier: app
|
|
1036
1202
|
owner: true
|
|
1037
|
-
requires: [main.ts, app.module.ts]
|
|
1203
|
+
requires: [main.ts, app.module.ts, "<app>.env.ts"]
|
|
1038
1204
|
allows: [main.ts, app.module.ts, "<app>.env.ts", "<app>.options.ts", "compose/<area>.ts"]
|
|
1039
|
-
tests: none #
|
|
1205
|
+
tests: none # full app boot is proven by useTestWorld({ apps }); lite by starci app check --wiring (R272)
|
|
1040
1206
|
coverage: none
|
|
1041
1207
|
budget: {app.module.ts: 250, main.ts: 80, "<app>.env.ts": 150, "<app>.options.ts": 200, "compose/<area>.ts": 120}
|
|
1042
|
-
rules: [BE_APP_COMPOSITION_ONLY, BE_APP_OPTIONS_COMPOSITION, BE_APP_COMPOSITION_BUDGET, BE_BACKGROUND_UNOWNED, BE_CONTEXT_OWNER]
|
|
1208
|
+
rules: [BE_APP_COMPOSITION_ONLY, BE_APP_OPTIONS_COMPOSITION, BE_APP_COMPOSITION_BUDGET, BE_BACKGROUND_UNOWNED, BE_CONTEXT_OWNER, BE_APP_ENV_REQUIRED]
|
|
1043
1209
|
- id: be.app.cli
|
|
1210
|
+
since: 3.1.0
|
|
1044
1211
|
profiles: [be]
|
|
1212
|
+
editionWhy: "ruling:R212/case-2 (owner:lite-production-swarm) (with the Work boundary amended by ruling:R212/case-4): Supabase CLI owns lite migrations; a lite cli app is optional for declared operator commands."
|
|
1045
1213
|
# The ONE app of every one-off action (migrate, seed, sync, backup, any operator command): be/apps/cli, on nest-commander.
|
|
1046
1214
|
# main.ts boots with CommandFactory.run (BE_CLI_BOOTSTRAP); app.module.ts composes the platform and the cli feature root
|
|
1047
1215
|
# (src/features/cli/cli.module.ts); cli.env.ts exports CliEnv and readCliEnv(env: EnvSource): CliEnv, and cli.options.ts carries the env-derived values,
|
|
@@ -1055,18 +1223,19 @@ slots:
|
|
|
1055
1223
|
tracked: tracked
|
|
1056
1224
|
tier: app
|
|
1057
1225
|
owner: true
|
|
1058
|
-
requires: [main.ts, app.module.ts]
|
|
1226
|
+
requires: [main.ts, app.module.ts, "<app>.env.ts"]
|
|
1059
1227
|
allows: [main.ts, app.module.ts, "<app>.env.ts", "<app>.options.ts", "compose/<area>.ts"]
|
|
1060
|
-
tests: none #
|
|
1228
|
+
tests: none # full app boot is proven by useTestWorld({ apps }); lite by starci app check --wiring (R272)
|
|
1061
1229
|
coverage: none
|
|
1062
1230
|
budget: {app.module.ts: 250, main.ts: 80, "<app>.env.ts": 150, "<app>.options.ts": 200, "compose/<area>.ts": 120}
|
|
1063
|
-
rules: [BE_CLI_REQUIRED, BE_CLI_BOOTSTRAP, BE_CLI_OWNER, BE_SCHEMA_AUTHORITY, BE_APP_OPTIONS_COMPOSITION, BE_APP_COMPOSITION_BUDGET]
|
|
1231
|
+
rules: [BE_CLI_REQUIRED, BE_CLI_BOOTSTRAP, BE_CLI_OWNER, BE_SCHEMA_AUTHORITY, BE_APP_OPTIONS_COMPOSITION, BE_APP_COMPOSITION_BUDGET, BE_APP_ENV_REQUIRED]
|
|
1064
1232
|
|
|
1065
1233
|
# ----- BE: features ------------------------------------------------------------------------------------
|
|
1066
1234
|
# src/features/ is grouped by TRIGGER KIND (owner decision 2026-10-02): features/api/<feature>/ (a request: application/ and
|
|
1067
1235
|
# transport/<protocol>/, composed by the api apps), features/cli/ (a one-off action: the cli feature root, compiled only into
|
|
1068
1236
|
# apps/cli). Every kind imports only downward into modules/*; no kind imports another.
|
|
1069
1237
|
- id: be.feature
|
|
1238
|
+
since: 3.1.0
|
|
1070
1239
|
profiles: [be]
|
|
1071
1240
|
path: "src/features/api/<feature>/"
|
|
1072
1241
|
presence: required
|
|
@@ -1076,12 +1245,13 @@ slots:
|
|
|
1076
1245
|
owner: true
|
|
1077
1246
|
minInstances: 1
|
|
1078
1247
|
requires: [index.ts, "<feature>.module.ts", application/]
|
|
1079
|
-
allows: [index.ts, "<feature>.module.ts", application/, transport/, messages/] # nothing else at feature root
|
|
1248
|
+
allows: [index.ts, "<feature>.module.ts", "<feature>.module-definition.ts", "<feature>.options.ts", application/, transport/, messages/] # nothing else at feature root
|
|
1080
1249
|
tests: none
|
|
1081
1250
|
coverage: none
|
|
1082
1251
|
budget: {files: 250, indexExports: 60} # a feature above 250 source files must be split by product capability
|
|
1083
1252
|
rules: [BE_FEATURE_SHAPE, BE_FEATURE_IMPORTS_FEATURE, BE_PUBLIC_SURFACE, BE_FEATURE_NOT_COMPOSED]
|
|
1084
1253
|
- id: be.feature.application
|
|
1254
|
+
since: 3.1.0
|
|
1085
1255
|
profiles: [be]
|
|
1086
1256
|
path: "src/features/{api/,jobs/,reactors/,saga/}<feature>/application/" # inside a trigger kind (jobs, reactors, saga) the feature folder is one level down
|
|
1087
1257
|
presence: required
|
|
@@ -1093,6 +1263,7 @@ slots:
|
|
|
1093
1263
|
coverage: none
|
|
1094
1264
|
rules: [BE_APPLICATION_IMPORTS_TRANSPORT, BE_APPLICATION_TRANSPORT_FRAMEWORK, BE_ENTITY_IN_CONTRACT, BE_CQRS_SHAPE]
|
|
1095
1265
|
- id: be.feature.saga
|
|
1266
|
+
since: 3.1.0
|
|
1096
1267
|
profiles: [be]
|
|
1097
1268
|
path: "src/features/saga/<saga>/"
|
|
1098
1269
|
pattern: saga # enabled by `patterns: ["saga"]` in hfs.json sides.be (R169 to R173); an undeclared saga folder is HFS_SLOT_NOT_ENABLED
|
|
@@ -1102,11 +1273,12 @@ slots:
|
|
|
1102
1273
|
tier: feature
|
|
1103
1274
|
owner: true
|
|
1104
1275
|
requires: [index.ts, "<saga>.module.ts", "<saga>.saga.service.ts", "<saga>.saga-state.ts", steps/, compensations/, transport/]
|
|
1105
|
-
allows: [index.ts, "<saga>.module.ts", "<saga>.saga.service.ts", "<saga>.saga.service.spec.ts", "<saga>.saga-state.ts", "<saga>.saga.log-events.ts", application/, steps/, compensations/, transport/] # the orchestrator is a `*.service.ts` because a handler injects only services; unit-tested beside it
|
|
1276
|
+
allows: [index.ts, "<saga>.module.ts", "<saga>.module-definition.ts", "<saga>.options.ts", "<saga>.saga.service.ts", "<saga>.saga.service.spec.ts", "<saga>.saga-state.ts", "<saga>.saga.log-events.ts", application/, steps/, compensations/, transport/] # the orchestrator is a `*.service.ts` because a handler injects only services; unit-tested beside it
|
|
1106
1277
|
tests: unit-beside # <saga>.saga.service.spec.ts beside the orchestrator; the state machine and its fence are platform/saga's service; each compensation path has an e2e spec
|
|
1107
1278
|
coverage: none
|
|
1108
1279
|
rules: [BE_KIND_ISOLATION, BE_FEATURE_IMPORTS_FEATURE, BE_FEATURE_NOT_COMPOSED, BE_SAGA_STEP_COMPENSATION, BE_SAGA_STATE_VERSIONED, BE_SAGA_EVENT_CONTRACT, BE_SAGA_E2E_MISSING]
|
|
1109
1280
|
- id: be.feature.saga.steps
|
|
1281
|
+
since: 3.1.0
|
|
1110
1282
|
profiles: [be]
|
|
1111
1283
|
path: "src/features/saga/<saga>/steps/"
|
|
1112
1284
|
pattern: saga
|
|
@@ -1118,6 +1290,7 @@ slots:
|
|
|
1118
1290
|
coverage: none
|
|
1119
1291
|
rules: [BE_SAGA_STEP_COMPENSATION, BE_SAGA_EVENT_CONTRACT]
|
|
1120
1292
|
- id: be.feature.saga.compensations
|
|
1293
|
+
since: 3.1.0
|
|
1121
1294
|
profiles: [be]
|
|
1122
1295
|
path: "src/features/saga/<saga>/compensations/"
|
|
1123
1296
|
pattern: saga
|
|
@@ -1141,30 +1314,33 @@ slots:
|
|
|
1141
1314
|
rules: [BE_APPLICATION_IMPORTS_TRANSPORT, BE_APPLICATION_TRANSPORT_FRAMEWORK, BE_FEATURE_IMPORTS_FEATURE]
|
|
1142
1315
|
since: 1.0.0
|
|
1143
1316
|
- id: be.transport.http
|
|
1317
|
+
since: 3.1.0
|
|
1144
1318
|
profiles: [be]
|
|
1145
1319
|
path: "src/features/api/<feature>/transport/http/"
|
|
1146
1320
|
presence: optional
|
|
1147
1321
|
tracked: tracked
|
|
1148
1322
|
tier: feature
|
|
1149
1323
|
requires: ["<feature>-http.module.ts"]
|
|
1150
|
-
allows: ["<action>.controller.ts", "dto/<action>.{request,response}.ts", "<action>.mapper.ts"]
|
|
1324
|
+
allows: ["<action>.controller.ts", "dto/<action>.{request,response}.ts", "<action>.mapper.ts", "<feature>-http.module-definition.ts", "<feature>-http.options.ts"]
|
|
1151
1325
|
tests: none
|
|
1152
1326
|
coverage: none
|
|
1153
1327
|
composedBy: [api]
|
|
1154
1328
|
rules: [BE_DEFAULT_DENY, BE_INPUT_BOUNDED, BE_TRANSPORT_SHAPE]
|
|
1155
1329
|
- id: be.transport.graphql
|
|
1330
|
+
since: 3.1.0
|
|
1156
1331
|
profiles: [be]
|
|
1157
1332
|
path: "src/features/api/<feature>/transport/graphql/"
|
|
1158
1333
|
presence: optional
|
|
1159
1334
|
tracked: tracked
|
|
1160
1335
|
tier: feature
|
|
1161
1336
|
requires: ["<feature>-graphql.module.ts"]
|
|
1162
|
-
allows: ["<action>.resolver.ts", "dto/<action>.{input,type,args}.ts", "<action>.mapper.ts"]
|
|
1337
|
+
allows: ["<action>.resolver.ts", "dto/<action>.{input,type,args}.ts", "<action>.mapper.ts", "<feature>-graphql.module-definition.ts", "<feature>-graphql.options.ts"]
|
|
1163
1338
|
tests: none
|
|
1164
1339
|
coverage: none
|
|
1165
1340
|
composedBy: [api]
|
|
1166
1341
|
rules: [BE_DEFAULT_DENY, BE_INPUT_BOUNDED, BE_TRANSPORT_SHAPE]
|
|
1167
1342
|
- id: be.transport.message
|
|
1343
|
+
since: 3.1.0
|
|
1168
1344
|
profiles: [be]
|
|
1169
1345
|
path: "src/features/{api/,reactors/,saga/}<feature>/transport/message/"
|
|
1170
1346
|
presence: opt-in # queue consumer: composed by the service that owns it (an api app) or by a worker app scaled apart
|
|
@@ -1172,7 +1348,7 @@ slots:
|
|
|
1172
1348
|
tracked: tracked
|
|
1173
1349
|
tier: feature
|
|
1174
1350
|
requires: ["<feature>-message.module.ts"]
|
|
1175
|
-
allows: ["<event>.consumer.ts"] # BE-CONVENTION 1.6: transport/message holds only consumers; a .message.ts is outside the closed suffix list
|
|
1351
|
+
allows: ["<event>.consumer.ts", "<feature>-message.module-definition.ts", "<feature>-message.options.ts"] # BE-CONVENTION 1.6: transport/message holds only consumers; a .message.ts is outside the closed suffix list
|
|
1176
1352
|
tests: none
|
|
1177
1353
|
coverage: none
|
|
1178
1354
|
composedBy: [api, worker]
|
|
@@ -1204,7 +1380,7 @@ slots:
|
|
|
1204
1380
|
tracked: tracked
|
|
1205
1381
|
tier: feature
|
|
1206
1382
|
requires: ["<provider>-http.module.ts", "<provider>.webhook.ts", "<provider>.webhook.spec.ts"]
|
|
1207
|
-
allows: ["<provider>-http.module.ts", "<provider>.webhook.ts", "<provider>.webhook.spec.ts", dto/]
|
|
1383
|
+
allows: ["<provider>-http.module.ts", "<provider>-http.module-definition.ts", "<provider>-http.options.ts", "<provider>.webhook.ts", "<provider>.webhook.spec.ts", dto/]
|
|
1208
1384
|
tests: unit-beside # the door's own spec (<provider>.webhook.spec.ts) beside the door: the one non-service unit spec of this kind
|
|
1209
1385
|
coverage: none
|
|
1210
1386
|
composedBy: [api]
|
|
@@ -1225,6 +1401,9 @@ slots:
|
|
|
1225
1401
|
since: 1.0.0
|
|
1226
1402
|
- id: be.feature.realtime
|
|
1227
1403
|
profiles: [be]
|
|
1404
|
+
exemplifiedBy:
|
|
1405
|
+
- {path: tests/fixtures/hfs-lite-slots/be/src/features/realtime/order-status/, edition: lite}
|
|
1406
|
+
editionWhy: "ruling:R212/case-2 (owner:lite-production-swarm) (with the Work boundary amended by ruling:R212/case-4): lite runs socket.io in the api process, one instance without Redis; full may scale its gateway."
|
|
1228
1407
|
path: "src/features/realtime/<channel>/"
|
|
1229
1408
|
presence: opt-in # a push channel: reads and pushes only (a socket gateway or a GraphQL subscription), no domain write
|
|
1230
1409
|
pattern: realtime # declaring the pattern (hfs.json sides.be.patterns) enables the kind (knowledge/patterns/be/realtime.yaml)
|
|
@@ -1245,15 +1424,16 @@ slots:
|
|
|
1245
1424
|
since: 1.0.0
|
|
1246
1425
|
- id: be.feature.realtime.graphql
|
|
1247
1426
|
profiles: [be]
|
|
1427
|
+
editionWhy: "ruling:R212/case-2 (owner:lite-production-swarm) (with the Work boundary amended by ruling:R212/case-4): lite uses only the in-process socket.io gateway; the GraphQL subscription door remains full-only."
|
|
1248
1428
|
path: "src/features/realtime/<channel>/transport/graphql/"
|
|
1249
1429
|
presence: opt-in # a GraphQL subscription over graphql-ws
|
|
1250
|
-
litePresence: forbidden #
|
|
1430
|
+
litePresence: forbidden # ruling:R212/case-2 (owner:lite-production-swarm) (with the Work boundary amended by ruling:R212/case-4): lite pushes through the api's in-process socket.io gateway
|
|
1251
1431
|
pattern: realtime
|
|
1252
1432
|
tracked: tracked
|
|
1253
1433
|
tier: feature
|
|
1254
1434
|
goesTo: "src/features/realtime/<channel>/transport/websocket/ (be.feature.realtime.websocket): both editions push through the socket.io gateway; lite runs it in the api process"
|
|
1255
1435
|
requires: ["<channel>-graphql.module.ts", "<channel>.subscription.ts", "<channel>.subscription.spec.ts"]
|
|
1256
|
-
allows: ["<channel>-graphql.module.ts", "<channel>.subscription.ts", "<channel>.subscription.spec.ts", dto/]
|
|
1436
|
+
allows: ["<channel>-graphql.module.ts", "<channel>-graphql.module-definition.ts", "<channel>-graphql.options.ts", "<channel>.subscription.ts", "<channel>.subscription.spec.ts", dto/]
|
|
1257
1437
|
tests: unit-beside # the door's own spec beside the door (a subscription has no service of its own)
|
|
1258
1438
|
coverage: none
|
|
1259
1439
|
composedBy: [api]
|
|
@@ -1261,6 +1441,7 @@ slots:
|
|
|
1261
1441
|
since: 1.0.0
|
|
1262
1442
|
- id: be.feature.realtime.dto
|
|
1263
1443
|
profiles: [be]
|
|
1444
|
+
editionWhy: "ruling:R212/case-2 (owner:lite-production-swarm) (with the Work boundary amended by ruling:R212/case-4): GraphQL subscription DTOs exist only with the full subscription door; lite uses socket.io contracts."
|
|
1264
1445
|
path: "src/features/realtime/<channel>/transport/graphql/dto/"
|
|
1265
1446
|
presence: opt-in
|
|
1266
1447
|
litePresence: forbidden # the dto of the graphql door, which lite does not serve
|
|
@@ -1282,20 +1463,21 @@ slots:
|
|
|
1282
1463
|
tracked: tracked
|
|
1283
1464
|
tier: feature
|
|
1284
1465
|
requires: ["<channel>-websocket.module.ts", "<channel>.gateway.ts", "<channel>.gateway.spec.ts"]
|
|
1285
|
-
allows: ["<channel>-websocket.module.ts", "<channel>.gateway.ts", "<channel>.gateway.spec.ts"]
|
|
1466
|
+
allows: ["<channel>-websocket.module.ts", "<channel>-websocket.module-definition.ts", "<channel>-websocket.options.ts", "<channel>.gateway.ts", "<channel>.gateway.spec.ts"]
|
|
1286
1467
|
tests: unit-beside # the door's own spec beside the door (a gateway has no service of its own)
|
|
1287
1468
|
coverage: none
|
|
1288
1469
|
composedBy: [api]
|
|
1289
1470
|
rules: [BE_REALTIME_SHAPE, BE_REALTIME_WRITES, BE_REALTIME_TOPIC_SCOPE, BE_DEFAULT_DENY]
|
|
1290
1471
|
since: 1.0.0
|
|
1291
1472
|
- id: be.cli
|
|
1473
|
+
since: 3.1.0
|
|
1292
1474
|
profiles: [be]
|
|
1293
1475
|
# The cli feature root: every one-off action of the back end, compiled only into apps/cli. A group is a nest-commander
|
|
1294
1476
|
# @Command (<group>/<group>.cli.ts) whose sub-commands are @SubCommand classes extending CommandRunner
|
|
1295
1477
|
# (<group>/subs/<name>.cli.ts), each with its unit spec beside it (<name>.cli.spec.ts): a command is an action runner, not a
|
|
1296
1478
|
# thin transport (BE_CLI_COMMAND_SHAPE). `.cli.ts` because `.command.ts` is the CQRS message (HFS delta 3). Every module of
|
|
1297
|
-
# the root
|
|
1298
|
-
# module provides its commands, and a command takes its inputs from a platform capability (the connections and their opener
|
|
1479
|
+
# the root has a module definition beside it (ruling:module-definition-everywhere) and no register override: cli.module.ts imports each
|
|
1480
|
+
# group module as <Group>Module.register({}), a group module provides its commands, and a command takes its inputs from a platform capability (the connections and their opener
|
|
1299
1481
|
# from platform/database) or from a command-line @Option.
|
|
1300
1482
|
path: "src/features/cli/"
|
|
1301
1483
|
presence: optional
|
|
@@ -1306,7 +1488,7 @@ slots:
|
|
|
1306
1488
|
requires: [index.ts, cli.module.ts]
|
|
1307
1489
|
# A composed root: an app of kind cli lists cli.module.ts, never a group module (BE_MODULE_SHAPE).
|
|
1308
1490
|
composedBy: [cli]
|
|
1309
|
-
allows: [index.ts, cli.module.ts, "<group>/<group>.cli.ts", "<group>/<group>.cli.spec.ts", "<group>/<group>.module.ts", "<group>/subs/<name>.cli.ts", "<group>/subs/<name>.cli.spec.ts"]
|
|
1491
|
+
allows: [index.ts, cli.module.ts, "cli.module-definition.ts", "cli.options.ts", "<group>/<group>.cli.ts", "<group>/<group>.cli.spec.ts", "<group>/<group>.module.ts", "<group>/<group>.module-definition.ts", "<group>/<group>.options.ts", "<group>/subs/<name>.cli.ts", "<group>/subs/<name>.cli.spec.ts"]
|
|
1310
1492
|
tests: unit-beside
|
|
1311
1493
|
coverage: none
|
|
1312
1494
|
rules: [BE_CLI_COMMAND_SHAPE, BE_CLI_OWNER, BE_CLI_REQUIRED, BE_MODULE_SHAPE, BE_SCHEMA_AUTHORITY]
|
|
@@ -1323,8 +1505,11 @@ slots:
|
|
|
1323
1505
|
tests: none
|
|
1324
1506
|
coverage: none
|
|
1325
1507
|
since: 3.0.0
|
|
1508
|
+
rules: [HFS_ASSET_OVERSIZE, HFS_ASSET_KIND]
|
|
1509
|
+
|
|
1326
1510
|
# ----- BE: modules (three tiers, closed set) -----------------------------------------------------------
|
|
1327
1511
|
- id: be.domain
|
|
1512
|
+
since: 3.1.0
|
|
1328
1513
|
profiles: [be]
|
|
1329
1514
|
path: "src/modules/domain/<capability>/"
|
|
1330
1515
|
presence: optional
|
|
@@ -1334,13 +1519,14 @@ slots:
|
|
|
1334
1519
|
requires: [index.ts]
|
|
1335
1520
|
# <name>.templates.ts holds the capability's static templates, copy and presets as TS constants (owner ruling 2026-10-13);
|
|
1336
1521
|
# user-editable ones are seed rows, large or user media live in object storage.
|
|
1337
|
-
allows: ["<capability>.module.ts", "<capability>.config.ts", "<capability>.options.ts", errors/, messages/, persistence/, policies/, "*.service.ts", "*.service.spec.ts", "*.contracts.ts", "*.templates.ts"]
|
|
1522
|
+
allows: ["<capability>.module.ts", "<capability>.module-definition.ts", "<capability>.config.ts", "<capability>.options.ts", errors/, messages/, persistence/, policies/, "*.service.ts", "*.service.spec.ts", "*.contracts.ts", "*.templates.ts"]
|
|
1338
1523
|
forbids: [testing/, exceptions/, utils/, helpers/, shared/]
|
|
1339
1524
|
tests: unit-beside # <name>.service.spec.ts beside each <name>.service.ts, nothing else
|
|
1340
1525
|
coverage: required
|
|
1341
1526
|
budget: {indexExports: 60}
|
|
1342
1527
|
rules: [BE_TIER_DIRECTION, ARCH_OWNER_CYCLE, BE_PUBLIC_SURFACE, BE_ERROR_HOME, BE_CONFIG_OWNER, BE_CONTEXT_COUPLING, BE_CONTEXT_TRANSACTION]
|
|
1343
1528
|
- id: be.errors
|
|
1529
|
+
since: 3.1.0
|
|
1344
1530
|
profiles: [be]
|
|
1345
1531
|
path: "src/modules/{domain,integrations}/<capability>/errors/"
|
|
1346
1532
|
presence: optional
|
|
@@ -1373,7 +1559,9 @@ slots:
|
|
|
1373
1559
|
rules: [BE_USER_COPY_LITERAL]
|
|
1374
1560
|
since: 1.0.0
|
|
1375
1561
|
- id: be.persistence
|
|
1562
|
+
since: 3.1.0
|
|
1376
1563
|
profiles: [be]
|
|
1564
|
+
editionWhy: "ruling:R212/case-2 (owner:lite-production-swarm) (with the Work boundary amended by ruling:R212/case-4): Supabase migrations own lite tables, with SQL and row types here; full keeps TypeORM entities and migrations here."
|
|
1377
1565
|
path: "src/modules/{domain,platform,projections}/<capability>/persistence/"
|
|
1378
1566
|
# R178: the platform capabilities whose tables exist on EVERY connection that uses them (the event-bus outbox, the inbox of every service, the job table); their arrays may be
|
|
1379
1567
|
# registered on several declared connections. Any other capability registers its arrays on exactly one (BE_SCHEMA_OWNER).
|
|
@@ -1394,6 +1582,7 @@ slots:
|
|
|
1394
1582
|
coverage: none
|
|
1395
1583
|
rules: [BE_SCHEMA_OWNER, BE_SCHEMA_AUTHORITY, BE_SQL_OUTSIDE_PERSISTENCE, BE_SQL_TABLE_OWNER, BE_SQL_RETURNING_SHAPE, BE_CONTEXT_COUPLING, BE_CONTEXT_PLATFORM_TABLES]
|
|
1396
1584
|
- id: be.platform
|
|
1585
|
+
since: 3.1.0
|
|
1397
1586
|
profiles: [be]
|
|
1398
1587
|
path: "src/modules/platform/<capability>/"
|
|
1399
1588
|
presence: required
|
|
@@ -1416,7 +1605,11 @@ slots:
|
|
|
1416
1605
|
rules: [BE_TIER_DIRECTION, ARCH_OWNER_CYCLE, BE_MODULE_SHAPE]
|
|
1417
1606
|
# ----- BE: pattern slots (opt-in; enabled by hfs.json sides.be.patterns). A path under one is HFS_SLOT_NOT_ENABLED unless its pattern is declared.
|
|
1418
1607
|
- id: be.platform.event-bus
|
|
1608
|
+
since: 3.1.0
|
|
1419
1609
|
profiles: [be]
|
|
1610
|
+
exemplifiedBy:
|
|
1611
|
+
- {path: tests/fixtures/hfs-lite-slots/be/src/modules/platform/event-bus/, edition: lite}
|
|
1612
|
+
editionWhy: "ruling:R212/case-2 (owner:lite-production-swarm) (with the Work boundary amended by ruling:R212/case-4): lite implements the event ports with outbox and pgmq; full uses its Kafka adapter."
|
|
1420
1613
|
path: "src/modules/platform/event-bus/"
|
|
1421
1614
|
presence: opt-in
|
|
1422
1615
|
pattern: event-bus # the ONE durable event transport: transactional outbox, relay, Kafka, inbox, retry and dead letter (knowledge/patterns/be/event-bus.yaml)
|
|
@@ -1435,7 +1628,9 @@ slots:
|
|
|
1435
1628
|
budget: {indexExports: 60}
|
|
1436
1629
|
rules: [BE_TIER_DIRECTION, ARCH_OWNER_CYCLE, BE_MODULE_SHAPE, BE_INFRA_OWNER]
|
|
1437
1630
|
- id: be.platform.queue
|
|
1631
|
+
since: 3.1.0
|
|
1438
1632
|
profiles: [be]
|
|
1633
|
+
editionWhy: "ruling:R212/case-2 (owner:lite-production-swarm) (with the Work boundary amended by ruling:R212/case-4): lite implements queue ports with pgmq and pg_cron; full uses BullMQ and Redis."
|
|
1439
1634
|
path: "src/modules/platform/queue/"
|
|
1440
1635
|
presence: opt-in
|
|
1441
1636
|
pattern: queue # the ONE BullMQ owner: the outbox relay into queues (jobId = outbox id) and job schedulers (knowledge/patterns/be/queues.yaml)
|
|
@@ -1454,6 +1649,7 @@ slots:
|
|
|
1454
1649
|
budget: {indexExports: 60}
|
|
1455
1650
|
rules: [BE_TIER_DIRECTION, ARCH_OWNER_CYCLE, BE_MODULE_SHAPE, BE_INFRA_OWNER]
|
|
1456
1651
|
- id: be.events
|
|
1652
|
+
since: 3.1.0
|
|
1457
1653
|
profiles: [be]
|
|
1458
1654
|
path: "src/modules/events/<service>/"
|
|
1459
1655
|
presence: opt-in
|
|
@@ -1467,6 +1663,7 @@ slots:
|
|
|
1467
1663
|
coverage: none
|
|
1468
1664
|
rules: [BE_EVENT_CLASS_CONTRACT, BE_TIER_DIRECTION]
|
|
1469
1665
|
- id: be.queues
|
|
1666
|
+
since: 3.1.0
|
|
1470
1667
|
profiles: [be]
|
|
1471
1668
|
path: "src/modules/queues/<queue>/"
|
|
1472
1669
|
presence: opt-in
|
|
@@ -1475,11 +1672,12 @@ slots:
|
|
|
1475
1672
|
tier: queues
|
|
1476
1673
|
owner: true
|
|
1477
1674
|
requires: [index.ts, "<queue>.queue.ts"] # the module exists when a domain module imports the producer; a scheduler-only queue has none
|
|
1478
|
-
allows: ["<queue>.queue.ts", "<queue>.module.ts", "<queue>.contracts.ts", index.ts]
|
|
1675
|
+
allows: ["<queue>.queue.ts", "<queue>.module.ts", "<queue>.module-definition.ts", "<queue>.options.ts", "<queue>.contracts.ts", index.ts]
|
|
1479
1676
|
tests: none
|
|
1480
1677
|
coverage: none
|
|
1481
1678
|
rules: [BE_OUTBOX_WRITE_TX, BE_TIER_DIRECTION]
|
|
1482
1679
|
- id: be.feature.reactors
|
|
1680
|
+
since: 3.1.0
|
|
1483
1681
|
profiles: [be]
|
|
1484
1682
|
path: "src/features/reactors/<reactor>/"
|
|
1485
1683
|
presence: opt-in
|
|
@@ -1489,11 +1687,12 @@ slots:
|
|
|
1489
1687
|
tier: feature
|
|
1490
1688
|
owner: true
|
|
1491
1689
|
requires: [index.ts, "<reactor>.module.ts", transport/]
|
|
1492
|
-
allows: [index.ts, "<reactor>.module.ts", application/, transport/]
|
|
1690
|
+
allows: [index.ts, "<reactor>.module.ts", "<reactor>.module-definition.ts", "<reactor>.options.ts", application/, transport/]
|
|
1493
1691
|
tests: none
|
|
1494
1692
|
coverage: none
|
|
1495
1693
|
rules: [BE_KIND_ISOLATION, BE_FEATURE_IMPORTS_FEATURE, BE_FEATURE_NOT_COMPOSED]
|
|
1496
1694
|
- id: be.feature.jobs
|
|
1695
|
+
since: 3.1.0
|
|
1497
1696
|
profiles: [be]
|
|
1498
1697
|
path: "src/features/jobs/<job>/"
|
|
1499
1698
|
presence: opt-in
|
|
@@ -1503,11 +1702,12 @@ slots:
|
|
|
1503
1702
|
tier: feature
|
|
1504
1703
|
owner: true
|
|
1505
1704
|
requires: [index.ts, "<job>.module.ts", steps/, transport/]
|
|
1506
|
-
allows: [index.ts, "<job>.module.ts", application/, steps/, transport/]
|
|
1705
|
+
allows: [index.ts, "<job>.module.ts", "<job>.module-definition.ts", "<job>.options.ts", application/, steps/, transport/]
|
|
1507
1706
|
tests: none
|
|
1508
1707
|
coverage: none
|
|
1509
1708
|
rules: [BE_KIND_ISOLATION, BE_FEATURE_IMPORTS_FEATURE, BE_FEATURE_NOT_COMPOSED]
|
|
1510
1709
|
- id: be.feature.jobs.queue
|
|
1710
|
+
since: 3.1.0
|
|
1511
1711
|
profiles: [be]
|
|
1512
1712
|
path: "src/features/jobs/<job>/transport/queue/"
|
|
1513
1713
|
presence: opt-in # the queue transport of the job: its module and the processor the job runner hands the fenced claim to
|
|
@@ -1515,12 +1715,13 @@ slots:
|
|
|
1515
1715
|
tracked: tracked
|
|
1516
1716
|
tier: feature
|
|
1517
1717
|
requires: ["<job>-queue.module.ts", "<job>.processor.ts"]
|
|
1518
|
-
allows: ["<job>-queue.module.ts", "<job>.processor.ts"]
|
|
1718
|
+
allows: ["<job>-queue.module.ts", "<job>-queue.module-definition.ts", "<job>-queue.options.ts", "<job>.processor.ts"]
|
|
1519
1719
|
tests: none
|
|
1520
1720
|
coverage: none
|
|
1521
1721
|
composedBy: [api, worker]
|
|
1522
1722
|
rules: [BE_BACKGROUND_UNOWNED]
|
|
1523
1723
|
- id: be.feature.jobs.steps
|
|
1724
|
+
since: 3.1.0
|
|
1524
1725
|
profiles: [be]
|
|
1525
1726
|
path: "src/features/jobs/<job>/steps/"
|
|
1526
1727
|
presence: opt-in
|
|
@@ -1532,7 +1733,9 @@ slots:
|
|
|
1532
1733
|
coverage: none
|
|
1533
1734
|
rules: [BE_JOB_SHAPE, BE_JOB_RUN_KEY]
|
|
1534
1735
|
- id: be.platform.jobs
|
|
1736
|
+
since: 3.1.0
|
|
1535
1737
|
profiles: [be]
|
|
1738
|
+
editionWhy: "ruling:R212/case-2 (owner:lite-production-swarm) (with the Work boundary amended by ruling:R212/case-4): lite persists fenced claims in Supabase migrations and dispatches through pgmq; full uses TypeORM schema authority and its queue adapter."
|
|
1536
1739
|
path: "src/modules/platform/jobs/"
|
|
1537
1740
|
presence: opt-in
|
|
1538
1741
|
pattern: fenced-job # the ONE fencing primitive: the job entity, the claim that bumps the token, guarded writes (knowledge/patterns/be/jobs.yaml)
|
|
@@ -1545,7 +1748,11 @@ slots:
|
|
|
1545
1748
|
budget: {indexExports: 60}
|
|
1546
1749
|
rules: [BE_TIER_DIRECTION, ARCH_OWNER_CYCLE, BE_MODULE_SHAPE, BE_JOB_WRITE_OUTSIDE_OWNER, BE_JOB_FENCE_REQUIRED]
|
|
1547
1750
|
- id: be.projections
|
|
1751
|
+
since: 3.1.0
|
|
1548
1752
|
profiles: [be]
|
|
1753
|
+
exemplifiedBy:
|
|
1754
|
+
- {path: tests/fixtures/hfs-lite-slots/be/src/modules/projections/order-summary/, edition: lite}
|
|
1755
|
+
editionWhy: "ruling:R212/case-2 (owner:lite-production-swarm) (with the Work boundary amended by ruling:R212/case-4): lite projection tables live in Supabase migrations; full keeps the TypeORM projection entity beside its projection."
|
|
1549
1756
|
path: "src/modules/projections/<name>/"
|
|
1550
1757
|
presence: opt-in
|
|
1551
1758
|
pattern: projection # a read model: only its projection writes its table; recompute is idempotent and replayable; readers use get*
|
|
@@ -1561,6 +1768,7 @@ slots:
|
|
|
1561
1768
|
coverage: required
|
|
1562
1769
|
rules: [BE_PROJECTION_WRITE_OWNER, BE_PROJECTION_SHAPE, BE_TIER_DIRECTION, BE_CONTEXT_COUPLING]
|
|
1563
1770
|
- id: be.integrations
|
|
1771
|
+
since: 3.1.0
|
|
1564
1772
|
profiles: [be]
|
|
1565
1773
|
path: "src/modules/integrations/<provider>/"
|
|
1566
1774
|
presence: optional
|
|
@@ -1573,6 +1781,7 @@ slots:
|
|
|
1573
1781
|
budget: {indexExports: 80} # a provider's port surface includes clients, config, options, tokens and errors
|
|
1574
1782
|
rules: [BE_TIER_DIRECTION, BE_ERROR_HOME, BE_SECRET_DEFAULT, BE_INTEGRATION_SPEC_MISSING]
|
|
1575
1783
|
- id: be.integrations.model
|
|
1784
|
+
since: 3.1.0
|
|
1576
1785
|
profiles: [be]
|
|
1577
1786
|
path: "src/modules/integrations/<provider>/model/"
|
|
1578
1787
|
presence: opt-in
|
|
@@ -1581,8 +1790,14 @@ slots:
|
|
|
1581
1790
|
tests: none
|
|
1582
1791
|
coverage: required
|
|
1583
1792
|
why: an ML or LLM model client is an integration; weights and datasets are never in git
|
|
1793
|
+
rules: [BE_TIER_DIRECTION, BE_INFRA_OWNER, BE_INTEGRATION_SPEC_MISSING]
|
|
1794
|
+
|
|
1584
1795
|
- id: be.integrations.supabase
|
|
1585
1796
|
profiles: [be]
|
|
1797
|
+
exemplifiedBy:
|
|
1798
|
+
- packages/hfs/templates/be/skeleton-lite/src/modules/integrations/supabase/
|
|
1799
|
+
- {path: tests/fixtures/hfs-callback-full/be/src/modules/integrations/supabase/, edition: full}
|
|
1800
|
+
editionWhy: "ruling:R212/case-2 (owner:lite-production-swarm) (with the Work boundary amended by ruling:R212/case-4): lite requires the Supabase Auth and Storage integration; full opts in through a connection."
|
|
1586
1801
|
# The only be import of @supabase/* (L10): the Auth admin and Storage admin client, plus the JWKS-based verification of
|
|
1587
1802
|
# the Supabase access token the guard chain reads (L16). The connection itself is ordinary Postgres (the shared
|
|
1588
1803
|
# EntityManager of the declared connection); what lives here are the service calls no database role covers.
|
|
@@ -1604,7 +1819,11 @@ slots:
|
|
|
1604
1819
|
# lives under src/tests/, one folder per kind, and the folder and the file suffix always agree (a file whose suffix does
|
|
1605
1820
|
# not match its folder matches no slot: HFS_SLOT_UNDECLARED).
|
|
1606
1821
|
- id: be.tests.world
|
|
1822
|
+
since: 3.1.0
|
|
1607
1823
|
profiles: [be]
|
|
1824
|
+
exemplifiedBy:
|
|
1825
|
+
- {path: tests/fixtures/hfs-lite-slots/be/src/tests/world/, edition: lite}
|
|
1826
|
+
editionWhy: "ruling:R212/case-2 (owner:lite-production-swarm) (with the Work boundary amended by ruling:R212/case-4): lite integration proofs use the local Supabase stack and network fakes; full also boots apps and self-hosted services."
|
|
1608
1827
|
path: "src/tests/world/"
|
|
1609
1828
|
presence: optional
|
|
1610
1829
|
tracked: tracked
|
|
@@ -1628,7 +1847,10 @@ slots:
|
|
|
1628
1847
|
failNext/replayWebhook/delay), world.waitFor; fakes/<provider>/ holds the fake servers and payload fixtures, kit/ the inlined test helpers, and the world root may hold role-suffixed helpers (<name>.client.ts, <name>.contracts.ts, ...). Nothing is
|
|
1629
1848
|
overridden in the DI container. It is the only test location that may import typeorm's DataSource or testcontainers,
|
|
1630
1849
|
call migrate/runMigrations/synchronize, or write process.env.
|
|
1850
|
+
rules: [BE_TEST_TOPOLOGY, BE_TEST_CONSTRAINTS_ON]
|
|
1851
|
+
|
|
1631
1852
|
- id: be.tests.world.kit
|
|
1853
|
+
since: 3.1.0
|
|
1632
1854
|
profiles: [be]
|
|
1633
1855
|
path: "src/tests/world/kit/"
|
|
1634
1856
|
presence: optional
|
|
@@ -1639,6 +1861,7 @@ slots:
|
|
|
1639
1861
|
coverage: none
|
|
1640
1862
|
why: the helpers the test world needs (polling, free ports, transports), inlined so the world imports no other repository; a bare <name>.ts entry in allows is what admits a plain name in BE_SOURCE_FORM
|
|
1641
1863
|
- id: be.tests.fixtures
|
|
1864
|
+
since: 3.1.0
|
|
1642
1865
|
profiles: [be]
|
|
1643
1866
|
path: "src/tests/fixtures/"
|
|
1644
1867
|
presence: optional
|
|
@@ -1649,6 +1872,7 @@ slots:
|
|
|
1649
1872
|
coverage: none
|
|
1650
1873
|
why: payload fixtures with a role suffix (the doubles come from @starci/jest-preset, not from here); the test data builders live in its builders/ slot; importable by unit specs, integration and e2e; never imports a feature
|
|
1651
1874
|
- id: be.tests.fixtures.builders
|
|
1875
|
+
since: 3.1.0
|
|
1652
1876
|
profiles: [be]
|
|
1653
1877
|
path: "src/tests/fixtures/builders/*.builder.ts"
|
|
1654
1878
|
presence: optional
|
|
@@ -1663,6 +1887,7 @@ slots:
|
|
|
1663
1887
|
*.fixture.ts or *.factory.ts anywhere, is a BE_SOURCE_FORM finding
|
|
1664
1888
|
rules: [BE_SOURCE_FORM]
|
|
1665
1889
|
- id: be.tests.fixtures.i18n
|
|
1890
|
+
since: 3.1.0
|
|
1666
1891
|
profiles: [be]
|
|
1667
1892
|
path: "src/tests/fixtures/i18n/"
|
|
1668
1893
|
presence: optional
|
|
@@ -1673,7 +1898,9 @@ slots:
|
|
|
1673
1898
|
coverage: none
|
|
1674
1899
|
why: the ONLY test-fixture location where Vietnamese or another language may appear in source (LANG_NOT_ENGLISH); placement is the marker, there is no comment pragma
|
|
1675
1900
|
- id: be.tests.integration
|
|
1901
|
+
since: 3.1.0
|
|
1676
1902
|
profiles: [be]
|
|
1903
|
+
editionWhy: "Lite integration specs use the local Supabase CLI stack without containers or toxiproxy (ruling:R212/case-1, amended by ruling:R212/case-2 (owner:lite-production-swarm) and ruling:R212/case-4, with contracts allowed by owner:lite-contract-specs); R284 requires declared-pattern proofs there. Full additionally proves infrastructure outages through its world (R112)."
|
|
1677
1904
|
path: "src/tests/integration/<capability>/*.integration-spec.ts"
|
|
1678
1905
|
presence: optional
|
|
1679
1906
|
tracked: tracked
|
|
@@ -1687,10 +1914,12 @@ slots:
|
|
|
1687
1914
|
why: one capability module through useTestWorld({ modules }), no HTTP door of ours (SQL, transactions, concurrency, inbox claims on the real database; and every integration client, `<capability>` being its provider folder, against our real infra, a real peer app or the library fake at the network edge, with its success, its ErrorCode refusal mapping and an outage through the world, R112); run by test:integration
|
|
1688
1915
|
rules: [BE_TEST_TOPOLOGY, BE_INTEGRATION_SPEC_MISSING]
|
|
1689
1916
|
- id: be.tests.e2e
|
|
1917
|
+
since: 3.1.0
|
|
1690
1918
|
profiles: [be]
|
|
1919
|
+
editionWhy: "ruling:R212/case-2 (owner:lite-production-swarm) (with the Work boundary amended by ruling:R212/case-4): full proves app flows through its e2e world; lite uses the R272 wiring proof and unit/integration specs."
|
|
1691
1920
|
path: "src/tests/e2e/<area>/*.e2e-spec.ts"
|
|
1692
1921
|
presence: optional
|
|
1693
|
-
litePresence: forbidden #
|
|
1922
|
+
litePresence: forbidden # ruling:R212/case-2 (owner:lite-production-swarm) (with the Work boundary amended by ruling:R212/case-4): lite has no e2e world
|
|
1694
1923
|
tracked: tracked
|
|
1695
1924
|
tier: e2e
|
|
1696
1925
|
tests: e2e
|
|
@@ -1699,18 +1928,20 @@ slots:
|
|
|
1699
1928
|
why: flow e2e through useTestWorld({ apps }); external services are the world's network fakes; run by test:e2e
|
|
1700
1929
|
rules: [HFS_CI_E2E_AUTOMATIC, BE_TEST_TOPOLOGY, BE_E2E_FLOW]
|
|
1701
1930
|
- id: be.tests.contract
|
|
1931
|
+
since: 3.1.0
|
|
1702
1932
|
profiles: [be]
|
|
1703
1933
|
path: "src/tests/contract/<provider>/*.contract-spec.ts"
|
|
1704
1934
|
presence: optional
|
|
1705
|
-
litePresence:
|
|
1935
|
+
litePresence: optional # owner decision 2026-10-14: lite may run provider sandbox contracts
|
|
1706
1936
|
tracked: tracked
|
|
1707
1937
|
tier: e2e
|
|
1708
1938
|
tests: e2e
|
|
1709
|
-
goesTo: "
|
|
1939
|
+
goesTo: "src/tests/contract/<provider>/*.contract-spec.ts in either edition: optional provider sandbox specs via test:contract; e2e stays full-only"
|
|
1710
1940
|
coverage: none
|
|
1711
1941
|
why: provider sandbox contracts, skipped without sandbox config; run only by test:contract, never part of test or test:e2e
|
|
1712
1942
|
rules: [BE_TEST_TOPOLOGY]
|
|
1713
1943
|
- id: be.tests.e2e-world-dir
|
|
1944
|
+
since: 3.1.0
|
|
1714
1945
|
profiles: [be]
|
|
1715
1946
|
path: "src/tests/e2e/world/"
|
|
1716
1947
|
presence: forbidden
|
|
@@ -1721,6 +1952,7 @@ slots:
|
|
|
1721
1952
|
|
|
1722
1953
|
# ----- FE: apps ----------------------------------------------------------------------------------------
|
|
1723
1954
|
- id: fe.app.next
|
|
1955
|
+
since: 3.1.0
|
|
1724
1956
|
profiles: [fe]
|
|
1725
1957
|
# Each fe app is an npm workspace: its package.json is @<project>/<app> with its own runtime dependencies and the fixed
|
|
1726
1958
|
# workspace scripts (HFS_MONO_FE_WORKSPACE); turbo runs them.
|
|
@@ -1735,6 +1967,7 @@ slots:
|
|
|
1735
1967
|
tests: none
|
|
1736
1968
|
rules: [FE_APP_ISOLATION, FE_ERROR_BOUNDARY_MISSING, FE_NEXT_CONVENTIONS, HFS_MONO_FE_WORKSPACE, HFS_MONO_WORKSPACE_DEP]
|
|
1737
1969
|
- id: fe.app-optional
|
|
1970
|
+
since: 3.1.0
|
|
1738
1971
|
profiles: [fe]
|
|
1739
1972
|
path: "apps/<app>/public/"
|
|
1740
1973
|
presence: optional
|
|
@@ -1742,12 +1975,13 @@ slots:
|
|
|
1742
1975
|
tier: none
|
|
1743
1976
|
tests: none
|
|
1744
1977
|
- id: fe.route
|
|
1978
|
+
since: 3.1.0
|
|
1745
1979
|
profiles: [fe]
|
|
1746
1980
|
path: "apps/<app>/src/app/"
|
|
1747
1981
|
presence: required
|
|
1748
1982
|
tracked: tracked
|
|
1749
1983
|
tier: route
|
|
1750
|
-
#
|
|
1984
|
+
# ruling:R212/case-2 (owner:lite-production-swarm) (with the Work boundary amended by ruling:R212/case-4) (lite = full): routes are thin and the front end serves no API - health/live is the one
|
|
1751
1985
|
# route handler of this slot, the Supabase auth callback is fe.route.callback, and no file holds a Server Action
|
|
1752
1986
|
# (FE_ROUTE_HANDLER_FORBIDDEN, FE_SERVER_ACTION_FORBIDDEN).
|
|
1753
1987
|
allows: ["[locale]/**/{page,layout,template,loading,error,not-found,default}.tsx", "[locale]/providers.tsx",
|
|
@@ -1760,6 +1994,7 @@ slots:
|
|
|
1760
1994
|
tests: none
|
|
1761
1995
|
rules: [FE_ROUTE_FILES_THIN, FE_CLIENT_BOUNDARY, FE_OWNER_REACHABLE, FE_ROUTE_HANDLER_FORBIDDEN, FE_SERVER_ACTION_FORBIDDEN]
|
|
1762
1996
|
- id: fe.source-root-pinned
|
|
1997
|
+
since: 3.1.0
|
|
1763
1998
|
profiles: [fe]
|
|
1764
1999
|
path: "apps/<app>/src/{proxy.ts,instrumentation.ts,instrumentation-client.ts,sentry.server.config.ts,sentry.edge.config.ts}"
|
|
1765
2000
|
presence: optional
|
|
@@ -1769,6 +2004,7 @@ slots:
|
|
|
1769
2004
|
tests: none
|
|
1770
2005
|
rules: [FE_NEXT_CONVENTIONS] # middleware.ts is refused on Next >= 16 (fe.source-root-middleware)
|
|
1771
2006
|
- id: fe.source-root-middleware
|
|
2007
|
+
since: 3.1.0
|
|
1772
2008
|
profiles: [fe]
|
|
1773
2009
|
path: "apps/<app>/src/{middleware.ts,middleware.js}"
|
|
1774
2010
|
presence: forbidden
|
|
@@ -1779,6 +2015,8 @@ slots:
|
|
|
1779
2015
|
rules: [FE_NEXT_CONVENTIONS]
|
|
1780
2016
|
- id: fe.route.callback
|
|
1781
2017
|
profiles: [fe]
|
|
2018
|
+
exemplifiedBy:
|
|
2019
|
+
- {path: tests/fixtures/hfs-callback-full/fe/apps/web/src/app/auth/callback/route.ts, edition: full}
|
|
1782
2020
|
# The auth callback of a Supabase session (the PKCE code exchange), kept only when the auth flow truly needs a server exchange
|
|
1783
2021
|
# (prefer the client-side PKCE exchange in fe.modules.auth): the one non-health route handler of an app with a Supabase
|
|
1784
2022
|
# connection; an app without one has no route handler in fe/ (L15).
|
|
@@ -1789,7 +2027,10 @@ slots:
|
|
|
1789
2027
|
tier: route
|
|
1790
2028
|
tests: none
|
|
1791
2029
|
since: 2.1.0
|
|
2030
|
+
rules: [FE_ROUTE_HANDLER_FORBIDDEN, FE_CALLBACK_SHAPE, FE_SUPABASE_CLIENT_OWNER, FE_SERVICE_ROLE_FORBIDDEN]
|
|
2031
|
+
|
|
1792
2032
|
- id: fe.feature
|
|
2033
|
+
since: 3.1.0
|
|
1793
2034
|
profiles: [fe]
|
|
1794
2035
|
path: "apps/<app>/src/features/{pages,layouts,overlays}/<name>/"
|
|
1795
2036
|
presence: required
|
|
@@ -1804,6 +2045,7 @@ slots:
|
|
|
1804
2045
|
budget: {component.tsx: 300, index.tsx: 200, dataHooks: 6, useState: 6}
|
|
1805
2046
|
rules: [FE_SIZE_BUDGET, FE_OWNER_REACHABLE, FE_I18N_LITERAL]
|
|
1806
2047
|
- id: fe.components
|
|
2048
|
+
since: 3.1.0
|
|
1807
2049
|
profiles: [fe]
|
|
1808
2050
|
path: "apps/<app>/src/components/{blocks,composites,branches,leaves}/<name>/"
|
|
1809
2051
|
presence: optional
|
|
@@ -1834,6 +2076,7 @@ slots:
|
|
|
1834
2076
|
since: 3.0.0
|
|
1835
2077
|
rules: [FE_CLIENT_BOUNDARY]
|
|
1836
2078
|
- id: fe.hooks
|
|
2079
|
+
since: 3.1.0
|
|
1837
2080
|
profiles: [fe]
|
|
1838
2081
|
path: "apps/<app>/src/hooks/<domain>/"
|
|
1839
2082
|
presence: optional
|
|
@@ -1956,7 +2199,10 @@ slots:
|
|
|
1956
2199
|
tests: none
|
|
1957
2200
|
budget: {file: 400}
|
|
1958
2201
|
since: 3.0.0
|
|
2202
|
+
rules: [FE_SOURCE_FORM, FE_HOOKS_ARE_HOOKS, FE_OWNER_REACHABLE]
|
|
2203
|
+
|
|
1959
2204
|
- id: fe.modules
|
|
2205
|
+
since: 3.1.0
|
|
1960
2206
|
profiles: [fe]
|
|
1961
2207
|
path: "apps/<app>/src/modules/<capability>/"
|
|
1962
2208
|
presence: required
|
|
@@ -1970,6 +2216,7 @@ slots:
|
|
|
1970
2216
|
budget: {file: 400}
|
|
1971
2217
|
rules: [FE_ENV_OWNER, FE_TRANSPORT_OWNER]
|
|
1972
2218
|
- id: fe.modules.api
|
|
2219
|
+
since: 3.1.0
|
|
1973
2220
|
profiles: [fe]
|
|
1974
2221
|
path: "apps/<app>/src/modules/api/"
|
|
1975
2222
|
presence: optional
|
|
@@ -1989,7 +2236,20 @@ slots:
|
|
|
1989
2236
|
entries: ["graphql/queries/query-*.ts", "graphql/queries/types/*.ts", "graphql/mutations/mutation-*.ts", "graphql/mutations/types/*.ts"]
|
|
1990
2237
|
tests: none
|
|
1991
2238
|
rules: [FE_TRANSPORT_OWNER, FE_HTTP_STATUS_COLLAPSE, FE_WIRE_GENERATED]
|
|
2239
|
+
- id: fe.transport.generated
|
|
2240
|
+
since: 3.2.0
|
|
2241
|
+
profiles: [fe]
|
|
2242
|
+
path: "apps/<app>/src/modules/api/graphql/__generated__/"
|
|
2243
|
+
presence: optional
|
|
2244
|
+
tracked: ignored
|
|
2245
|
+
tier: transport
|
|
2246
|
+
tests: none
|
|
2247
|
+
why: the canonical GraphQL client output of codegen is local and never committed; this specific slot overrides the tracked API parent
|
|
2248
|
+
rules: [FE_WIRE_GENERATED]
|
|
2249
|
+
exemplifiedBy:
|
|
2250
|
+
- {path: tests/fixtures/hfs-generated-wire/fe/apps/web/src/modules/api/graphql/__generated__/graphql.ts, editions: [lite, full]}
|
|
1992
2251
|
- id: fe.transport.client
|
|
2252
|
+
since: 3.1.0
|
|
1993
2253
|
profiles: [fe]
|
|
1994
2254
|
path: "apps/<app>/src/modules/api/graphql/clients/"
|
|
1995
2255
|
presence: optional
|
|
@@ -1999,6 +2259,7 @@ slots:
|
|
|
1999
2259
|
why: the auth and no-auth Apollo clients and their link chain (retry, error, timeout, token refresh, attach access token, http) of the one app that calls the back end; when several apps do, the shared client is fe.package.api.client
|
|
2000
2260
|
rules: [FE_TRANSPORT_OWNER]
|
|
2001
2261
|
- id: fe.transport.outcome
|
|
2262
|
+
since: 3.1.0
|
|
2002
2263
|
profiles: [fe]
|
|
2003
2264
|
path: "apps/<app>/src/modules/api/graphql/types.ts"
|
|
2004
2265
|
presence: optional
|
|
@@ -2009,6 +2270,7 @@ slots:
|
|
|
2009
2270
|
why: the one GraphQLResponse union ({success, message, error, data}) of the one app that calls the back end; when several apps do, the shared union is fe.package.api.outcome
|
|
2010
2271
|
rules: [FE_HTTP_STATUS_COLLAPSE]
|
|
2011
2272
|
- id: fe.modules.config
|
|
2273
|
+
since: 3.1.0
|
|
2012
2274
|
profiles: [fe]
|
|
2013
2275
|
path: "apps/<app>/src/modules/config/"
|
|
2014
2276
|
presence: required
|
|
@@ -2016,7 +2278,10 @@ slots:
|
|
|
2016
2278
|
tier: foundation
|
|
2017
2279
|
tests: none
|
|
2018
2280
|
why: the only reader of process.env and NEXT_PUBLIC_*; fails fast, no localhost fallback
|
|
2281
|
+
rules: [FE_ENV_OWNER]
|
|
2282
|
+
|
|
2019
2283
|
- id: fe.modules.i18n
|
|
2284
|
+
since: 3.1.0
|
|
2020
2285
|
profiles: [fe]
|
|
2021
2286
|
path: "apps/<app>/src/modules/i18n/"
|
|
2022
2287
|
presence: required
|
|
@@ -2029,6 +2294,7 @@ slots:
|
|
|
2029
2294
|
tests: none
|
|
2030
2295
|
rules: [FE_I18N_PLACEMENT, FE_I18N_CATALOG]
|
|
2031
2296
|
- id: fe.modules.brand
|
|
2297
|
+
since: 3.1.0
|
|
2032
2298
|
profiles: [fe]
|
|
2033
2299
|
path: "apps/<app>/src/modules/brand/brand.css"
|
|
2034
2300
|
presence: optional
|
|
@@ -2038,6 +2304,7 @@ slots:
|
|
|
2038
2304
|
why: the ONLY app file that may hold colour values; sets grammar brand tokens only, light and dark
|
|
2039
2305
|
rules: [FE_STYLE_TOKEN_ONLY]
|
|
2040
2306
|
- id: fe.modules.routes
|
|
2307
|
+
since: 3.1.0
|
|
2041
2308
|
profiles: [fe]
|
|
2042
2309
|
path: "apps/<app>/src/modules/routes/"
|
|
2043
2310
|
presence: required
|
|
@@ -2045,7 +2312,10 @@ slots:
|
|
|
2045
2312
|
tier: foundation
|
|
2046
2313
|
tests: none
|
|
2047
2314
|
why: every href builder; FE_HREF_RESOLVES checks each against app/
|
|
2315
|
+
rules: [FE_HREF_RESOLVES, FE_ROUTE_HARDCODED]
|
|
2316
|
+
|
|
2048
2317
|
- id: fe.modules.types
|
|
2318
|
+
since: 3.1.0
|
|
2049
2319
|
profiles: [fe]
|
|
2050
2320
|
path: "apps/<app>/src/modules/types/"
|
|
2051
2321
|
presence: optional
|
|
@@ -2053,6 +2323,8 @@ slots:
|
|
|
2053
2323
|
tier: foundation
|
|
2054
2324
|
tests: none
|
|
2055
2325
|
why: shared types with no runtime behaviour; a component may import them
|
|
2326
|
+
rules: [FE_TYPE_ESCAPE, HFS_TS_STRICT]
|
|
2327
|
+
|
|
2056
2328
|
- id: fe.modules.types.ambient
|
|
2057
2329
|
parent: fe.modules.types
|
|
2058
2330
|
profiles: [fe]
|
|
@@ -2065,6 +2337,8 @@ slots:
|
|
|
2065
2337
|
tests: none
|
|
2066
2338
|
since: 2.2.0
|
|
2067
2339
|
why: global ambient declarations loaded by the app compiler; this owner exposes no runtime API
|
|
2340
|
+
rules: [FE_TYPE_ESCAPE, HFS_TS_STRICT]
|
|
2341
|
+
|
|
2068
2342
|
- id: fe.modules.auth
|
|
2069
2343
|
profiles: [fe]
|
|
2070
2344
|
# The identity-provider client (fe-stack auth, FE-AUTH): the session, the access token the Apollo link attaches and the
|
|
@@ -2076,6 +2350,7 @@ slots:
|
|
|
2076
2350
|
tier: modules
|
|
2077
2351
|
owner: true
|
|
2078
2352
|
requires: [index.ts]
|
|
2353
|
+
entries: [server.ts] # the server-only PKCE exchange API used by the opted-in callback route
|
|
2079
2354
|
tests: none
|
|
2080
2355
|
since: 3.0.0
|
|
2081
2356
|
rules: [FE_SUPABASE_CLIENT_OWNER, FE_SERVICE_ROLE_FORBIDDEN]
|
|
@@ -2093,7 +2368,10 @@ slots:
|
|
|
2093
2368
|
roles: {entry: index.ts, hooks: hooks.ts} # hooks.ts: the use*WithToast hooks of the toast module
|
|
2094
2369
|
tests: none
|
|
2095
2370
|
since: 3.0.0
|
|
2371
|
+
rules: [FE_I18N_LITERAL, FE_HOOKS_ARE_HOOKS, FE_HTTP_STATUS_COLLAPSE]
|
|
2372
|
+
|
|
2096
2373
|
- id: fe.package.ui
|
|
2374
|
+
since: 3.1.0
|
|
2097
2375
|
profiles: [fe]
|
|
2098
2376
|
path: "packages/<family>-ui/"
|
|
2099
2377
|
presence: opt-in
|
|
@@ -2108,6 +2386,7 @@ slots:
|
|
|
2108
2386
|
why: built to dist with explicit named exports; only composites, branches and leaves; no unused export; no colour literal
|
|
2109
2387
|
rules: [FE_PACKAGE_SHAPE, HFS_UNUSED_EXPORT, FE_STYLE_TOKEN_ONLY]
|
|
2110
2388
|
- id: fe.package.api
|
|
2389
|
+
since: 3.1.0
|
|
2111
2390
|
profiles: [fe]
|
|
2112
2391
|
path: "packages/<family>-api/"
|
|
2113
2392
|
presence: opt-in
|
|
@@ -2119,6 +2398,7 @@ slots:
|
|
|
2119
2398
|
why: the one transport client and the one Outcome union shared by every app of the repository; no app keeps its own
|
|
2120
2399
|
rules: [FE_TRANSPORT_OWNER, FE_PACKAGE_SHAPE, HFS_UNUSED_EXPORT]
|
|
2121
2400
|
- id: fe.package.api.client
|
|
2401
|
+
since: 3.1.0
|
|
2122
2402
|
profiles: [fe]
|
|
2123
2403
|
path: "packages/<family>-api/src/graphql/clients/"
|
|
2124
2404
|
presence: optional # enabled with its package (fe.package.api is the opt-in)
|
|
@@ -2128,6 +2408,7 @@ slots:
|
|
|
2128
2408
|
why: the repository's one pair of Apollo clients and their link chain
|
|
2129
2409
|
rules: [FE_TRANSPORT_OWNER]
|
|
2130
2410
|
- id: fe.package.api.outcome
|
|
2411
|
+
since: 3.1.0
|
|
2131
2412
|
profiles: [fe]
|
|
2132
2413
|
path: "packages/<family>-api/src/graphql/types.ts"
|
|
2133
2414
|
presence: optional # enabled with its package (fe.package.api is the opt-in)
|
|
@@ -2138,6 +2419,7 @@ slots:
|
|
|
2138
2419
|
why: the repository's one GraphQLResponse union
|
|
2139
2420
|
rules: [FE_HTTP_STATUS_COLLAPSE]
|
|
2140
2421
|
- id: fe.package.i18n
|
|
2422
|
+
since: 3.1.0
|
|
2141
2423
|
profiles: [fe]
|
|
2142
2424
|
path: "packages/<family>-i18n/"
|
|
2143
2425
|
presence: opt-in
|