@starci/hfs 4.0.10 → 4.2.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 +5 -1
- package/emit/operations.mjs +1 -1
- package/emit/type-schema.mjs +2 -1
- package/lint/run.mjs +65 -24
- package/package.json +1 -1
- package/report/order.mjs +2 -0
- package/runtime/config.example.yaml +194 -0
- package/runtime/engine/by-code-unit.mjs +12 -0
- package/runtime/engine/config.mjs +222 -239
- package/runtime/engine/invalid-config.mjs +5 -5
- package/runtime/engine/model-config.mjs +90 -0
- package/runtime/engine/orca-config.mjs +3 -1
- package/runtime/engine/release-config.mjs +28 -0
- package/runtime/engine/removed-vocabulary.mjs +28 -0
- package/runtime/engine/resources-config.mjs +52 -0
- package/runtime/engine/runtime-root.mjs +17 -0
- package/runtime/engine/secrets.mjs +35 -31
- package/runtime/engine/sonar-config.mjs +21 -0
- package/runtime/engine/temp-root.mjs +32 -0
- package/runtime/knowledge/hfs/canon-pins.yaml +14 -14
- package/runtime/knowledge/hfs/peer-integrations.yaml +2 -3
- package/runtime/knowledge/hfs/rules.yaml +161 -65
- package/runtime/knowledge/hfs/slots.yaml +11 -14
- package/runtime/knowledge/patterns/be/api.yaml +7 -24
- package/runtime/knowledge/patterns/be/cli.yaml +3 -12
- package/runtime/knowledge/patterns/be/realtime.yaml +1 -14
- package/runtime/knowledge/patterns/be/webhooks.yaml +0 -19
- package/runtime/modules/kernel/failure-codes.yaml +1 -41
- package/runtime/modules/kernel/removed-vocabulary.yaml +268 -0
- package/runtime/modules/models/registry.yaml +25 -359
- package/runtime/modules/models/runtimes.yaml +126 -265
- package/runtime/modules/models/tiers.yaml +66 -0
- package/runtime/scripts/api/fs/claim-file.mjs +30 -0
- package/runtime/scripts/api/fs/ensure-temp-root.mjs +27 -0
- package/runtime/scripts/api/fs/forbidden-root.mjs +2 -1
- package/runtime/scripts/api/fs/make-temp-dir.mjs +11 -0
- package/runtime/scripts/api/fs/safe-remove.mjs +52 -35
- package/runtime/scripts/api/git/lib.mjs +34 -1
- package/runtime/scripts/api/process/resolve-real-tool.mjs +14 -8
- package/runtime/scripts/api/process/run-program.mjs +2 -1
- package/runtime/scripts/api/sops/lib.mjs +198 -120
- package/runtime/scripts/hfs/allows.mjs +3 -3
- package/runtime/scripts/hfs/architecture/ast-walks.mjs +63 -50
- package/runtime/scripts/hfs/architecture/automatic-gates.mjs +96 -0
- package/runtime/scripts/hfs/architecture/backend.mjs +261 -175
- package/runtime/scripts/hfs/architecture/background-unowned.mjs +69 -34
- package/runtime/scripts/hfs/architecture/client-reaches-server.mjs +55 -54
- package/runtime/scripts/hfs/architecture/clones.mjs +179 -106
- package/runtime/scripts/hfs/architecture/config-unread.mjs +5 -22
- package/runtime/scripts/hfs/architecture/config.mjs +95 -80
- package/runtime/scripts/hfs/architecture/connection-map.mjs +233 -141
- package/runtime/scripts/hfs/architecture/constructor-deps.mjs +15 -9
- package/runtime/scripts/hfs/architecture/context-coupling.mjs +76 -52
- package/runtime/scripts/hfs/architecture/context-map.mjs +215 -141
- package/runtime/scripts/hfs/architecture/context-owner.mjs +39 -30
- package/runtime/scripts/hfs/architecture/context-platform-tables.mjs +31 -17
- package/runtime/scripts/hfs/architecture/context-transaction.mjs +38 -25
- package/runtime/scripts/hfs/architecture/contract-fixture-guard.mjs +86 -40
- package/runtime/scripts/hfs/architecture/contracts-readonly.mjs +342 -0
- package/runtime/scripts/hfs/architecture/contracts.mjs +263 -455
- package/runtime/scripts/hfs/architecture/cross-app-duplicate.mjs +43 -34
- package/runtime/scripts/hfs/architecture/dead-exports.mjs +201 -154
- package/runtime/scripts/hfs/architecture/default-deny.mjs +118 -93
- package/runtime/scripts/hfs/architecture/doc-language.mjs +2 -1
- package/runtime/scripts/hfs/architecture/entrypoint.mjs +45 -30
- package/runtime/scripts/hfs/architecture/error-codes.mjs +19 -12
- package/runtime/scripts/hfs/architecture/error-masked.mjs +39 -26
- package/runtime/scripts/hfs/architecture/fe-slot-allows.mjs +2 -1
- package/runtime/scripts/hfs/architecture/feature-shape.mjs +26 -20
- package/runtime/scripts/hfs/architecture/framework-pinned.mjs +1 -1
- package/runtime/scripts/hfs/architecture/frontend-grammar.mjs +190 -0
- package/runtime/scripts/hfs/architecture/frontend-routing.mjs +186 -0
- package/runtime/scripts/hfs/architecture/frontend-world-render.mjs +311 -0
- package/runtime/scripts/hfs/architecture/frontend-world.mjs +328 -0
- package/runtime/scripts/hfs/architecture/frontend.mjs +109 -829
- package/runtime/scripts/hfs/architecture/hfs-graph.mjs +14 -2
- package/runtime/scripts/hfs/architecture/hfs.mjs +120 -288
- package/runtime/scripts/hfs/architecture/hooks-are-hooks.mjs +46 -26
- package/runtime/scripts/hfs/architecture/i18n-keys.mjs +114 -76
- package/runtime/scripts/hfs/architecture/index.mjs +164 -104
- package/runtime/scripts/hfs/architecture/injection-token-exported.mjs +33 -28
- package/runtime/scripts/hfs/architecture/machine-ast.mjs +180 -144
- package/runtime/scripts/hfs/architecture/managed-scripts.mjs +8 -2
- package/runtime/scripts/hfs/architecture/module-per-transport.mjs +150 -101
- package/runtime/scripts/hfs/architecture/next-data-contract.mjs +51 -43
- package/runtime/scripts/hfs/architecture/next-data-key.mjs +338 -0
- package/runtime/scripts/hfs/architecture/next-data.mjs +179 -427
- package/runtime/scripts/hfs/architecture/owners.mjs +27 -13
- package/runtime/scripts/hfs/architecture/package-shape.mjs +51 -23
- package/runtime/scripts/hfs/architecture/presentation.mjs +165 -0
- package/runtime/scripts/hfs/architecture/reachability.mjs +127 -61
- package/runtime/scripts/hfs/architecture/register-once.mjs +112 -63
- package/runtime/scripts/hfs/architecture/registration.mjs +187 -110
- package/runtime/scripts/hfs/architecture/required-files.mjs +121 -70
- package/runtime/scripts/hfs/architecture/route-files-thin.mjs +83 -58
- package/runtime/scripts/hfs/architecture/schema-owner.mjs +182 -108
- package/runtime/scripts/hfs/architecture/source-names-shape.mjs +318 -0
- package/runtime/scripts/hfs/architecture/source-names.mjs +83 -248
- package/runtime/scripts/hfs/architecture/sql-owner.mjs +175 -118
- package/runtime/scripts/hfs/architecture/sql-returning.mjs +39 -28
- package/runtime/scripts/hfs/architecture/sql-tokens.mjs +302 -172
- package/runtime/scripts/hfs/architecture/supabase-ast.mjs +1 -1
- package/runtime/scripts/hfs/architecture/supabase-be.mjs +55 -45
- package/runtime/scripts/hfs/architecture/supabase-results.mjs +182 -0
- package/runtime/scripts/hfs/architecture/supabase-tables.mjs +20 -12
- package/runtime/scripts/hfs/architecture/supabase.mjs +191 -263
- package/runtime/scripts/hfs/architecture/symbols.mjs +175 -99
- package/runtime/scripts/hfs/architecture/test-world-files.mjs +82 -41
- package/runtime/scripts/hfs/architecture/tiers.mjs +120 -69
- package/runtime/scripts/hfs/architecture/transport-owner.mjs +84 -57
- package/runtime/scripts/hfs/architecture/type-context.mjs +282 -0
- package/runtime/scripts/hfs/architecture/typescript.mjs +116 -276
- package/runtime/scripts/hfs/architecture/unit-spec-providers.mjs +84 -62
- package/runtime/scripts/hfs/architecture.mjs +3 -4
- package/runtime/scripts/hfs/catalog-once.mjs +15 -0
- package/runtime/scripts/hfs/check.mjs +63 -71
- package/runtime/scripts/hfs/coverage-scope.mjs +84 -38
- package/runtime/scripts/hfs/declaration-shape.mjs +77 -36
- package/runtime/scripts/hfs/declaration-slots.mjs +1 -1
- package/runtime/scripts/hfs/edition-slots.mjs +5 -3
- package/runtime/scripts/hfs/linear-text.mjs +30 -0
- package/runtime/scripts/hfs/manifest-shape.mjs +164 -63
- package/runtime/scripts/hfs/path-findings.mjs +58 -38
- package/runtime/scripts/hfs/pin-findings.mjs +31 -0
- package/runtime/scripts/hfs/repo-identity.mjs +21 -4
- package/runtime/scripts/hfs/rule-catalog.mjs +194 -0
- package/runtime/scripts/hfs/rule-params-shape.mjs +33 -27
- package/runtime/scripts/hfs/rules/cli.mjs +5 -1
- package/runtime/scripts/hfs/rules/contract-compat.mjs +40 -22
- package/runtime/scripts/hfs/rules/contract.mjs +35 -27
- package/runtime/scripts/hfs/rules/database-config.mjs +57 -32
- package/runtime/scripts/hfs/rules/database-migrations.mjs +52 -31
- package/runtime/scripts/hfs/rules/database-plpgsql.mjs +106 -91
- package/runtime/scripts/hfs/rules/database-sql.mjs +254 -173
- package/runtime/scripts/hfs/rules/database.mjs +36 -23
- package/runtime/scripts/hfs/rules/deps.mjs +64 -32
- package/runtime/scripts/hfs/rules/docker.mjs +113 -65
- package/runtime/scripts/hfs/rules/edition.mjs +118 -111
- package/runtime/scripts/hfs/rules/event-bus.mjs +91 -45
- package/runtime/scripts/hfs/rules/fe-contract-documents.mjs +17 -16
- package/runtime/scripts/hfs/rules/fe-no-tests.mjs +36 -16
- package/runtime/scripts/hfs/rules/frontend-tree.mjs +4 -3
- package/runtime/scripts/hfs/rules/integration-specs.mjs +75 -41
- package/runtime/scripts/hfs/rules/kinds.mjs +59 -25
- package/runtime/scripts/hfs/rules/lint-suppression.mjs +9 -3
- package/runtime/scripts/hfs/rules/monorepo.mjs +82 -45
- package/runtime/scripts/hfs/rules/peer-integrations.mjs +24 -3
- package/runtime/scripts/hfs/rules/pipeline.mjs +58 -9
- package/runtime/scripts/hfs/rules/proof-commands.mjs +14 -8
- package/runtime/scripts/hfs/rules/repo-local-checks.mjs +11 -6
- package/runtime/scripts/hfs/rules/saga.mjs +128 -61
- package/runtime/scripts/hfs/rules/secrets.mjs +4 -3
- package/runtime/scripts/hfs/rules/services.mjs +34 -16
- package/runtime/scripts/hfs/rules/stacks.mjs +21 -14
- package/runtime/scripts/hfs/rules/supabase-secrets.mjs +32 -23
- package/runtime/scripts/hfs/rules/test-topology.mjs +25 -11
- package/runtime/scripts/hfs/secret.mjs +59 -48
- package/runtime/scripts/hfs/slot-app-view.mjs +102 -0
- package/runtime/scripts/hfs/slot-classify.mjs +65 -0
- package/runtime/scripts/hfs/slot-errors.mjs +11 -0
- package/runtime/scripts/hfs/slot-imports.mjs +76 -0
- package/runtime/scripts/hfs/slot-manifest-shape.mjs +73 -0
- package/runtime/scripts/hfs/slot-match.mjs +153 -0
- package/runtime/scripts/hfs/slot-path.mjs +8 -0
- package/runtime/scripts/hfs/slot-required.mjs +76 -0
- package/runtime/scripts/hfs/slot-semantic-problems.mjs +148 -0
- package/runtime/scripts/hfs/slot-side-problems.mjs +28 -0
- package/runtime/scripts/hfs/slots.mjs +74 -695
- package/runtime/scripts/hfs/sql/pg-parse.mjs +5 -2
- package/runtime/scripts/hfs/trailing-slashes.mjs +8 -0
- package/runtime/scripts/hfs/tree.mjs +19 -14
- package/runtime/scripts/hfs/typescript-programs.mjs +6 -4
- package/runtime/scripts/hfs/view.mjs +1 -1
- package/runtime/scripts/lib/dockerfile.mjs +56 -33
- package/runtime/scripts/lib/env.mjs +10 -0
- package/runtime/scripts/lib/event-contract.mjs +17 -13
- package/runtime/scripts/lib/git.mjs +2 -2
- package/runtime/scripts/lib/graphql-contract.mjs +158 -322
- package/runtime/scripts/lib/graphql-sdl.mjs +262 -0
- package/runtime/scripts/lib/i18n.mjs +3 -2
- package/runtime/scripts/lib/in-order.mjs +71 -0
- package/runtime/scripts/lib/language.mjs +3 -3
- package/runtime/scripts/lib/list.mjs +8 -1
- package/runtime/scripts/lib/path-key.mjs +38 -5
- package/runtime/scripts/lib/pid-alive.mjs +7 -0
- package/runtime/scripts/lib/regex.mjs +1 -1
- package/runtime/scripts/lib/same-text.mjs +1 -1
- package/runtime/scripts/lib/secret-patterns.mjs +6 -2
- package/runtime/scripts/lib/sleep-sync.mjs +2 -2
- package/runtime/scripts/lib/sops-envelope.mjs +95 -48
- package/runtime/scripts/lib/stack-services.mjs +1 -2
- package/runtime/scripts/lib/ts-ast.mjs +9 -0
- package/runtime/scripts/lib/walk.mjs +6 -1
- package/runtime/scripts/lib/yaml-cached.mjs +14 -0
- package/scaffold/add-cli-lite.mjs +2 -1
- package/scaffold/add-table.mjs +2 -2
- package/scaffold/add.mjs +3 -2
- package/scaffold/app.mjs +4 -2
- package/scaffold/edition-gate.mjs +25 -25
- package/scaffold/lite-exports.mjs +2 -1
- package/scaffold/service.mjs +5 -4
- package/sync/hygiene.mjs +15 -9
- package/sync/index.mjs +7 -3
- package/templates/app/ci-workflows/github/workflows/ci.yml +4 -4
- package/templates/app/ci-workflows/github/workflows/e2e.yml +3 -3
- package/templates/app/ci-workflows/github/workflows/images.yml +3 -3
- package/templates/app/ci-workflows/sonar-steps.yml +2 -2
- package/templates/app/ci-workflows-lite/github/workflows/ci.yml +6 -6
- package/templates/app/ci-workflows-lite/github/workflows/db-deploy.yml +3 -3
- package/templates/app/ci-workflows-lite/github/workflows/images.yml +3 -3
- package/templates/app/starciwork.gitignore +1 -1
- package/templates/be/image/api/Dockerfile +1 -1
- package/templates/be/image/cli/Dockerfile +1 -1
- package/templates/be/image/worker/Dockerfile +1 -1
- package/templates/be/patterns/cli/group.cli.spec.ts.tpl +1 -1
- package/templates/be/patterns/cli/group.cli.ts.tpl +2 -2
- package/templates/be/patterns/event-bus/platform/event-runner.service.spec.ts.tpl +13 -0
- package/templates/be/patterns/event-bus/platform/event-runner.service.ts.tpl +7 -2
- package/templates/be/patterns/event-bus/platform/event.policy.ts.tpl +4 -1
- package/templates/be/patterns/event-bus/platform/kafka-event-transport.client.ts.tpl +2 -1
- package/templates/be/patterns/outbox/platform/outbox-relay.policy.ts.tpl +24 -9
- package/templates/be/patterns/queues/platform/queue-relay.service.ts.tpl +3 -2
- package/templates/be/patterns/queues/platform/queue-worker.service.ts.tpl +8 -5
- package/templates/be/skeleton/src/features/cli/migrate/migrate.cli.spec.ts +1 -1
- package/templates/be/skeleton/src/features/cli/migrate/migrate.cli.ts +2 -2
- package/templates/be/skeleton/src/features/cli/seed/seed.cli.spec.ts +1 -1
- package/templates/be/skeleton/src/features/cli/seed/seed.cli.ts +2 -2
- package/templates/be/skeleton/src/modules/platform/database/migrate-connections.client.ts +3 -2
- package/templates/be/skeleton/src/modules/platform/database/seed-connections.client.ts +5 -4
- package/templates/be/skeleton/src/modules/platform/primitives/index.ts +1 -0
- package/templates/be/skeleton/src/modules/platform/primitives/sequence.policy.spec.ts +47 -0
- package/templates/be/skeleton/src/modules/platform/primitives/sequence.policy.ts +16 -0
- package/templates/be/skeleton-lite/apps/api/Dockerfile +1 -0
- package/templates/be/skeleton-lite/src/modules/platform/primitives/index.ts +2 -0
- package/templates/fe/image/next/Dockerfile +2 -1
- package/templates/fe/skeleton/apps/app/src/app/[locale]/page.tsx +2 -2
- package/templates/fe/skeleton/apps/landing/src/app/[locale]/page.tsx +2 -2
- package/templates/fe/skeleton/packages/__project__-i18n/src/index.ts +1 -2
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/layout.tsx +1 -2
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/page.tsx +2 -2
- package/templates/fe/skeleton-lite/apps/web/src/components/blocks/SignInForm/index.tsx +7 -1
- package/templates/fe/skeleton-lite/apps/web/src/modules/db/auth/write-sign-out.ts +1 -1
- package/templates/fe/skeleton-lite/apps/web/src/modules/db/validation/validation.mapper.ts +10 -2
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/request.ts +10 -3
- package/upgrade/index.mjs +9 -2
- package/runtime/engine/admission.mjs +0 -308
- package/runtime/engine/canonical-json.mjs +0 -10
- package/runtime/engine/db/blob.mjs +0 -315
- package/runtime/engine/db/ledger-paths.mjs +0 -83
- package/runtime/engine/db/ledger.mjs +0 -1118
- package/runtime/engine/db/machine-connection.mjs +0 -135
- package/runtime/engine/db/machine-schema.mjs +0 -84
- package/runtime/engine/db/machine.mjs +0 -1367
- package/runtime/engine/db/migrations/machine/0001-init.sql +0 -924
- package/runtime/engine/db/migrations/runtime/0001-init.sql +0 -1108
- package/runtime/engine/db/provider-reservations.mjs +0 -101
- package/runtime/engine/digest.mjs +0 -16
- package/runtime/engine/refuse.mjs +0 -11
- package/runtime/modules/ops/_labels.yaml +0 -53
- package/runtime/scripts/api/node/lib.mjs +0 -14
- package/runtime/scripts/api/node/spawn-node.mjs +0 -6
- package/runtime/scripts/api/process/lib.mjs +0 -111
- package/runtime/scripts/api/process/owned-process.mjs +0 -78
- package/runtime/scripts/api/process/stop-owned-process.mjs +0 -9
- package/runtime/scripts/connectors/lib.mjs +0 -488
- package/runtime/scripts/lib/clip.mjs +0 -19
- package/runtime/scripts/lib/display-names.mjs +0 -259
- package/runtime/scripts/lib/example-refs.mjs +0 -158
- package/runtime/scripts/lib/json-schema.mjs +0 -52
- package/runtime/scripts/lib/process-identity.mjs +0 -6
- package/runtime/scripts/lib/read-yaml.mjs +0 -18
- package/runtime/scripts/lib/redact.mjs +0 -161
- package/runtime/scripts/lib/sleep.mjs +0 -7
- package/runtime/scripts/lib/source-phrases.mjs +0 -34
- package/runtime/scripts/lib/sqlite.mjs +0 -21
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# knowledge/hfs/rules.yaml - the machine-readable catalog of the HFS rules (
|
|
1
|
+
# knowledge/hfs/rules.yaml - the machine-readable catalog of the HFS rules (count the rules, never type the count).
|
|
2
2
|
#
|
|
3
3
|
# One entry per rule: its id, its finding code, the law in one line, how a violation is fixed
|
|
4
4
|
# (`kinds`), the gates it runs at, the failure codes it reports, and every enforcer that judges it. The prose of each
|
|
@@ -21,7 +21,7 @@ version: 2.0.0
|
|
|
21
21
|
gates:
|
|
22
22
|
pre-commit: the managed .husky/pre-commit at the app root - starci app hygiene (staged .starciwork and .starcistacks paths, secrets guard), typecheck, ESLint per side and stylelint on the staged files, prettier --check, the unit specs the staged files touch
|
|
23
23
|
pre-push: typecheck, lint (starci app lint), format:check and the unit specs affected since origin/main; runs every pre-commit rule again
|
|
24
|
-
settle: the op gate
|
|
24
|
+
settle: the op gate `starci gate run` over the op's changed files (merge guard, starci app lint --changed, codegen, tsc, the slice's specs; only findings new against the workflow's previous checkpoint block), re-read at starci kernel settle, which refuses a red done; a green settle commits the op's checkpoint on wf-<workflowId>
|
|
25
25
|
land: the workflow's finish, the only time main is touched - gate.mjs over the whole workflow branch against its merge-base with main, the merge guard, review.verify of the exact head that lands, the branch rebased onto main, then main fast-forwarded and pushed
|
|
26
26
|
ci: GitHub Actions runs the same pinned starci app lint, format:check, typecheck, unit with the per-file coverage threshold, build:be and build:fe, then the Sonar scan and gate
|
|
27
27
|
sonar: Sonar quality gate (knowledge/sonar-gate.yaml) - the imported lint findings, duplication, cognitive complexity and the coverage of the logic of be/src/modules at 100
|
|
@@ -101,14 +101,6 @@ rules:
|
|
|
101
101
|
failureCodes: ["HFS_AGENT_DATA_TRACKED"]
|
|
102
102
|
enforcers:
|
|
103
103
|
- {kind: work-validate, id: agent-data-tracked, at: scripts/work/validate/check-example-work.mjs}
|
|
104
|
-
- id: R08
|
|
105
|
-
code: "HFS_WORK_NODE_RETIRED"
|
|
106
|
-
law: "Only flat family records, never `work/node`."
|
|
107
|
-
kinds: [check, codemod]
|
|
108
|
-
gates: [settle, land, ci]
|
|
109
|
-
failureCodes: ["HFS_WORK_NODE_RETIRED"]
|
|
110
|
-
enforcers:
|
|
111
|
-
- {kind: work-validate, id: work-node-retired, at: scripts/work/validate/check-example-work.mjs}
|
|
112
104
|
- id: R09
|
|
113
105
|
code: "HFS_IDENTITY_CUSTODY"
|
|
114
106
|
law: "An identity points its secret at `secrets/identity-<slug>.enc`; UAT chooses by role."
|
|
@@ -483,7 +475,7 @@ rules:
|
|
|
483
475
|
editions: [full]
|
|
484
476
|
enforcers:
|
|
485
477
|
- {kind: hfs, id: test-topology, at: scripts/hfs/rules/test-topology.mjs}
|
|
486
|
-
- {kind: machine, id: test-kind-
|
|
478
|
+
- {kind: machine, id: test-kind-unsupported, at: scripts/hfs/architecture/hfs.mjs}
|
|
487
479
|
- {kind: eslint-be, id: test-world-files}
|
|
488
480
|
- {kind: machine, id: contract-fixture-guard, at: scripts/hfs/architecture/contract-fixture-guard.mjs}
|
|
489
481
|
- {kind: eslint-be, id: unit-test-colocated}
|
|
@@ -1232,15 +1224,6 @@ rules:
|
|
|
1232
1224
|
failureCodes: ["RT_SOURCE_NAME"]
|
|
1233
1225
|
enforcers:
|
|
1234
1226
|
- {kind: runtime, id: source-name, at: scripts/hfs/runtime-rules/source-name.mjs}
|
|
1235
|
-
- id: R120
|
|
1236
|
-
code: "RT_RETIRED_PRESENT"
|
|
1237
|
-
law: "No path of retired[] and no `from` of moved[] in modules/kernel/retired-paths.yaml is tracked again, and no symbol of its retiredSymbols[] is declared in runtime production code."
|
|
1238
|
-
scope: runtime
|
|
1239
|
-
kinds: [check]
|
|
1240
|
-
gates: [land, runtime]
|
|
1241
|
-
failureCodes: ["RT_RETIRED_PRESENT"]
|
|
1242
|
-
enforcers:
|
|
1243
|
-
- {kind: runtime, id: retired-present, at: scripts/hfs/runtime-rules/retired.mjs}
|
|
1244
1227
|
- id: R121
|
|
1245
1228
|
code: "RT_GENERATED_DRIFT"
|
|
1246
1229
|
law: "A generated root of ruleParams.runtime.generated is git-ignored output of its declared generatedBy: the repo's check regenerates every root, the runtime copies (packages/hfs/runtime, packages/eslint/be/runtime, packages/eslint/fe/runtime) are judged equal to what scripts/hfs/sync-runtime.mjs writes from the single source, and no git-tracked path lies under a generated root."
|
|
@@ -1253,7 +1236,7 @@ rules:
|
|
|
1253
1236
|
- {kind: runtime, id: generated-untracked, at: scripts/hfs/runtime-rules/generated-untracked.mjs}
|
|
1254
1237
|
- id: R122
|
|
1255
1238
|
code: "RT_CITED_PATH_MISSING"
|
|
1256
|
-
law: "A runtime path cited by live prose or contracts (modules, docs, skills, knowledge, CONTEXT.md, README.md, CONTRIBUTING.md, init, the ui docs) exists; a
|
|
1239
|
+
law: "A runtime path cited by live prose or contracts (modules, docs, skills, knowledge, CONTEXT.md, README.md, CONTRIBUTING.md, init, the ui docs) exists; history (a changelog, a benchmark file) is not read."
|
|
1257
1240
|
scope: runtime
|
|
1258
1241
|
kinds: [check]
|
|
1259
1242
|
gates: [land, runtime]
|
|
@@ -1262,13 +1245,13 @@ rules:
|
|
|
1262
1245
|
- {kind: runtime, id: cited-path, at: scripts/checks/check-contract-cites.mjs}
|
|
1263
1246
|
- id: R124
|
|
1264
1247
|
code: "RT_PINNED_PATH_MOVED"
|
|
1265
|
-
law: "A pinned path of ruleParams.runtime.pinned (persisted outside git) exists
|
|
1248
|
+
law: "A pinned path of ruleParams.runtime.pinned (persisted outside git) exists."
|
|
1266
1249
|
scope: runtime
|
|
1267
1250
|
kinds: [check]
|
|
1268
1251
|
gates: [land, runtime]
|
|
1269
1252
|
failureCodes: ["RT_PINNED_PATH_MOVED"]
|
|
1270
1253
|
enforcers:
|
|
1271
|
-
- {kind: runtime, id: pinned-path, at: scripts/hfs/runtime-rules/
|
|
1254
|
+
- {kind: runtime, id: pinned-path, at: scripts/hfs/runtime-rules/pinned.mjs}
|
|
1272
1255
|
- id: R125
|
|
1273
1256
|
code: "RT_NODE_MODULES_LINK"
|
|
1274
1257
|
law: "No runtime source creates a junction or a symlink for a node_modules folder, through node:fs (symlink, symlinkSync, directly or through a local wrapper) or a spawned link command (mklink /J or /D, New-Item -ItemType Junction or SymbolicLink, ln -s): every checkout installs its own dependencies with a real npm ci from the cache."
|
|
@@ -1736,11 +1719,11 @@ rules:
|
|
|
1736
1719
|
- {kind: runtime, id: facts, at: scripts/hfs/runtime-rules/facts.mjs}
|
|
1737
1720
|
- id: R193
|
|
1738
1721
|
code: "RT_RULE_ID_UNKNOWN"
|
|
1739
|
-
law: "Every rule id (R<digits>) that tracked knowledge, docs, code or data names is a rule of this catalog, and
|
|
1722
|
+
law: "Every rule id (R<digits>) that tracked knowledge, docs, code or data names is a rule of this catalog, and rule ids are unique and well-formed (a gap between two ids is fine), and every rule of the catalog is named by the `hfsRules:` of at least one knowledge/patterns topic or carries `scope: runtime` (RT_RULE_UNCITED). History paths (changelogs, benchmark findings, .starciwork records), specs and generated copies are not read."
|
|
1740
1723
|
scope: runtime
|
|
1741
1724
|
kinds: [check]
|
|
1742
1725
|
gates: [land, runtime]
|
|
1743
|
-
failureCodes: ["RT_RULE_ID_UNKNOWN", "
|
|
1726
|
+
failureCodes: ["RT_RULE_ID_UNKNOWN", "RT_RULE_UNCITED"]
|
|
1744
1727
|
enforcers:
|
|
1745
1728
|
- {kind: runtime, id: rule-ids, at: scripts/hfs/runtime-rules/rule-ids.mjs}
|
|
1746
1729
|
- id: R194
|
|
@@ -1754,7 +1737,7 @@ rules:
|
|
|
1754
1737
|
- {kind: runtime, id: generated-block, at: scripts/hfs/runtime-rules/generated-block.mjs}
|
|
1755
1738
|
- id: R195
|
|
1756
1739
|
code: "RT_PROSE_PATH_NO_SLOT"
|
|
1757
|
-
law: "Every product path the knowledge, the docs and the READMEs name (a be/ or fe/ path, whole or with placeholders, globs and braces) is owned by a slot of knowledge/hfs/slots.yaml or is a folder above one; a path no slot owns is prose that invented a place or kept one a slot no longer has. App names are free: a literal app name is one
|
|
1740
|
+
law: "Every product path the knowledge, the docs and the READMEs name (a be/ or fe/ path, whole or with placeholders, globs and braces) is owned by a slot of knowledge/hfs/slots.yaml or is a folder above one; a path no slot owns is prose that invented a place or kept one a slot no longer has. App names are free: a literal app name is one a shipped examples/*/hfs.json declares, a placeholder stands for any; a ** glob and a topic file of the knowledge are not paths."
|
|
1758
1741
|
scope: runtime
|
|
1759
1742
|
kinds: [check]
|
|
1760
1743
|
gates: [land, runtime]
|
|
@@ -1770,15 +1753,6 @@ rules:
|
|
|
1770
1753
|
failureCodes: ["RT_PROSE_RESTATES_SLOTS"]
|
|
1771
1754
|
enforcers:
|
|
1772
1755
|
- {kind: runtime, id: prose-restates, at: scripts/hfs/runtime-rules/prose-restates.mjs}
|
|
1773
|
-
- id: R197
|
|
1774
|
-
code: "RT_RETIRED_CLI_CALL"
|
|
1775
|
-
law: "No tracked file invokes a command the unified CLI removed: the retired kernel, hfs, test-stack and top-level runtime spellings listed in the `removed` fields of the catalog and in the dispatcher table; those fields and changelogs are exempt."
|
|
1776
|
-
scope: runtime
|
|
1777
|
-
kinds: [check]
|
|
1778
|
-
gates: [land, runtime]
|
|
1779
|
-
failureCodes: ["RT_RETIRED_CLI_CALL"]
|
|
1780
|
-
enforcers:
|
|
1781
|
-
- {kind: runtime, id: retired-cli, at: scripts/checks/check-retired-cli.mjs}
|
|
1782
1756
|
- id: R198
|
|
1783
1757
|
code: "RT_CLI_CATALOG_DRIFT"
|
|
1784
1758
|
law: "Every generated output of the unified CLI catalog (packages/cli/src/catalog.generated.mjs, docs/cli.md, packages/cli/completions/*) equals what scripts/cli/gen-catalog.mjs writes from modules/cli/commands."
|
|
@@ -1799,7 +1773,7 @@ rules:
|
|
|
1799
1773
|
- {kind: runtime, id: cli-parity, at: scripts/checks/check-cli-parity.mjs}
|
|
1800
1774
|
- id: R200
|
|
1801
1775
|
code: "RT_CLI_APP_ONLY_TEMPLATES"
|
|
1802
|
-
law: "Managed app templates and their generated runtime copies invoke product actions only through `starci app <verb>`;
|
|
1776
|
+
law: "Managed app templates and their generated runtime copies invoke product actions only through `starci app <verb>`; any other starci group is forbidden there."
|
|
1803
1777
|
scope: runtime
|
|
1804
1778
|
kinds: [check]
|
|
1805
1779
|
gates: [land, runtime]
|
|
@@ -1858,15 +1832,6 @@ rules:
|
|
|
1858
1832
|
failureCodes: ["RT_EXAMPLE_COUPLING", "RT_PRODUCT_NAME_IN_SOURCE", "RT_HOST_PATH_IN_TEMPLATE"]
|
|
1859
1833
|
enforcers:
|
|
1860
1834
|
- {kind: runtime, id: example-coupling, at: scripts/checks/check-example-coupling.mjs}
|
|
1861
|
-
- id: R207
|
|
1862
|
-
code: "RT_RETIRED_NAME_LIVE"
|
|
1863
|
-
law: "A name the retired registry declares dead never appears in a live tracked file — not in prose, a comment, a string literal or a path: every retired[].path and moved[].from of modules/kernel/retired-paths.yaml, and every retiredNames[] naming that is not a path (a deleted app, a layer naming, a verb prefix). History (a changelog, a benchmark finding, a .starciwork record), the registry itself and the files of the check may name what was deleted; a slot manifest's forbids value or forbidden-presence tombstone declares a refusal, not a use."
|
|
1864
|
-
scope: runtime
|
|
1865
|
-
kinds: [check]
|
|
1866
|
-
gates: [land, runtime]
|
|
1867
|
-
failureCodes: ["RT_RETIRED_NAME_LIVE"]
|
|
1868
|
-
enforcers:
|
|
1869
|
-
- {kind: runtime, id: retired-names, at: scripts/checks/check-retired-names.mjs}
|
|
1870
1835
|
- id: R208
|
|
1871
1836
|
code: "RT_PORT_RESTATED"
|
|
1872
1837
|
law: "Every port literal of the runtime is spelled once, by its owner, and every other file references the owner: the harness UI ports live in modules/models/runtimes.yaml statusApp.port/devPort read through ui/ports.mjs, the local SonarQube host in scripts/gates/sonar-local.mjs DEFAULT_HOST. No in-scope file that is not the owner spells an owned literal in a port position (a scheme://host:NNNN or host:NNNN authority, a *port*/PORT assignment, a listen(NNNN) call), and an unowned literal in a port position of two or more runtime source files gets one owning exported constant the rest reference. Specs, tests, node_modules, the generated runtime copies, changelogs, .starciwork and the product stack declarations (.starcistacks, starcistacks-services - a product owns the ports of the services it runs) are out of scope."
|
|
@@ -1979,7 +1944,7 @@ rules:
|
|
|
1979
1944
|
- {kind: hfs, id: lite-secret-custody, at: scripts/hfs/rules/supabase-secrets.mjs}
|
|
1980
1945
|
- id: R221
|
|
1981
1946
|
code: "CI_TRIGGERS_RELEASE_ONLY"
|
|
1982
|
-
law: "The only workflow triggers are a push of release tags
|
|
1947
|
+
law: "The only workflow triggers are a push of release tags, a person dispatching it and, in the runtime's own workflows (`.github/workflows/` at the root), a push to `main`: every tracked workflow declares an `on` that holds only `push` filtered to `tags: ['v*']` (and, in the runtime's own workflows, `branches: [main]`; no paths or ignore filter beside them) and `workflow_dispatch`. The examples and the hfs app templates (so every scaffolded app inherits it) take the tag and the dispatch only: an app's CI runs once per release, on its tag. A push to any other branch, a pull_request, a schedule, a workflow_call or any other event refuses the workflow."
|
|
1983
1948
|
scope: runtime
|
|
1984
1949
|
kinds: [check]
|
|
1985
1950
|
gates: [land, runtime]
|
|
@@ -2015,7 +1980,7 @@ rules:
|
|
|
2015
1980
|
- {kind: runtime, id: protected-zone, at: scripts/hfs/runtime-rules/rights-policy.mjs}
|
|
2016
1981
|
- id: R225
|
|
2017
1982
|
code: "RT_HOOK_SHAPE"
|
|
2018
|
-
law: "The app hook templates keep the gate model: pre-commit is L0 only (no typecheck, no test run); pre-push checks the release gate (the starci-release L4 record, refs/backup/, v[0-9] tags) and runs no npm test, jest, typecheck or lint."
|
|
1983
|
+
law: "The app hook templates keep the gate model: pre-commit is L0 only (no typecheck, no test run); pre-push checks the release gate (the starci-release L4 record, refs/backup/, v[0-9] tags; the runtime's own hook is scripts/guards/release-push-gate.mjs, which judges main and v* tags against the release definition and lets every other ref through) and runs no npm test, jest, typecheck or lint."
|
|
2019
1984
|
scope: runtime
|
|
2020
1985
|
kinds: [check]
|
|
2021
1986
|
gates: [land, runtime]
|
|
@@ -2024,7 +1989,7 @@ rules:
|
|
|
2024
1989
|
- {kind: runtime, id: hook-shape, at: scripts/hfs/runtime-rules/hook-shape.mjs}
|
|
2025
1990
|
- id: R226
|
|
2026
1991
|
code: "RT_SPEC_OVER_BUDGET"
|
|
2027
|
-
law: "Every recorded spec duration should stay within the shared per-file land budget in modules/kernel/spec-durations.yaml:
|
|
1992
|
+
law: "Every recorded spec duration should stay within the shared per-file land budget in modules/kernel/spec-durations.yaml: an over-budget spec is an advisory info finding ranked by its excess (it does not fail the check stage), and every recorded spec path must still exist. An over-budget spec is repaired without removing tests or assertions: share a per-process fixture, remove per-test install or boot, inject the clock and never lengthen a timeout; stale rows are refreshed from the current land record."
|
|
2028
1993
|
scope: runtime
|
|
2029
1994
|
kinds: [check]
|
|
2030
1995
|
gates: [land, runtime]
|
|
@@ -2040,20 +2005,151 @@ rules:
|
|
|
2040
2005
|
failureCodes: ["RT_SYNTAX_INVALID"]
|
|
2041
2006
|
enforcers:
|
|
2042
2007
|
- {kind: runtime, id: syntax, at: scripts/hfs/runtime-rules/syntax.mjs}
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2008
|
+
- id: R228
|
|
2009
|
+
code: "RT_IDENTIFIER_UNDECLARED"
|
|
2010
|
+
law: "Every identifier a runtime ES module (scripts/, engine/ without the vendored yaml bundle, ui/api/, packages/cli/bin and src) reads or writes resolves, by scope analysis over its TypeScript syntax tree, to a declaration, import, parameter or Node global of an enclosing scope. A re-export (export {x} from './y.mjs') binds x in no local scope; require, module, exports and __dirname are not globals of a module; browser globals resolve only inside a function that a module hands to page.evaluate, here or through an import. A name a refactor left behind is refused before the line that reads it runs."
|
|
2011
|
+
scope: runtime
|
|
2012
|
+
kinds: [check]
|
|
2013
|
+
gates: [land, runtime]
|
|
2014
|
+
failureCodes: ["RT_IDENTIFIER_UNDECLARED"]
|
|
2015
|
+
enforcers:
|
|
2016
|
+
- {kind: runtime, id: undeclared-identifiers, at: scripts/checks/check-undeclared-identifiers.mjs}
|
|
2017
|
+
- id: R229
|
|
2018
|
+
code: "RT_REMOVED_VOCABULARY"
|
|
2019
|
+
law: "No text an agent or the owner reads teaches a spelling the runtime refuses. The refused names - config keys, routing-bias fields, verbs, flags, codes and vocabulary - are the one list modules/kernel/removed-vocabulary.yaml, each with its replacement and the release that removed it; the config and routing-bias refusals read the same list. Every tracked file of skills/, docs/, README.md, CONTEXT.md, CONTRIBUTING.md, modules/ and knowledge/ (Markdown and yaml) is scanned line by line, and a line that spells a listed name is a finding naming file:line, the name and its replacement, (and every tests/**/*.spec.mjs, where a name is allowed only on a line or under a test title that says refuse, reject, removed, throws or unknown) unless the file is the list, CHANGELOG.md, or the line or block carries the [removed-list] marker (a marker alone on a comment line covers the lines up to the next blank one). Removing or renaming a key, flag, verb, field or code adds its entry to the list in the same commit."
|
|
2020
|
+
scope: runtime
|
|
2021
|
+
kinds: [check]
|
|
2022
|
+
gates: [land, runtime]
|
|
2023
|
+
failureCodes: ["RT_REMOVED_VOCABULARY"]
|
|
2024
|
+
enforcers:
|
|
2025
|
+
- {kind: runtime, id: removed-vocabulary, at: scripts/checks/check-removed-vocabulary.mjs}
|
|
2026
|
+
- id: R230
|
|
2027
|
+
code: "RT_PROSE_COMMAND_UNKNOWN"
|
|
2028
|
+
law: "A command an instruction shows exists as written. Every `starci <group> <verb>` in a backticked span, fenced code line or yaml example item of skills/, docs/, README.md, CONTEXT.md, CONTRIBUTING.md and modules/ names a verb of the CLI catalog (modules/cli/commands) in a catalogued group, and every --flag on it is a flag of that verb or a global flag; the text after a lone -- belongs to another program and a [removed-list] marked line is exempt. The catalog is generated into the valid set, never typed twice."
|
|
2029
|
+
scope: runtime
|
|
2030
|
+
kinds: [check]
|
|
2031
|
+
gates: [land, runtime]
|
|
2032
|
+
failureCodes: ["RT_PROSE_COMMAND_UNKNOWN"]
|
|
2033
|
+
enforcers:
|
|
2034
|
+
- {kind: runtime, id: prose-commands, at: scripts/checks/check-prose-commands.mjs}
|
|
2035
|
+
- id: R231
|
|
2036
|
+
code: "RT_DOCUMENTED_DEFAULT_DRIFT"
|
|
2037
|
+
law: "A default a document states equals the value the code reads. A document states a default by citing its key - default <value> = `<key>` - where the key is a config.yaml path (resolved through the validator's settings reader, so an absent block yields its code default) or <yaml file under modules/>:<dotted path>; the check resolves the key and compares, and a key that resolves to nothing is a finding. docs/, README.md, CONTEXT.md, config.example.yaml and modules/ prose and yaml are scanned; a bare number stated as a default without its key is not judged, so the owner of a default is cited, never retyped."
|
|
2038
|
+
scope: runtime
|
|
2039
|
+
kinds: [check]
|
|
2040
|
+
gates: [land, runtime]
|
|
2041
|
+
failureCodes: ["RT_DOCUMENTED_DEFAULT_DRIFT"]
|
|
2042
|
+
enforcers:
|
|
2043
|
+
- {kind: runtime, id: documented-defaults, at: scripts/checks/check-documented-defaults.mjs}
|
|
2044
|
+
- id: R232
|
|
2045
|
+
code: "RT_ROLES_CONTRACT_DRIFT"
|
|
2046
|
+
law: "A role is described once. modules/kernel/roles.yaml holds, per role, its scope, what it does, what it must clean up, what it never does, whom it reports to and who oversees it; a Markdown surface of the role carries the block generated from it, a yaml or script surface cites modules/kernel/roles.yaml#<role>, and no surface teaches a spelling the role's contradicts list names."
|
|
2047
|
+
scope: runtime
|
|
2048
|
+
kinds: [check]
|
|
2049
|
+
gates: [land, runtime]
|
|
2050
|
+
failureCodes: ["RT_ROLES_CONTRACT_DRIFT"]
|
|
2051
|
+
enforcers:
|
|
2052
|
+
- {kind: runtime, id: roles-contract, at: scripts/checks/check-roles-contract.mjs}
|
|
2053
|
+
- id: R233
|
|
2054
|
+
code: "RT_EDGE_CASE_REGISTRY"
|
|
2055
|
+
law: "Every edge case met is a declared entry of modules/reconciler/edge-cases.yaml with its family, its real occurrence and a status of covered or open; a covered entry names the rule that handles it and the spec that reproduces it, and both exist, and an open entry says why."
|
|
2056
|
+
scope: runtime
|
|
2057
|
+
kinds: [check]
|
|
2058
|
+
gates: [land, runtime]
|
|
2059
|
+
failureCodes: ["RT_EDGE_CASE_REGISTRY"]
|
|
2060
|
+
enforcers:
|
|
2061
|
+
- {kind: runtime, id: edge-case-registry, at: scripts/checks/check-edge-case-registry.mjs}
|
|
2062
|
+
- id: R234
|
|
2063
|
+
code: "RT_SECRET_TRACKED"
|
|
2064
|
+
law: "The runtime repository is public and tracks no secret material, not even ciphertext: no tracked *.enc member and no tracked secret.env or .env file, except the paths ruleParams.runtime.heldSecrets names with their reason (a list that only shrinks). Every secret the host needs lives in the one untracked .claude/secret.env, read only through engine/secrets.mjs, its names listed in the tracked secret.env.example; a product repository keeps its own custody in its own repository. Two Sonar servers, no overlap: SonarCloud serves everything that lives in the runtime repository (the runtime and the example apps, token SONAR_TOKEN), the self-hosted ext/sonar serves product repositories only."
|
|
2065
|
+
scope: runtime
|
|
2066
|
+
kinds: [check]
|
|
2067
|
+
gates: [land, runtime]
|
|
2068
|
+
failureCodes: ["RT_SECRET_TRACKED"]
|
|
2069
|
+
enforcers:
|
|
2070
|
+
- {kind: runtime, id: secret-tracked, at: scripts/hfs/runtime-rules/secret-tracked.mjs}
|
|
2071
|
+
- id: R235
|
|
2072
|
+
code: "SCAN_SMELL"
|
|
2073
|
+
law: "Code carries none of the smells SonarCloud measures that a lint rule can decide: no `await` inside a loop (S9382; an endless loop is spared), no `charCodeAt`/`fromCharCode` (S7758), no string that only escapes backslashes (S7780; a Next `matcher` is spared, it is read statically), no nested conditional expression (S3358), no `void` on anything but a call (S3735), no unused import (S1128), no import that is only handed on instead of `export ... from` (S7763), no prop of a component's props type the component never reads (S6767, front end), and, through typescript-eslint with type information, no deprecated API (S1874), no object turned into text (S6551) and no `async` function that never awaits and returns no promise (S7503). The files a unit or an end-to-end spec names are outside the scan, so outside these rules."
|
|
2074
|
+
kinds: [lint, codemod]
|
|
2075
|
+
gates: [pre-commit, pre-push, settle, land, ci]
|
|
2076
|
+
failureCodes: ["SCAN_SMELL"]
|
|
2077
|
+
enforcers:
|
|
2078
|
+
- {kind: eslint-be, id: no-await-in-loop}
|
|
2079
|
+
- {kind: eslint-be, id: prefer-code-point}
|
|
2080
|
+
- {kind: eslint-be, id: prefer-string-raw}
|
|
2081
|
+
- {kind: eslint-be, id: no-nested-conditional}
|
|
2082
|
+
- {kind: eslint-be, id: no-void-operator}
|
|
2083
|
+
- {kind: eslint-be, id: no-unused-import}
|
|
2084
|
+
- {kind: eslint-be, id: prefer-export-from}
|
|
2085
|
+
- {kind: eslint-fe, id: no-await-in-loop}
|
|
2086
|
+
- {kind: eslint-fe, id: prefer-code-point}
|
|
2087
|
+
- {kind: eslint-fe, id: prefer-string-raw}
|
|
2088
|
+
- {kind: eslint-fe, id: no-nested-conditional}
|
|
2089
|
+
- {kind: eslint-fe, id: no-void-operator}
|
|
2090
|
+
- {kind: eslint-fe, id: no-unused-import}
|
|
2091
|
+
- {kind: eslint-fe, id: prefer-export-from}
|
|
2092
|
+
- {kind: eslint-fe, id: no-unused-prop-types}
|
|
2093
|
+
- id: R236
|
|
2094
|
+
code: "RT_GATE_LOOSENING"
|
|
2095
|
+
law: "A change that loosens a gate or check is owner-class: a check removed from a gate file's list, a floor lowered or a ceiling raised there, an allowlist entry added, a spec deleted or weakened or skipped together with product code (modules/kernel/gate-loosening.yaml). It lands, and passes the check, only when the tree it is judged against already holds the owner's approval of exactly that loosening, an owner-rulings entry gate-loosening-<fingerprint>."
|
|
2096
|
+
scope: runtime
|
|
2097
|
+
kinds: [check]
|
|
2098
|
+
gates: [land, runtime]
|
|
2099
|
+
failureCodes: ["RT_GATE_LOOSENING"]
|
|
2100
|
+
enforcers:
|
|
2101
|
+
- {kind: runtime, id: gate-loosening, at: scripts/checks/check-gate-loosening.mjs}
|
|
2102
|
+
- id: R237
|
|
2103
|
+
code: "RT_REVISION_SCOPE"
|
|
2104
|
+
law: "What a change of the runtime tree asks of each role is declared once, in modules/kernel/revision-scope.yaml: every tracked path matches a row, the engine-loaded files are derived from the import graph of the engine's entries and never hand-listed, the files each seat's launch prompt is built from are the files its generator reads, and a wording-only declaration holds only for an edit that keeps the file's structure."
|
|
2105
|
+
scope: runtime
|
|
2106
|
+
kinds: [check]
|
|
2107
|
+
gates: [land, runtime]
|
|
2108
|
+
failureCodes: ["RT_REVISION_SCOPE"]
|
|
2109
|
+
enforcers:
|
|
2110
|
+
- {kind: runtime, id: revision-scope, at: scripts/checks/check-revision-scope.mjs}
|
|
2111
|
+
- id: R238
|
|
2112
|
+
code: "RT_EVENT_SPILL_BYPASS"
|
|
2113
|
+
law: "A ledger event payload that grows with its input (a file list, a manifest, a digest, a report, a critique, a menu) goes to the blob store through the one path of appendEvent: engine/db/event-compact.mjs eventPayloadRecord keeps a bounded inline view of at most 16 KiB and the whole payload behind events.payload_sha, and readers resolve it through engine/db/event-payload.mjs. No runtime source file other than engine/db/ledger.mjs and engine/db/event-compact.mjs passes a payloadSha to a write, catches the payload-too-large refusal to spill by hand, or inserts into the events table directly. Specs, tests, node_modules, the generated runtime copies and packages are out of scope."
|
|
2114
|
+
scope: runtime
|
|
2115
|
+
kinds: [check]
|
|
2116
|
+
gates: [land, runtime]
|
|
2117
|
+
failureCodes: ["RT_EVENT_SPILL_BYPASS"]
|
|
2118
|
+
enforcers:
|
|
2119
|
+
- {kind: runtime, id: event-spill, at: scripts/checks/check-event-spill.mjs}
|
|
2120
|
+
- id: R239
|
|
2121
|
+
code: "RT_STORAGE_UNDECLARED"
|
|
2122
|
+
law: "Content lives as a file in the content-addressed blob store under the runtime state dir (<runtime root>/.runtime/artifacts, engine/db/blob.mjs) and a database row holds only the reference (the sha) plus the small scalars it is queried by (owner ruling 2026-10-09). Always content, whatever its size: a report, a critique or verdict, a prompt or dispatch contract, a read manifest, a list of files, a log or command output, a menu or digest snapshot, an image or render, a diff. What may stay inline: ids, states, timestamps, counts, short codes and one-line reasons up to 256 bytes; any other free text up to 1024 bytes; a payload of any size is class spill (appendEvent and supEvent keep the whole payload behind the sha and a small inline form). Every text column of the product ledger and of machine.sqlite is declared in modules/schemas/storage-convention.yaml with its class (scalar, bounded, reference, spill, migrate), so a new table or column cannot appear without one; a column that holds content inline today is class migrate with its measured size, and the migration to a reference is a proposed runtime step, idempotent and journalled, never a hand rewrite of a live store."
|
|
2123
|
+
scope: runtime
|
|
2124
|
+
kinds: [check]
|
|
2125
|
+
gates: [land, runtime]
|
|
2126
|
+
failureCodes: ["RT_STORAGE_UNDECLARED"]
|
|
2127
|
+
enforcers:
|
|
2128
|
+
- {kind: runtime, id: storage-convention, at: scripts/checks/check-storage-convention.mjs}
|
|
2129
|
+
- id: R240
|
|
2130
|
+
code: "RT_ROOT_ADHOC"
|
|
2131
|
+
law: "Which directory a component reads or writes is decided in one module, scripts/lib/roots.mjs: the runtime tree, its state directory, the temp root, the invocation directory, the Work directory name, the order in which two trees that can hold the same record are tried (the tree the work happens in first, then the product's main checkout: workDirHolding, workRecordPath) and the order a tool is looked up (the tree the work happens in, then the project, the runtime's own install last: toolSearchDirs). A runtime source file does not resolve a root by itself: it neither spells process.cwd() nor the Work directory name as a literal outside that module more often than the tree the check landed on held it: a file keeps the count it had and only loses it as it is converted, and a new file starts at none. Four times in two days a component read or wrote the wrong tree because it resolved the directory itself."
|
|
2132
|
+
scope: runtime
|
|
2133
|
+
kinds: [check]
|
|
2134
|
+
gates: [land, runtime]
|
|
2135
|
+
failureCodes: ["RT_ROOT_ADHOC"]
|
|
2136
|
+
enforcers:
|
|
2137
|
+
- {kind: runtime, id: root-adhoc, at: scripts/checks/check-root-adhoc.mjs}
|
|
2138
|
+
- id: R241
|
|
2139
|
+
code: "RT_HELD_UNSURFACED"
|
|
2140
|
+
law: "A failure code the catalogue gives to the Supervisor or the owner as a runtime fault is a state that role must be able to learn of; a stop that nobody is told of stands for hours until someone reads source. Each such entry of modules/kernel/failure-codes.yaml names the house mechanism that reaches its owner (surfacedBy: decision-item, sla-clock, incident, digest, notifier, caller or open) with surfacedAs (the item kind, clock code, incident kind, digest problem line or urgent class) and surfacedAt (the file that does it), and the check proves the mechanism exists and is wired in that file: the kind is a Decision Item kind and the file opens items naming it, the code is an SLA code with an owner and the file sets its clock, the problem line is a departure of the operating standard, a caller is a verb the owning role ran itself and never a reconciler file (a loop is no owner). An entry nothing reaches is surfacedBy open and names an open edge-case registry entry with the exact reason; it is never exempted silently."
|
|
2141
|
+
scope: runtime
|
|
2142
|
+
kinds: [check]
|
|
2143
|
+
gates: [land, runtime]
|
|
2144
|
+
failureCodes: ["RT_HELD_UNSURFACED"]
|
|
2145
|
+
enforcers:
|
|
2146
|
+
- {kind: runtime, id: held-surfacing, at: scripts/checks/check-held-surfacing.mjs}
|
|
2147
|
+
- id: R242
|
|
2148
|
+
code: "RT_OP_JUDGE_UNDECLARED"
|
|
2149
|
+
law: "Every op declares who judges its product (principle P2: the maker never judges its own work). The contract of each op (modules/ops/ops/<op>.yaml) carries the required field `judge`, a list of at least one entry, each with a one-line why: machine (independent measures the runtime owns or re-runs at settle, named from modules/kernel/op-judges.yaml, and the contract proofs they rely on; a test file the same attempt wrote is never one by itself), critic (the independent Critic and its rubric, declared exactly for the op kinds that modules/kernel/critic.yaml coverage marks covered), owner (only where modules/kernel/op-judges.yaml ownerGates holds an owner gate) and next-leg (a registered reviewing leg whose contract is to review this product). Nobody cannot be declared. The registry and the contracts are checked against the runtime's own tables (op-gate and sonar-gate enforcedOps, opProofs, proof media, the Critic coverage), so the declarations and what the runtime does cannot drift apart. Surfaced in the full status text of a leg and in the op prompt."
|
|
2150
|
+
scope: runtime
|
|
2151
|
+
kinds: [check]
|
|
2152
|
+
gates: [land, runtime]
|
|
2153
|
+
failureCodes: ["RT_OP_JUDGE_UNDECLARED"]
|
|
2154
|
+
enforcers:
|
|
2155
|
+
- {kind: runtime, id: op-judge, at: scripts/checks/check-op-judge.mjs}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# HFS slot manifest (owner-approved 2026-09-29, decisions 1-10 in the owner decisions record).
|
|
2
2
|
# The one place that says what may exist in a StarCi product repository and where. A product is ONE app repository
|
|
3
3
|
# (hfs 4, manifest major 2): the app root holds the one package.json, lockfile and node_modules, the hfs.json of kind app,
|
|
4
|
-
# the CI, the hooks and .starciwork (slots of profile `app`); `be/` and `fe/` are its two sides, each laid out as
|
|
4
|
+
# the CI, the hooks and .starciwork (slots of profile `app`); `be/` and `fe/` are its two sides, each laid out as a
|
|
5
5
|
# standalone repository root (slots of profile be or fe, paths relative to the side folder). Every tracked path of every
|
|
6
6
|
# repository must match exactly one slot. Checks, lint factories, the architecture machine, templates and the why
|
|
7
7
|
# catalog READ this file through scripts/hfs/slots.mjs; none of them hardcodes a path.
|
|
@@ -20,9 +20,6 @@ versioning:
|
|
|
20
20
|
change or remove an existing slot, make an optional slot required, promote a rule warn to error, change the
|
|
21
21
|
direction matrix. Needs owner approval and a migration lane per repo. There is no compatibility window: the old
|
|
22
22
|
MAJOR is refused the day the new one lands (owner ruling: no backward compatibility).
|
|
23
|
-
retire: >-
|
|
24
|
-
a slot is never edited in place; it gets retiredIn (a major) and a successor slot id. After that major a
|
|
25
|
-
path matching a retired slot is HFS_SLOT_RETIRED (error).
|
|
26
23
|
pins: knowledge/hfs/canon-pins.yaml carries the exact versions of every @starci/* package and framework this MAJOR supports.
|
|
27
24
|
# 2.1.0 is such a minor: it ADDS the edition mechanism (the `editions` list, the per-slot `editions`, `litePresence`, `lite`
|
|
28
25
|
# overlays and `provider`, and `sides.<side>.reads` of an app-root tree) and the lite slots; an app without `edition` in its
|
|
@@ -83,7 +80,7 @@ versioning:
|
|
|
83
80
|
# requiredInstances), each shaped as the same field in the slot body; every other field is the edition's.
|
|
84
81
|
# Under lite `tests` is none on every slot: a lite app has no test world.
|
|
85
82
|
# provider the hfs.json connections[].provider that enables this opt-in slot (supabase); never an app kind.
|
|
86
|
-
# since
|
|
83
|
+
# since the manifest version that introduced the slot.
|
|
87
84
|
presenceValues: [required, optional, opt-in, forbidden]
|
|
88
85
|
trackedValues: [tracked, ignored, external]
|
|
89
86
|
testValues: [unit-beside, e2e, none]
|
|
@@ -177,7 +174,7 @@ ruleParams:
|
|
|
177
174
|
http: [platform/http]
|
|
178
175
|
https: [platform/http]
|
|
179
176
|
cache-manager: [integrations/cache, integrations/redis]
|
|
180
|
-
"@nestjs/cache-manager": [integrations/cache, integrations/redis] # the CACHE_MANAGER token: caching goes through the cache capability
|
|
177
|
+
"@nestjs/cache-manager": [integrations/cache, integrations/redis] # the CACHE_MANAGER token: caching goes through the cache capability
|
|
181
178
|
redis: [integrations/cache, integrations/redis]
|
|
182
179
|
ioredis: [integrations/cache, integrations/redis]
|
|
183
180
|
bullmq: [platform/queue] # pattern queue: BullMQ jobs and job schedulers (the relay of the outbox into queues)
|
|
@@ -389,7 +386,7 @@ slots:
|
|
|
389
386
|
rules: [HFS_MANAGED_FILE_DRIFT, HFS_TOOL_CONFIG_LOCAL]
|
|
390
387
|
- id: app.tool-config-optional
|
|
391
388
|
profiles: [app]
|
|
392
|
-
path: .npmrc
|
|
389
|
+
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
|
|
393
390
|
presence: optional
|
|
394
391
|
tracked: tracked
|
|
395
392
|
tier: none
|
|
@@ -530,12 +527,12 @@ slots:
|
|
|
530
527
|
"features/<feature>/<family>/<name>/index.yaml", "features/<feature>/br/<rule>/ac/<name>/index.yaml",
|
|
531
528
|
"features/<feature>/ui/<name>/assets/<approved-file>", "features/<feature>/uat/<name>/{index,accounts,fixtures}.yaml",
|
|
532
529
|
"features/<feature>/uat/<name>/{seed,cleanup}.sql", "_resources/{identities,environments,fixtures}/<slug>/resource.yaml"]
|
|
533
|
-
forbids: [evidence/, logs/, reports/, captures/, runs/, "kernel-*/", "*.sqlite*", _derived
|
|
530
|
+
forbids: [evidence/, logs/, reports/, captures/, runs/, "kernel-*/", "*.sqlite*", _derived/]
|
|
534
531
|
# Lite keeps the product catalog, feature overviews, grammar and brand drawing records; UAT records belong to the forbidden slot below.
|
|
535
532
|
lite:
|
|
536
533
|
requires: [.gitignore, workspace.yaml, index.yaml]
|
|
537
534
|
allows: [workspace.yaml, index.yaml, "brand/**", shell/index.yaml, "features/<feature>/index.yaml", "features/<feature>/ui/<name>/index.yaml", "features/<feature>/ui/<name>/assets/<approved-file>"]
|
|
538
|
-
rules: [HFS_AGENT_DATA_TRACKED,
|
|
535
|
+
rules: [HFS_AGENT_DATA_TRACKED, HFS_IDENTITY_CUSTODY]
|
|
539
536
|
- id: app.starciwork.uat
|
|
540
537
|
profiles: [app]
|
|
541
538
|
# The UAT records of the full edition: a lite app has no UAT (no tests, no record of one), so the trees are forbidden paths
|
|
@@ -699,7 +696,7 @@ slots:
|
|
|
699
696
|
goesTo: "a be feature: webhooks/<provider> for a third-party callback, api for an endpoint, a cli command on a schedule (pg_cron / pg_net)"
|
|
700
697
|
since: 2.1.0
|
|
701
698
|
|
|
702
|
-
# ----- side root (both sides): what
|
|
699
|
+
# ----- side root (both sides): what a standalone repository root holds, less the app-root files ----------
|
|
703
700
|
- id: repo.side-root-forbidden
|
|
704
701
|
profiles: [be, fe]
|
|
705
702
|
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/}"
|
|
@@ -1640,7 +1637,7 @@ slots:
|
|
|
1640
1637
|
coverage: none
|
|
1641
1638
|
why: provider sandbox contracts, skipped without sandbox config; run only by test:contract, never part of test or test:e2e
|
|
1642
1639
|
rules: [BE_TEST_TOPOLOGY]
|
|
1643
|
-
- id: be.tests.e2e-world-
|
|
1640
|
+
- id: be.tests.e2e-world-dir
|
|
1644
1641
|
profiles: [be]
|
|
1645
1642
|
path: "src/tests/e2e/world/"
|
|
1646
1643
|
presence: forbidden
|
|
@@ -1694,15 +1691,15 @@ slots:
|
|
|
1694
1691
|
tier: route
|
|
1695
1692
|
roles: {proxy: proxy.ts, instrumentation: instrumentation.ts, instrumentation-client: instrumentation-client.ts}
|
|
1696
1693
|
tests: none
|
|
1697
|
-
rules: [FE_NEXT_CONVENTIONS] # middleware.ts is refused on Next >= 16 (fe.source-root-
|
|
1698
|
-
- id: fe.source-root-
|
|
1694
|
+
rules: [FE_NEXT_CONVENTIONS] # middleware.ts is refused on Next >= 16 (fe.source-root-middleware)
|
|
1695
|
+
- id: fe.source-root-middleware
|
|
1699
1696
|
profiles: [fe]
|
|
1700
1697
|
path: "apps/<app>/src/{middleware.ts,middleware.js}"
|
|
1701
1698
|
presence: forbidden
|
|
1702
1699
|
tracked: external
|
|
1703
1700
|
tier: none
|
|
1704
1701
|
tests: none
|
|
1705
|
-
goesTo: "apps/<app>/src/proxy.ts: Next >= 16
|
|
1702
|
+
goesTo: "apps/<app>/src/proxy.ts: Next >= 16 reads proxy.ts and refuses middleware.ts"
|
|
1706
1703
|
rules: [FE_NEXT_CONVENTIONS]
|
|
1707
1704
|
- id: fe.route.callback
|
|
1708
1705
|
profiles: [fe]
|
|
@@ -94,32 +94,15 @@ rules:
|
|
|
94
94
|
`@CommandHandler(X)` or `@QueryHandler(X)` on a class that `extends ICQRSHandler<X, XResult>` and implements
|
|
95
95
|
`protected override async process(message)`; it never overrides `execute`. `ICQRSHandler.execute` is load-bearing: it
|
|
96
96
|
logs `OperationFailed` with the operation name and the error and rethrows, and counts refused outcomes. `<Action>Result`
|
|
97
|
-
is a plain projection type or an `Outcome<Value, Code>` and the return type of `process` is declared. The handler
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
holds ownership, nothing written on a refusal)
|
|
97
|
+
is a plain projection type or an `Outcome<Value, Code>` and the return type of `process` is declared. The handler is
|
|
98
|
+
thin (R203 `BE_FEATURE_THIN`): its `process` maps the message, makes one delegating call to an injected `*.service`
|
|
99
|
+
and returns the result, so it has no unit spec. The service owns the work: it opens the one transaction
|
|
100
|
+
(`this.entityManager.transaction(async (manager) => ...)`), stamps `this.clock.now()` once and passes it down, and it
|
|
101
|
+
holds the decisions (persisted state, the `where` that holds ownership, nothing written on a refusal), which its
|
|
102
|
+
`<name>.service.spec.ts` asserts under the per-file coverage law of BE-TEST-13.
|
|
102
103
|
rationale: |-
|
|
103
104
|
A single template gives every operation the same failure log and metric, and the service spec pins the decision the
|
|
104
105
|
handler delegates.
|
|
105
|
-
cases:
|
|
106
|
-
- id: case-1
|
|
107
|
-
when: A handler
|
|
108
|
-
write: |-
|
|
109
|
-
@CommandHandler(StartCheckoutCommand)
|
|
110
|
-
/** Opens a checkout, or refuses with a purchase code. */
|
|
111
|
-
export class StartCheckoutHandler extends ICQRSHandler<StartCheckoutCommand, StartCheckoutResult> {
|
|
112
|
-
constructor(
|
|
113
|
-
@InjectPrimaryEntityManager() private readonly entityManager: EntityManager,
|
|
114
|
-
@InjectClock() private readonly clock: Clock,
|
|
115
|
-
private readonly purchaseService: PurchaseService,
|
|
116
|
-
) { super() }
|
|
117
|
-
|
|
118
|
-
protected override async process(command: StartCheckoutCommand): Promise<StartCheckoutResult> {
|
|
119
|
-
const at = this.clock.now()
|
|
120
|
-
return this.entityManager.transaction(async (manager) => this.purchaseService.open({ manager, learnerId: command.params.principal.id, offerId: command.params.request.offerId, at }))
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
106
|
verification:
|
|
124
107
|
automated:
|
|
125
108
|
- BE_CQRS_SHAPE
|
|
@@ -129,7 +112,7 @@ rules:
|
|
|
129
112
|
- BE_FEATURE_THIN
|
|
130
113
|
manual:
|
|
131
114
|
- Confirm the service spec the handler calls asserts refusal paths and ownership, not only that a mock was called.
|
|
132
|
-
relatedExamples: []
|
|
115
|
+
relatedExamples: [api]
|
|
133
116
|
- id: BE-CQRS-4
|
|
134
117
|
title: The bus is the only way into a handler; nothing forwards
|
|
135
118
|
kind: mandatory
|
|
@@ -87,22 +87,13 @@ rules:
|
|
|
87
87
|
command and its spec are found from the name alone.
|
|
88
88
|
cases:
|
|
89
89
|
- id: case-1
|
|
90
|
-
when: The migrate group
|
|
91
|
-
write: |-
|
|
92
|
-
// be/src/features/cli/migrate/migrate.cli.ts
|
|
93
|
-
@Command({ name: "migrate", subCommands: [RunCli], description: "Schema migrations of every connection" })
|
|
94
|
-
/** The migrate group; without a sub-command it shows its help. */
|
|
95
|
-
export class MigrateCli extends CommandRunner {
|
|
96
|
-
async run(): Promise<void> { this.command.help() }
|
|
97
|
-
}
|
|
98
|
-
- id: case-2
|
|
99
90
|
when: A `@SubCommand` declared in src/modules/platform/database/reindex.cli.ts or in features/api/
|
|
100
91
|
write: 'Refused (BE_CLI_COMMAND_SHAPE, BE_CLI_OWNER): move it to src/features/cli/<group>/subs/reindex.cli.ts and register it in the group module.'
|
|
101
92
|
verification:
|
|
102
93
|
automated:
|
|
103
94
|
- BE_CLI_COMMAND_SHAPE
|
|
104
95
|
manual: []
|
|
105
|
-
relatedExamples: []
|
|
96
|
+
relatedExamples: [cli]
|
|
106
97
|
- id: BE-CLI-3
|
|
107
98
|
title: A command is an action runner with its own unit spec
|
|
108
99
|
kind: mandatory
|
|
@@ -134,7 +125,7 @@ rules:
|
|
|
134
125
|
- BE_FEATURE_THIN
|
|
135
126
|
manual:
|
|
136
127
|
- Confirm a command that writes is safe to re-run or refuses a second run explicitly.
|
|
137
|
-
relatedExamples: []
|
|
128
|
+
relatedExamples: [cli]
|
|
138
129
|
- id: BE-CLI-4
|
|
139
130
|
title: Only the cli parses the command line
|
|
140
131
|
kind: mandatory
|
|
@@ -180,7 +171,7 @@ rules:
|
|
|
180
171
|
- BE_CLI_REQUIRED
|
|
181
172
|
manual:
|
|
182
173
|
- Confirm the deploy runs `cli migrate run` to completion before the api and worker apps start.
|
|
183
|
-
relatedExamples: []
|
|
174
|
+
relatedExamples: [cli]
|
|
184
175
|
files:
|
|
185
176
|
- path: apps/cli/Dockerfile
|
|
186
177
|
slot: repo.app-image
|
|
@@ -78,7 +78,7 @@ rules:
|
|
|
78
78
|
- BE_CONTRACT_UNGUARDED
|
|
79
79
|
- BE_REALTIME_SHAPE
|
|
80
80
|
manual:
|
|
81
|
-
- Confirm
|
|
81
|
+
- Confirm no query or mutation lives in a subscription door; it lives in an api feature.
|
|
82
82
|
relatedExamples: [realtime]
|
|
83
83
|
- id: BE-REALTIME-2
|
|
84
84
|
title: A realtime door reads and pushes, it never writes
|
|
@@ -95,19 +95,6 @@ rules:
|
|
|
95
95
|
rationale: |-
|
|
96
96
|
A door that can only touch the hub cannot change business state, so the biggest risk of a long-lived unauthenticated-looking
|
|
97
97
|
connection, a write through it, is removed by type.
|
|
98
|
-
cases:
|
|
99
|
-
- id: case-1
|
|
100
|
-
when: A subscription that pushes order status changes
|
|
101
|
-
write: |-
|
|
102
|
-
@Resolver()
|
|
103
|
-
export class OrderStatusSubscription {
|
|
104
|
-
constructor(private readonly hub: RealtimeHub) {}
|
|
105
|
-
|
|
106
|
-
@Subscription(() => OrderStatusChangedType)
|
|
107
|
-
orderStatusChanged(@CurrentPrincipal() principal: Principal, @Args("orderId") orderId: string): AsyncIterable<OrderStatusChangedType> {
|
|
108
|
-
return this.hub.subscribe(orderStatusTopic(principal.id, orderId))
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
98
|
verification:
|
|
112
99
|
automated:
|
|
113
100
|
- BE_REALTIME_WRITES
|
|
@@ -92,25 +92,6 @@ rules:
|
|
|
92
92
|
that skips it, and a door with one call and no decision has nothing left to test except the proof and the hand-over.
|
|
93
93
|
cases:
|
|
94
94
|
- id: case-1
|
|
95
|
-
when: The door of a payment gateway
|
|
96
|
-
write: |-
|
|
97
|
-
@Controller("webhooks/payment-gateway")
|
|
98
|
-
export class PaymentGatewayWebhook {
|
|
99
|
-
constructor(
|
|
100
|
-
@InjectWebhookSignature() private readonly signature: WebhookSignatureService,
|
|
101
|
-
private readonly payments: PaymentService,
|
|
102
|
-
) {}
|
|
103
|
-
|
|
104
|
-
@Post()
|
|
105
|
-
@HttpCode(HttpStatus.NO_CONTENT)
|
|
106
|
-
@Public({ reason: PublicReason.SignedWebhook })
|
|
107
|
-
@RateLimit(RateTier.Strict)
|
|
108
|
-
async receive(@Req() request: RawBodyRequest<Request>, @Headers("x-signature") signature: string | undefined, @Headers("x-timestamp") timestamp: string | undefined, @Body() body: PaymentNotificationRequest): Promise<void> {
|
|
109
|
-
this.signature.verify({ provider: "payment-gateway", rawBody: request.rawBody, signature, timestamp })
|
|
110
|
-
await this.payments.acceptNotification(body)
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
- id: case-2
|
|
114
95
|
when: A delivery whose raw body must be hashed
|
|
115
96
|
write: 'enable the raw body for the app (`NestFactory.create(..., { rawBody: true })` in the api app) and read `request.rawBody`; never hash a re-serialized `body`.'
|
|
116
97
|
verification:
|