@starci/hfs 4.9.9 → 4.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +33 -0
- package/README.md +20 -8
- package/db/backup-receipt.mjs +10 -47
- package/db/drift.mjs +289 -0
- package/db/dump.mjs +12 -11
- package/db/entity-list.mjs +9 -0
- package/db/migrate.mjs +84 -260
- package/db/new.mjs +46 -0
- package/db/post-cutover.mjs +13 -17
- package/db/postgres.mjs +80 -0
- package/emit/contracts.mjs +5 -9
- package/lint/run.mjs +12 -2
- package/package.json +5 -3
- package/release/gate.mjs +82 -31
- package/release/index.mjs +19 -8
- package/release/push.mjs +4 -4
- package/release/records.mjs +54 -0
- package/release/urgent.mjs +129 -0
- package/runtime/knowledge/hfs/canon-pins.yaml +54 -24
- package/runtime/knowledge/hfs/design-truths.yaml +1 -1
- package/runtime/knowledge/hfs/fe-stack.yaml +4 -11
- package/runtime/knowledge/hfs/rules.yaml +250 -157
- package/runtime/knowledge/hfs/slots.yaml +199 -174
- package/runtime/knowledge/patterns/be/event-bus.yaml +3 -3
- package/runtime/knowledge/patterns/be/idempotency.yaml +3 -3
- package/runtime/knowledge/patterns/be/jobs.yaml +4 -4
- package/runtime/knowledge/patterns/be/projections.yaml +7 -5
- package/runtime/knowledge/patterns/be/queues.yaml +8 -8
- package/runtime/knowledge/patterns/be/reactors.yaml +2 -2
- package/runtime/knowledge/patterns/be/realtime.yaml +6 -6
- package/runtime/knowledge/patterns/be/saga.yaml +3 -3
- package/runtime/knowledge/patterns/be/webhooks.yaml +1 -1
- package/runtime/modules/kernel/command-policy.yaml +1 -1
- package/runtime/modules/kernel/failure-codes.yaml +86 -115
- package/runtime/modules/schemas/application-stacks.schema.yaml +5 -5
- package/runtime/scripts/hfs/app-wiring.mjs +23 -2
- package/runtime/scripts/hfs/architecture/canon-clones.mjs +1 -1
- package/runtime/scripts/hfs/architecture/{supabase-be.mjs → identity.mjs} +45 -15
- package/runtime/scripts/hfs/architecture/index.mjs +2 -3
- package/runtime/scripts/hfs/architecture/{supabase-ast.mjs → jwt-ast.mjs} +2 -103
- package/runtime/scripts/hfs/architecture/register-once.mjs +1 -1
- package/runtime/scripts/hfs/architecture/schema-owner.mjs +29 -4
- package/runtime/scripts/hfs/architecture/server-action.mjs +3 -4
- package/runtime/scripts/hfs/architecture/side-boundary.mjs +1 -1
- package/runtime/scripts/hfs/architecture/{supabase-keys.mjs → sql-keys.mjs} +1 -1
- package/runtime/scripts/hfs/architecture/sql-owner.mjs +25 -27
- package/runtime/scripts/hfs/architecture/transport-owner.mjs +7 -3
- package/runtime/scripts/hfs/check.mjs +8 -8
- package/runtime/scripts/hfs/coverage-scope.mjs +1 -1
- package/runtime/scripts/hfs/db-baseline-verb.mjs +48 -30
- package/runtime/scripts/hfs/db-plan-verb.mjs +17 -106
- package/runtime/scripts/hfs/db-restamp-verb.mjs +11 -10
- package/runtime/scripts/hfs/declaration-shape.mjs +15 -18
- package/runtime/scripts/hfs/manifest-shape.mjs +6 -5
- package/runtime/scripts/hfs/rule-params-shape.mjs +4 -1
- package/runtime/scripts/hfs/rules/a15-findings.mjs +1 -1
- package/runtime/scripts/hfs/rules/cli.mjs +1 -1
- package/runtime/scripts/hfs/rules/database-access.mjs +8 -74
- package/runtime/scripts/hfs/rules/database-baseline.mjs +3 -3
- package/runtime/scripts/hfs/rules/database-config.mjs +6 -214
- package/runtime/scripts/hfs/rules/database-constants.mjs +8 -10
- package/runtime/scripts/hfs/rules/database-migrations.mjs +3 -3
- package/runtime/scripts/hfs/rules/database-queues.mjs +4 -5
- package/runtime/scripts/hfs/rules/database-sql.mjs +1 -1
- package/runtime/scripts/hfs/rules/database.mjs +36 -39
- package/runtime/scripts/hfs/rules/edition.mjs +1 -1
- package/runtime/scripts/hfs/rules/{supabase-secrets.mjs → lite-secrets.mjs} +6 -6
- package/runtime/scripts/hfs/rules/script-hygiene.mjs +1 -10
- package/runtime/scripts/hfs/rules/stacks-lite-backing.mjs +5 -4
- package/runtime/scripts/hfs/rules/stacks-release.mjs +3 -2
- package/runtime/scripts/hfs/rules/stacks-runtime.mjs +2 -2
- package/runtime/scripts/hfs/rules/stacks.mjs +1 -1
- package/runtime/scripts/hfs/slot-app-view.mjs +1 -1
- package/runtime/scripts/hfs/slots.mjs +8 -2
- package/runtime/scripts/hfs/sql/pg-parse.mjs +2 -2
- package/runtime/scripts/lib/secret-patterns.mjs +2 -2
- package/runtime/scripts/lib/swarm-stack.mjs +7 -4
- package/scaffold/add-app.mjs +2 -10
- package/scaffold/add-cli-lite.mjs +6 -6
- package/scaffold/add-compose.mjs +1 -1
- package/scaffold/add-inbox-lite.mjs +4 -8
- package/scaffold/add-plan.mjs +1 -1
- package/scaffold/add-queue-lite.mjs +16 -24
- package/scaffold/add-table.mjs +34 -26
- package/scaffold/add.mjs +2 -2
- package/scaffold/app.mjs +19 -86
- package/scaffold/lite-exports.mjs +1 -1
- package/src/main.mjs +24 -22
- package/stack/cli.mjs +10 -2
- package/stack/deploy.mjs +21 -4
- package/stack/target-binding.mjs +0 -60
- package/stack/urgent-floor.mjs +147 -0
- package/sync/ci-commands.mjs +3 -1
- package/sync/index.mjs +32 -8
- package/templates/app/ci-workflows/github/workflows/stack-deploy.yml +4 -1
- package/templates/app/ci-workflows-lite/github/workflows/ci.yml +1 -17
- package/templates/app/ci-workflows-lite/github/workflows/db-deploy.yml +6 -8
- package/templates/app/ci-workflows-lite/github/workflows/stack-deploy.yml +4 -1
- package/templates/app/codegen-lite/scripts/codegen.mjs +2 -39
- package/templates/app/db-bootstrap/db/bootstrap.sql +139 -0
- package/templates/app/gitignore-lite +0 -3
- package/templates/app/package-scripts/db.json +9 -0
- package/templates/app/package-scripts/package.json +0 -1
- package/templates/app/package-scripts-lite/package.json +1 -1
- package/templates/app/skeleton/.starcistacks/dev/README.md +1 -1
- package/templates/app/skeleton/.starcistacks/production/README.md +1 -1
- package/templates/app/skeleton/README.md +1 -1
- package/templates/app/skeleton-lite/.starcistacks/application-stacks.yaml +84 -19
- package/templates/app/skeleton-lite/.starcistacks/dev/README.md +5 -5
- package/templates/app/skeleton-lite/.starcistacks/dev/environment.json +1 -1
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/recovery/restore-proof.md +1 -1
- package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/stack.yaml +69 -6
- package/templates/app/skeleton-lite/.starcistacks/dev/runtime/env/KEYS.md +18 -7
- package/templates/app/skeleton-lite/.starcistacks/production/README.md +6 -6
- package/templates/app/skeleton-lite/.starcistacks/production/environment.json +1 -1
- package/templates/app/skeleton-lite/.starcistacks/production/infra/swarm/recovery/restore-proof.md +2 -2
- package/templates/app/skeleton-lite/.starcistacks/production/infra/swarm/stack.yaml +69 -6
- package/templates/app/skeleton-lite/.starcistacks/production/runtime/env/KEYS.md +17 -9
- package/templates/app/skeleton-lite/README.md +11 -11
- package/templates/app/skeleton-lite/{supabase → db}/migrations/__timestamp___baseline.sql +21 -1
- package/templates/app/tool-config-lite/prettierignore +0 -1
- package/templates/be/inbox/database-client.spec.ts.tpl +9 -5
- package/templates/be/inbox/database-client.ts.tpl +13 -4
- package/templates/be/inbox/platform/client-operation-outcomes.sql.tpl +47 -0
- package/templates/be/inbox/platform/event-bus/event-bus.service.spec.ts.tpl +3 -1
- package/templates/be/inbox/platform/event-bus/event-bus.service.ts.tpl +2 -2
- package/templates/be/inbox/platform/inbox/inbox.service.spec.ts.tpl +6 -1
- package/templates/be/inbox/platform/inbox/inbox.service.ts.tpl +39 -7
- package/templates/be/inbox/platform/migration.sql.tpl +22 -0
- package/templates/be/inbox/service.spec.ts.tpl +2 -2
- package/templates/be/inbox/service.ts.tpl +1 -2
- package/templates/be/patterns/jobs/platform-lite/job-claim.service.spec.ts.tpl +156 -0
- package/templates/be/patterns/jobs/platform-lite/job-claim.service.ts.tpl +114 -0
- package/templates/be/patterns/jobs/platform-lite/jobs.sql.tpl +51 -0
- package/templates/be/patterns/queues/platform-lite/queue-outbox.service.spec.ts.tpl +62 -0
- package/templates/be/patterns/queues/platform-lite/queue-outbox.service.ts.tpl +20 -0
- package/templates/be/patterns/queues/platform-lite/queue-outbox.sql.tpl +35 -0
- package/templates/be/patterns/queues/platform-lite/queue-relay.service.spec.ts.tpl +4 -1
- package/templates/be/patterns/queues/platform-lite/queue-relay.service.ts.tpl +16 -5
- package/templates/be/patterns/realtime/channel.gateway.spec.ts.tpl +8 -8
- package/templates/be/patterns/realtime/channel.websocket.module.ts.tpl +3 -3
- package/templates/be/patterns/realtime/channel.websocket.options.ts.tpl +2 -2
- package/templates/be/skeleton/src/modules/domain/identity/identity.decorators.ts +18 -7
- package/templates/be/skeleton/src/modules/platform/composition/composition.decorators.ts +3 -0
- package/templates/be/skeleton/src/modules/platform/composition/index.ts +1 -1
- package/templates/be/skeleton/src/modules/platform/errors/errors.filter.spec.ts +15 -1
- package/templates/be/skeleton/src/modules/platform/errors/errors.filter.ts +10 -9
- package/templates/be/skeleton/src/modules/platform/http-security/index.ts +1 -1
- package/templates/be/skeleton/src/modules/platform/http-security/origin.guard.spec.ts +34 -18
- package/templates/be/skeleton/src/modules/platform/http-security/origin.guard.ts +29 -8
- package/templates/be/skeleton/src/modules/platform/http-security/rate-limit.guard.spec.ts +80 -4
- package/templates/be/skeleton/src/modules/platform/http-security/rate-limit.guard.ts +39 -11
- package/templates/be/skeleton-lite/apps/api/Dockerfile +0 -1
- package/templates/be/skeleton-lite/apps/api/src/api.env.ts +9 -5
- package/templates/be/skeleton-lite/apps/api/src/api.options.ts +7 -4
- package/templates/be/skeleton-lite/apps/api/src/app.module.ts +4 -4
- package/templates/be/skeleton-lite/apps/api/src/compose/domain.ts +3 -3
- package/templates/be/skeleton-lite/apps/api/src/compose/integrations.ts +9 -13
- package/templates/be/skeleton-lite/apps/api/src/compose/platform.ts +4 -2
- package/templates/be/skeleton-lite/apps/cli/src/cli.env.ts +2 -2
- package/templates/be/skeleton-lite/apps/cli/src/compose/platform.ts +3 -2
- package/templates/be/skeleton-lite/src/modules/domain/identity/auth.guard.spec.ts +4 -4
- package/templates/be/skeleton-lite/src/modules/domain/identity/auth.guard.ts +3 -3
- package/templates/be/skeleton-lite/src/modules/domain/identity/identity.contracts.ts +1 -1
- package/templates/be/skeleton-lite/src/modules/domain/identity/identity.decorators.ts +23 -13
- package/templates/be/skeleton-lite/src/modules/domain/identity/identity.options.ts +3 -3
- package/templates/be/skeleton-lite/src/modules/integrations/object-storage/index.ts +4 -0
- package/templates/be/skeleton-lite/src/modules/integrations/object-storage/object-storage.client.spec.ts +22 -0
- package/templates/be/skeleton-lite/src/modules/integrations/object-storage/object-storage.client.ts +25 -0
- package/templates/be/skeleton-lite/src/modules/integrations/object-storage/object-storage.config.ts +12 -0
- package/templates/be/skeleton-lite/src/modules/integrations/object-storage/object-storage.decorators.ts +9 -0
- package/templates/be/skeleton-lite/src/modules/integrations/object-storage/object-storage.options.ts +35 -0
- package/templates/be/skeleton-lite/src/modules/integrations/passport/errors/passport.error.ts +22 -0
- package/templates/be/skeleton-lite/src/modules/integrations/passport/index.ts +12 -0
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.config.ts +23 -0
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.cookie.ts +29 -0
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.decorators.ts +22 -0
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.options.ts +63 -0
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.password.ts +11 -0
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.tokens.spec.ts +32 -0
- package/templates/be/skeleton-lite/src/modules/integrations/passport/passport.tokens.ts +69 -0
- package/templates/be/skeleton-lite/src/modules/integrations/passport/strategies/google.strategy.ts +37 -0
- package/templates/be/skeleton-lite/src/modules/integrations/passport/strategies/local.strategy.ts +36 -0
- package/templates/be/skeleton-lite/src/modules/integrations/passport/strategies/oauth-state.store.ts +59 -0
- package/templates/be/skeleton-lite/src/modules/platform/database/database.functions.ts +24 -0
- package/templates/be/skeleton-lite/src/modules/platform/database/database.module.ts +2 -1
- package/templates/be/skeleton-lite/src/modules/platform/database/database.options.ts +4 -2
- package/templates/be/skeleton-lite/src/modules/platform/database/database.sql.ts +19 -6
- package/templates/be/skeleton-lite/src/modules/platform/database/entities.ts +6 -0
- package/templates/be/skeleton-lite/src/modules/platform/database/index.ts +1 -0
- package/templates/be/skeleton-lite/src/modules/platform/database/migration-runner.service.ts +1 -1
- package/templates/be/skeleton-lite/src/modules/platform/database/primary.config.ts +2 -2
- package/templates/be/skeleton-lite/src/modules/platform/database/seed-runner.service.ts +2 -2
- package/templates/be/skeleton-lite/src/modules/platform/http-security/index.ts +1 -1
- package/templates/be/skeleton-lite/src/tests/fixtures/builders/api.builder.ts +18 -5
- package/templates/be/skeleton-lite/src/tests/fixtures/builders/cli.builder.ts +1 -1
- package/templates/be/table/entities.index.ts.tpl +4 -0
- package/templates/be/table/entity.ts.tpl +21 -0
- package/templates/be/table/index.ts.tpl +1 -0
- package/templates/be/table/module.ts.tpl +1 -1
- package/templates/be/table/service.api.ts.tpl +10 -11
- package/templates/be/table/service.ts.tpl +5 -6
- package/templates/fe/skeleton-lite/apps/__app__/src/modules/auth/auth-door.ts +30 -0
- package/templates/fe/skeleton-lite/apps/__app__/src/modules/auth/client.ts +46 -14
- package/templates/fe/skeleton-lite/apps/__app__/src/modules/auth/token-store.ts +31 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/config/index.ts +2 -5
- package/db/local-migrate.mjs +0 -194
- package/db/reset.mjs +0 -125
- package/emit/db-types.mjs +0 -271
- package/runtime/scripts/api/supabase/database-dump.mjs +0 -5
- package/runtime/scripts/api/supabase/database-push.mjs +0 -5
- package/runtime/scripts/api/supabase/database-query.mjs +0 -5
- package/runtime/scripts/api/supabase/lib.mjs +0 -55
- package/runtime/scripts/api/supabase/migration-repair.mjs +0 -5
- package/runtime/scripts/hfs/architecture/supabase-tables.mjs +0 -50
- package/runtime/scripts/hfs/architecture/supabase.mjs +0 -226
- package/scaffold/supabase-ports.mjs +0 -26
- package/templates/app/package-scripts/supabase.json +0 -7
- package/templates/app/skeleton-lite/supabase/config.toml +0 -48
- package/templates/be/inbox/platform/event-bus/persistence/event-bus.sql.ts.tpl +0 -5
- package/templates/be/inbox/platform/inbox/persistence/inbox.rows.ts.tpl +0 -33
- package/templates/be/inbox/platform/inbox/persistence/inbox.sql.ts.tpl +0 -33
- package/templates/be/inbox/sql.ts.tpl +0 -16
- package/templates/be/patterns/jobs/platform-lite/persistence.jobs.sql.ts.tpl +0 -33
- package/templates/be/patterns/queues/platform-lite/persistence.queue.sql.ts.tpl +0 -12
- package/templates/be/skeleton-lite/src/modules/integrations/supabase/errors/supabase.error.ts +0 -16
- package/templates/be/skeleton-lite/src/modules/integrations/supabase/index.ts +0 -6
- package/templates/be/skeleton-lite/src/modules/integrations/supabase/supabase.client.spec.ts +0 -27
- package/templates/be/skeleton-lite/src/modules/integrations/supabase/supabase.client.ts +0 -24
- package/templates/be/skeleton-lite/src/modules/integrations/supabase/supabase.config.ts +0 -8
- package/templates/be/skeleton-lite/src/modules/integrations/supabase/supabase.decorators.ts +0 -17
- package/templates/be/skeleton-lite/src/modules/integrations/supabase/supabase.jwks.ts +0 -32
- package/templates/be/skeleton-lite/src/modules/integrations/supabase/supabase.options.ts +0 -24
- package/templates/be/table/rows.ts.tpl +0 -4
- package/templates/be/table/sql.api.ts.tpl +0 -7
- package/templates/be/table/sql.ts.tpl +0 -4
- package/templates/fe/auth/supabase-client.ts.tpl +0 -38
- package/templates/fe/skeleton/apps/__app__/src/app/auth/callback/route.ts +0 -11
- package/templates/fe/skeleton/apps/__app__/src/modules/auth/pkce-client.ts +0 -18
- package/templates/fe/skeleton/apps/__app__/src/modules/auth/server.ts +0 -19
- /package/templates/app/skeleton-lite/{supabase → db}/seed.sql +0 -0
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
# catalog READ this file through scripts/hfs/slots.mjs; none of them hardcodes a path.
|
|
8
8
|
# Shape: modules/schemas/hfs-slots.schema.yaml. The repository side is hfs.json: modules/schemas/hfs-repo.schema.yaml.
|
|
9
9
|
schema: starci/hfs-slots@3
|
|
10
|
-
version: 3.
|
|
10
|
+
version: 3.9.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).
|
|
@@ -38,6 +38,9 @@ versioning:
|
|
|
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
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.
|
|
41
|
+
# 3.9.0 (ruling:lite-self-hosted-postgres-passport-minio, 2026-10-29) is a major for lite and changes no full slot but the S3 owner's
|
|
42
|
+
# folder: lite leaves Supabase. The app.supabase* and be.integrations.supabase slots and fe.route.callback are removed; app.db (db/, its
|
|
43
|
+
# migrations, archive, seed and managed bootstrap), be.integrations.passport and be.integrations.object-storage are added; lite's schemaAuthority is sql.
|
|
41
44
|
# 3.4.0 is a minor (alpha.25): it ADDS the opt-in be.contract.mcp-tools slot, the committed MCP tool-catalog snapshot of an app.
|
|
42
45
|
# 3.5.0 is a minor (alpha.27): it ADDS the opt-in app.browser-config slot, the managed browser/tsconfig.json that type-checks the browser
|
|
43
46
|
# journey (enabled with app.browser by `browser: true`).
|
|
@@ -45,6 +48,8 @@ versioning:
|
|
|
45
48
|
# 2026-10-24: lite may serve SSE, and SSE is the default door for one-way push), and lets fe.modules.api hold the SSE stream operations (sse/).
|
|
46
49
|
# 3.7.0 is a minor (alpha.31): it ADDS the optional app.scripts.integration slot (scripts/integration/*.mjs, the product test-support scripts that only the integration harness runs) and the optional fe.transport.stream slot, the fetch reader of server-sent-events streams (the stream half of the
|
|
47
50
|
# transport client layer), and lets fe.hooks.swr hold the SWR subscription hooks (api/sse/subscriptions/useSubscribe<X>Swr.ts, one useSWRSubscription per stream operation).
|
|
51
|
+
# 3.8.0 is a minor (urgent release, 2026-10-29): it ADDS the optional fe.dev-server-generated slot (apps/*/AGENTS.md, which `next dev` rewrites) and the
|
|
52
|
+
# slot field rewrittenBy, so the release gate's clean-tree check passes over an untracked file a declared dev server writes, and over nothing else.
|
|
48
53
|
|
|
49
54
|
# Field meaning, per slot.
|
|
50
55
|
# id stable name, never changes meaning inside a major.
|
|
@@ -56,6 +61,8 @@ versioning:
|
|
|
56
61
|
# A slot is optional in the sense of the requirements list when its presence is not required.
|
|
57
62
|
# tracked tracked (committed) | ignored (may exist, must be gitignored) | external (must not exist in the tree;
|
|
58
63
|
# goesTo says where it lives).
|
|
64
|
+
# rewrittenBy on an ignored file slot: the dev server that writes the file into the working tree while it runs (`next dev`). The
|
|
65
|
+
# release gate's clean-tree check (release/gate.mjs dirtyPaths) passes over an untracked file such a slot owns, and only it.
|
|
59
66
|
# tier node of the import direction matrix (tiers below); none = takes no part in import checks;
|
|
60
67
|
# inherit = the tier of the enclosing owner.
|
|
61
68
|
# owner true when every directory matching the slot is an owner: the unit cross-owner imports and cycles use.
|
|
@@ -102,34 +109,34 @@ versioning:
|
|
|
102
109
|
# requiredInstances, why), each shaped as the same field in the slot body; every other field is the edition's.
|
|
103
110
|
# `why` under lite names the adapter behind the slot's ports (pgmq, Postgres locks, in-process socket.io).
|
|
104
111
|
# `tests` is never overlaid: a lite slot owes exactly the unit specs its full slot owes.
|
|
105
|
-
# provider the hfs.json connections[].provider that enables this opt-in slot (
|
|
112
|
+
# provider the hfs.json connections[].provider that enables this opt-in slot (postgres); never an app kind.
|
|
106
113
|
# since the manifest version that introduced the slot.
|
|
107
114
|
presenceValues: [required, optional, opt-in, forbidden]
|
|
108
115
|
trackedValues: [tracked, ignored, external]
|
|
109
116
|
testValues: [unit-beside, e2e, none]
|
|
110
117
|
|
|
111
118
|
# 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
|
|
112
|
-
# the full architecture
|
|
119
|
+
# the full architecture on its own self-hosted PostgreSQL (ruling:lite-self-hosted-postgres-passport-minio) - the same monorepo, slots, CQRS shape and patterns (queue,
|
|
113
120
|
# event bus, saga, reactors, projection, realtime, webhooks, fenced job), declared in hfs.json the same way, and the same unit
|
|
114
121
|
# specs (unitRoles, logicRoles, 100 per file, @starci/jest-preset doubles, builders). What differs is ADAPTERS and DELIVERY
|
|
115
|
-
# (ruling:R212/case-10): schema authority in
|
|
122
|
+
# (ruling:R212/case-10): schema authority in db/migrations, the queue port on BullMQ over the Redis hfs.json
|
|
116
123
|
# sides.be.backing declares (as in full, fed by the Postgres outbox), event-bus ports on pgmq-backed topics or in-process
|
|
117
124
|
# delivery, cache and locks on Postgres or the declared Redis, schedules on pg_cron (+ pg_net) enqueueing through the queue
|
|
118
|
-
# outbox, storage and auth
|
|
125
|
+
# outbox, storage on S3-compatible MinIO (integrations/object-storage) and auth through integrations/passport. Realtime uses SSE or the socket.io gateway
|
|
119
126
|
# in the api process, which scales horizontally and stateless (memory hub for one replica or tests; the Redis hub and @socket.io/redis-adapter when Redis is declared, else the Postgres hub and @socket.io/postgres-adapter, for N replicas; websocket only, no sticky sessions). Integration and provider sandbox contract specs are optional;
|
|
120
127
|
# declared patterns owe their integration proofs (R284). Lite has no e2e world. Production uses declared docker-swarm under
|
|
121
|
-
# .starcistacks/production/infra/swarm/ (owner ruling 2026-10-14); lite runs
|
|
122
|
-
# services sides.be.backing declares. The front end is full's (R97).
|
|
128
|
+
# .starcistacks/production/infra/swarm/ (owner ruling 2026-10-14); lite runs its PostgreSQL as the db service of its
|
|
129
|
+
# stack and only the backing services sides.be.backing declares. The front end is full's (R97).
|
|
123
130
|
# Lite is this same manifest filtered: a slot whose `editions` lacks lite does not exist for a lite app, `litePresence` replaces
|
|
124
131
|
# presence and a `lite` map replaces the fields it names. The edition is chosen once at scaffold; there is no upgrade
|
|
125
132
|
# or downgrade between lite and full. Lite contracts are allowed (owner decision 2026-10-14).
|
|
126
133
|
editions: [full, lite]
|
|
127
134
|
|
|
128
135
|
# 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
|
|
129
|
-
# may read (hfs.json sides.<side>.reads names a subset), or a tree an app-root slot owns
|
|
136
|
+
# may read (hfs.json sides.<side>.reads names a subset), or a tree an app-root slot owns; nothing else crosses sides.
|
|
130
137
|
sides:
|
|
131
|
-
be: {reads: [
|
|
132
|
-
fe: {reads: [be/contracts
|
|
138
|
+
be: {reads: []}
|
|
139
|
+
fe: {reads: [be/contracts/]} # be/contracts/: the committed contract snapshots, the input of the front end's codegen
|
|
133
140
|
|
|
134
141
|
# The trigger kinds of the back end's features: what starts the work of a feature. A feature slot names its kind in `trigger`; no kind imports another
|
|
135
142
|
# (BE_KIND_ISOLATION), every cross-kind call is an event on the bus, and only be/apps/* compose features.
|
|
@@ -192,9 +199,9 @@ crossApp: apps never import each other; shared code is a packages/<pkg> slot.
|
|
|
192
199
|
ruleParams:
|
|
193
200
|
db:
|
|
194
201
|
# R259: btree_gist supplies UUID equality for the booking interval exclusion constraint.
|
|
195
|
-
# R259: unaccent
|
|
202
|
+
# R259: unaccent ships in the supabase/postgres image of the db service, used by Vietnamese-content search functions.
|
|
196
203
|
extensions: [pgmq, pg_cron, pgcrypto, pg_trgm, vector, uuid-ossp, pg_net, supabase_vault, unaccent, btree_gist]
|
|
197
|
-
# R258 DB_CRON_OWNED: THE queue outbox of a
|
|
204
|
+
# R258 DB_CRON_OWNED: THE queue outbox of a lite schema (the relay hands its unsent rows to BullMQ, ruling:R212/case-10). A pg_cron
|
|
198
205
|
# job enqueues only by inserting one row into this table, writing only these columns, one of which is `queue`.
|
|
199
206
|
queueOutbox: {table: public.queue, columns: [queue, payload, created_at, correlation_id]}
|
|
200
207
|
common:
|
|
@@ -218,8 +225,18 @@ ruleParams:
|
|
|
218
225
|
"node:https": [platform/http]
|
|
219
226
|
http: [platform/http]
|
|
220
227
|
https: [platform/http]
|
|
221
|
-
"@aws-sdk/client-s3": [integrations/object-storage] # the ObjectStorage port: one S3 adapter for MinIO, S3
|
|
228
|
+
"@aws-sdk/client-s3": [integrations/object-storage] # the ObjectStorage port: one S3 adapter for MinIO, S3 and Spaces (R331)
|
|
222
229
|
"@aws-sdk/s3-request-presigner": [integrations/object-storage]
|
|
230
|
+
minio: [] # storage goes through the S3 client of integrations/object-storage
|
|
231
|
+
"@supabase": [] # no Supabase client in a back end
|
|
232
|
+
"@nestjs/passport": [integrations/passport] # the identity libraries (R328)
|
|
233
|
+
passport: [integrations/passport]
|
|
234
|
+
passport-google-oauth20: [integrations/passport]
|
|
235
|
+
passport-local: [integrations/passport]
|
|
236
|
+
"@nestjs/jwt": [integrations/passport]
|
|
237
|
+
jose: [integrations/passport]
|
|
238
|
+
bcryptjs: [integrations/passport]
|
|
239
|
+
bcrypt: []
|
|
223
240
|
cache-manager: [integrations/cache, integrations/redis]
|
|
224
241
|
"@nestjs/cache-manager": [integrations/cache, integrations/redis] # the CACHE_MANAGER token: caching goes through the cache capability
|
|
225
242
|
redis: [integrations/cache, integrations/redis]
|
|
@@ -254,6 +271,25 @@ ruleParams:
|
|
|
254
271
|
"@socket.io/postgres-adapter": [platform/realtime] # the stateless socket.io adapter of a scaled lite back end without Redis
|
|
255
272
|
"@socket.io/redis-adapter": [platform/realtime] # the sharded socket.io adapter of a scaled back end on the declared Redis
|
|
256
273
|
pg: [platform/realtime] # the LISTEN connection of the Postgres hub; every other query goes through TypeORM
|
|
274
|
+
# BE-AI-1, BE-AI-3: every model call goes through the ai-router gateway. Its custody owner registers @starci/ai-router with the
|
|
275
|
+
# provider keys; the one LangChain integration owns @langchain/*; no provider SDK, openai client or agent framework is imported.
|
|
276
|
+
"@starci/ai-router": [integrations/ai-gateway]
|
|
277
|
+
"@starci/ai-router/testing": [integrations/ai-gateway, integrations/ai] # the fake provider a spec of either side boots the gateway with
|
|
278
|
+
"@langchain": [integrations/ai] # a scope key: every @langchain/* package
|
|
279
|
+
"@langchain/langgraph": [] # an agent loop runs in the agent runtime, never in code
|
|
280
|
+
"@langchain/community": [] # retrieval reads the app's own persistence through its owner
|
|
281
|
+
langchain: [] # createAgent is an agent loop
|
|
282
|
+
openai: [] # code calls models through @langchain/openai
|
|
283
|
+
"@anthropic-ai/sdk": []
|
|
284
|
+
"@google/genai": []
|
|
285
|
+
"@google/generative-ai": []
|
|
286
|
+
"@mistralai/mistralai": []
|
|
287
|
+
"@openrouter/ai-sdk-provider": []
|
|
288
|
+
"@ai-sdk": []
|
|
289
|
+
ai: []
|
|
290
|
+
cohere-ai: []
|
|
291
|
+
groq-sdk: []
|
|
292
|
+
ollama: []
|
|
257
293
|
# R87 BE_CQRS_SHAPE no-event-bus: the in-process event vocabulary the rule refuses. `imports` maps a module
|
|
258
294
|
# specifier to the member names that are an in-process event; `[]` flags the package itself (its emitter packages
|
|
259
295
|
# are the `[]` owners of infraOwners above). `classes` maps a module specifier to the class names that are an
|
|
@@ -372,21 +408,41 @@ ruleParams:
|
|
|
372
408
|
# end may declare in hfs.json sides.be.backing. `clients` are the libraries that consume the service: one of them as a dependency
|
|
373
409
|
# is its consumer, and under lite none of them may be a dependency while the service is undeclared. `patterns` are the declared
|
|
374
410
|
# patterns that need the service. `images` are the image repositories (the last path segment) a stack runs it from.
|
|
375
|
-
# `editions` limits admission: owner:lite-vector-is-pgvector keeps vector search on
|
|
411
|
+
# `editions` limits admission: owner:lite-vector-is-pgvector keeps vector search on pgvector in lite.
|
|
376
412
|
backing:
|
|
377
413
|
redis: {clients: [bullmq, "@nestjs/bullmq", bull, "@nestjs/bull", ioredis, redis, "@keyv/redis", cache-manager-redis-yet, cache-manager-ioredis-yet, "@socket.io/redis-adapter"], patterns: [queue, fenced-job], images: [redis, redis-stack, redis-stack-server]}
|
|
378
414
|
qdrant: {clients: ["@qdrant/js-client-rest", "@qdrant/qdrant-js"], patterns: [], images: [qdrant], editions: [full]}
|
|
379
415
|
# Edition lite (ruleParams(manifest, 'be', 'lite') merges these keys over the base; a map merges one level deep): the schema
|
|
380
|
-
# authority is the
|
|
416
|
+
# authority is the SQL migration tree (db/migrations/*.sql, forward-only), so a lite back end holds no TypeORM
|
|
381
417
|
# migration or entity under be/ (R34/R35 read schemaAuthority; be.persistence's lite overlay allows neither). The unit roles,
|
|
382
418
|
# 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: full's owners,
|
|
383
419
|
# bullmq in platform/queue and ioredis/redis in integrations/cache and integrations/redis (ruling:R212/case-10: the queue is
|
|
384
420
|
# Redis in both editions; a backing library is legal only when hfs.json sides.be.backing declares its service, which
|
|
385
|
-
# HFS_BACKING_UNDECLARED checks), except Kafka, which is owned nowhere (platform/event-bus runs
|
|
421
|
+
# HFS_BACKING_UNDECLARED checks), except Kafka, which is owned nowhere (platform/event-bus runs pgmq queues through
|
|
386
422
|
# the shared EntityManager); realtime is the same socket.io gateway as full, run inside the api process, scaled horizontally and stateless (hub redis when
|
|
387
423
|
# Redis is declared, else hub postgres with @socket.io/postgres-adapter; no sticky sessions).
|
|
424
|
+
# R334: the database functions code may call through platform/database's callDbFunction, advisoryXactLock and notify helpers,
|
|
425
|
+
# each {name, reason}; the canon lists the pg built-ins it uses itself, and an app adds its own in hfs.json sides.be.dbFunctions.
|
|
426
|
+
dbFunctions:
|
|
427
|
+
- {name: pg_advisory_xact_lock, reason: "advisoryXactLock: a transaction-scoped lock TypeORM has no API for"}
|
|
428
|
+
- {name: hashtextextended, reason: "advisoryXactLock: the 64-bit key of a text lock name"}
|
|
429
|
+
- {name: pg_notify, reason: "notify: LISTEN/NOTIFY of the Postgres realtime hub"}
|
|
430
|
+
- {name: to_regproc, reason: "callDbFunction: whether an optional database function exists"}
|
|
431
|
+
- {name: job_claim, reason: "platform/jobs (add job, lite): the fenced claim upsert of jobs.sql"}
|
|
432
|
+
- {name: job_advance, reason: "platform/jobs (add job, lite): the token-guarded step write of jobs.sql"}
|
|
433
|
+
- {name: job_complete, reason: "platform/jobs (add job, lite): the token-guarded completion of jobs.sql"}
|
|
434
|
+
- {name: job_fail, reason: "platform/jobs (add job, lite): the token-guarded failure of jobs.sql"}
|
|
435
|
+
- {name: queue_write, reason: "platform/queue (add queue, lite): the outbox insert of queue-outbox.sql"}
|
|
436
|
+
- {name: queue_waiting, reason: "platform/queue (add queue, lite): the relay read FOR UPDATE SKIP LOCKED of queue-outbox.sql"}
|
|
437
|
+
- {name: queue_mark_sent, reason: "platform/queue (add queue, lite): the relay mark of queue-outbox.sql"}
|
|
438
|
+
- {name: inbox_claim_event, reason: "platform/inbox (add webhook, lite): the conflict-safe delivery claim"}
|
|
439
|
+
- {name: inbox_claim_client, reason: "platform/inbox (add webhook, lite): the client claim upsert returning its committed outcome"}
|
|
440
|
+
- {name: inbox_save_client_outcome, reason: "platform/inbox (add webhook, lite): the saved client outcome"}
|
|
441
|
+
- {name: inbox_sweep_clients, reason: "platform/inbox (add webhook, lite): the bounded retention sweep FOR UPDATE SKIP LOCKED"}
|
|
442
|
+
- {name: event_bus_write, reason: "platform/event-bus (add webhook, lite): the typed envelope insert of the outbox"}
|
|
443
|
+
|
|
388
444
|
lite:
|
|
389
|
-
schemaAuthority:
|
|
445
|
+
schemaAuthority: sql
|
|
390
446
|
infraOwners:
|
|
391
447
|
kafkajs: []
|
|
392
448
|
"@qdrant/js-client-rest": []
|
|
@@ -416,7 +472,7 @@ slots:
|
|
|
416
472
|
- id: app.package-manifest
|
|
417
473
|
since: 3.1.0
|
|
418
474
|
profiles: [app]
|
|
419
|
-
editionWhy: "ruling:R212/case-2 (with the Work boundary amended by ruling:R212/case-4): lite manages
|
|
475
|
+
editionWhy: "ruling:R212/case-2 (with the Work boundary amended by ruling:R212/case-4): lite manages its database and wiring scripts; full also manages e2e and stack test scripts. Both may run optional contracts."
|
|
420
476
|
# The one package.json of the app, at the app root: be's dependencies and every tool, and every script; npm workspaces are
|
|
421
477
|
# exactly fe/apps/* and fe/packages/* (HFS_MONO_WORKSPACES), each with its own package.json. be has no package.json of its own.
|
|
422
478
|
path: package.json
|
|
@@ -485,7 +541,7 @@ slots:
|
|
|
485
541
|
- id: app.format-config
|
|
486
542
|
since: 3.1.0
|
|
487
543
|
profiles: [app]
|
|
488
|
-
editionWhy: "ruling:R212/case-2 (with the Work boundary amended by ruling:R212/case-4): lite additionally ignores
|
|
544
|
+
editionWhy: "ruling:R212/case-2 (with the Work boundary amended by ruling:R212/case-4): lite additionally ignores the generated database row types when formatting; full has its TypeORM schema authority."
|
|
489
545
|
# The one formatter configuration of the app (prettier walks up from every file of both sides to it): .prettierrc names
|
|
490
546
|
# @starci/prettier-config, .prettierignore is the template.
|
|
491
547
|
path: "{.prettierrc,.prettierignore}"
|
|
@@ -494,7 +550,7 @@ slots:
|
|
|
494
550
|
tier: none
|
|
495
551
|
tests: none
|
|
496
552
|
managedBy: tool-config
|
|
497
|
-
liteManagedBy: tool-config-lite # the same files
|
|
553
|
+
liteManagedBy: tool-config-lite # the same files as full
|
|
498
554
|
rules: [HFS_MANAGED_FILE_DRIFT, HFS_TOOL_CONFIG_LOCAL]
|
|
499
555
|
- id: app.tool-config-optional
|
|
500
556
|
since: 3.1.0
|
|
@@ -537,7 +593,7 @@ slots:
|
|
|
537
593
|
- id: app.ci
|
|
538
594
|
since: 3.1.0
|
|
539
595
|
profiles: [app]
|
|
540
|
-
editionWhy: "ruling:R212/case-2 (with the Work boundary amended by ruling:R212/case-4): lite CI proves boot through R272 wiring and checks
|
|
596
|
+
editionWhy: "ruling:R212/case-2 (with the Work boundary amended by ruling:R212/case-4): lite CI proves boot through R272 wiring and checks its database artifacts; full also provides its e2e lane."
|
|
541
597
|
path: ".github/workflows/ci.yml"
|
|
542
598
|
presence: required
|
|
543
599
|
tracked: tracked
|
|
@@ -562,7 +618,7 @@ slots:
|
|
|
562
618
|
- id: app.ci-images
|
|
563
619
|
since: 3.1.0
|
|
564
620
|
profiles: [app]
|
|
565
|
-
editionWhy: "ruling:R212/case-2 (with the Work boundary amended by ruling:R212/case-4): lite renders its
|
|
621
|
+
editionWhy: "ruling:R212/case-2 (with the Work boundary amended by ruling:R212/case-4): lite renders its image workflow; full renders images for its separately deployed services."
|
|
566
622
|
# Builds every image of the app (one matrix entry per declared app, path-filtered), never pushes: rendered by starci app sync.
|
|
567
623
|
path: ".github/workflows/images.yml"
|
|
568
624
|
presence: required
|
|
@@ -577,7 +633,7 @@ slots:
|
|
|
577
633
|
exemplifiedBy:
|
|
578
634
|
- packages/hfs/templates/app/ci-workflows-lite/github/workflows/db-deploy.yml
|
|
579
635
|
- {path: packages/hfs/src/hfs-release-provenance.spec.mjs, edition: full}
|
|
580
|
-
editionWhy: "ruling:R212/case-2 (with the Work boundary amended by ruling:R212/case-4): lite manages its
|
|
636
|
+
editionWhy: "ruling:R212/case-2 (with the Work boundary amended by ruling:R212/case-4): lite manages its database deploy workflow; full owns an optional provider-specific workflow."
|
|
581
637
|
# The database deploy workflow: workflow_dispatch only (like app.ci-e2e); starci app sync renders it under the managed ci group.
|
|
582
638
|
path: ".github/workflows/db-deploy.yml"
|
|
583
639
|
presence: optional
|
|
@@ -615,7 +671,7 @@ slots:
|
|
|
615
671
|
since: 3.1.0
|
|
616
672
|
profiles: [app]
|
|
617
673
|
# Production delivery tooling for the app's own processes: compose files, Caddyfile, *.env.example and ops shell scripts.
|
|
618
|
-
# Lite
|
|
674
|
+
# Lite keeps its encrypted custody in app.starcistacks; R06 scans every file here.
|
|
619
675
|
path: "deploy/"
|
|
620
676
|
presence: optional
|
|
621
677
|
tracked: tracked
|
|
@@ -699,7 +755,7 @@ slots:
|
|
|
699
755
|
profiles: [app]
|
|
700
756
|
exemplifiedBy:
|
|
701
757
|
- {path: packages/hfs/templates/app/skeleton/scripts/codegen.mjs, edition: full}
|
|
702
|
-
editionWhy: "Lite manages its
|
|
758
|
+
editionWhy: "Lite manages its database-types codegen script; full owns operational scripts through the generic path (ruling:R212/case-2 (with the Work boundary amended by ruling:R212/case-4))."
|
|
703
759
|
# The app's operational scripts only (`*.mjs`, `*.cjs`, `*.ps1`, `*.sh`), or nothing: an empty folder is `scripts/.gitkeep`, which
|
|
704
760
|
# `starci app scaffold` writes. A spec or a test (BE_SPEC_PLACEMENT, FE_NO_TESTS), a check or lint source (`check-*`, `eslint-local-rules*`,
|
|
705
761
|
# 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.
|
|
@@ -784,7 +840,7 @@ slots:
|
|
|
784
840
|
- id: app.starcistacks
|
|
785
841
|
since: 3.1.0
|
|
786
842
|
profiles: [app]
|
|
787
|
-
editionWhy: "owner:lite-production-swarm: lite requires declared docker-swarm production with
|
|
843
|
+
editionWhy: "owner:lite-production-swarm: lite requires declared docker-swarm production with its PostgreSQL as a stack service; owner:dev-docker-prod-swarm keeps dependencies in Docker Linux containers in dev and the app and dependencies on Swarm in prod."
|
|
788
844
|
exemplifiedBy:
|
|
789
845
|
- {path: packages/hfs/templates/app/skeleton/.starcistacks/, edition: full}
|
|
790
846
|
- {path: packages/hfs/templates/app/skeleton-lite/.starcistacks/, edition: lite}
|
|
@@ -799,7 +855,7 @@ slots:
|
|
|
799
855
|
"<env>/runtime/{config,env}/**", "<env>/runtime/env/KEYS.md", "<env>/secrets/<slug>.enc", "<env>/secrets/.gitkeep", "<env>/seeds/**"]
|
|
800
856
|
forbids: ["<env>/runtime/files/**", "**/*.enc outside <env>/secrets/", DESIGN.md, deployment.json, "k8s/ at root"]
|
|
801
857
|
# Owner ruling 2026-10-14: lite declares docker-swarm production, app runtime config, sealed custody and seed data.
|
|
802
|
-
#
|
|
858
|
+
# The db service supplies the database and broker (pgmq, pg_cron); production has no Compose or host cron.
|
|
803
859
|
lite:
|
|
804
860
|
requires: [application-stacks.yaml]
|
|
805
861
|
allows: [application-stacks.yaml, "<env>/README.md", "<env>/environment.json", "<env>/infra/swarm/**", "<env>/infra/metadata.json",
|
|
@@ -862,122 +918,78 @@ slots:
|
|
|
862
918
|
goesTo: ".starcistacks/<env>/secrets/<slug>.enc (at the app root); decrypted values only in memory or under %LOCALAPPDATA%/StarCi/secrets/<project>/<env>/ via *_FILE"
|
|
863
919
|
rules: [HFS_PLAINTEXT_SECRET]
|
|
864
920
|
|
|
865
|
-
# ----- app root: the
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
- packages/hfs/templates/app/skeleton-lite/supabase/
|
|
870
|
-
- {path: packages/hfs/src/hfs-database-access.spec.mjs, edition: full}
|
|
871
|
-
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."
|
|
872
|
-
# The one Supabase project directory, at the app root beside be/, fe/ and .starcistacks (never under a side).
|
|
873
|
-
path: "supabase/"
|
|
874
|
-
presence: opt-in # enabled by a connection with provider: supabase; required under edition lite
|
|
875
|
-
provider: supabase
|
|
876
|
-
litePresence: required
|
|
877
|
-
tracked: tracked
|
|
878
|
-
tier: none
|
|
879
|
-
tests: none
|
|
880
|
-
since: 2.1.0
|
|
881
|
-
- id: app.supabase.config
|
|
921
|
+
# ----- app root: the database of a lite app (ruling:lite-self-hosted-postgres-passport-minio): a self-hosted PostgreSQL in the app's
|
|
922
|
+
# stack (the `db` service, image supabase/postgres for pg_cron, pg_net, pgmq, vector, unaccent and pgcrypto), migrated by
|
|
923
|
+
# `starci app db migrate --url-env <NAME>`. A full app keeps its TypeORM migrations under be/ and has no such folder.
|
|
924
|
+
- id: app.db
|
|
882
925
|
profiles: [app]
|
|
926
|
+
editions: [lite]
|
|
883
927
|
exemplifiedBy:
|
|
884
|
-
- packages/hfs/templates/app/skeleton-lite/
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
path: "supabase/config.toml"
|
|
889
|
-
presence: opt-in
|
|
890
|
-
provider: supabase
|
|
891
|
-
litePresence: required
|
|
928
|
+
- packages/hfs/templates/app/skeleton-lite/db/
|
|
929
|
+
# The one database directory, at the app root beside be/, fe/ and .starcistacks (never under a side).
|
|
930
|
+
path: "db/"
|
|
931
|
+
presence: required
|
|
892
932
|
tracked: tracked
|
|
893
933
|
tier: none
|
|
894
934
|
tests: none
|
|
895
|
-
since:
|
|
896
|
-
- id: app.
|
|
935
|
+
since: 3.9.0
|
|
936
|
+
- id: app.db.migrations
|
|
897
937
|
profiles: [app]
|
|
938
|
+
editions: [lite]
|
|
898
939
|
exemplifiedBy:
|
|
899
|
-
- packages/hfs/templates/app/skeleton-lite/
|
|
900
|
-
- {path: packages/hfs/src/db-migration-shape.spec.mjs, edition:
|
|
901
|
-
|
|
902
|
-
#
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
provider: supabase
|
|
907
|
-
litePresence: required
|
|
908
|
-
lite: {minInstances: 1}
|
|
940
|
+
- packages/hfs/templates/app/skeleton-lite/db/migrations/__timestamp___baseline.sql
|
|
941
|
+
- {path: packages/hfs/src/db-migration-shape.spec.mjs, edition: lite}
|
|
942
|
+
# The schema authority of a lite app: forward-only SQL migrations named <timestamp14>_<kebab-name>.sql, one file per change,
|
|
943
|
+
# immutable once on origin/main, applied in order by `starci app db migrate` and recorded in the history table.
|
|
944
|
+
path: "db/migrations/<stamp>_<name>.sql"
|
|
945
|
+
presence: required
|
|
946
|
+
minInstances: 1
|
|
909
947
|
tracked: tracked
|
|
910
948
|
tier: none
|
|
911
949
|
tests: none
|
|
912
|
-
since:
|
|
913
|
-
|
|
950
|
+
since: 3.9.0
|
|
951
|
+
rules: [DB_MIGRATION_SHAPE, LITE_DB_MIGRATION]
|
|
952
|
+
- id: app.db.migrations.archive
|
|
914
953
|
profiles: [app]
|
|
915
|
-
|
|
916
|
-
#
|
|
917
|
-
# older files move here.
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
presence: opt-in
|
|
921
|
-
provider: supabase
|
|
954
|
+
editions: [lite]
|
|
955
|
+
# The migrations an app retired at a baseline cutover: the live schema is dumped into ONE baseline migration, marked applied
|
|
956
|
+
# without a re-run, and the older files move here. `starci app db migrate` never applies this folder and no migration rule judges it.
|
|
957
|
+
path: "db/migrations/archive/"
|
|
958
|
+
presence: optional
|
|
922
959
|
tracked: tracked
|
|
923
960
|
tier: none
|
|
924
961
|
tests: none
|
|
925
|
-
since: 3.
|
|
926
|
-
- id: app.
|
|
962
|
+
since: 3.9.0
|
|
963
|
+
- id: app.db.seed
|
|
927
964
|
profiles: [app]
|
|
928
|
-
|
|
965
|
+
editions: [lite]
|
|
966
|
+
path: "db/seed.sql"
|
|
929
967
|
presence: optional # synthetic dev data only; a lite app seeds nothing it cannot regenerate
|
|
930
968
|
tracked: tracked
|
|
931
969
|
tier: none
|
|
932
970
|
tests: none
|
|
933
|
-
since:
|
|
934
|
-
- id: app.
|
|
935
|
-
profiles: [app]
|
|
936
|
-
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."
|
|
937
|
-
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."
|
|
938
|
-
# The generated Database types (database.types.ts) contract:emit writes from the migrations; both sides read it
|
|
939
|
-
# (sides.<side>.reads supabase/types/); drift against regeneration is L09.
|
|
940
|
-
path: "supabase/types/database.types.ts"
|
|
941
|
-
presence: opt-in
|
|
942
|
-
provider: supabase
|
|
943
|
-
litePresence: required
|
|
944
|
-
tracked: tracked
|
|
945
|
-
tier: none
|
|
946
|
-
tests: none
|
|
947
|
-
since: 2.1.0
|
|
948
|
-
exemplifiedBy:
|
|
949
|
-
- {path: packages/hfs/src/db-types-returned-collector.spec.mjs, edition: full}
|
|
950
|
-
- id: app.supabase.templates
|
|
971
|
+
since: 3.9.0
|
|
972
|
+
- id: app.db.bootstrap
|
|
951
973
|
profiles: [app]
|
|
952
|
-
|
|
953
|
-
|
|
974
|
+
editions: [lite]
|
|
975
|
+
# The managed pre-migration bootstrap (templates/app/db-bootstrap): the auth, storage and extensions schemas, auth.users with the
|
|
976
|
+
# GoTrue column set, auth.uid/role/jwt stubs, the NOLOGIN browser roles and the realtime publication, so migrations written for a
|
|
977
|
+
# hosted Supabase apply to the self-hosted db service. `starci app db migrate` applies it before the first migration of an empty database.
|
|
978
|
+
path: "db/bootstrap.sql"
|
|
979
|
+
presence: required
|
|
954
980
|
tracked: tracked
|
|
981
|
+
managedBy: db-bootstrap
|
|
955
982
|
tier: none
|
|
956
983
|
tests: none
|
|
957
|
-
since:
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
path: "supabase/{.temp,.branches}/"
|
|
961
|
-
presence: optional
|
|
962
|
-
tracked: ignored # the local CLI's working state: exists, never committed
|
|
963
|
-
tier: none
|
|
964
|
-
tests: none
|
|
965
|
-
since: 2.1.0
|
|
966
|
-
- id: app.supabase.functions
|
|
967
|
-
profiles: [app]
|
|
968
|
-
path: "supabase/functions/"
|
|
969
|
-
presence: forbidden # a Deno runtime is a second language runtime outside the lint canon and the slot machine (lite v1)
|
|
970
|
-
tracked: external
|
|
971
|
-
tier: none
|
|
972
|
-
tests: none
|
|
973
|
-
goesTo: "a be feature: webhooks/<provider> for a third-party callback, api for an endpoint, a job of a queue (BullMQ on the declared Redis, its tick enqueued through the queue outbox) or a cli command on a schedule (pg_cron / pg_net)"
|
|
974
|
-
since: 2.1.0
|
|
984
|
+
since: 3.9.0
|
|
985
|
+
rules: [HFS_MANAGED_FILE_DRIFT]
|
|
986
|
+
|
|
975
987
|
|
|
976
988
|
# ----- side root (both sides): what a standalone repository root holds, less the app-root files ----------
|
|
977
989
|
- id: repo.side-root-forbidden
|
|
978
990
|
since: 3.1.0
|
|
979
991
|
profiles: [be, fe]
|
|
980
|
-
path: "{package.json,package-lock.json,hfs.json,README.md,.gitignore,.gitattributes,.husky/,.github/,.starciwork/,.starcistacks/,
|
|
992
|
+
path: "{package.json,package-lock.json,hfs.json,README.md,.gitignore,.gitattributes,.husky/,.github/,.starciwork/,.starcistacks/,db/,.sops.yaml,sonar-project.properties,codecov.yml,.prettierrc,.prettierignore,scripts/}"
|
|
981
993
|
presence: forbidden
|
|
982
994
|
tracked: external
|
|
983
995
|
tier: none
|
|
@@ -1136,6 +1148,16 @@ slots:
|
|
|
1136
1148
|
tier: none
|
|
1137
1149
|
tests: none
|
|
1138
1150
|
why: produced by `npm run codegen` (prebuild and pretypecheck); never committed
|
|
1151
|
+
- id: fe.dev-server-generated
|
|
1152
|
+
since: 3.8.0
|
|
1153
|
+
profiles: [fe]
|
|
1154
|
+
path: "apps/*/AGENTS.md"
|
|
1155
|
+
presence: optional
|
|
1156
|
+
tracked: ignored
|
|
1157
|
+
rewrittenBy: next dev
|
|
1158
|
+
tier: none
|
|
1159
|
+
tests: none
|
|
1160
|
+
why: the agent notes `next dev` writes at a Next app's root each time it starts; never committed, and never a dirty tree for the release gate
|
|
1139
1161
|
|
|
1140
1162
|
# ----- never in the working tree: caches, scratch, agent output ----------------------------------------
|
|
1141
1163
|
- id: repo.tool-cache
|
|
@@ -1247,8 +1269,8 @@ slots:
|
|
|
1247
1269
|
- {path: packages/hfs/src/mcp-tools-snapshot.spec.mjs, editions: [lite, full]}
|
|
1248
1270
|
|
|
1249
1271
|
# ----- BE: apps (one slot per app kind; a new kind = a new slot, minor bump) ---------------------------
|
|
1250
|
-
# The files of every app (owner ruling 2026-10-14, R245 and R246, since 3.1.0), each with a line budget
|
|
1251
|
-
# BE_APP_COMPOSITION_BUDGET holds absolutely (not a ratchet):
|
|
1272
|
+
# The files of every app (owner ruling 2026-10-14, R245 and R246, since 3.1.0), each with a line budget (lines excluding
|
|
1273
|
+
# import declarations, multi-line imports included) that BE_APP_COMPOSITION_BUDGET holds absolutely (not a ratchet):
|
|
1252
1274
|
# main.ts the entry: builds EnvSource once, imports and calls read<App>Env from <app>.env.ts, builds options and boots.
|
|
1253
1275
|
# <app>.env.ts exports interface <App>Env and read<App>Env(env: EnvSource): <App>Env (for example ApiEnv/readApiEnv);
|
|
1254
1276
|
# fail-fast environment parsing only, via EnvSource and unchanged capability parse helpers; no localhost
|
|
@@ -1298,7 +1320,7 @@ slots:
|
|
|
1298
1320
|
- id: be.app.cli
|
|
1299
1321
|
since: 3.1.0
|
|
1300
1322
|
profiles: [be]
|
|
1301
|
-
editionWhy: "ruling:R212/case-2 (owner:lite-production-swarm) (with the Work boundary amended by ruling:R212/case-4):
|
|
1323
|
+
editionWhy: "ruling:R212/case-2 (owner:lite-production-swarm) (with the Work boundary amended by ruling:R212/case-4): `starci app db migrate` owns lite migrations; a lite cli app is optional for declared operator commands."
|
|
1302
1324
|
# The ONE app of every one-off action (migrate, seed, sync, backup, any operator command): be/apps/cli, on nest-commander.
|
|
1303
1325
|
# main.ts boots with CommandFactory.run (BE_CLI_BOOTSTRAP); app.module.ts composes the platform and the cli feature root
|
|
1304
1326
|
# (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,
|
|
@@ -1308,7 +1330,7 @@ slots:
|
|
|
1308
1330
|
path: "apps/<app>/src/"
|
|
1309
1331
|
appKind: cli
|
|
1310
1332
|
presence: opt-in
|
|
1311
|
-
litePresence: optional # lite: schema migration is
|
|
1333
|
+
litePresence: optional # lite: schema migration is `starci app db migrate`'s job; a cli app exists only when the app declares one
|
|
1312
1334
|
tracked: tracked
|
|
1313
1335
|
tier: app
|
|
1314
1336
|
owner: true
|
|
@@ -1659,7 +1681,7 @@ slots:
|
|
|
1659
1681
|
- id: be.persistence
|
|
1660
1682
|
since: 3.1.0
|
|
1661
1683
|
profiles: [be]
|
|
1662
|
-
editionWhy: "ruling:R212/case-2 (owner:lite-production-swarm) (with the Work boundary amended by ruling:R212/case-4):
|
|
1684
|
+
editionWhy: "ruling:R212/case-2 (owner:lite-production-swarm) (with the Work boundary amended by ruling:R212/case-4): db/migrations own lite tables and both editions map them with TypeORM entities here (ruling:lite-persistence-typeorm-entities); full also keeps its TypeORM migrations here."
|
|
1663
1685
|
path: "src/modules/{domain,platform,projections}/<capability>/persistence/"
|
|
1664
1686
|
# 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
|
|
1665
1687
|
# registered on several declared connections. Any other capability registers its arrays on exactly one (BE_SCHEMA_OWNER).
|
|
@@ -1670,15 +1692,17 @@ slots:
|
|
|
1670
1692
|
requires: [connection.ts] # the capability's <c>Entities / <c>Migrations arrays (the owner index re-exports the two arrays by name); no CONNECTION alias: the connection is the one the apps register the arrays on
|
|
1671
1693
|
allows: ["entities/<table>.entity.ts", "migrations/<epochMs13>-<kebab-name>.ts", "<name>.sql.ts", "<name>.rows.ts", connection.ts]
|
|
1672
1694
|
forbids: [index.ts, "<name>.repository.ts"]
|
|
1673
|
-
# Lite
|
|
1674
|
-
#
|
|
1695
|
+
# Lite maps its tables with the same TypeORM entities as full (ruling:lite-persistence-typeorm-entities): each capability's
|
|
1696
|
+
# persistence/entities/<table>.entity.ts and an entities/index.ts exporting its <c>Entities array, which platform/database
|
|
1697
|
+
# lists by name; the schema authority stays db/migrations (ruleParams.be.lite.schemaAuthority), so no be migration and no
|
|
1698
|
+
# raw SQL file (R334 BE_PERSISTENCE_NO_RAW_SQL).
|
|
1675
1699
|
lite:
|
|
1676
|
-
requires: []
|
|
1677
|
-
allows: ["
|
|
1678
|
-
forbids: [index.ts, "<name>.repository.ts",
|
|
1700
|
+
requires: []
|
|
1701
|
+
allows: ["entities/<table>.entity.ts", entities/index.ts, connection.ts]
|
|
1702
|
+
forbids: [index.ts, "<name>.repository.ts", migrations/, "<name>.sql.ts", "<name>.rows.ts"]
|
|
1679
1703
|
tests: none
|
|
1680
1704
|
coverage: none
|
|
1681
|
-
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]
|
|
1705
|
+
rules: [BE_SCHEMA_OWNER, BE_SCHEMA_AUTHORITY, BE_SQL_OUTSIDE_PERSISTENCE, BE_PERSISTENCE_NO_RAW_SQL, BE_SQL_TABLE_OWNER, BE_SQL_RETURNING_SHAPE, BE_CONTEXT_COUPLING, BE_CONTEXT_PLATFORM_TABLES]
|
|
1682
1706
|
- id: be.platform
|
|
1683
1707
|
since: 3.1.0
|
|
1684
1708
|
profiles: [be]
|
|
@@ -1729,7 +1753,7 @@ slots:
|
|
|
1729
1753
|
owner: true
|
|
1730
1754
|
lite:
|
|
1731
1755
|
why: >-
|
|
1732
|
-
the same EventPublisher and EventConsumer ports on
|
|
1756
|
+
the same EventPublisher and EventConsumer ports on pgmq queues: the outbox relay sends each committed row to the
|
|
1733
1757
|
pgmq queue of every consumer group of its topic through the shared EntityManager, a consumer reads with a visibility
|
|
1734
1758
|
timeout, retries by letting it lapse and archives the message on dead letter (read_ct over the budget); a single api may
|
|
1735
1759
|
deliver in process. No Kafka (knowledge/patterns/be/event-bus.yaml liteAdapter)
|
|
@@ -1743,7 +1767,7 @@ slots:
|
|
|
1743
1767
|
- id: be.platform.queue
|
|
1744
1768
|
since: 3.1.0
|
|
1745
1769
|
profiles: [be]
|
|
1746
|
-
editionWhy: "ruling:R212/case-10: both editions implement the queue ports with BullMQ on Redis; lite declares its Redis in hfs.json sides.be.backing and keeps the outbox table in a
|
|
1770
|
+
editionWhy: "ruling:R212/case-10: both editions implement the queue ports with BullMQ on Redis; lite declares its Redis in hfs.json sides.be.backing and keeps the outbox table in a db migration."
|
|
1747
1771
|
path: "src/modules/platform/queue/"
|
|
1748
1772
|
presence: opt-in
|
|
1749
1773
|
pattern: queue # the ONE BullMQ owner: the outbox relay into queues (jobId = outbox id) and job schedulers (knowledge/patterns/be/queues.yaml)
|
|
@@ -1754,7 +1778,7 @@ slots:
|
|
|
1754
1778
|
why: >-
|
|
1755
1779
|
the same QueueOutbox and QueueWorkerRegistry ports and full's BullMQ adapter on the declared Redis (backing: [redis]): the
|
|
1756
1780
|
relay adds each committed outbox row with the outbox id as the job id, workers retry with exponential backoff and keep a
|
|
1757
|
-
job that exhausts its attempts as failed (the dead letter); the outbox table comes from a
|
|
1781
|
+
job that exhausts its attempts as failed (the dead letter); the outbox table comes from a db migration, and a
|
|
1758
1782
|
schedule is a BullMQ job scheduler or a pg_cron tick that inserts one outbox row (knowledge/patterns/be/queues.yaml liteAdapter)
|
|
1759
1783
|
requires: [index.ts, queue.module.ts, queue.port.ts, queue.contracts.ts, queue-outbox.service.ts, queue-outbox.service.spec.ts]
|
|
1760
1784
|
tests: unit-beside
|
|
@@ -1848,7 +1872,7 @@ slots:
|
|
|
1848
1872
|
- id: be.platform.jobs
|
|
1849
1873
|
since: 3.1.0
|
|
1850
1874
|
profiles: [be]
|
|
1851
|
-
editionWhy: "ruling:R212/case-2 (owner:lite-production-swarm) (with the Work boundary amended by ruling:R212/case-4): lite persists fenced claims in
|
|
1875
|
+
editionWhy: "ruling:R212/case-2 (owner:lite-production-swarm) (with the Work boundary amended by ruling:R212/case-4): lite persists fenced claims in db migrations and dispatches through the BullMQ queue on its declared Redis (ruling:R212/case-10); full uses TypeORM schema authority and the same queue adapter."
|
|
1852
1876
|
path: "src/modules/platform/jobs/"
|
|
1853
1877
|
presence: opt-in
|
|
1854
1878
|
pattern: fenced-job # the ONE fencing primitive: the job entity, the claim that bumps the token, guarded writes (knowledge/patterns/be/jobs.yaml)
|
|
@@ -1863,7 +1887,7 @@ slots:
|
|
|
1863
1887
|
- id: be.projections
|
|
1864
1888
|
since: 3.1.0
|
|
1865
1889
|
profiles: [be]
|
|
1866
|
-
editionWhy: "ruling:R212/case-2 (owner:lite-production-swarm) (with the Work boundary amended by ruling:R212/case-4): lite projection tables live in
|
|
1890
|
+
editionWhy: "ruling:R212/case-2 (owner:lite-production-swarm) (with the Work boundary amended by ruling:R212/case-4): lite projection tables live in db migrations; full keeps the TypeORM projection entity beside its projection."
|
|
1867
1891
|
path: "src/modules/projections/<name>/"
|
|
1868
1892
|
presence: opt-in
|
|
1869
1893
|
pattern: projection # a read model: only its projection writes its table; recompute is idempotent and replayable; readers use get*
|
|
@@ -1872,7 +1896,7 @@ slots:
|
|
|
1872
1896
|
owner: true
|
|
1873
1897
|
requires: [index.ts, "<name>.module.ts", "<name>.projection.ts", "<name>.projection-entity.ts"]
|
|
1874
1898
|
allows: [index.ts, "<name>.module.ts", "<name>.module-definition.ts", "<name>.options.ts", "<name>.projection.ts", "<name>.projection-entity.ts", "<name>.contracts.ts", persistence/]
|
|
1875
|
-
lite: # the read model's table is a
|
|
1899
|
+
lite: # the read model's table is a db migration (lite's schema authority) mapped by its entity under persistence/entities/ (R334)
|
|
1876
1900
|
requires: [index.ts, "<name>.module.ts", "<name>.projection.ts"]
|
|
1877
1901
|
allows: [index.ts, "<name>.module.ts", "<name>.module-definition.ts", "<name>.options.ts", "<name>.projection.ts", "<name>.contracts.ts", persistence/]
|
|
1878
1902
|
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)
|
|
@@ -1892,7 +1916,7 @@ slots:
|
|
|
1892
1916
|
tests: unit-beside # <name>.service.spec.ts beside each <name>.service.ts; plus src/tests/integration/<provider>/ (R112)
|
|
1893
1917
|
coverage: required
|
|
1894
1918
|
budget: {indexExports: 80} # a provider's port surface includes clients, config, options, tokens and errors
|
|
1895
|
-
rules: [BE_TIER_DIRECTION, BE_ERROR_HOME, BE_SECRET_DEFAULT, BE_INTEGRATION_SPEC_MISSING]
|
|
1919
|
+
rules: [BE_TIER_DIRECTION, BE_ERROR_HOME, BE_SECRET_DEFAULT, BE_INTEGRATION_SPEC_MISSING, BE_AI_KEY_CUSTODY, BE_AI_GATEWAY_CLIENT]
|
|
1896
1920
|
- id: be.integrations.model
|
|
1897
1921
|
since: 3.1.0
|
|
1898
1922
|
profiles: [be]
|
|
@@ -1902,31 +1926,47 @@ slots:
|
|
|
1902
1926
|
tier: integrations
|
|
1903
1927
|
tests: none
|
|
1904
1928
|
coverage: required
|
|
1905
|
-
why:
|
|
1929
|
+
why: a self-hosted ML model client is an integration; weights and datasets are never in git, and a language model is called through the ai-router gateway by the integrations/ai LangChain client (BE-AI-1, BE-AI-3)
|
|
1906
1930
|
rules: [BE_TIER_DIRECTION, BE_INFRA_OWNER, BE_INTEGRATION_SPEC_MISSING]
|
|
1907
1931
|
|
|
1908
|
-
- id: be.integrations.
|
|
1932
|
+
- id: be.integrations.passport
|
|
1909
1933
|
profiles: [be]
|
|
1934
|
+
editions: [lite]
|
|
1910
1935
|
exemplifiedBy:
|
|
1911
|
-
- packages/hfs/templates/be/skeleton-lite/src/modules/integrations/
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
#
|
|
1915
|
-
#
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1936
|
+
- packages/hfs/templates/be/skeleton-lite/src/modules/integrations/passport/
|
|
1937
|
+
# The one owner of the lite identity libraries (ruleParams.be.infraOwners: @nestjs/passport, passport, passport-google-oauth20,
|
|
1938
|
+
# passport-local, @nestjs/jwt, jose, bcryptjs): the Google OAuth 2.0 strategy (state and PKCE), the local email and password
|
|
1939
|
+
# strategy (bcrypt), the app's own asymmetric access tokens and their JWKS, and the rotating refresh cookie. domain/identity
|
|
1940
|
+
# verifies a token through this integration's verifier and builds the principal (contexts/code/be/api-auth.md).
|
|
1941
|
+
path: "src/modules/integrations/passport/"
|
|
1942
|
+
presence: required
|
|
1943
|
+
tracked: tracked
|
|
1944
|
+
tier: integrations
|
|
1945
|
+
owner: true
|
|
1946
|
+
requires: [index.ts, passport.config.ts, passport.options.ts, passport.decorators.ts, passport.tokens.ts, passport.password.ts, passport.cookie.ts] # Secrets, no defaults (R44); options and named injectors (R83); tokens and JWKS; bcrypt; the refresh cookie
|
|
1947
|
+
allows: [index.ts, passport.config.ts, passport.options.ts, passport.decorators.ts, passport.tokens.ts, passport.password.ts, passport.cookie.ts, strategies/, errors/]
|
|
1948
|
+
tests: unit-beside
|
|
1949
|
+
rules: [BE_TIER_DIRECTION, BE_ERROR_HOME, BE_SECRET_DEFAULT, BE_INFRA_OWNER, BE_AUTH_PASSPORT_OWNER, BE_AUTH_JWT_OWN_JWKS, BE_AUTH_REFRESH_COOKIE]
|
|
1950
|
+
coverage: required
|
|
1951
|
+
since: 3.9.0
|
|
1952
|
+
- id: be.integrations.object-storage
|
|
1953
|
+
profiles: [be]
|
|
1954
|
+
exemplifiedBy:
|
|
1955
|
+
- packages/hfs/templates/be/skeleton-lite/src/modules/integrations/object-storage/
|
|
1956
|
+
# The one owner of the S3 client (@aws-sdk/client-s3 and @aws-sdk/s3-request-presigner) over S3-compatible storage (MinIO in
|
|
1957
|
+
# lite): the ObjectStorage port with presigned PUT and GET; no other module imports an S3 SDK or minio.
|
|
1958
|
+
path: "src/modules/integrations/object-storage/"
|
|
1959
|
+
presence: optional
|
|
1920
1960
|
litePresence: required
|
|
1921
1961
|
tracked: tracked
|
|
1922
1962
|
tier: integrations
|
|
1923
1963
|
owner: true
|
|
1924
|
-
requires: [index.ts,
|
|
1925
|
-
allows: [index.ts,
|
|
1964
|
+
requires: [index.ts, storage.config.ts, storage.options.ts, storage.decorators.ts, storage.client.ts]
|
|
1965
|
+
allows: [index.ts, storage.config.ts, storage.options.ts, storage.decorators.ts, storage.client.ts, errors/]
|
|
1926
1966
|
tests: unit-beside
|
|
1927
|
-
rules: [BE_TIER_DIRECTION, BE_ERROR_HOME, BE_SECRET_DEFAULT]
|
|
1967
|
+
rules: [BE_TIER_DIRECTION, BE_ERROR_HOME, BE_SECRET_DEFAULT, BE_INFRA_OWNER, BE_STORAGE_OWNER]
|
|
1928
1968
|
coverage: required
|
|
1929
|
-
since:
|
|
1969
|
+
since: 3.9.0
|
|
1930
1970
|
|
|
1931
1971
|
# ----- BE: tests (owner test layout 2026-09-30). Unit specs are <name>.service.spec.ts beside their service and nothing else; everything else
|
|
1932
1972
|
# lives under src/tests/, one folder per kind, and the folder and the file suffix always agree (a file whose suffix does
|
|
@@ -1934,15 +1974,15 @@ slots:
|
|
|
1934
1974
|
- id: be.tests.world
|
|
1935
1975
|
since: 3.1.0
|
|
1936
1976
|
profiles: [be]
|
|
1937
|
-
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
|
|
1977
|
+
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 db service and network fakes; full also boots apps and self-hosted services."
|
|
1938
1978
|
path: "src/tests/world/"
|
|
1939
1979
|
presence: optional
|
|
1940
1980
|
tracked: tracked
|
|
1941
1981
|
tier: e2e
|
|
1942
1982
|
lite:
|
|
1943
1983
|
why: >-
|
|
1944
|
-
the world of the optional integration specs of a lite app: global-setup.ts connects to the local
|
|
1945
|
-
(`
|
|
1984
|
+
the world of the optional integration specs of a lite app: global-setup.ts connects to the local db service
|
|
1985
|
+
(`npm run db:start`, with the migrations applied by `npm run db:push`) and starts no container, no toxiproxy and no stack of its
|
|
1946
1986
|
own; use-test-world.ts exports useTestWorld({ modules: [...] }) -> world.db.<connection> (the shared EntityManager) and
|
|
1947
1987
|
world.fake.<provider> (a network-edge fake of an external SaaS). There is no world.apps (no e2e) and no world.infra
|
|
1948
1988
|
(no self-hosted service to cut)
|
|
@@ -2011,7 +2051,7 @@ slots:
|
|
|
2011
2051
|
- id: be.tests.integration
|
|
2012
2052
|
since: 3.1.0
|
|
2013
2053
|
profiles: [be]
|
|
2014
|
-
editionWhy: "Lite integration specs use the local
|
|
2054
|
+
editionWhy: "Lite integration specs use the local db service 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)."
|
|
2015
2055
|
path: "src/tests/integration/<capability>/*.integration-spec.ts"
|
|
2016
2056
|
presence: optional
|
|
2017
2057
|
tracked: tracked
|
|
@@ -2020,8 +2060,8 @@ slots:
|
|
|
2020
2060
|
coverage: none
|
|
2021
2061
|
lite:
|
|
2022
2062
|
why: >-
|
|
2023
|
-
optional under lite: one capability module through useTestWorld({ modules }) against the local
|
|
2024
|
-
(`
|
|
2063
|
+
optional under lite: one capability module through useTestWorld({ modules }) against the local db service
|
|
2064
|
+
(`npm run db:start`), run by test:integration; no container, no toxiproxy and no outage through the world (R112 is full's)
|
|
2025
2065
|
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
|
|
2026
2066
|
rules: [BE_TEST_TOPOLOGY, BE_INTEGRATION_SPEC_MISSING]
|
|
2027
2067
|
- id: be.tests.e2e
|
|
@@ -2034,7 +2074,7 @@ slots:
|
|
|
2034
2074
|
tracked: tracked
|
|
2035
2075
|
tier: e2e
|
|
2036
2076
|
tests: e2e
|
|
2037
|
-
goesTo: "the full edition: a lite app has no e2e world; its logic is proven by unit specs and its database by optional integration specs on `
|
|
2077
|
+
goesTo: "the full edition: a lite app has no e2e world; its logic is proven by unit specs and its database by optional integration specs on `npm run db:start`"
|
|
2038
2078
|
coverage: none
|
|
2039
2079
|
why: flow e2e through useTestWorld({ apps }); external services are the world's network fakes; run by test:e2e
|
|
2040
2080
|
rules: [HFS_CI_E2E_AUTOMATIC, BE_TEST_TOPOLOGY, BE_E2E_FLOW]
|
|
@@ -2093,7 +2133,7 @@ slots:
|
|
|
2093
2133
|
tracked: tracked
|
|
2094
2134
|
tier: route
|
|
2095
2135
|
# 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
|
|
2096
|
-
# route handler of this slot, the
|
|
2136
|
+
# route handler of this slot, the OAuth callback is a back-end door, and no file holds a Server Action
|
|
2097
2137
|
# (FE_ROUTE_HANDLER_FORBIDDEN, FE_SERVER_ACTION_FORBIDDEN).
|
|
2098
2138
|
allows: ["[locale]/**/{page,layout,template,loading,error,not-found,default}.tsx", "[locale]/providers.tsx",
|
|
2099
2139
|
global-error.tsx, page.tsx, # the root page.tsx only redirects (FE_ROUTE_FILES_THIN)
|
|
@@ -2124,20 +2164,6 @@ slots:
|
|
|
2124
2164
|
tests: none
|
|
2125
2165
|
goesTo: "apps/<app>/src/proxy.ts: Next >= 16 reads proxy.ts and refuses middleware.ts"
|
|
2126
2166
|
rules: [FE_NEXT_CONVENTIONS]
|
|
2127
|
-
- id: fe.route.callback
|
|
2128
|
-
profiles: [fe]
|
|
2129
|
-
# The auth callback of a Supabase session (the PKCE code exchange), kept only when the auth flow truly needs a server exchange
|
|
2130
|
-
# (prefer the client-side PKCE exchange in fe.modules.auth): the one non-health route handler of an app with a Supabase
|
|
2131
|
-
# connection; an app without one has no route handler in fe/ (L15).
|
|
2132
|
-
path: "apps/<app>/src/app/auth/callback/route.ts"
|
|
2133
|
-
presence: opt-in
|
|
2134
|
-
provider: supabase
|
|
2135
|
-
tracked: tracked
|
|
2136
|
-
tier: route
|
|
2137
|
-
tests: none
|
|
2138
|
-
since: 2.1.0
|
|
2139
|
-
rules: [FE_ROUTE_HANDLER_FORBIDDEN, FE_CALLBACK_SHAPE, FE_SUPABASE_CLIENT_OWNER, FE_SERVICE_ROLE_FORBIDDEN]
|
|
2140
|
-
|
|
2141
2167
|
- id: fe.feature
|
|
2142
2168
|
since: 3.1.0
|
|
2143
2169
|
profiles: [fe]
|
|
@@ -2469,18 +2495,17 @@ slots:
|
|
|
2469
2495
|
- id: fe.modules.auth
|
|
2470
2496
|
profiles: [fe]
|
|
2471
2497
|
# The identity-provider client (fe-stack auth, FE-AUTH): the session, the access token the Apollo link attaches and the
|
|
2472
|
-
# sign-in/sign-out calls.
|
|
2473
|
-
#
|
|
2498
|
+
# sign-in/sign-out calls. It talks only to the app's own back-end auth doors and holds no credential: the refresh token lives in
|
|
2499
|
+
# the back end's httpOnly cookie and the access token in memory (ruling:lite-self-hosted-postgres-passport-minio).
|
|
2474
2500
|
path: "apps/<app>/src/modules/auth/"
|
|
2475
2501
|
presence: optional
|
|
2476
2502
|
tracked: tracked
|
|
2477
2503
|
tier: modules
|
|
2478
2504
|
owner: true
|
|
2479
2505
|
requires: [index.ts]
|
|
2480
|
-
entries: [server.ts] # the server-only PKCE exchange API used by the opted-in callback route
|
|
2481
2506
|
tests: none
|
|
2482
2507
|
since: 3.0.0
|
|
2483
|
-
rules: [
|
|
2508
|
+
rules: [FE_STACK_FORBIDDEN]
|
|
2484
2509
|
- id: fe.modules.toast
|
|
2485
2510
|
profiles: [fe]
|
|
2486
2511
|
# Mutation feedback (fe-stack toast, FE-TOAST): the HeroUI toast wrapper, showGraphQLToast over a GraphQLResponse and the
|