@starci/hfs 4.0.10 → 4.1.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.
Files changed (271) hide show
  1. package/CHANGELOG.md +1 -1
  2. package/emit/operations.mjs +1 -1
  3. package/emit/type-schema.mjs +2 -1
  4. package/lint/run.mjs +3 -2
  5. package/package.json +1 -1
  6. package/report/order.mjs +2 -0
  7. package/runtime/config.example.yaml +187 -0
  8. package/runtime/engine/by-code-unit.mjs +12 -0
  9. package/runtime/engine/config.mjs +221 -239
  10. package/runtime/engine/invalid-config.mjs +5 -5
  11. package/runtime/engine/model-config.mjs +90 -0
  12. package/runtime/engine/orca-config.mjs +3 -1
  13. package/runtime/engine/removed-vocabulary.mjs +28 -0
  14. package/runtime/engine/resources-config.mjs +52 -0
  15. package/runtime/engine/runtime-root.mjs +17 -0
  16. package/runtime/engine/secrets.mjs +35 -31
  17. package/runtime/engine/sonar-config.mjs +21 -0
  18. package/runtime/engine/temp-root.mjs +32 -0
  19. package/runtime/knowledge/hfs/canon-pins.yaml +14 -14
  20. package/runtime/knowledge/hfs/peer-integrations.yaml +2 -3
  21. package/runtime/knowledge/hfs/rules.yaml +98 -65
  22. package/runtime/knowledge/hfs/slots.yaml +11 -14
  23. package/runtime/knowledge/patterns/be/api.yaml +7 -24
  24. package/runtime/knowledge/patterns/be/cli.yaml +3 -12
  25. package/runtime/knowledge/patterns/be/realtime.yaml +1 -14
  26. package/runtime/knowledge/patterns/be/webhooks.yaml +0 -19
  27. package/runtime/modules/kernel/failure-codes.yaml +1 -41
  28. package/runtime/modules/kernel/removed-vocabulary.yaml +168 -0
  29. package/runtime/modules/models/registry.yaml +25 -359
  30. package/runtime/modules/models/runtimes.yaml +92 -256
  31. package/runtime/modules/models/tiers.yaml +65 -0
  32. package/runtime/scripts/api/fs/claim-file.mjs +30 -0
  33. package/runtime/scripts/api/fs/ensure-temp-root.mjs +27 -0
  34. package/runtime/scripts/api/fs/forbidden-root.mjs +2 -1
  35. package/runtime/scripts/api/fs/make-temp-dir.mjs +11 -0
  36. package/runtime/scripts/api/fs/safe-remove.mjs +52 -35
  37. package/runtime/scripts/api/git/lib.mjs +34 -1
  38. package/runtime/scripts/api/process/resolve-real-tool.mjs +14 -8
  39. package/runtime/scripts/api/process/run-program.mjs +2 -1
  40. package/runtime/scripts/api/sops/lib.mjs +198 -120
  41. package/runtime/scripts/hfs/allows.mjs +3 -3
  42. package/runtime/scripts/hfs/architecture/ast-walks.mjs +63 -50
  43. package/runtime/scripts/hfs/architecture/automatic-gates.mjs +96 -0
  44. package/runtime/scripts/hfs/architecture/backend.mjs +261 -175
  45. package/runtime/scripts/hfs/architecture/background-unowned.mjs +69 -34
  46. package/runtime/scripts/hfs/architecture/client-reaches-server.mjs +55 -54
  47. package/runtime/scripts/hfs/architecture/clones.mjs +179 -106
  48. package/runtime/scripts/hfs/architecture/config-unread.mjs +5 -22
  49. package/runtime/scripts/hfs/architecture/config.mjs +95 -80
  50. package/runtime/scripts/hfs/architecture/connection-map.mjs +233 -141
  51. package/runtime/scripts/hfs/architecture/constructor-deps.mjs +15 -9
  52. package/runtime/scripts/hfs/architecture/context-coupling.mjs +76 -52
  53. package/runtime/scripts/hfs/architecture/context-map.mjs +215 -141
  54. package/runtime/scripts/hfs/architecture/context-owner.mjs +39 -30
  55. package/runtime/scripts/hfs/architecture/context-platform-tables.mjs +31 -17
  56. package/runtime/scripts/hfs/architecture/context-transaction.mjs +38 -25
  57. package/runtime/scripts/hfs/architecture/contract-fixture-guard.mjs +86 -40
  58. package/runtime/scripts/hfs/architecture/contracts-readonly.mjs +342 -0
  59. package/runtime/scripts/hfs/architecture/contracts.mjs +263 -455
  60. package/runtime/scripts/hfs/architecture/cross-app-duplicate.mjs +43 -34
  61. package/runtime/scripts/hfs/architecture/dead-exports.mjs +201 -154
  62. package/runtime/scripts/hfs/architecture/default-deny.mjs +118 -93
  63. package/runtime/scripts/hfs/architecture/doc-language.mjs +2 -1
  64. package/runtime/scripts/hfs/architecture/entrypoint.mjs +45 -30
  65. package/runtime/scripts/hfs/architecture/error-codes.mjs +19 -12
  66. package/runtime/scripts/hfs/architecture/error-masked.mjs +39 -26
  67. package/runtime/scripts/hfs/architecture/fe-slot-allows.mjs +2 -1
  68. package/runtime/scripts/hfs/architecture/feature-shape.mjs +26 -20
  69. package/runtime/scripts/hfs/architecture/framework-pinned.mjs +1 -1
  70. package/runtime/scripts/hfs/architecture/frontend-grammar.mjs +190 -0
  71. package/runtime/scripts/hfs/architecture/frontend-routing.mjs +186 -0
  72. package/runtime/scripts/hfs/architecture/frontend-world-render.mjs +311 -0
  73. package/runtime/scripts/hfs/architecture/frontend-world.mjs +328 -0
  74. package/runtime/scripts/hfs/architecture/frontend.mjs +109 -829
  75. package/runtime/scripts/hfs/architecture/hfs-graph.mjs +14 -2
  76. package/runtime/scripts/hfs/architecture/hfs.mjs +120 -288
  77. package/runtime/scripts/hfs/architecture/hooks-are-hooks.mjs +46 -26
  78. package/runtime/scripts/hfs/architecture/i18n-keys.mjs +114 -76
  79. package/runtime/scripts/hfs/architecture/index.mjs +158 -102
  80. package/runtime/scripts/hfs/architecture/injection-token-exported.mjs +33 -28
  81. package/runtime/scripts/hfs/architecture/machine-ast.mjs +180 -144
  82. package/runtime/scripts/hfs/architecture/managed-scripts.mjs +8 -2
  83. package/runtime/scripts/hfs/architecture/module-per-transport.mjs +150 -101
  84. package/runtime/scripts/hfs/architecture/next-data-contract.mjs +51 -43
  85. package/runtime/scripts/hfs/architecture/next-data-key.mjs +338 -0
  86. package/runtime/scripts/hfs/architecture/next-data.mjs +179 -427
  87. package/runtime/scripts/hfs/architecture/owners.mjs +27 -13
  88. package/runtime/scripts/hfs/architecture/package-shape.mjs +51 -23
  89. package/runtime/scripts/hfs/architecture/presentation.mjs +165 -0
  90. package/runtime/scripts/hfs/architecture/reachability.mjs +127 -61
  91. package/runtime/scripts/hfs/architecture/register-once.mjs +112 -63
  92. package/runtime/scripts/hfs/architecture/registration.mjs +187 -110
  93. package/runtime/scripts/hfs/architecture/required-files.mjs +100 -68
  94. package/runtime/scripts/hfs/architecture/route-files-thin.mjs +83 -58
  95. package/runtime/scripts/hfs/architecture/schema-owner.mjs +182 -108
  96. package/runtime/scripts/hfs/architecture/source-names-shape.mjs +318 -0
  97. package/runtime/scripts/hfs/architecture/source-names.mjs +83 -248
  98. package/runtime/scripts/hfs/architecture/sql-owner.mjs +175 -118
  99. package/runtime/scripts/hfs/architecture/sql-returning.mjs +39 -28
  100. package/runtime/scripts/hfs/architecture/sql-tokens.mjs +302 -172
  101. package/runtime/scripts/hfs/architecture/supabase-ast.mjs +1 -1
  102. package/runtime/scripts/hfs/architecture/supabase-be.mjs +55 -45
  103. package/runtime/scripts/hfs/architecture/supabase-results.mjs +182 -0
  104. package/runtime/scripts/hfs/architecture/supabase-tables.mjs +20 -12
  105. package/runtime/scripts/hfs/architecture/supabase.mjs +191 -263
  106. package/runtime/scripts/hfs/architecture/symbols.mjs +175 -99
  107. package/runtime/scripts/hfs/architecture/test-world-files.mjs +82 -41
  108. package/runtime/scripts/hfs/architecture/tiers.mjs +120 -69
  109. package/runtime/scripts/hfs/architecture/transport-owner.mjs +84 -57
  110. package/runtime/scripts/hfs/architecture/type-context.mjs +282 -0
  111. package/runtime/scripts/hfs/architecture/typescript.mjs +116 -276
  112. package/runtime/scripts/hfs/architecture/unit-spec-providers.mjs +84 -62
  113. package/runtime/scripts/hfs/architecture.mjs +3 -4
  114. package/runtime/scripts/hfs/check.mjs +61 -70
  115. package/runtime/scripts/hfs/coverage-scope.mjs +84 -38
  116. package/runtime/scripts/hfs/declaration-shape.mjs +77 -36
  117. package/runtime/scripts/hfs/declaration-slots.mjs +1 -1
  118. package/runtime/scripts/hfs/edition-slots.mjs +5 -3
  119. package/runtime/scripts/hfs/linear-text.mjs +30 -0
  120. package/runtime/scripts/hfs/manifest-shape.mjs +164 -63
  121. package/runtime/scripts/hfs/path-findings.mjs +58 -38
  122. package/runtime/scripts/hfs/pin-findings.mjs +31 -0
  123. package/runtime/scripts/hfs/repo-identity.mjs +5 -2
  124. package/runtime/scripts/hfs/rule-catalog.mjs +194 -0
  125. package/runtime/scripts/hfs/rule-params-shape.mjs +33 -27
  126. package/runtime/scripts/hfs/rules/cli.mjs +5 -1
  127. package/runtime/scripts/hfs/rules/contract-compat.mjs +40 -22
  128. package/runtime/scripts/hfs/rules/contract.mjs +35 -27
  129. package/runtime/scripts/hfs/rules/database-config.mjs +57 -32
  130. package/runtime/scripts/hfs/rules/database-migrations.mjs +52 -31
  131. package/runtime/scripts/hfs/rules/database-plpgsql.mjs +106 -91
  132. package/runtime/scripts/hfs/rules/database-sql.mjs +254 -173
  133. package/runtime/scripts/hfs/rules/database.mjs +36 -23
  134. package/runtime/scripts/hfs/rules/deps.mjs +64 -32
  135. package/runtime/scripts/hfs/rules/docker.mjs +113 -65
  136. package/runtime/scripts/hfs/rules/edition.mjs +118 -111
  137. package/runtime/scripts/hfs/rules/event-bus.mjs +91 -45
  138. package/runtime/scripts/hfs/rules/fe-contract-documents.mjs +17 -16
  139. package/runtime/scripts/hfs/rules/fe-no-tests.mjs +36 -16
  140. package/runtime/scripts/hfs/rules/frontend-tree.mjs +4 -3
  141. package/runtime/scripts/hfs/rules/integration-specs.mjs +75 -41
  142. package/runtime/scripts/hfs/rules/kinds.mjs +59 -25
  143. package/runtime/scripts/hfs/rules/lint-suppression.mjs +9 -3
  144. package/runtime/scripts/hfs/rules/monorepo.mjs +82 -45
  145. package/runtime/scripts/hfs/rules/peer-integrations.mjs +24 -3
  146. package/runtime/scripts/hfs/rules/pipeline.mjs +58 -9
  147. package/runtime/scripts/hfs/rules/proof-commands.mjs +14 -8
  148. package/runtime/scripts/hfs/rules/repo-local-checks.mjs +11 -6
  149. package/runtime/scripts/hfs/rules/saga.mjs +128 -61
  150. package/runtime/scripts/hfs/rules/secrets.mjs +4 -3
  151. package/runtime/scripts/hfs/rules/services.mjs +34 -16
  152. package/runtime/scripts/hfs/rules/stacks.mjs +21 -14
  153. package/runtime/scripts/hfs/rules/supabase-secrets.mjs +32 -23
  154. package/runtime/scripts/hfs/rules/test-topology.mjs +25 -11
  155. package/runtime/scripts/hfs/secret.mjs +59 -48
  156. package/runtime/scripts/hfs/slot-app-view.mjs +102 -0
  157. package/runtime/scripts/hfs/slot-classify.mjs +65 -0
  158. package/runtime/scripts/hfs/slot-errors.mjs +11 -0
  159. package/runtime/scripts/hfs/slot-imports.mjs +76 -0
  160. package/runtime/scripts/hfs/slot-manifest-shape.mjs +73 -0
  161. package/runtime/scripts/hfs/slot-match.mjs +153 -0
  162. package/runtime/scripts/hfs/slot-path.mjs +8 -0
  163. package/runtime/scripts/hfs/slot-required.mjs +76 -0
  164. package/runtime/scripts/hfs/slot-semantic-problems.mjs +148 -0
  165. package/runtime/scripts/hfs/slot-side-problems.mjs +28 -0
  166. package/runtime/scripts/hfs/slots.mjs +72 -693
  167. package/runtime/scripts/hfs/sql/pg-parse.mjs +5 -2
  168. package/runtime/scripts/hfs/trailing-slashes.mjs +8 -0
  169. package/runtime/scripts/hfs/tree.mjs +19 -14
  170. package/runtime/scripts/hfs/typescript-programs.mjs +6 -4
  171. package/runtime/scripts/hfs/view.mjs +1 -1
  172. package/runtime/scripts/lib/dockerfile.mjs +56 -33
  173. package/runtime/scripts/lib/event-contract.mjs +17 -13
  174. package/runtime/scripts/lib/git.mjs +2 -2
  175. package/runtime/scripts/lib/graphql-contract.mjs +158 -322
  176. package/runtime/scripts/lib/graphql-sdl.mjs +262 -0
  177. package/runtime/scripts/lib/i18n.mjs +3 -2
  178. package/runtime/scripts/lib/in-order.mjs +71 -0
  179. package/runtime/scripts/lib/language.mjs +3 -3
  180. package/runtime/scripts/lib/list.mjs +8 -1
  181. package/runtime/scripts/lib/path-key.mjs +38 -5
  182. package/runtime/scripts/lib/pid-alive.mjs +7 -0
  183. package/runtime/scripts/lib/regex.mjs +1 -1
  184. package/runtime/scripts/lib/same-text.mjs +1 -1
  185. package/runtime/scripts/lib/secret-patterns.mjs +6 -2
  186. package/runtime/scripts/lib/sleep-sync.mjs +2 -2
  187. package/runtime/scripts/lib/sops-envelope.mjs +95 -48
  188. package/runtime/scripts/lib/stack-services.mjs +1 -2
  189. package/runtime/scripts/lib/ts-ast.mjs +9 -0
  190. package/runtime/scripts/lib/walk.mjs +6 -1
  191. package/scaffold/add-cli-lite.mjs +2 -1
  192. package/scaffold/add-table.mjs +2 -2
  193. package/scaffold/add.mjs +3 -2
  194. package/scaffold/app.mjs +4 -2
  195. package/scaffold/edition-gate.mjs +25 -25
  196. package/scaffold/lite-exports.mjs +2 -1
  197. package/scaffold/service.mjs +5 -4
  198. package/sync/hygiene.mjs +15 -9
  199. package/sync/index.mjs +7 -3
  200. package/templates/app/ci-workflows/github/workflows/ci.yml +4 -4
  201. package/templates/app/ci-workflows/github/workflows/e2e.yml +3 -3
  202. package/templates/app/ci-workflows/github/workflows/images.yml +3 -3
  203. package/templates/app/ci-workflows/sonar-steps.yml +2 -2
  204. package/templates/app/ci-workflows-lite/github/workflows/ci.yml +6 -6
  205. package/templates/app/ci-workflows-lite/github/workflows/db-deploy.yml +3 -3
  206. package/templates/app/ci-workflows-lite/github/workflows/images.yml +3 -3
  207. package/templates/app/starciwork.gitignore +1 -1
  208. package/templates/be/image/api/Dockerfile +1 -1
  209. package/templates/be/image/cli/Dockerfile +1 -1
  210. package/templates/be/image/worker/Dockerfile +1 -1
  211. package/templates/be/patterns/cli/group.cli.spec.ts.tpl +1 -1
  212. package/templates/be/patterns/cli/group.cli.ts.tpl +2 -2
  213. package/templates/be/patterns/event-bus/platform/event-runner.service.spec.ts.tpl +13 -0
  214. package/templates/be/patterns/event-bus/platform/event-runner.service.ts.tpl +7 -2
  215. package/templates/be/patterns/event-bus/platform/event.policy.ts.tpl +4 -1
  216. package/templates/be/patterns/event-bus/platform/kafka-event-transport.client.ts.tpl +2 -1
  217. package/templates/be/patterns/outbox/platform/outbox-relay.policy.ts.tpl +24 -9
  218. package/templates/be/patterns/queues/platform/queue-relay.service.ts.tpl +3 -2
  219. package/templates/be/patterns/queues/platform/queue-worker.service.ts.tpl +8 -5
  220. package/templates/be/skeleton/src/features/cli/migrate/migrate.cli.spec.ts +1 -1
  221. package/templates/be/skeleton/src/features/cli/migrate/migrate.cli.ts +2 -2
  222. package/templates/be/skeleton/src/features/cli/seed/seed.cli.spec.ts +1 -1
  223. package/templates/be/skeleton/src/features/cli/seed/seed.cli.ts +2 -2
  224. package/templates/be/skeleton/src/modules/platform/database/migrate-connections.client.ts +3 -2
  225. package/templates/be/skeleton/src/modules/platform/database/seed-connections.client.ts +5 -4
  226. package/templates/be/skeleton/src/modules/platform/primitives/index.ts +1 -0
  227. package/templates/be/skeleton/src/modules/platform/primitives/sequence.policy.spec.ts +47 -0
  228. package/templates/be/skeleton/src/modules/platform/primitives/sequence.policy.ts +16 -0
  229. package/templates/be/skeleton-lite/apps/api/Dockerfile +1 -0
  230. package/templates/be/skeleton-lite/src/modules/platform/primitives/index.ts +2 -0
  231. package/templates/fe/image/next/Dockerfile +2 -1
  232. package/templates/fe/skeleton/apps/app/src/app/[locale]/page.tsx +2 -2
  233. package/templates/fe/skeleton/apps/landing/src/app/[locale]/page.tsx +2 -2
  234. package/templates/fe/skeleton/packages/__project__-i18n/src/index.ts +1 -2
  235. package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/layout.tsx +1 -2
  236. package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/page.tsx +2 -2
  237. package/templates/fe/skeleton-lite/apps/web/src/components/blocks/SignInForm/index.tsx +7 -1
  238. package/templates/fe/skeleton-lite/apps/web/src/modules/db/auth/write-sign-out.ts +1 -1
  239. package/templates/fe/skeleton-lite/apps/web/src/modules/db/validation/validation.mapper.ts +10 -2
  240. package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/request.ts +10 -3
  241. package/upgrade/index.mjs +9 -2
  242. package/runtime/engine/admission.mjs +0 -308
  243. package/runtime/engine/canonical-json.mjs +0 -10
  244. package/runtime/engine/db/blob.mjs +0 -315
  245. package/runtime/engine/db/ledger-paths.mjs +0 -83
  246. package/runtime/engine/db/ledger.mjs +0 -1118
  247. package/runtime/engine/db/machine-connection.mjs +0 -135
  248. package/runtime/engine/db/machine-schema.mjs +0 -84
  249. package/runtime/engine/db/machine.mjs +0 -1367
  250. package/runtime/engine/db/migrations/machine/0001-init.sql +0 -924
  251. package/runtime/engine/db/migrations/runtime/0001-init.sql +0 -1108
  252. package/runtime/engine/db/provider-reservations.mjs +0 -101
  253. package/runtime/engine/digest.mjs +0 -16
  254. package/runtime/engine/refuse.mjs +0 -11
  255. package/runtime/modules/ops/_labels.yaml +0 -53
  256. package/runtime/scripts/api/node/lib.mjs +0 -14
  257. package/runtime/scripts/api/node/spawn-node.mjs +0 -6
  258. package/runtime/scripts/api/process/lib.mjs +0 -111
  259. package/runtime/scripts/api/process/owned-process.mjs +0 -78
  260. package/runtime/scripts/api/process/stop-owned-process.mjs +0 -9
  261. package/runtime/scripts/connectors/lib.mjs +0 -488
  262. package/runtime/scripts/lib/clip.mjs +0 -19
  263. package/runtime/scripts/lib/display-names.mjs +0 -259
  264. package/runtime/scripts/lib/example-refs.mjs +0 -158
  265. package/runtime/scripts/lib/json-schema.mjs +0 -52
  266. package/runtime/scripts/lib/process-identity.mjs +0 -6
  267. package/runtime/scripts/lib/read-yaml.mjs +0 -18
  268. package/runtime/scripts/lib/redact.mjs +0 -161
  269. package/runtime/scripts/lib/sleep.mjs +0 -7
  270. package/runtime/scripts/lib/source-phrases.mjs +0 -34
  271. 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 (the ids run R01 up; an id with no rule is listed under `retired` with its reason, never reused; count the rules, never type the count).
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 scripts/gates/gate.mjs 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>
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-retired, at: scripts/hfs/architecture/hfs.mjs}
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 retired or moved path is cited only by history."
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, or moved through a modules/kernel/retired-paths.yaml moved[] entry marked quiesced: true, landed with the workers stopped."
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/retired.mjs}
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 the ids run from R01 to the last rule with no undeclared gap: an id with no rule is listed under `retired` with its reason (RT_RULE_ID_GAP), 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). A retired id is a history name that only changelogs may still spell; specs and generated copies are not read."
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", "RT_RULE_ID_GAP", "RT_RULE_UNCITED"]
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 the examples or the starter declare, a placeholder stands for any; a ** glob and a topic file of the knowledge are not paths."
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>`; retired hfs and test-stack commands, and any other starci group, are forbidden there."
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 and a person dispatching it: every tracked workflow of the runtime, its examples and the hfs app templates (so every scaffolded app inherits it) declares an `on` that holds only `push` filtered to `tags: ['v*']` (no branches, paths or ignore filter beside it) and `workflow_dispatch`. A branch push, a pull_request, a schedule, a workflow_call or any other event refuses the workflow: CI runs once per release, on its tag, and Codecov and Sonar upload only from that run."
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: for alpha.4 an over-budget spec is an advisory info finding ranked by its excess (it does not fail the check stage; the budget turns blocking in alpha.5), 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."
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,88 @@ rules:
2040
2005
  failureCodes: ["RT_SYNTAX_INVALID"]
2041
2006
  enforcers:
2042
2007
  - {kind: runtime, id: syntax, at: scripts/hfs/runtime-rules/syntax.mjs}
2043
- retired:
2044
- - {id: R66, reason: "FE_E2E_SHAPE retired with eslint-canon-fe 7.0.0: a front end has no tests by standard"}
2045
- - {id: R67, reason: "FE_SPEC_QUALITY retired with eslint-canon-fe 7.0.0: a front end has no tests by standard"}
2046
- - {id: R123, reason: "RT_PENDING_STALE retired with the pending list itself: ruleParams.runtime.pending and its pending-ratchet enforcer are gone; nothing ratchets a tolerated-finding list"}
2047
- - {id: R128, reason: "skipped when the shape block was renumbered past the ids of main (R143 up); never the id of a landed rule"}
2048
- - {id: R129, reason: "skipped when the shape block was renumbered past the ids of main (R143 up); never the id of a landed rule"}
2049
- - {id: R130, reason: "skipped when the shape block was renumbered past the ids of main (R143 up); never the id of a landed rule"}
2050
- - {id: R131, reason: "skipped when the shape block was renumbered past the ids of main (R143 up); never the id of a landed rule"}
2051
- - {id: R132, reason: "skipped when the shape block was renumbered past the ids of main (R143 up); never the id of a landed rule"}
2052
- - {id: R133, reason: "skipped when the shape block was renumbered past the ids of main (R143 up); never the id of a landed rule"}
2053
- - {id: R134, reason: "skipped when the shape block was renumbered past the ids of main (R143 up); never the id of a landed rule"}
2054
- - {id: R135, reason: "skipped when the shape block was renumbered past the ids of main (R143 up); never the id of a landed rule"}
2055
- - {id: R136, reason: "skipped when the shape block was renumbered past the ids of main (R143 up); never the id of a landed rule"}
2056
- - {id: R137, reason: "skipped when the shape block was renumbered past the ids of main (R143 up); never the id of a landed rule"}
2057
- - {id: R138, reason: "skipped when the shape block was renumbered past the ids of main (R143 up); never the id of a landed rule"}
2058
- - {id: R139, reason: "skipped when the shape block was renumbered past the ids of main (R143 up); never the id of a landed rule"}
2059
- - {id: R140, reason: "skipped when the shape block was renumbered past the ids of main (R143 up); never the id of a landed rule"}
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}
@@ -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 the old
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 / retiredIn / successor lifecycle.
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 (retired local rule must-use-cache-service)
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/, "work/node records"]
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, HFS_WORK_NODE_RETIRED, HFS_IDENTITY_CUSTODY]
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 the old standalone repository root held, less the app-root files ----------
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-retired
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-retired)
1698
- - id: fe.source-root-retired
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 renamed middleware to proxy and refuses the old name"
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 opens
98
- the transaction (`this.entityManager.transaction(async (manager) => ...)`), stamps `this.clock.now()` once and passes
99
- it down. The handler is thin (R203 `BE_FEATURE_THIN`): its `process` maps the message, makes one delegating call to an
100
- injected `*.service` and returns the result, so it has no unit spec; the decisions (persisted state, the `where` that
101
- holds ownership, nothing written on a refusal) live in the service and its `<name>.service.spec.ts` asserts them.
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 a query or mutation an old subscription door carried now lives in an api feature.
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:
@@ -1337,7 +1337,7 @@ HFS_APP_LAYOUT_INVALID:
1337
1337
  HFS_ARCH_CONFIG_UNREAD:
1338
1338
  title: "The machine reads `hfs.json`; zero files analysed is red"
1339
1339
  title_vi: "Máy kiến trúc không đọc được hfs.json"
1340
- meaning_vi: "Kiểm tra kiến trúc không đọc được `hfs.json` phân tích 0 tệp, hoặc repo còn một tệp cấu hình mà máy không đọc nữa (`architecture.json`) — đây là đỏ, không phải 'chưa có dữ liệu'."
1340
+ meaning_vi: "Kiểm tra kiến trúc không đọc được `hfs.json` phân tích 0 tệp — đây là đỏ, không phải 'chưa có dữ liệu'."
1341
1341
  causes_vi:
1342
1342
  - "Vi phạm luật R24: Máy kiến trúc tự đọc `hfs.json`; phân tích 0 tệp hay `unavailable` cho một profile đã khai = thất bại."
1343
1343
  nextStep_vi: "Op vừa tạo ra bản này sẽ được chạy lại để sửa theo lời nhắn của bộ kiểm tra; không cần ai can thiệp thêm."
@@ -1642,46 +1642,6 @@ HFS_LITE_SECRET_CUSTODY:
1642
1642
  owner: owner
1643
1643
  kind: check-finding
1644
1644
 
1645
- kernel-caller-stale:
1646
- title: "Kernel incarnation does not own this mutation"
1647
- title_vi: "Phiên Kernel không còn sở hữu thao tác"
1648
- meaning_vi: "Quyền hiện tại đã đổi trước khi ghi dữ liệu hoặc thực hiện tác động."
1649
- causes_vi:
1650
- - "Terminal, Dispatch, thế hệ, lượt khởi động hoặc singleton token đã bị thay thế."
1651
- nextStep_vi: "Giữ nguyên quyền đang tranh chấp; Supervisor kiểm tra phiên hiện tại trước khi thử lại."
1652
- owner: supervisor
1653
- kind: verb-refusal
1654
-
1655
- kernel-caller-unknown:
1656
- title: "Managed caller custody is unavailable"
1657
- title_vi: "Không xác minh được quyền của caller"
1658
- meaning_vi: "Lịch sử hoặc binding của caller không đọc được hoặc mâu thuẫn; không suy ra quyền owner."
1659
- causes_vi:
1660
- - "Kho ledger hoặc machine không đọc được; binding sai, cũ hoặc thuộc workflow khác."
1661
- nextStep_vi: "Khôi phục khả năng đọc và xác minh caller thực tế; không dùng biến môi trường để giả quyền."
1662
- owner: supervisor
1663
- kind: verb-refusal
1664
-
1665
- kernel-caller-actor:
1666
- title: "Actor claim contradicts native caller"
1667
- title_vi: "Actor khai báo mâu thuẫn với caller"
1668
- meaning_vi: "Giá trị --by không khớp với quyền caller mà owner hiện có xác minh."
1669
- causes_vi:
1670
- - "Caller dùng tên Kernel, Op hoặc Supervisor khác với phiên thực tế."
1671
- nextStep_vi: "Bỏ khai báo giả và dùng đúng caller; chuỗi actor không cấp quyền."
1672
- owner: supervisor
1673
- kind: verb-refusal
1674
-
1675
- kernel-read-unverified:
1676
- title: "Current Kernel READ attestation is incomplete"
1677
- title_vi: "READ của Kernel hiện tại chưa đầy đủ"
1678
- meaning_vi: "Manifest READ không chứng minh toàn bộ bytes cần đọc cho phiên Kernel hiện tại."
1679
- causes_vi:
1680
- - "File thiếu, hash sai, bytes dirty, descriptor sai hoặc lượt Kernel đã thay đổi."
1681
- nextStep_vi: "Lấy --plan, đọc đủ từng file và gửi manifest nguyên vẹn với --read-manifest từ Kernel hiện tại."
1682
- owner: supervisor
1683
- kind: verb-refusal
1684
-
1685
1645
  HFS_MANAGED_FILE_DRIFT:
1686
1646
  title: "A managed file equals its template render"
1687
1647
  title_vi: "Tệp do .claude quản lý bị lệch"