@starci/eslint-canon-fe 8.3.0 → 8.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -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/config.example.yaml +7 -6
- package/runtime/engine/config.mjs +10 -18
- package/runtime/engine/removed-vocabulary.mjs +0 -2
- package/runtime/knowledge/hfs/canon-pins.yaml +11 -11
- package/runtime/knowledge/hfs/fe-stack.yaml +22 -1
- package/runtime/knowledge/hfs/slots.yaml +333 -62
- package/runtime/knowledge/patterns/fe/folder.yaml +1 -0
- package/runtime/knowledge/sonar-gate.yaml +1 -1
- package/runtime/modules/kernel/removed-vocabulary.yaml +30 -0
- package/runtime/modules/models/runtimes.yaml +5 -7
- package/runtime/packages/hfs/templates/app/package-scripts/package.json +1 -0
- package/runtime/scripts/api/git/lib.mjs +4 -1
- 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/config.mjs +1 -1
- 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/index.mjs +1 -1
- 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 +4 -4
- 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 +69 -9
- package/runtime/scripts/hfs/param-names.mjs +1 -1
- package/runtime/scripts/hfs/project-rule.mjs +3 -1
- package/runtime/scripts/hfs/rule-catalog.mjs +158 -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 +3 -2
- package/runtime/scripts/hfs/typescript-programs.mjs +0 -8
- package/runtime/scripts/lib/env.mjs +20 -0
- package/runtime/scripts/lib/event-contract.mjs +107 -0
- package/runtime/scripts/lib/git.mjs +1 -1
- package/runtime/scripts/lib/i18n.mjs +0 -2
- package/runtime/scripts/lib/json-clone.mjs +61 -0
- package/runtime/scripts/lib/language.mjs +1 -1
- 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,9 @@ 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
|
+
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
539
|
# The database deploy workflow: workflow_dispatch only (like app.ci-e2e); starci app sync renders it under the managed ci group.
|
|
493
540
|
path: ".github/workflows/db-deploy.yml"
|
|
494
541
|
presence: optional
|
|
@@ -498,7 +545,22 @@ slots:
|
|
|
498
545
|
liteManagedBy: ci-workflows-lite
|
|
499
546
|
rules: [HFS_MANAGED_FILE_DRIFT]
|
|
500
547
|
since: 2.1.0
|
|
548
|
+
exampleNa: "owner:no-runtime-specs (2026-10-17): the only example of this slot in an edition was a deleted runtime fixture; the scaffold e2e exercises the scaffolded slot"
|
|
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,9 @@ 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
|
+
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
661
|
editions: [lite]
|
|
576
662
|
# Product-owned environment, secret and build tooling, kept apart from lite's managed codegen script.
|
|
577
663
|
# Full already owns these files through app.scripts; its generic *.mjs pattern must not compete with build-*.mjs.
|
|
@@ -581,7 +667,9 @@ slots:
|
|
|
581
667
|
tier: none
|
|
582
668
|
tests: none
|
|
583
669
|
rules: [HFS_PLAINTEXT_SECRET, HFS_SCRIPT_ONE_OFF, BE_SPEC_PLACEMENT, HFS_REPO_LOCAL_CHECK]
|
|
670
|
+
exampleNa: "owner:no-runtime-specs (2026-10-17): the only example of this slot in an edition was a deleted runtime fixture; the scaffold e2e exercises the scaffolded slot"
|
|
584
671
|
- id: app.sides
|
|
672
|
+
since: 3.1.0
|
|
585
673
|
profiles: [app]
|
|
586
674
|
# 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
675
|
# this slot answers only for the side folders themselves.
|
|
@@ -591,7 +679,12 @@ slots:
|
|
|
591
679
|
tier: none
|
|
592
680
|
tests: none
|
|
593
681
|
- id: app.starciwork
|
|
682
|
+
since: 3.1.0
|
|
594
683
|
profiles: [app]
|
|
684
|
+
exemplifiedBy:
|
|
685
|
+
- {path: packages/hfs/templates/app/skeleton/.starciwork/, edition: full}
|
|
686
|
+
- {path: packages/hfs/templates/app/skeleton-lite/.starciwork/, edition: lite}
|
|
687
|
+
editionWhy: "ruling:R212/case-4: lite optionally keeps .gitignore, workspace.yaml and brand records; full keeps the Work catalog and product summaries."
|
|
595
688
|
path: ".starciwork/"
|
|
596
689
|
presence: required
|
|
597
690
|
tracked: tracked
|
|
@@ -601,9 +694,10 @@ slots:
|
|
|
601
694
|
allows: [workspace.yaml, index.yaml, "brand/**", shell/index.yaml, "features/<feature>/index.yaml",
|
|
602
695
|
"features/<feature>/<family>/<name>/index.yaml", "features/<feature>/br/<rule>/ac/<name>/index.yaml",
|
|
603
696
|
"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
|
-
|
|
697
|
+
"features/<feature>/uat/<name>/{seed,cleanup}.sql", "_resources/{identities,environments,fixtures}/<slug>/resource.yaml",
|
|
698
|
+
"_derived/{index.yaml,frontier.md,critique.yaml,critique.md}"]
|
|
699
|
+
forbids: [evidence/, logs/, reports/, captures/, runs/, "kernel-*/", "*.sqlite*"]
|
|
700
|
+
# ruling:R212/case-4: lite keeps workspace.yaml for project identity and repo roles, plus brand drawing records,
|
|
607
701
|
# references and assets. No brand means no Work tree is needed; workspace.yaml is legal but optional.
|
|
608
702
|
litePresence: optional
|
|
609
703
|
lite:
|
|
@@ -611,7 +705,12 @@ slots:
|
|
|
611
705
|
allows: [workspace.yaml, "brand/**"]
|
|
612
706
|
rules: [HFS_AGENT_DATA_TRACKED, HFS_IDENTITY_CUSTODY]
|
|
613
707
|
- id: app.starcistacks
|
|
708
|
+
since: 3.1.0
|
|
614
709
|
profiles: [app]
|
|
710
|
+
editionWhy: "owner:lite-production-swarm: lite requires declared docker-swarm production with Supabase backing; full also admits Compose development, Kubernetes and Terraform."
|
|
711
|
+
exemplifiedBy:
|
|
712
|
+
- {path: packages/hfs/templates/app/skeleton/.starcistacks/, edition: full}
|
|
713
|
+
- {path: packages/hfs/templates/app/skeleton-lite/.starcistacks/, edition: lite}
|
|
615
714
|
# The stack declarations and the sealed custody of the app, at the app root beside be/, fe/ and .starciwork (never under a side).
|
|
616
715
|
path: ".starcistacks/"
|
|
617
716
|
presence: required
|
|
@@ -619,18 +718,19 @@ slots:
|
|
|
619
718
|
tier: none
|
|
620
719
|
tests: none
|
|
621
720
|
requires: [application-stacks.yaml]
|
|
622
|
-
allows: [application-stacks.yaml, "<env>/README.md", "<env>/environment.json", "<env>/infra/{compose,k8s,terraform
|
|
721
|
+
allows: [application-stacks.yaml, "<env>/README.md", "<env>/environment.json", "<env>/infra/{compose,swarm,k8s,terraform}/**", "<env>/infra/metadata.json",
|
|
623
722
|
"<env>/runtime/{config,env}/**", "<env>/runtime/env/KEYS.md", "<env>/secrets/<slug>.enc", "<env>/secrets/.gitkeep", "<env>/seeds/**"]
|
|
624
723
|
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.
|
|
724
|
+
# Owner ruling 2026-10-14: lite declares docker-swarm production, app runtime config, sealed custody and seed data.
|
|
725
|
+
# Supabase supplies the database and broker; production has no Compose or host cron.
|
|
628
726
|
lite:
|
|
629
727
|
requires: [application-stacks.yaml]
|
|
630
|
-
allows: [application-stacks.yaml, "<env>/README.md", "<env>/environment.json", "<env>/infra/swarm/**", "<env>/
|
|
631
|
-
|
|
728
|
+
allows: [application-stacks.yaml, "<env>/README.md", "<env>/environment.json", "<env>/infra/swarm/**", "<env>/infra/metadata.json",
|
|
729
|
+
"<env>/runtime/{config,env}/**", "<env>/runtime/env/KEYS.md", "<env>/secrets/<slug>.enc", "<env>/secrets/.gitkeep", "<env>/seeds/**"]
|
|
730
|
+
forbids: ["<env>/runtime/files/**", "**/*.enc outside <env>/secrets/", DESIGN.md, deployment.json, "k8s/ at root"]
|
|
632
731
|
rules: [HFS_STACKS_SHAPE, HFS_STACKS_IN_SIDE, HFS_PLAINTEXT_SECRET, HFS_IDENTITY_CUSTODY]
|
|
633
732
|
- id: app.sops
|
|
733
|
+
since: 3.1.0
|
|
634
734
|
profiles: [app]
|
|
635
735
|
# The sops custody rule of .starcistacks/<env>/secrets/*.enc, at the app root beside the tree it seals.
|
|
636
736
|
path: .sops.yaml
|
|
@@ -639,6 +739,7 @@ slots:
|
|
|
639
739
|
tier: none
|
|
640
740
|
tests: none
|
|
641
741
|
- id: app.build-output
|
|
742
|
+
since: 3.1.0
|
|
642
743
|
profiles: [app]
|
|
643
744
|
path: "{**/node_modules/,coverage/,reports/,test-results/,**/*.tsbuildinfo}"
|
|
644
745
|
presence: optional
|
|
@@ -646,6 +747,7 @@ slots:
|
|
|
646
747
|
tier: none
|
|
647
748
|
tests: none
|
|
648
749
|
- id: app.tool-cache
|
|
750
|
+
since: 3.1.0
|
|
649
751
|
profiles: [app]
|
|
650
752
|
path: "{.eslintcache,.turbo/,.scannerwork/,.sonar/,.jest-cache/,.cache/,.tools/}"
|
|
651
753
|
presence: forbidden
|
|
@@ -654,6 +756,7 @@ slots:
|
|
|
654
756
|
tests: none
|
|
655
757
|
goesTo: "${STARCI_CACHE_HOME:-%LOCALAPPDATA%/StarCi/cache}/<repo>/<tool>/ (the canon presets set eslint --cache-location, jest cacheDirectory, turbo cacheDir, sonar.working.directory)"
|
|
656
758
|
- id: app.agent-output
|
|
759
|
+
since: 3.1.0
|
|
657
760
|
profiles: [app]
|
|
658
761
|
path: "{report*.json,*.tmp.json,%*%,nul,.artifacts/,.tmp-*,*-lint.json,lf.json,lt.json,*.log,design-plans/,.gitmounts/,.dat,vi-flat.txt}"
|
|
659
762
|
presence: forbidden
|
|
@@ -663,6 +766,7 @@ slots:
|
|
|
663
766
|
goesTo: "agent scratchpad or the StarCi blob store (%LOCALAPPDATA%/StarCi/blobs), cited by {name, sha256}"
|
|
664
767
|
rules: [HFS_UNTRACKED_ROOT_ENTRY, HFS_AGENT_DATA_TRACKED]
|
|
665
768
|
- id: app.worktrees
|
|
769
|
+
since: 3.1.0
|
|
666
770
|
profiles: [app]
|
|
667
771
|
path: "{.worktrees/,worktrees/,.starciwork/worktrees/}"
|
|
668
772
|
presence: forbidden
|
|
@@ -671,6 +775,7 @@ slots:
|
|
|
671
775
|
tests: none
|
|
672
776
|
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
777
|
- id: app.plaintext-env
|
|
778
|
+
since: 3.1.0
|
|
674
779
|
profiles: [app]
|
|
675
780
|
path: "{.env,.env.*,secrets.env,.secrets/,**/*.pem,**/*.key}"
|
|
676
781
|
presence: forbidden
|
|
@@ -683,6 +788,9 @@ slots:
|
|
|
683
788
|
# ----- app root: the Supabase project of an app whose connection declares provider: supabase (every lite app) -------
|
|
684
789
|
- id: app.supabase
|
|
685
790
|
profiles: [app]
|
|
791
|
+
exemplifiedBy:
|
|
792
|
+
- packages/hfs/templates/app/skeleton-lite/supabase/
|
|
793
|
+
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
794
|
# The one Supabase project directory, at the app root beside be/, fe/ and .starcistacks (never under a side).
|
|
687
795
|
path: "supabase/"
|
|
688
796
|
presence: opt-in # enabled by a connection with provider: supabase; required under edition lite
|
|
@@ -692,8 +800,12 @@ slots:
|
|
|
692
800
|
tier: none
|
|
693
801
|
tests: none
|
|
694
802
|
since: 2.1.0
|
|
803
|
+
exampleNa: "owner:no-runtime-specs (2026-10-17): the only example of this slot in an edition was a deleted runtime fixture; the scaffold e2e exercises the scaffolded slot"
|
|
695
804
|
- id: app.supabase.config
|
|
696
805
|
profiles: [app]
|
|
806
|
+
exemplifiedBy:
|
|
807
|
+
- packages/hfs/templates/app/skeleton-lite/supabase/config.toml
|
|
808
|
+
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
809
|
# The project configuration: auth keys are env(NAME) references, never literals (L08 judges its policy keys).
|
|
698
810
|
path: "supabase/config.toml"
|
|
699
811
|
presence: opt-in
|
|
@@ -703,8 +815,12 @@ slots:
|
|
|
703
815
|
tier: none
|
|
704
816
|
tests: none
|
|
705
817
|
since: 2.1.0
|
|
818
|
+
exampleNa: "owner:no-runtime-specs (2026-10-17): the only example of this slot in an edition was a deleted runtime fixture; the scaffold e2e exercises the scaffolded slot"
|
|
706
819
|
- id: app.supabase.migrations
|
|
707
820
|
profiles: [app]
|
|
821
|
+
exemplifiedBy:
|
|
822
|
+
- packages/hfs/templates/app/skeleton-lite/supabase/migrations/__timestamp___baseline.sql
|
|
823
|
+
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
824
|
# The schema authority of an app on Supabase: forward-only SQL migrations named <timestamp14>_<kebab-name>.sql,
|
|
709
825
|
# one file per change, immutable once on origin/main (L02-L08 judge them).
|
|
710
826
|
path: "supabase/migrations/<stamp>_<name>.sql"
|
|
@@ -716,6 +832,7 @@ slots:
|
|
|
716
832
|
tier: none
|
|
717
833
|
tests: none
|
|
718
834
|
since: 2.1.0
|
|
835
|
+
exampleNa: "owner:no-runtime-specs (2026-10-17): the only example of this slot in an edition was a deleted runtime fixture; the scaffold e2e exercises the scaffolded slot"
|
|
719
836
|
- id: app.supabase.migrations.archive
|
|
720
837
|
profiles: [app]
|
|
721
838
|
# The migrations an existing app retired at its cutover (owner ruling 2026-10-13, migration baseline policy): the live schema is
|
|
@@ -739,6 +856,8 @@ slots:
|
|
|
739
856
|
since: 2.1.0
|
|
740
857
|
- id: app.supabase.types
|
|
741
858
|
profiles: [app]
|
|
859
|
+
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."
|
|
860
|
+
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
861
|
# The generated Database types (database.types.ts) contract:emit writes from the migrations; both sides read it
|
|
743
862
|
# (sides.<side>.reads supabase/types/); drift against regeneration is L09.
|
|
744
863
|
path: "supabase/types/database.types.ts"
|
|
@@ -749,6 +868,7 @@ slots:
|
|
|
749
868
|
tier: none
|
|
750
869
|
tests: none
|
|
751
870
|
since: 2.1.0
|
|
871
|
+
exampleNa: "owner:no-runtime-specs (2026-10-17): the only example of this slot in an edition was a deleted runtime fixture; the scaffold e2e exercises the scaffolded slot"
|
|
752
872
|
- id: app.supabase.templates
|
|
753
873
|
profiles: [app]
|
|
754
874
|
path: "supabase/templates/*.html"
|
|
@@ -777,6 +897,7 @@ slots:
|
|
|
777
897
|
|
|
778
898
|
# ----- side root (both sides): what a standalone repository root holds, less the app-root files ----------
|
|
779
899
|
- id: repo.side-root-forbidden
|
|
900
|
+
since: 3.1.0
|
|
780
901
|
profiles: [be, fe]
|
|
781
902
|
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
903
|
presence: forbidden
|
|
@@ -785,6 +906,7 @@ slots:
|
|
|
785
906
|
tests: none
|
|
786
907
|
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
908
|
- id: be.tool-config
|
|
909
|
+
since: 3.1.0
|
|
788
910
|
profiles: [be]
|
|
789
911
|
# Managed files (starci app sync renders them, starci app check compares them): the whole tool configuration of a back end. Each is
|
|
790
912
|
# a reference to a canon package, never a configuration: tsconfig.json extends @starci/tsconfig/be.json and adds the
|
|
@@ -800,11 +922,12 @@ slots:
|
|
|
800
922
|
tracked: tracked
|
|
801
923
|
tier: none
|
|
802
924
|
tests: none
|
|
803
|
-
# Lite renders the same files (owner ruling 2026-10-12): the unit run and its coverage are full's.
|
|
925
|
+
# 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
926
|
coverage: none
|
|
805
927
|
managedBy: tool-config
|
|
806
928
|
rules: [HFS_MANAGED_FILE_DRIFT, HFS_TOOL_CONFIG_LOCAL, HFS_TS_STRICT]
|
|
807
929
|
- id: be.nest-cli
|
|
930
|
+
since: 3.1.0
|
|
808
931
|
profiles: [be]
|
|
809
932
|
# The Nest monorepo of the back end: `monorepo: true` and one project per declared be app (apps/<app>, even for one service).
|
|
810
933
|
path: nest-cli.json
|
|
@@ -815,6 +938,7 @@ slots:
|
|
|
815
938
|
coverage: none
|
|
816
939
|
rules: [HFS_MONO_NEST_PROJECTS]
|
|
817
940
|
- id: be.tool-config-local
|
|
941
|
+
since: 3.1.0
|
|
818
942
|
profiles: [be]
|
|
819
943
|
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
944
|
presence: forbidden
|
|
@@ -824,6 +948,7 @@ slots:
|
|
|
824
948
|
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
949
|
rules: [HFS_TOOL_CONFIG_LOCAL]
|
|
826
950
|
- id: fe.tool-config
|
|
951
|
+
since: 3.1.0
|
|
827
952
|
profiles: [fe]
|
|
828
953
|
# Managed files (starci app sync renders them, starci app check compares them): the tool configuration of a front end. Each is a
|
|
829
954
|
# reference to a canon package, never a configuration: tsconfig.json extends @starci/tsconfig/next.json and only
|
|
@@ -839,6 +964,7 @@ slots:
|
|
|
839
964
|
managedBy: tool-config
|
|
840
965
|
rules: [HFS_MANAGED_FILE_DRIFT, HFS_TOOL_CONFIG_LOCAL, HFS_RULE_OFF_WITHOUT_REPLACEMENT, HFS_TS_STRICT]
|
|
841
966
|
- id: fe.tool-config-local
|
|
967
|
+
since: 3.1.0
|
|
842
968
|
profiles: [fe]
|
|
843
969
|
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
970
|
presence: forbidden
|
|
@@ -848,6 +974,7 @@ slots:
|
|
|
848
974
|
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
975
|
rules: [HFS_TOOL_CONFIG_LOCAL]
|
|
850
976
|
- id: repo.docs
|
|
977
|
+
since: 3.1.0
|
|
851
978
|
profiles: [be, fe]
|
|
852
979
|
path: "docs/{adr,runbooks,guides}/**/*.md"
|
|
853
980
|
presence: opt-in
|
|
@@ -857,6 +984,7 @@ slots:
|
|
|
857
984
|
coverage: none
|
|
858
985
|
rules: [HFS_ROOT_MARKDOWN_FORBIDDEN]
|
|
859
986
|
- id: repo.docs-media
|
|
987
|
+
since: 3.1.0
|
|
860
988
|
profiles: [be, fe]
|
|
861
989
|
path: "docs/**/*.{png,svg,webp}"
|
|
862
990
|
presence: opt-in
|
|
@@ -865,7 +993,21 @@ slots:
|
|
|
865
993
|
tests: none
|
|
866
994
|
coverage: none
|
|
867
995
|
budget: {bytes: 512000}
|
|
996
|
+
- id: repo.i18n-dynamic-keys
|
|
997
|
+
since: 3.3.0
|
|
998
|
+
profiles: [be, fe]
|
|
999
|
+
# The side's authored ledger of catalog keys the R280 detector cannot see (fe/i18n-dynamic-keys.yaml, be/i18n-dynamic-keys.yaml:
|
|
1000
|
+
# 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.
|
|
1001
|
+
path: i18n-dynamic-keys.yaml
|
|
1002
|
+
presence: opt-in
|
|
1003
|
+
tracked: tracked
|
|
1004
|
+
tier: none
|
|
1005
|
+
tests: none
|
|
1006
|
+
coverage: none
|
|
1007
|
+
rules: [HFS_UNUSED_SYMBOL]
|
|
1008
|
+
exampleNa: "owner:no-runtime-specs (2026-10-17): the only example of this slot in an edition was a deleted runtime fixture; the scaffold e2e exercises the scaffolded slot"
|
|
868
1009
|
- id: repo.packages
|
|
1010
|
+
since: 3.1.0
|
|
869
1011
|
profiles: [be, fe]
|
|
870
1012
|
path: "packages/<pkg>/"
|
|
871
1013
|
presence: opt-in
|
|
@@ -877,7 +1019,14 @@ slots:
|
|
|
877
1019
|
coverage: none
|
|
878
1020
|
rules: [FE_PACKAGE_SHAPE, HFS_UNUSED_EXPORT]
|
|
879
1021
|
- id: repo.app-image
|
|
1022
|
+
since: 3.1.0
|
|
880
1023
|
profiles: [be, fe]
|
|
1024
|
+
exemplifiedBy:
|
|
1025
|
+
- packages/hfs/templates/be/image/api/Dockerfile
|
|
1026
|
+
- packages/hfs/templates/be/image/cli/Dockerfile
|
|
1027
|
+
- {path: packages/hfs/templates/be/image/worker/Dockerfile, edition: full}
|
|
1028
|
+
- packages/hfs/templates/fe/image/next/Dockerfile
|
|
1029
|
+
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
1030
|
# One image per app: every declared app has its own Dockerfile (apps/<app>/Dockerfile), built from the APP ROOT (the folder with
|
|
882
1031
|
# 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
1032
|
# A Dockerfile anywhere else is owned by no slot.
|
|
@@ -892,6 +1041,7 @@ slots:
|
|
|
892
1041
|
|
|
893
1042
|
# ----- never tracked: build output the tools need in place ---------------------------------------------
|
|
894
1043
|
- id: repo.build-output
|
|
1044
|
+
since: 3.1.0
|
|
895
1045
|
profiles: [be, fe]
|
|
896
1046
|
path: "{**/node_modules/,dist/,apps/*/dist/,packages/*/dist/,.next/,apps/*/.next/,coverage/,reports/,test-results/,**/next-env.d.ts,**/*.tsbuildinfo}"
|
|
897
1047
|
presence: optional
|
|
@@ -899,6 +1049,7 @@ slots:
|
|
|
899
1049
|
tier: none
|
|
900
1050
|
tests: none
|
|
901
1051
|
- id: repo.generated
|
|
1052
|
+
since: 3.1.0
|
|
902
1053
|
profiles: [be, fe]
|
|
903
1054
|
path: "{**/__generated__/,apps/*/src/schema.gql}"
|
|
904
1055
|
presence: optional
|
|
@@ -909,6 +1060,7 @@ slots:
|
|
|
909
1060
|
|
|
910
1061
|
# ----- never in the working tree: caches, scratch, agent output ----------------------------------------
|
|
911
1062
|
- id: repo.tool-cache
|
|
1063
|
+
since: 3.1.0
|
|
912
1064
|
profiles: [be, fe]
|
|
913
1065
|
path: "{.eslintcache,.turbo/,.scannerwork/,.sonar/,.jest-cache/,.cache/,.tools/}"
|
|
914
1066
|
presence: forbidden
|
|
@@ -917,6 +1069,7 @@ slots:
|
|
|
917
1069
|
tests: none
|
|
918
1070
|
goesTo: "${STARCI_CACHE_HOME:-%LOCALAPPDATA%/StarCi/cache}/<repo>/<tool>/ (the canon presets set eslint --cache-location, jest cacheDirectory, turbo cacheDir, sonar.working.directory)"
|
|
919
1071
|
- id: repo.agent-output
|
|
1072
|
+
since: 3.1.0
|
|
920
1073
|
profiles: [be, fe]
|
|
921
1074
|
path: "{report*.json,*.tmp.json,%*%,nul,.artifacts/,.tmp-*,*-lint.json,lf.json,lt.json,*.log,design-plans/,.gitmounts/,.dat,vi-flat.txt}"
|
|
922
1075
|
presence: forbidden
|
|
@@ -926,6 +1079,7 @@ slots:
|
|
|
926
1079
|
goesTo: "agent scratchpad or the StarCi blob store (%LOCALAPPDATA%/StarCi/blobs), cited by {name, sha256}"
|
|
927
1080
|
rules: [HFS_UNTRACKED_ROOT_ENTRY, HFS_AGENT_DATA_TRACKED]
|
|
928
1081
|
- id: repo.worktrees
|
|
1082
|
+
since: 3.1.0
|
|
929
1083
|
profiles: [be, fe]
|
|
930
1084
|
path: "{.worktrees/,worktrees/,.starciwork/worktrees/}"
|
|
931
1085
|
presence: forbidden
|
|
@@ -934,6 +1088,7 @@ slots:
|
|
|
934
1088
|
tests: none
|
|
935
1089
|
goesTo: "<lanes root>/<project>/<lane>/ (outside every repository)"
|
|
936
1090
|
- id: repo.plaintext-env
|
|
1091
|
+
since: 3.1.0
|
|
937
1092
|
profiles: [be, fe]
|
|
938
1093
|
path: "{.env,.env.*,.secrets/,**/*.pem,**/*.key}"
|
|
939
1094
|
presence: forbidden
|
|
@@ -943,6 +1098,7 @@ slots:
|
|
|
943
1098
|
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
1099
|
rules: [HFS_PLAINTEXT_SECRET]
|
|
945
1100
|
- id: be.root-e2e
|
|
1101
|
+
since: 3.1.0
|
|
946
1102
|
profiles: [be]
|
|
947
1103
|
path: "e2e/"
|
|
948
1104
|
presence: forbidden
|
|
@@ -953,6 +1109,7 @@ slots:
|
|
|
953
1109
|
|
|
954
1110
|
# ----- contract snapshots (the committed machine-emitted files, because they are a cross-repo interface) --
|
|
955
1111
|
- id: be.contract.graphql
|
|
1112
|
+
since: 3.1.0
|
|
956
1113
|
profiles: [be]
|
|
957
1114
|
path: "contracts/<app>/schema.graphql"
|
|
958
1115
|
presence: opt-in
|
|
@@ -963,6 +1120,7 @@ slots:
|
|
|
963
1120
|
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
1121
|
rules: [HFS_CONTRACT_SNAPSHOT_DRIFT]
|
|
965
1122
|
- id: be.contract.openapi
|
|
1123
|
+
since: 3.1.0
|
|
966
1124
|
profiles: [be]
|
|
967
1125
|
path: "contracts/<app>/openapi.json"
|
|
968
1126
|
presence: opt-in
|
|
@@ -973,6 +1131,7 @@ slots:
|
|
|
973
1131
|
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
1132
|
rules: [HFS_CONTRACT_SNAPSHOT_DRIFT]
|
|
975
1133
|
- id: be.contract.events
|
|
1134
|
+
since: 3.1.0
|
|
976
1135
|
profiles: [be]
|
|
977
1136
|
path: "contracts/<app>/events.json"
|
|
978
1137
|
presence: opt-in
|
|
@@ -983,6 +1142,7 @@ slots:
|
|
|
983
1142
|
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
1143
|
rules: [HFS_EVENT_CONTRACT]
|
|
985
1144
|
- id: be.contract.events.pin
|
|
1145
|
+
since: 3.1.0
|
|
986
1146
|
profiles: [be]
|
|
987
1147
|
path: "contracts/<app>/events.pin.json"
|
|
988
1148
|
presence: opt-in
|
|
@@ -1012,6 +1172,7 @@ slots:
|
|
|
1012
1172
|
# area's global ports when it has any); composition only, no business logic or process.env/EnvSource read.
|
|
1013
1173
|
# app.module.ts the composition root: imports the groups and spreads them, binds the app-wide guards and filter.
|
|
1014
1174
|
- id: be.app.api
|
|
1175
|
+
since: 3.1.0
|
|
1015
1176
|
profiles: [be]
|
|
1016
1177
|
path: "apps/<app>/src/"
|
|
1017
1178
|
appKind: api
|
|
@@ -1020,13 +1181,14 @@ slots:
|
|
|
1020
1181
|
tier: app
|
|
1021
1182
|
owner: true
|
|
1022
1183
|
minInstances: 1
|
|
1023
|
-
requires: [main.ts, app.module.ts]
|
|
1184
|
+
requires: [main.ts, app.module.ts, "<app>.env.ts"]
|
|
1024
1185
|
allows: [main.ts, app.module.ts, "<app>.env.ts", "<app>.options.ts", "compose/<area>.ts", operations.ts]
|
|
1025
|
-
tests: none #
|
|
1186
|
+
tests: none # full app boot is proven by useTestWorld({ apps }); lite by starci app check --wiring (R272)
|
|
1026
1187
|
coverage: none
|
|
1027
1188
|
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]
|
|
1189
|
+
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
1190
|
- id: be.app.worker
|
|
1191
|
+
since: 3.1.0
|
|
1030
1192
|
profiles: [be]
|
|
1031
1193
|
path: "apps/<app>/src/"
|
|
1032
1194
|
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 +1196,16 @@ slots:
|
|
|
1034
1196
|
tracked: tracked
|
|
1035
1197
|
tier: app
|
|
1036
1198
|
owner: true
|
|
1037
|
-
requires: [main.ts, app.module.ts]
|
|
1199
|
+
requires: [main.ts, app.module.ts, "<app>.env.ts"]
|
|
1038
1200
|
allows: [main.ts, app.module.ts, "<app>.env.ts", "<app>.options.ts", "compose/<area>.ts"]
|
|
1039
|
-
tests: none #
|
|
1201
|
+
tests: none # full app boot is proven by useTestWorld({ apps }); lite by starci app check --wiring (R272)
|
|
1040
1202
|
coverage: none
|
|
1041
1203
|
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]
|
|
1204
|
+
rules: [BE_APP_COMPOSITION_ONLY, BE_APP_OPTIONS_COMPOSITION, BE_APP_COMPOSITION_BUDGET, BE_BACKGROUND_UNOWNED, BE_CONTEXT_OWNER, BE_APP_ENV_REQUIRED]
|
|
1043
1205
|
- id: be.app.cli
|
|
1206
|
+
since: 3.1.0
|
|
1044
1207
|
profiles: [be]
|
|
1208
|
+
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
1209
|
# The ONE app of every one-off action (migrate, seed, sync, backup, any operator command): be/apps/cli, on nest-commander.
|
|
1046
1210
|
# main.ts boots with CommandFactory.run (BE_CLI_BOOTSTRAP); app.module.ts composes the platform and the cli feature root
|
|
1047
1211
|
# (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 +1219,19 @@ slots:
|
|
|
1055
1219
|
tracked: tracked
|
|
1056
1220
|
tier: app
|
|
1057
1221
|
owner: true
|
|
1058
|
-
requires: [main.ts, app.module.ts]
|
|
1222
|
+
requires: [main.ts, app.module.ts, "<app>.env.ts"]
|
|
1059
1223
|
allows: [main.ts, app.module.ts, "<app>.env.ts", "<app>.options.ts", "compose/<area>.ts"]
|
|
1060
|
-
tests: none #
|
|
1224
|
+
tests: none # full app boot is proven by useTestWorld({ apps }); lite by starci app check --wiring (R272)
|
|
1061
1225
|
coverage: none
|
|
1062
1226
|
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]
|
|
1227
|
+
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
1228
|
|
|
1065
1229
|
# ----- BE: features ------------------------------------------------------------------------------------
|
|
1066
1230
|
# src/features/ is grouped by TRIGGER KIND (owner decision 2026-10-02): features/api/<feature>/ (a request: application/ and
|
|
1067
1231
|
# transport/<protocol>/, composed by the api apps), features/cli/ (a one-off action: the cli feature root, compiled only into
|
|
1068
1232
|
# apps/cli). Every kind imports only downward into modules/*; no kind imports another.
|
|
1069
1233
|
- id: be.feature
|
|
1234
|
+
since: 3.1.0
|
|
1070
1235
|
profiles: [be]
|
|
1071
1236
|
path: "src/features/api/<feature>/"
|
|
1072
1237
|
presence: required
|
|
@@ -1076,12 +1241,13 @@ slots:
|
|
|
1076
1241
|
owner: true
|
|
1077
1242
|
minInstances: 1
|
|
1078
1243
|
requires: [index.ts, "<feature>.module.ts", application/]
|
|
1079
|
-
allows: [index.ts, "<feature>.module.ts", application/, transport/, messages/] # nothing else at feature root
|
|
1244
|
+
allows: [index.ts, "<feature>.module.ts", "<feature>.module-definition.ts", "<feature>.options.ts", application/, transport/, messages/] # nothing else at feature root
|
|
1080
1245
|
tests: none
|
|
1081
1246
|
coverage: none
|
|
1082
1247
|
budget: {files: 250, indexExports: 60} # a feature above 250 source files must be split by product capability
|
|
1083
1248
|
rules: [BE_FEATURE_SHAPE, BE_FEATURE_IMPORTS_FEATURE, BE_PUBLIC_SURFACE, BE_FEATURE_NOT_COMPOSED]
|
|
1084
1249
|
- id: be.feature.application
|
|
1250
|
+
since: 3.1.0
|
|
1085
1251
|
profiles: [be]
|
|
1086
1252
|
path: "src/features/{api/,jobs/,reactors/,saga/}<feature>/application/" # inside a trigger kind (jobs, reactors, saga) the feature folder is one level down
|
|
1087
1253
|
presence: required
|
|
@@ -1093,6 +1259,7 @@ slots:
|
|
|
1093
1259
|
coverage: none
|
|
1094
1260
|
rules: [BE_APPLICATION_IMPORTS_TRANSPORT, BE_APPLICATION_TRANSPORT_FRAMEWORK, BE_ENTITY_IN_CONTRACT, BE_CQRS_SHAPE]
|
|
1095
1261
|
- id: be.feature.saga
|
|
1262
|
+
since: 3.1.0
|
|
1096
1263
|
profiles: [be]
|
|
1097
1264
|
path: "src/features/saga/<saga>/"
|
|
1098
1265
|
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 +1269,12 @@ slots:
|
|
|
1102
1269
|
tier: feature
|
|
1103
1270
|
owner: true
|
|
1104
1271
|
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
|
|
1272
|
+
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
1273
|
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
1274
|
coverage: none
|
|
1108
1275
|
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
1276
|
- id: be.feature.saga.steps
|
|
1277
|
+
since: 3.1.0
|
|
1110
1278
|
profiles: [be]
|
|
1111
1279
|
path: "src/features/saga/<saga>/steps/"
|
|
1112
1280
|
pattern: saga
|
|
@@ -1118,6 +1286,7 @@ slots:
|
|
|
1118
1286
|
coverage: none
|
|
1119
1287
|
rules: [BE_SAGA_STEP_COMPENSATION, BE_SAGA_EVENT_CONTRACT]
|
|
1120
1288
|
- id: be.feature.saga.compensations
|
|
1289
|
+
since: 3.1.0
|
|
1121
1290
|
profiles: [be]
|
|
1122
1291
|
path: "src/features/saga/<saga>/compensations/"
|
|
1123
1292
|
pattern: saga
|
|
@@ -1141,30 +1310,33 @@ slots:
|
|
|
1141
1310
|
rules: [BE_APPLICATION_IMPORTS_TRANSPORT, BE_APPLICATION_TRANSPORT_FRAMEWORK, BE_FEATURE_IMPORTS_FEATURE]
|
|
1142
1311
|
since: 1.0.0
|
|
1143
1312
|
- id: be.transport.http
|
|
1313
|
+
since: 3.1.0
|
|
1144
1314
|
profiles: [be]
|
|
1145
1315
|
path: "src/features/api/<feature>/transport/http/"
|
|
1146
1316
|
presence: optional
|
|
1147
1317
|
tracked: tracked
|
|
1148
1318
|
tier: feature
|
|
1149
1319
|
requires: ["<feature>-http.module.ts"]
|
|
1150
|
-
allows: ["<action>.controller.ts", "dto/<action>.{request,response}.ts", "<action>.mapper.ts"]
|
|
1320
|
+
allows: ["<action>.controller.ts", "dto/<action>.{request,response}.ts", "<action>.mapper.ts", "<feature>-http.module-definition.ts", "<feature>-http.options.ts"]
|
|
1151
1321
|
tests: none
|
|
1152
1322
|
coverage: none
|
|
1153
1323
|
composedBy: [api]
|
|
1154
1324
|
rules: [BE_DEFAULT_DENY, BE_INPUT_BOUNDED, BE_TRANSPORT_SHAPE]
|
|
1155
1325
|
- id: be.transport.graphql
|
|
1326
|
+
since: 3.1.0
|
|
1156
1327
|
profiles: [be]
|
|
1157
1328
|
path: "src/features/api/<feature>/transport/graphql/"
|
|
1158
1329
|
presence: optional
|
|
1159
1330
|
tracked: tracked
|
|
1160
1331
|
tier: feature
|
|
1161
1332
|
requires: ["<feature>-graphql.module.ts"]
|
|
1162
|
-
allows: ["<action>.resolver.ts", "dto/<action>.{input,type,args}.ts", "<action>.mapper.ts"]
|
|
1333
|
+
allows: ["<action>.resolver.ts", "dto/<action>.{input,type,args}.ts", "<action>.mapper.ts", "<feature>-graphql.module-definition.ts", "<feature>-graphql.options.ts"]
|
|
1163
1334
|
tests: none
|
|
1164
1335
|
coverage: none
|
|
1165
1336
|
composedBy: [api]
|
|
1166
1337
|
rules: [BE_DEFAULT_DENY, BE_INPUT_BOUNDED, BE_TRANSPORT_SHAPE]
|
|
1167
1338
|
- id: be.transport.message
|
|
1339
|
+
since: 3.1.0
|
|
1168
1340
|
profiles: [be]
|
|
1169
1341
|
path: "src/features/{api/,reactors/,saga/}<feature>/transport/message/"
|
|
1170
1342
|
presence: opt-in # queue consumer: composed by the service that owns it (an api app) or by a worker app scaled apart
|
|
@@ -1172,7 +1344,7 @@ slots:
|
|
|
1172
1344
|
tracked: tracked
|
|
1173
1345
|
tier: feature
|
|
1174
1346
|
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
|
|
1347
|
+
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
1348
|
tests: none
|
|
1177
1349
|
coverage: none
|
|
1178
1350
|
composedBy: [api, worker]
|
|
@@ -1204,7 +1376,7 @@ slots:
|
|
|
1204
1376
|
tracked: tracked
|
|
1205
1377
|
tier: feature
|
|
1206
1378
|
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/]
|
|
1379
|
+
allows: ["<provider>-http.module.ts", "<provider>-http.module-definition.ts", "<provider>-http.options.ts", "<provider>.webhook.ts", "<provider>.webhook.spec.ts", dto/]
|
|
1208
1380
|
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
1381
|
coverage: none
|
|
1210
1382
|
composedBy: [api]
|
|
@@ -1225,6 +1397,7 @@ slots:
|
|
|
1225
1397
|
since: 1.0.0
|
|
1226
1398
|
- id: be.feature.realtime
|
|
1227
1399
|
profiles: [be]
|
|
1400
|
+
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
1401
|
path: "src/features/realtime/<channel>/"
|
|
1229
1402
|
presence: opt-in # a push channel: reads and pushes only (a socket gateway or a GraphQL subscription), no domain write
|
|
1230
1403
|
pattern: realtime # declaring the pattern (hfs.json sides.be.patterns) enables the kind (knowledge/patterns/be/realtime.yaml)
|
|
@@ -1243,17 +1416,19 @@ slots:
|
|
|
1243
1416
|
coverage: none
|
|
1244
1417
|
rules: [BE_KIND_ISOLATION, BE_FEATURE_IMPORTS_FEATURE, BE_FEATURE_NOT_COMPOSED]
|
|
1245
1418
|
since: 1.0.0
|
|
1419
|
+
exampleNa: "owner:no-runtime-specs (2026-10-17): the only example of this slot in an edition was a deleted runtime fixture; the scaffold e2e exercises the scaffolded slot"
|
|
1246
1420
|
- id: be.feature.realtime.graphql
|
|
1247
1421
|
profiles: [be]
|
|
1422
|
+
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
1423
|
path: "src/features/realtime/<channel>/transport/graphql/"
|
|
1249
1424
|
presence: opt-in # a GraphQL subscription over graphql-ws
|
|
1250
|
-
litePresence: forbidden #
|
|
1425
|
+
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
1426
|
pattern: realtime
|
|
1252
1427
|
tracked: tracked
|
|
1253
1428
|
tier: feature
|
|
1254
1429
|
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
1430
|
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/]
|
|
1431
|
+
allows: ["<channel>-graphql.module.ts", "<channel>-graphql.module-definition.ts", "<channel>-graphql.options.ts", "<channel>.subscription.ts", "<channel>.subscription.spec.ts", dto/]
|
|
1257
1432
|
tests: unit-beside # the door's own spec beside the door (a subscription has no service of its own)
|
|
1258
1433
|
coverage: none
|
|
1259
1434
|
composedBy: [api]
|
|
@@ -1261,6 +1436,7 @@ slots:
|
|
|
1261
1436
|
since: 1.0.0
|
|
1262
1437
|
- id: be.feature.realtime.dto
|
|
1263
1438
|
profiles: [be]
|
|
1439
|
+
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
1440
|
path: "src/features/realtime/<channel>/transport/graphql/dto/"
|
|
1265
1441
|
presence: opt-in
|
|
1266
1442
|
litePresence: forbidden # the dto of the graphql door, which lite does not serve
|
|
@@ -1282,20 +1458,21 @@ slots:
|
|
|
1282
1458
|
tracked: tracked
|
|
1283
1459
|
tier: feature
|
|
1284
1460
|
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"]
|
|
1461
|
+
allows: ["<channel>-websocket.module.ts", "<channel>-websocket.module-definition.ts", "<channel>-websocket.options.ts", "<channel>.gateway.ts", "<channel>.gateway.spec.ts"]
|
|
1286
1462
|
tests: unit-beside # the door's own spec beside the door (a gateway has no service of its own)
|
|
1287
1463
|
coverage: none
|
|
1288
1464
|
composedBy: [api]
|
|
1289
1465
|
rules: [BE_REALTIME_SHAPE, BE_REALTIME_WRITES, BE_REALTIME_TOPIC_SCOPE, BE_DEFAULT_DENY]
|
|
1290
1466
|
since: 1.0.0
|
|
1291
1467
|
- id: be.cli
|
|
1468
|
+
since: 3.1.0
|
|
1292
1469
|
profiles: [be]
|
|
1293
1470
|
# The cli feature root: every one-off action of the back end, compiled only into apps/cli. A group is a nest-commander
|
|
1294
1471
|
# @Command (<group>/<group>.cli.ts) whose sub-commands are @SubCommand classes extending CommandRunner
|
|
1295
1472
|
# (<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
1473
|
# 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
|
|
1474
|
+
# the root has a module definition beside it (ruling:module-definition-everywhere) and no register override: cli.module.ts imports each
|
|
1475
|
+
# 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
1476
|
# from platform/database) or from a command-line @Option.
|
|
1300
1477
|
path: "src/features/cli/"
|
|
1301
1478
|
presence: optional
|
|
@@ -1306,7 +1483,7 @@ slots:
|
|
|
1306
1483
|
requires: [index.ts, cli.module.ts]
|
|
1307
1484
|
# A composed root: an app of kind cli lists cli.module.ts, never a group module (BE_MODULE_SHAPE).
|
|
1308
1485
|
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"]
|
|
1486
|
+
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
1487
|
tests: unit-beside
|
|
1311
1488
|
coverage: none
|
|
1312
1489
|
rules: [BE_CLI_COMMAND_SHAPE, BE_CLI_OWNER, BE_CLI_REQUIRED, BE_MODULE_SHAPE, BE_SCHEMA_AUTHORITY]
|
|
@@ -1323,8 +1500,11 @@ slots:
|
|
|
1323
1500
|
tests: none
|
|
1324
1501
|
coverage: none
|
|
1325
1502
|
since: 3.0.0
|
|
1503
|
+
rules: [HFS_ASSET_OVERSIZE, HFS_ASSET_KIND]
|
|
1504
|
+
|
|
1326
1505
|
# ----- BE: modules (three tiers, closed set) -----------------------------------------------------------
|
|
1327
1506
|
- id: be.domain
|
|
1507
|
+
since: 3.1.0
|
|
1328
1508
|
profiles: [be]
|
|
1329
1509
|
path: "src/modules/domain/<capability>/"
|
|
1330
1510
|
presence: optional
|
|
@@ -1334,13 +1514,14 @@ slots:
|
|
|
1334
1514
|
requires: [index.ts]
|
|
1335
1515
|
# <name>.templates.ts holds the capability's static templates, copy and presets as TS constants (owner ruling 2026-10-13);
|
|
1336
1516
|
# 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"]
|
|
1517
|
+
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
1518
|
forbids: [testing/, exceptions/, utils/, helpers/, shared/]
|
|
1339
1519
|
tests: unit-beside # <name>.service.spec.ts beside each <name>.service.ts, nothing else
|
|
1340
1520
|
coverage: required
|
|
1341
1521
|
budget: {indexExports: 60}
|
|
1342
1522
|
rules: [BE_TIER_DIRECTION, ARCH_OWNER_CYCLE, BE_PUBLIC_SURFACE, BE_ERROR_HOME, BE_CONFIG_OWNER, BE_CONTEXT_COUPLING, BE_CONTEXT_TRANSACTION]
|
|
1343
1523
|
- id: be.errors
|
|
1524
|
+
since: 3.1.0
|
|
1344
1525
|
profiles: [be]
|
|
1345
1526
|
path: "src/modules/{domain,integrations}/<capability>/errors/"
|
|
1346
1527
|
presence: optional
|
|
@@ -1373,7 +1554,9 @@ slots:
|
|
|
1373
1554
|
rules: [BE_USER_COPY_LITERAL]
|
|
1374
1555
|
since: 1.0.0
|
|
1375
1556
|
- id: be.persistence
|
|
1557
|
+
since: 3.1.0
|
|
1376
1558
|
profiles: [be]
|
|
1559
|
+
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
1560
|
path: "src/modules/{domain,platform,projections}/<capability>/persistence/"
|
|
1378
1561
|
# 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
1562
|
# registered on several declared connections. Any other capability registers its arrays on exactly one (BE_SCHEMA_OWNER).
|
|
@@ -1394,6 +1577,7 @@ slots:
|
|
|
1394
1577
|
coverage: none
|
|
1395
1578
|
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
1579
|
- id: be.platform
|
|
1580
|
+
since: 3.1.0
|
|
1397
1581
|
profiles: [be]
|
|
1398
1582
|
path: "src/modules/platform/<capability>/"
|
|
1399
1583
|
presence: required
|
|
@@ -1416,7 +1600,9 @@ slots:
|
|
|
1416
1600
|
rules: [BE_TIER_DIRECTION, ARCH_OWNER_CYCLE, BE_MODULE_SHAPE]
|
|
1417
1601
|
# ----- 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
1602
|
- id: be.platform.event-bus
|
|
1603
|
+
since: 3.1.0
|
|
1419
1604
|
profiles: [be]
|
|
1605
|
+
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
1606
|
path: "src/modules/platform/event-bus/"
|
|
1421
1607
|
presence: opt-in
|
|
1422
1608
|
pattern: event-bus # the ONE durable event transport: transactional outbox, relay, Kafka, inbox, retry and dead letter (knowledge/patterns/be/event-bus.yaml)
|
|
@@ -1434,8 +1620,11 @@ slots:
|
|
|
1434
1620
|
coverage: required
|
|
1435
1621
|
budget: {indexExports: 60}
|
|
1436
1622
|
rules: [BE_TIER_DIRECTION, ARCH_OWNER_CYCLE, BE_MODULE_SHAPE, BE_INFRA_OWNER]
|
|
1623
|
+
exampleNa: "owner:no-runtime-specs (2026-10-17): the only example of this slot in an edition was a deleted runtime fixture; the scaffold e2e exercises the scaffolded slot"
|
|
1437
1624
|
- id: be.platform.queue
|
|
1625
|
+
since: 3.1.0
|
|
1438
1626
|
profiles: [be]
|
|
1627
|
+
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
1628
|
path: "src/modules/platform/queue/"
|
|
1440
1629
|
presence: opt-in
|
|
1441
1630
|
pattern: queue # the ONE BullMQ owner: the outbox relay into queues (jobId = outbox id) and job schedulers (knowledge/patterns/be/queues.yaml)
|
|
@@ -1454,6 +1643,7 @@ slots:
|
|
|
1454
1643
|
budget: {indexExports: 60}
|
|
1455
1644
|
rules: [BE_TIER_DIRECTION, ARCH_OWNER_CYCLE, BE_MODULE_SHAPE, BE_INFRA_OWNER]
|
|
1456
1645
|
- id: be.events
|
|
1646
|
+
since: 3.1.0
|
|
1457
1647
|
profiles: [be]
|
|
1458
1648
|
path: "src/modules/events/<service>/"
|
|
1459
1649
|
presence: opt-in
|
|
@@ -1467,6 +1657,7 @@ slots:
|
|
|
1467
1657
|
coverage: none
|
|
1468
1658
|
rules: [BE_EVENT_CLASS_CONTRACT, BE_TIER_DIRECTION]
|
|
1469
1659
|
- id: be.queues
|
|
1660
|
+
since: 3.1.0
|
|
1470
1661
|
profiles: [be]
|
|
1471
1662
|
path: "src/modules/queues/<queue>/"
|
|
1472
1663
|
presence: opt-in
|
|
@@ -1475,11 +1666,12 @@ slots:
|
|
|
1475
1666
|
tier: queues
|
|
1476
1667
|
owner: true
|
|
1477
1668
|
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]
|
|
1669
|
+
allows: ["<queue>.queue.ts", "<queue>.module.ts", "<queue>.module-definition.ts", "<queue>.options.ts", "<queue>.contracts.ts", index.ts]
|
|
1479
1670
|
tests: none
|
|
1480
1671
|
coverage: none
|
|
1481
1672
|
rules: [BE_OUTBOX_WRITE_TX, BE_TIER_DIRECTION]
|
|
1482
1673
|
- id: be.feature.reactors
|
|
1674
|
+
since: 3.1.0
|
|
1483
1675
|
profiles: [be]
|
|
1484
1676
|
path: "src/features/reactors/<reactor>/"
|
|
1485
1677
|
presence: opt-in
|
|
@@ -1489,11 +1681,12 @@ slots:
|
|
|
1489
1681
|
tier: feature
|
|
1490
1682
|
owner: true
|
|
1491
1683
|
requires: [index.ts, "<reactor>.module.ts", transport/]
|
|
1492
|
-
allows: [index.ts, "<reactor>.module.ts", application/, transport/]
|
|
1684
|
+
allows: [index.ts, "<reactor>.module.ts", "<reactor>.module-definition.ts", "<reactor>.options.ts", application/, transport/]
|
|
1493
1685
|
tests: none
|
|
1494
1686
|
coverage: none
|
|
1495
1687
|
rules: [BE_KIND_ISOLATION, BE_FEATURE_IMPORTS_FEATURE, BE_FEATURE_NOT_COMPOSED]
|
|
1496
1688
|
- id: be.feature.jobs
|
|
1689
|
+
since: 3.1.0
|
|
1497
1690
|
profiles: [be]
|
|
1498
1691
|
path: "src/features/jobs/<job>/"
|
|
1499
1692
|
presence: opt-in
|
|
@@ -1503,11 +1696,12 @@ slots:
|
|
|
1503
1696
|
tier: feature
|
|
1504
1697
|
owner: true
|
|
1505
1698
|
requires: [index.ts, "<job>.module.ts", steps/, transport/]
|
|
1506
|
-
allows: [index.ts, "<job>.module.ts", application/, steps/, transport/]
|
|
1699
|
+
allows: [index.ts, "<job>.module.ts", "<job>.module-definition.ts", "<job>.options.ts", application/, steps/, transport/]
|
|
1507
1700
|
tests: none
|
|
1508
1701
|
coverage: none
|
|
1509
1702
|
rules: [BE_KIND_ISOLATION, BE_FEATURE_IMPORTS_FEATURE, BE_FEATURE_NOT_COMPOSED]
|
|
1510
1703
|
- id: be.feature.jobs.queue
|
|
1704
|
+
since: 3.1.0
|
|
1511
1705
|
profiles: [be]
|
|
1512
1706
|
path: "src/features/jobs/<job>/transport/queue/"
|
|
1513
1707
|
presence: opt-in # the queue transport of the job: its module and the processor the job runner hands the fenced claim to
|
|
@@ -1515,12 +1709,13 @@ slots:
|
|
|
1515
1709
|
tracked: tracked
|
|
1516
1710
|
tier: feature
|
|
1517
1711
|
requires: ["<job>-queue.module.ts", "<job>.processor.ts"]
|
|
1518
|
-
allows: ["<job>-queue.module.ts", "<job>.processor.ts"]
|
|
1712
|
+
allows: ["<job>-queue.module.ts", "<job>-queue.module-definition.ts", "<job>-queue.options.ts", "<job>.processor.ts"]
|
|
1519
1713
|
tests: none
|
|
1520
1714
|
coverage: none
|
|
1521
1715
|
composedBy: [api, worker]
|
|
1522
1716
|
rules: [BE_BACKGROUND_UNOWNED]
|
|
1523
1717
|
- id: be.feature.jobs.steps
|
|
1718
|
+
since: 3.1.0
|
|
1524
1719
|
profiles: [be]
|
|
1525
1720
|
path: "src/features/jobs/<job>/steps/"
|
|
1526
1721
|
presence: opt-in
|
|
@@ -1532,7 +1727,9 @@ slots:
|
|
|
1532
1727
|
coverage: none
|
|
1533
1728
|
rules: [BE_JOB_SHAPE, BE_JOB_RUN_KEY]
|
|
1534
1729
|
- id: be.platform.jobs
|
|
1730
|
+
since: 3.1.0
|
|
1535
1731
|
profiles: [be]
|
|
1732
|
+
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
1733
|
path: "src/modules/platform/jobs/"
|
|
1537
1734
|
presence: opt-in
|
|
1538
1735
|
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 +1742,9 @@ slots:
|
|
|
1545
1742
|
budget: {indexExports: 60}
|
|
1546
1743
|
rules: [BE_TIER_DIRECTION, ARCH_OWNER_CYCLE, BE_MODULE_SHAPE, BE_JOB_WRITE_OUTSIDE_OWNER, BE_JOB_FENCE_REQUIRED]
|
|
1547
1744
|
- id: be.projections
|
|
1745
|
+
since: 3.1.0
|
|
1548
1746
|
profiles: [be]
|
|
1747
|
+
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
1748
|
path: "src/modules/projections/<name>/"
|
|
1550
1749
|
presence: opt-in
|
|
1551
1750
|
pattern: projection # a read model: only its projection writes its table; recompute is idempotent and replayable; readers use get*
|
|
@@ -1560,7 +1759,9 @@ slots:
|
|
|
1560
1759
|
tests: unit-beside # a measured slot: <name>.projection.spec.ts beside its projection is legal (the file owes 100 per file; a spec of its own is optional)
|
|
1561
1760
|
coverage: required
|
|
1562
1761
|
rules: [BE_PROJECTION_WRITE_OWNER, BE_PROJECTION_SHAPE, BE_TIER_DIRECTION, BE_CONTEXT_COUPLING]
|
|
1762
|
+
exampleNa: "owner:no-runtime-specs (2026-10-17): the only example of this slot in an edition was a deleted runtime fixture; the scaffold e2e exercises the scaffolded slot"
|
|
1563
1763
|
- id: be.integrations
|
|
1764
|
+
since: 3.1.0
|
|
1564
1765
|
profiles: [be]
|
|
1565
1766
|
path: "src/modules/integrations/<provider>/"
|
|
1566
1767
|
presence: optional
|
|
@@ -1573,6 +1774,7 @@ slots:
|
|
|
1573
1774
|
budget: {indexExports: 80} # a provider's port surface includes clients, config, options, tokens and errors
|
|
1574
1775
|
rules: [BE_TIER_DIRECTION, BE_ERROR_HOME, BE_SECRET_DEFAULT, BE_INTEGRATION_SPEC_MISSING]
|
|
1575
1776
|
- id: be.integrations.model
|
|
1777
|
+
since: 3.1.0
|
|
1576
1778
|
profiles: [be]
|
|
1577
1779
|
path: "src/modules/integrations/<provider>/model/"
|
|
1578
1780
|
presence: opt-in
|
|
@@ -1581,8 +1783,13 @@ slots:
|
|
|
1581
1783
|
tests: none
|
|
1582
1784
|
coverage: required
|
|
1583
1785
|
why: an ML or LLM model client is an integration; weights and datasets are never in git
|
|
1786
|
+
rules: [BE_TIER_DIRECTION, BE_INFRA_OWNER, BE_INTEGRATION_SPEC_MISSING]
|
|
1787
|
+
|
|
1584
1788
|
- id: be.integrations.supabase
|
|
1585
1789
|
profiles: [be]
|
|
1790
|
+
exemplifiedBy:
|
|
1791
|
+
- packages/hfs/templates/be/skeleton-lite/src/modules/integrations/supabase/
|
|
1792
|
+
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
1793
|
# The only be import of @supabase/* (L10): the Auth admin and Storage admin client, plus the JWKS-based verification of
|
|
1587
1794
|
# the Supabase access token the guard chain reads (L16). The connection itself is ordinary Postgres (the shared
|
|
1588
1795
|
# EntityManager of the declared connection); what lives here are the service calls no database role covers.
|
|
@@ -1599,12 +1806,15 @@ slots:
|
|
|
1599
1806
|
rules: [BE_TIER_DIRECTION, BE_ERROR_HOME, BE_SECRET_DEFAULT]
|
|
1600
1807
|
coverage: required
|
|
1601
1808
|
since: 2.1.0
|
|
1809
|
+
exampleNa: "owner:no-runtime-specs (2026-10-17): the only example of this slot in an edition was a deleted runtime fixture; the scaffold e2e exercises the scaffolded slot"
|
|
1602
1810
|
|
|
1603
1811
|
# ----- BE: tests (owner test layout 2026-09-30). Unit specs are <name>.service.spec.ts beside their service and nothing else; everything else
|
|
1604
1812
|
# lives under src/tests/, one folder per kind, and the folder and the file suffix always agree (a file whose suffix does
|
|
1605
1813
|
# not match its folder matches no slot: HFS_SLOT_UNDECLARED).
|
|
1606
1814
|
- id: be.tests.world
|
|
1815
|
+
since: 3.1.0
|
|
1607
1816
|
profiles: [be]
|
|
1817
|
+
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
1818
|
path: "src/tests/world/"
|
|
1609
1819
|
presence: optional
|
|
1610
1820
|
tracked: tracked
|
|
@@ -1628,7 +1838,11 @@ slots:
|
|
|
1628
1838
|
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
1839
|
overridden in the DI container. It is the only test location that may import typeorm's DataSource or testcontainers,
|
|
1630
1840
|
call migrate/runMigrations/synchronize, or write process.env.
|
|
1841
|
+
rules: [BE_TEST_TOPOLOGY, BE_TEST_CONSTRAINTS_ON]
|
|
1842
|
+
exampleNa: "owner:no-runtime-specs (2026-10-17): the only example of this slot in an edition was a deleted runtime fixture; the scaffold e2e exercises the scaffolded slot"
|
|
1843
|
+
|
|
1631
1844
|
- id: be.tests.world.kit
|
|
1845
|
+
since: 3.1.0
|
|
1632
1846
|
profiles: [be]
|
|
1633
1847
|
path: "src/tests/world/kit/"
|
|
1634
1848
|
presence: optional
|
|
@@ -1639,6 +1853,7 @@ slots:
|
|
|
1639
1853
|
coverage: none
|
|
1640
1854
|
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
1855
|
- id: be.tests.fixtures
|
|
1856
|
+
since: 3.1.0
|
|
1642
1857
|
profiles: [be]
|
|
1643
1858
|
path: "src/tests/fixtures/"
|
|
1644
1859
|
presence: optional
|
|
@@ -1649,6 +1864,7 @@ slots:
|
|
|
1649
1864
|
coverage: none
|
|
1650
1865
|
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
1866
|
- id: be.tests.fixtures.builders
|
|
1867
|
+
since: 3.1.0
|
|
1652
1868
|
profiles: [be]
|
|
1653
1869
|
path: "src/tests/fixtures/builders/*.builder.ts"
|
|
1654
1870
|
presence: optional
|
|
@@ -1663,6 +1879,7 @@ slots:
|
|
|
1663
1879
|
*.fixture.ts or *.factory.ts anywhere, is a BE_SOURCE_FORM finding
|
|
1664
1880
|
rules: [BE_SOURCE_FORM]
|
|
1665
1881
|
- id: be.tests.fixtures.i18n
|
|
1882
|
+
since: 3.1.0
|
|
1666
1883
|
profiles: [be]
|
|
1667
1884
|
path: "src/tests/fixtures/i18n/"
|
|
1668
1885
|
presence: optional
|
|
@@ -1673,7 +1890,9 @@ slots:
|
|
|
1673
1890
|
coverage: none
|
|
1674
1891
|
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
1892
|
- id: be.tests.integration
|
|
1893
|
+
since: 3.1.0
|
|
1676
1894
|
profiles: [be]
|
|
1895
|
+
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
1896
|
path: "src/tests/integration/<capability>/*.integration-spec.ts"
|
|
1678
1897
|
presence: optional
|
|
1679
1898
|
tracked: tracked
|
|
@@ -1687,10 +1906,12 @@ slots:
|
|
|
1687
1906
|
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
1907
|
rules: [BE_TEST_TOPOLOGY, BE_INTEGRATION_SPEC_MISSING]
|
|
1689
1908
|
- id: be.tests.e2e
|
|
1909
|
+
since: 3.1.0
|
|
1690
1910
|
profiles: [be]
|
|
1911
|
+
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
1912
|
path: "src/tests/e2e/<area>/*.e2e-spec.ts"
|
|
1692
1913
|
presence: optional
|
|
1693
|
-
litePresence: forbidden #
|
|
1914
|
+
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
1915
|
tracked: tracked
|
|
1695
1916
|
tier: e2e
|
|
1696
1917
|
tests: e2e
|
|
@@ -1699,18 +1920,20 @@ slots:
|
|
|
1699
1920
|
why: flow e2e through useTestWorld({ apps }); external services are the world's network fakes; run by test:e2e
|
|
1700
1921
|
rules: [HFS_CI_E2E_AUTOMATIC, BE_TEST_TOPOLOGY, BE_E2E_FLOW]
|
|
1701
1922
|
- id: be.tests.contract
|
|
1923
|
+
since: 3.1.0
|
|
1702
1924
|
profiles: [be]
|
|
1703
1925
|
path: "src/tests/contract/<provider>/*.contract-spec.ts"
|
|
1704
1926
|
presence: optional
|
|
1705
|
-
litePresence:
|
|
1927
|
+
litePresence: optional # owner decision 2026-10-14: lite may run provider sandbox contracts
|
|
1706
1928
|
tracked: tracked
|
|
1707
1929
|
tier: e2e
|
|
1708
1930
|
tests: e2e
|
|
1709
|
-
goesTo: "
|
|
1931
|
+
goesTo: "src/tests/contract/<provider>/*.contract-spec.ts in either edition: optional provider sandbox specs via test:contract; e2e stays full-only"
|
|
1710
1932
|
coverage: none
|
|
1711
1933
|
why: provider sandbox contracts, skipped without sandbox config; run only by test:contract, never part of test or test:e2e
|
|
1712
1934
|
rules: [BE_TEST_TOPOLOGY]
|
|
1713
1935
|
- id: be.tests.e2e-world-dir
|
|
1936
|
+
since: 3.1.0
|
|
1714
1937
|
profiles: [be]
|
|
1715
1938
|
path: "src/tests/e2e/world/"
|
|
1716
1939
|
presence: forbidden
|
|
@@ -1721,6 +1944,7 @@ slots:
|
|
|
1721
1944
|
|
|
1722
1945
|
# ----- FE: apps ----------------------------------------------------------------------------------------
|
|
1723
1946
|
- id: fe.app.next
|
|
1947
|
+
since: 3.1.0
|
|
1724
1948
|
profiles: [fe]
|
|
1725
1949
|
# Each fe app is an npm workspace: its package.json is @<project>/<app> with its own runtime dependencies and the fixed
|
|
1726
1950
|
# workspace scripts (HFS_MONO_FE_WORKSPACE); turbo runs them.
|
|
@@ -1735,6 +1959,7 @@ slots:
|
|
|
1735
1959
|
tests: none
|
|
1736
1960
|
rules: [FE_APP_ISOLATION, FE_ERROR_BOUNDARY_MISSING, FE_NEXT_CONVENTIONS, HFS_MONO_FE_WORKSPACE, HFS_MONO_WORKSPACE_DEP]
|
|
1737
1961
|
- id: fe.app-optional
|
|
1962
|
+
since: 3.1.0
|
|
1738
1963
|
profiles: [fe]
|
|
1739
1964
|
path: "apps/<app>/public/"
|
|
1740
1965
|
presence: optional
|
|
@@ -1742,12 +1967,13 @@ slots:
|
|
|
1742
1967
|
tier: none
|
|
1743
1968
|
tests: none
|
|
1744
1969
|
- id: fe.route
|
|
1970
|
+
since: 3.1.0
|
|
1745
1971
|
profiles: [fe]
|
|
1746
1972
|
path: "apps/<app>/src/app/"
|
|
1747
1973
|
presence: required
|
|
1748
1974
|
tracked: tracked
|
|
1749
1975
|
tier: route
|
|
1750
|
-
#
|
|
1976
|
+
# 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
1977
|
# route handler of this slot, the Supabase auth callback is fe.route.callback, and no file holds a Server Action
|
|
1752
1978
|
# (FE_ROUTE_HANDLER_FORBIDDEN, FE_SERVER_ACTION_FORBIDDEN).
|
|
1753
1979
|
allows: ["[locale]/**/{page,layout,template,loading,error,not-found,default}.tsx", "[locale]/providers.tsx",
|
|
@@ -1760,6 +1986,7 @@ slots:
|
|
|
1760
1986
|
tests: none
|
|
1761
1987
|
rules: [FE_ROUTE_FILES_THIN, FE_CLIENT_BOUNDARY, FE_OWNER_REACHABLE, FE_ROUTE_HANDLER_FORBIDDEN, FE_SERVER_ACTION_FORBIDDEN]
|
|
1762
1988
|
- id: fe.source-root-pinned
|
|
1989
|
+
since: 3.1.0
|
|
1763
1990
|
profiles: [fe]
|
|
1764
1991
|
path: "apps/<app>/src/{proxy.ts,instrumentation.ts,instrumentation-client.ts,sentry.server.config.ts,sentry.edge.config.ts}"
|
|
1765
1992
|
presence: optional
|
|
@@ -1769,6 +1996,7 @@ slots:
|
|
|
1769
1996
|
tests: none
|
|
1770
1997
|
rules: [FE_NEXT_CONVENTIONS] # middleware.ts is refused on Next >= 16 (fe.source-root-middleware)
|
|
1771
1998
|
- id: fe.source-root-middleware
|
|
1999
|
+
since: 3.1.0
|
|
1772
2000
|
profiles: [fe]
|
|
1773
2001
|
path: "apps/<app>/src/{middleware.ts,middleware.js}"
|
|
1774
2002
|
presence: forbidden
|
|
@@ -1789,7 +2017,10 @@ slots:
|
|
|
1789
2017
|
tier: route
|
|
1790
2018
|
tests: none
|
|
1791
2019
|
since: 2.1.0
|
|
2020
|
+
rules: [FE_ROUTE_HANDLER_FORBIDDEN, FE_CALLBACK_SHAPE, FE_SUPABASE_CLIENT_OWNER, FE_SERVICE_ROLE_FORBIDDEN]
|
|
2021
|
+
|
|
1792
2022
|
- id: fe.feature
|
|
2023
|
+
since: 3.1.0
|
|
1793
2024
|
profiles: [fe]
|
|
1794
2025
|
path: "apps/<app>/src/features/{pages,layouts,overlays}/<name>/"
|
|
1795
2026
|
presence: required
|
|
@@ -1804,6 +2035,7 @@ slots:
|
|
|
1804
2035
|
budget: {component.tsx: 300, index.tsx: 200, dataHooks: 6, useState: 6}
|
|
1805
2036
|
rules: [FE_SIZE_BUDGET, FE_OWNER_REACHABLE, FE_I18N_LITERAL]
|
|
1806
2037
|
- id: fe.components
|
|
2038
|
+
since: 3.1.0
|
|
1807
2039
|
profiles: [fe]
|
|
1808
2040
|
path: "apps/<app>/src/components/{blocks,composites,branches,leaves}/<name>/"
|
|
1809
2041
|
presence: optional
|
|
@@ -1834,6 +2066,7 @@ slots:
|
|
|
1834
2066
|
since: 3.0.0
|
|
1835
2067
|
rules: [FE_CLIENT_BOUNDARY]
|
|
1836
2068
|
- id: fe.hooks
|
|
2069
|
+
since: 3.1.0
|
|
1837
2070
|
profiles: [fe]
|
|
1838
2071
|
path: "apps/<app>/src/hooks/<domain>/"
|
|
1839
2072
|
presence: optional
|
|
@@ -1956,7 +2189,10 @@ slots:
|
|
|
1956
2189
|
tests: none
|
|
1957
2190
|
budget: {file: 400}
|
|
1958
2191
|
since: 3.0.0
|
|
2192
|
+
rules: [FE_SOURCE_FORM, FE_HOOKS_ARE_HOOKS, FE_OWNER_REACHABLE]
|
|
2193
|
+
|
|
1959
2194
|
- id: fe.modules
|
|
2195
|
+
since: 3.1.0
|
|
1960
2196
|
profiles: [fe]
|
|
1961
2197
|
path: "apps/<app>/src/modules/<capability>/"
|
|
1962
2198
|
presence: required
|
|
@@ -1970,6 +2206,7 @@ slots:
|
|
|
1970
2206
|
budget: {file: 400}
|
|
1971
2207
|
rules: [FE_ENV_OWNER, FE_TRANSPORT_OWNER]
|
|
1972
2208
|
- id: fe.modules.api
|
|
2209
|
+
since: 3.1.0
|
|
1973
2210
|
profiles: [fe]
|
|
1974
2211
|
path: "apps/<app>/src/modules/api/"
|
|
1975
2212
|
presence: optional
|
|
@@ -1989,7 +2226,19 @@ slots:
|
|
|
1989
2226
|
entries: ["graphql/queries/query-*.ts", "graphql/queries/types/*.ts", "graphql/mutations/mutation-*.ts", "graphql/mutations/types/*.ts"]
|
|
1990
2227
|
tests: none
|
|
1991
2228
|
rules: [FE_TRANSPORT_OWNER, FE_HTTP_STATUS_COLLAPSE, FE_WIRE_GENERATED]
|
|
2229
|
+
- id: fe.transport.generated
|
|
2230
|
+
since: 3.2.0
|
|
2231
|
+
profiles: [fe]
|
|
2232
|
+
path: "apps/<app>/src/modules/api/graphql/__generated__/"
|
|
2233
|
+
presence: optional
|
|
2234
|
+
tracked: ignored
|
|
2235
|
+
tier: transport
|
|
2236
|
+
tests: none
|
|
2237
|
+
why: the canonical GraphQL client output of codegen is local and never committed; this specific slot overrides the tracked API parent
|
|
2238
|
+
rules: [FE_WIRE_GENERATED]
|
|
2239
|
+
exampleNa: "owner:no-runtime-specs (2026-10-17): the only example of this slot in an edition was a deleted runtime fixture; the scaffold e2e exercises the scaffolded slot"
|
|
1992
2240
|
- id: fe.transport.client
|
|
2241
|
+
since: 3.1.0
|
|
1993
2242
|
profiles: [fe]
|
|
1994
2243
|
path: "apps/<app>/src/modules/api/graphql/clients/"
|
|
1995
2244
|
presence: optional
|
|
@@ -1999,6 +2248,7 @@ slots:
|
|
|
1999
2248
|
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
2249
|
rules: [FE_TRANSPORT_OWNER]
|
|
2001
2250
|
- id: fe.transport.outcome
|
|
2251
|
+
since: 3.1.0
|
|
2002
2252
|
profiles: [fe]
|
|
2003
2253
|
path: "apps/<app>/src/modules/api/graphql/types.ts"
|
|
2004
2254
|
presence: optional
|
|
@@ -2009,6 +2259,7 @@ slots:
|
|
|
2009
2259
|
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
2260
|
rules: [FE_HTTP_STATUS_COLLAPSE]
|
|
2011
2261
|
- id: fe.modules.config
|
|
2262
|
+
since: 3.1.0
|
|
2012
2263
|
profiles: [fe]
|
|
2013
2264
|
path: "apps/<app>/src/modules/config/"
|
|
2014
2265
|
presence: required
|
|
@@ -2016,7 +2267,10 @@ slots:
|
|
|
2016
2267
|
tier: foundation
|
|
2017
2268
|
tests: none
|
|
2018
2269
|
why: the only reader of process.env and NEXT_PUBLIC_*; fails fast, no localhost fallback
|
|
2270
|
+
rules: [FE_ENV_OWNER]
|
|
2271
|
+
|
|
2019
2272
|
- id: fe.modules.i18n
|
|
2273
|
+
since: 3.1.0
|
|
2020
2274
|
profiles: [fe]
|
|
2021
2275
|
path: "apps/<app>/src/modules/i18n/"
|
|
2022
2276
|
presence: required
|
|
@@ -2029,6 +2283,7 @@ slots:
|
|
|
2029
2283
|
tests: none
|
|
2030
2284
|
rules: [FE_I18N_PLACEMENT, FE_I18N_CATALOG]
|
|
2031
2285
|
- id: fe.modules.brand
|
|
2286
|
+
since: 3.1.0
|
|
2032
2287
|
profiles: [fe]
|
|
2033
2288
|
path: "apps/<app>/src/modules/brand/brand.css"
|
|
2034
2289
|
presence: optional
|
|
@@ -2038,6 +2293,7 @@ slots:
|
|
|
2038
2293
|
why: the ONLY app file that may hold colour values; sets grammar brand tokens only, light and dark
|
|
2039
2294
|
rules: [FE_STYLE_TOKEN_ONLY]
|
|
2040
2295
|
- id: fe.modules.routes
|
|
2296
|
+
since: 3.1.0
|
|
2041
2297
|
profiles: [fe]
|
|
2042
2298
|
path: "apps/<app>/src/modules/routes/"
|
|
2043
2299
|
presence: required
|
|
@@ -2045,7 +2301,10 @@ slots:
|
|
|
2045
2301
|
tier: foundation
|
|
2046
2302
|
tests: none
|
|
2047
2303
|
why: every href builder; FE_HREF_RESOLVES checks each against app/
|
|
2304
|
+
rules: [FE_HREF_RESOLVES, FE_ROUTE_HARDCODED]
|
|
2305
|
+
|
|
2048
2306
|
- id: fe.modules.types
|
|
2307
|
+
since: 3.1.0
|
|
2049
2308
|
profiles: [fe]
|
|
2050
2309
|
path: "apps/<app>/src/modules/types/"
|
|
2051
2310
|
presence: optional
|
|
@@ -2053,6 +2312,8 @@ slots:
|
|
|
2053
2312
|
tier: foundation
|
|
2054
2313
|
tests: none
|
|
2055
2314
|
why: shared types with no runtime behaviour; a component may import them
|
|
2315
|
+
rules: [FE_TYPE_ESCAPE, HFS_TS_STRICT]
|
|
2316
|
+
|
|
2056
2317
|
- id: fe.modules.types.ambient
|
|
2057
2318
|
parent: fe.modules.types
|
|
2058
2319
|
profiles: [fe]
|
|
@@ -2065,6 +2326,8 @@ slots:
|
|
|
2065
2326
|
tests: none
|
|
2066
2327
|
since: 2.2.0
|
|
2067
2328
|
why: global ambient declarations loaded by the app compiler; this owner exposes no runtime API
|
|
2329
|
+
rules: [FE_TYPE_ESCAPE, HFS_TS_STRICT]
|
|
2330
|
+
|
|
2068
2331
|
- id: fe.modules.auth
|
|
2069
2332
|
profiles: [fe]
|
|
2070
2333
|
# The identity-provider client (fe-stack auth, FE-AUTH): the session, the access token the Apollo link attaches and the
|
|
@@ -2076,6 +2339,7 @@ slots:
|
|
|
2076
2339
|
tier: modules
|
|
2077
2340
|
owner: true
|
|
2078
2341
|
requires: [index.ts]
|
|
2342
|
+
entries: [server.ts] # the server-only PKCE exchange API used by the opted-in callback route
|
|
2079
2343
|
tests: none
|
|
2080
2344
|
since: 3.0.0
|
|
2081
2345
|
rules: [FE_SUPABASE_CLIENT_OWNER, FE_SERVICE_ROLE_FORBIDDEN]
|
|
@@ -2093,7 +2357,10 @@ slots:
|
|
|
2093
2357
|
roles: {entry: index.ts, hooks: hooks.ts} # hooks.ts: the use*WithToast hooks of the toast module
|
|
2094
2358
|
tests: none
|
|
2095
2359
|
since: 3.0.0
|
|
2360
|
+
rules: [FE_I18N_LITERAL, FE_HOOKS_ARE_HOOKS, FE_HTTP_STATUS_COLLAPSE]
|
|
2361
|
+
|
|
2096
2362
|
- id: fe.package.ui
|
|
2363
|
+
since: 3.1.0
|
|
2097
2364
|
profiles: [fe]
|
|
2098
2365
|
path: "packages/<family>-ui/"
|
|
2099
2366
|
presence: opt-in
|
|
@@ -2108,6 +2375,7 @@ slots:
|
|
|
2108
2375
|
why: built to dist with explicit named exports; only composites, branches and leaves; no unused export; no colour literal
|
|
2109
2376
|
rules: [FE_PACKAGE_SHAPE, HFS_UNUSED_EXPORT, FE_STYLE_TOKEN_ONLY]
|
|
2110
2377
|
- id: fe.package.api
|
|
2378
|
+
since: 3.1.0
|
|
2111
2379
|
profiles: [fe]
|
|
2112
2380
|
path: "packages/<family>-api/"
|
|
2113
2381
|
presence: opt-in
|
|
@@ -2119,6 +2387,7 @@ slots:
|
|
|
2119
2387
|
why: the one transport client and the one Outcome union shared by every app of the repository; no app keeps its own
|
|
2120
2388
|
rules: [FE_TRANSPORT_OWNER, FE_PACKAGE_SHAPE, HFS_UNUSED_EXPORT]
|
|
2121
2389
|
- id: fe.package.api.client
|
|
2390
|
+
since: 3.1.0
|
|
2122
2391
|
profiles: [fe]
|
|
2123
2392
|
path: "packages/<family>-api/src/graphql/clients/"
|
|
2124
2393
|
presence: optional # enabled with its package (fe.package.api is the opt-in)
|
|
@@ -2128,6 +2397,7 @@ slots:
|
|
|
2128
2397
|
why: the repository's one pair of Apollo clients and their link chain
|
|
2129
2398
|
rules: [FE_TRANSPORT_OWNER]
|
|
2130
2399
|
- id: fe.package.api.outcome
|
|
2400
|
+
since: 3.1.0
|
|
2131
2401
|
profiles: [fe]
|
|
2132
2402
|
path: "packages/<family>-api/src/graphql/types.ts"
|
|
2133
2403
|
presence: optional # enabled with its package (fe.package.api is the opt-in)
|
|
@@ -2138,6 +2408,7 @@ slots:
|
|
|
2138
2408
|
why: the repository's one GraphQLResponse union
|
|
2139
2409
|
rules: [FE_HTTP_STATUS_COLLAPSE]
|
|
2140
2410
|
- id: fe.package.i18n
|
|
2411
|
+
since: 3.1.0
|
|
2141
2412
|
profiles: [fe]
|
|
2142
2413
|
path: "packages/<family>-i18n/"
|
|
2143
2414
|
presence: opt-in
|