@starci/hfs 4.3.0 → 4.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (515) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/db/migrate.mjs +361 -0
  3. package/db/post-cutover.mjs +136 -0
  4. package/emit/db-types.mjs +10 -1
  5. package/lint/run.mjs +1 -1
  6. package/package.json +4 -1
  7. package/release/gate.mjs +298 -0
  8. package/release/index.mjs +103 -0
  9. package/release/push.mjs +110 -0
  10. package/runtime/knowledge/hfs/canon-pins.yaml +11 -11
  11. package/runtime/knowledge/hfs/fe-stack.yaml +22 -1
  12. package/runtime/knowledge/hfs/rules.yaml +3401 -152
  13. package/runtime/knowledge/hfs/slots.yaml +344 -62
  14. package/runtime/knowledge/patterns/be/api.yaml +32 -5
  15. package/runtime/knowledge/patterns/be/cli.yaml +14 -4
  16. package/runtime/knowledge/patterns/be/event-bus.yaml +3 -3
  17. package/runtime/knowledge/patterns/be/jobs.yaml +20 -2
  18. package/runtime/knowledge/patterns/be/projections.yaml +4 -2
  19. package/runtime/knowledge/patterns/be/queues.yaml +11 -3
  20. package/runtime/knowledge/patterns/be/reactors.yaml +20 -1
  21. package/runtime/knowledge/patterns/be/realtime.yaml +25 -3
  22. package/runtime/knowledge/patterns/be/saga.yaml +20 -3
  23. package/runtime/knowledge/patterns/be/webhooks.yaml +23 -4
  24. package/runtime/knowledge/patterns/fe/folder.yaml +1 -0
  25. package/runtime/knowledge/sonar-gate.yaml +1 -1
  26. package/runtime/modules/kernel/failure-codes.yaml +3113 -43
  27. package/runtime/modules/models/runtimes.yaml +1 -1
  28. package/runtime/modules/schemas/application-stacks.schema.yaml +663 -0
  29. package/runtime/packages/cli/src/catalog.generated.mjs +14104 -0
  30. package/runtime/packages/cli/src/validate-args.mjs +213 -0
  31. package/runtime/scripts/api/git/add.mjs +5 -0
  32. package/runtime/scripts/api/git/init.mjs +7 -0
  33. package/runtime/scripts/api/git/lib.mjs +3 -0
  34. package/runtime/scripts/api/git/tag.mjs +5 -0
  35. package/runtime/scripts/api/node/lib.mjs +20 -0
  36. package/runtime/scripts/api/node/run-node.mjs +6 -0
  37. package/runtime/scripts/api/sops/lib.mjs +41 -0
  38. package/runtime/scripts/api/sops/selected-age-recipient.mjs +7 -0
  39. package/runtime/scripts/api/supabase/database-dump.mjs +5 -0
  40. package/runtime/scripts/api/supabase/database-query.mjs +5 -0
  41. package/runtime/scripts/api/supabase/lib.mjs +44 -0
  42. package/runtime/scripts/api/supabase/migration-repair.mjs +5 -0
  43. package/runtime/scripts/hfs/app-wiring.mjs +201 -0
  44. package/runtime/scripts/hfs/architecture/backend-app-files.mjs +1 -8
  45. package/runtime/scripts/hfs/architecture/backend.mjs +2 -7
  46. package/runtime/scripts/hfs/architecture/client-reaches-server.mjs +6 -3
  47. package/runtime/scripts/hfs/architecture/dead-exports-bindings.mjs +149 -0
  48. package/runtime/scripts/hfs/architecture/dead-exports.mjs +9 -41
  49. package/runtime/scripts/hfs/architecture/default-deny.mjs +8 -6
  50. package/runtime/scripts/hfs/architecture/frontend-world.mjs +1 -1
  51. package/runtime/scripts/hfs/architecture/function-spec-providers.mjs +153 -0
  52. package/runtime/scripts/hfs/architecture/hfs.mjs +3 -3
  53. package/runtime/scripts/hfs/architecture/module-per-transport.mjs +28 -13
  54. package/runtime/scripts/hfs/architecture/next-data-inference.mjs +176 -0
  55. package/runtime/scripts/hfs/architecture/next-data-symbols.mjs +26 -0
  56. package/runtime/scripts/hfs/architecture/next-data.mjs +31 -23
  57. package/runtime/scripts/hfs/architecture/reachability.mjs +14 -0
  58. package/runtime/scripts/hfs/architecture/register-once.mjs +2 -2
  59. package/runtime/scripts/hfs/architecture/source-names.mjs +48 -3
  60. package/runtime/scripts/hfs/architecture/supabase-ast.mjs +211 -11
  61. package/runtime/scripts/hfs/architecture/supabase-be.mjs +1 -1
  62. package/runtime/scripts/hfs/architecture/supabase.mjs +59 -21
  63. package/runtime/scripts/hfs/architecture/surface.mjs +2 -1
  64. package/runtime/scripts/hfs/architecture/tiers.mjs +3 -3
  65. package/runtime/scripts/hfs/architecture/unit-spec-providers.mjs +8 -2
  66. package/runtime/scripts/hfs/check-results.mjs +28 -0
  67. package/runtime/scripts/hfs/check.mjs +46 -47
  68. package/runtime/scripts/hfs/coverage-from.mjs +70 -0
  69. package/runtime/scripts/hfs/db-baseline-verb.mjs +276 -0
  70. package/runtime/scripts/hfs/db-plan-verb.mjs +254 -0
  71. package/runtime/scripts/hfs/db-restamp-verb.mjs +60 -0
  72. package/runtime/scripts/hfs/declaration-shape.mjs +39 -3
  73. package/runtime/scripts/hfs/doc-commands.mjs +367 -0
  74. package/runtime/scripts/hfs/edition-slots.mjs +2 -2
  75. package/runtime/scripts/hfs/example-secrets.mjs +158 -0
  76. package/runtime/scripts/hfs/manifest-shape.mjs +67 -7
  77. package/runtime/scripts/hfs/refusal-codes.mjs +6 -0
  78. package/runtime/scripts/hfs/rule-catalog.mjs +156 -11
  79. package/runtime/scripts/hfs/rule-params-shape.mjs +22 -10
  80. package/runtime/scripts/hfs/rules/a15-findings.mjs +88 -0
  81. package/runtime/scripts/hfs/rules/app-env-required.mjs +20 -0
  82. package/runtime/scripts/hfs/rules/app-root.mjs +2 -2
  83. package/runtime/scripts/hfs/rules/assets.mjs +79 -0
  84. package/runtime/scripts/hfs/rules/boot-proof.mjs +16 -0
  85. package/runtime/scripts/hfs/rules/canon-copy-drift.mjs +59 -0
  86. package/runtime/scripts/hfs/rules/contract.mjs +66 -5
  87. package/runtime/scripts/hfs/rules/coverage-below.mjs +230 -0
  88. package/runtime/scripts/hfs/rules/database-access.mjs +343 -0
  89. package/runtime/scripts/hfs/rules/database-baseline.mjs +142 -0
  90. package/runtime/scripts/hfs/rules/database-config.mjs +250 -2
  91. package/runtime/scripts/hfs/rules/database-constants.mjs +9 -0
  92. package/runtime/scripts/hfs/rules/database-migrations.mjs +286 -7
  93. package/runtime/scripts/hfs/rules/database-plpgsql.mjs +119 -9
  94. package/runtime/scripts/hfs/rules/database-queue-sources.mjs +176 -0
  95. package/runtime/scripts/hfs/rules/database-queues.mjs +361 -0
  96. package/runtime/scripts/hfs/rules/database-sql.mjs +91 -9
  97. package/runtime/scripts/hfs/rules/database.mjs +9 -0
  98. package/runtime/scripts/hfs/rules/deploy-scan.mjs +192 -0
  99. package/runtime/scripts/hfs/rules/deploy-workflow.mjs +219 -0
  100. package/runtime/scripts/hfs/rules/doc-fresh.mjs +286 -0
  101. package/runtime/scripts/hfs/rules/docker-health.mjs +249 -0
  102. package/runtime/scripts/hfs/rules/docker.mjs +24 -7
  103. package/runtime/scripts/hfs/rules/edition.mjs +9 -8
  104. package/runtime/scripts/hfs/rules/env-parity.mjs +373 -0
  105. package/runtime/scripts/hfs/rules/env-value-shape.mjs +113 -0
  106. package/runtime/scripts/hfs/rules/event-bus.mjs +3 -2
  107. package/runtime/scripts/hfs/rules/event-tables.mjs +100 -0
  108. package/runtime/scripts/hfs/rules/fe-contract-documents.mjs +115 -15
  109. package/runtime/scripts/hfs/rules/fe-stack.mjs +57 -0
  110. package/runtime/scripts/hfs/rules/feature-files.mjs +28 -0
  111. package/runtime/scripts/hfs/rules/frontend-tree.mjs +54 -35
  112. package/runtime/scripts/hfs/rules/integration-specs.mjs +3 -2
  113. package/runtime/scripts/hfs/rules/lexicon.mjs +62 -0
  114. package/runtime/scripts/hfs/rules/lite-proof-lane.mjs +89 -0
  115. package/runtime/scripts/hfs/rules/monorepo.mjs +14 -1
  116. package/runtime/scripts/hfs/rules/pipeline.mjs +8 -1
  117. package/runtime/scripts/hfs/rules/release-provenance.mjs +211 -0
  118. package/runtime/scripts/hfs/rules/saga-delivery.mjs +66 -0
  119. package/runtime/scripts/hfs/rules/saga.mjs +8 -16
  120. package/runtime/scripts/hfs/rules/script-hygiene.mjs +151 -0
  121. package/runtime/scripts/hfs/rules/secret-custody-only.mjs +65 -0
  122. package/runtime/scripts/hfs/rules/secrets.mjs +13 -0
  123. package/runtime/scripts/hfs/rules/services.mjs +4 -4
  124. package/runtime/scripts/hfs/rules/stack-tooling.mjs +93 -0
  125. package/runtime/scripts/hfs/rules/stacks-deploy.mjs +76 -0
  126. package/runtime/scripts/hfs/rules/stacks-ingress.mjs +87 -0
  127. package/runtime/scripts/hfs/rules/stacks-lite-backing.mjs +111 -0
  128. package/runtime/scripts/hfs/rules/stacks-release.mjs +366 -0
  129. package/runtime/scripts/hfs/rules/stacks-runtime.mjs +41 -1
  130. package/runtime/scripts/hfs/rules/stacks-swarm.mjs +417 -0
  131. package/runtime/scripts/hfs/rules/supabase-secrets.mjs +1 -1
  132. package/runtime/scripts/hfs/rules/supply-chain.mjs +167 -0
  133. package/runtime/scripts/hfs/rules/unused-symbol-flow.mjs +101 -0
  134. package/runtime/scripts/hfs/rules/unused-symbol-ledger.mjs +77 -0
  135. package/runtime/scripts/hfs/rules/unused-symbol-translators.mjs +165 -0
  136. package/runtime/scripts/hfs/rules/unused-symbol-values.mjs +103 -0
  137. package/runtime/scripts/hfs/rules/unused-symbol.mjs +320 -0
  138. package/runtime/scripts/hfs/runtime-rules/source-ast.mjs +185 -0
  139. package/runtime/scripts/hfs/runtime-rules/workflow-source.mjs +19 -0
  140. package/runtime/scripts/hfs/sealed-members.mjs +101 -0
  141. package/runtime/scripts/hfs/secret-bootstrap-crypto.mjs +200 -0
  142. package/runtime/scripts/hfs/secret-edit.mjs +170 -0
  143. package/runtime/scripts/hfs/secret-init.mjs +127 -19
  144. package/runtime/scripts/hfs/secret-sops.mjs +38 -6
  145. package/runtime/scripts/hfs/secret-store.mjs +56 -54
  146. package/runtime/scripts/hfs/secret.mjs +34 -5
  147. package/runtime/scripts/hfs/slot-imports.mjs +18 -5
  148. package/runtime/scripts/hfs/slots.mjs +1 -0
  149. package/runtime/scripts/hfs/stack-config-kit.mjs +34 -0
  150. package/runtime/scripts/hfs/stack-deploy-config.mjs +313 -0
  151. package/runtime/scripts/hfs/stack-ingress-config.mjs +237 -0
  152. package/runtime/scripts/hfs/stacks-gate.mjs +414 -0
  153. package/runtime/scripts/hfs/starcistacks-runtime.mjs +61 -0
  154. package/runtime/scripts/lib/command-tokens.mjs +126 -0
  155. package/runtime/scripts/lib/compose-values.mjs +11 -0
  156. package/runtime/scripts/lib/deployment-findings.mjs +7 -0
  157. package/runtime/scripts/lib/json-clone.mjs +61 -0
  158. package/runtime/scripts/lib/list.mjs +4 -0
  159. package/runtime/scripts/lib/runtime-example.mjs +13 -0
  160. package/runtime/scripts/lib/secret-patterns.mjs +60 -5
  161. package/runtime/scripts/lib/sql-ast.mjs +32 -0
  162. package/runtime/scripts/lib/stack-services.mjs +1 -1
  163. package/runtime/scripts/lib/swarm-stack.mjs +5 -18
  164. package/runtime/scripts/lib/trim-end.mjs +8 -0
  165. package/runtime/scripts/lib/ts-analysis.mjs +5 -2
  166. package/runtime/scripts/lib/ts-ast.mjs +27 -2
  167. package/runtime/scripts/lib/workflow-events.mjs +8 -0
  168. package/scaffold/add-app.mjs +77 -59
  169. package/scaffold/add-cli-lite.mjs +14 -6
  170. package/scaffold/add-compose.mjs +79 -0
  171. package/scaffold/add-database-lite.mjs +14 -0
  172. package/scaffold/add-fe-overlay.mjs +122 -0
  173. package/scaffold/add-fe-page.mjs +269 -0
  174. package/scaffold/add-inbox-lite.mjs +74 -42
  175. package/scaffold/add-table.mjs +110 -87
  176. package/scaffold/add.mjs +159 -44
  177. package/scaffold/app.mjs +124 -39
  178. package/scaffold/edition-gate.mjs +3 -3
  179. package/scaffold/lite-exports.mjs +2 -1
  180. package/scaffold/service.mjs +2 -2
  181. package/src/main.mjs +229 -116
  182. package/stack/age-install.mjs +37 -0
  183. package/stack/backup.mjs +100 -0
  184. package/stack/build-args.mjs +42 -0
  185. package/stack/build.mjs +173 -0
  186. package/stack/cli.mjs +130 -0
  187. package/stack/config.mjs +57 -0
  188. package/stack/context.mjs +57 -0
  189. package/stack/converge.mjs +107 -0
  190. package/stack/deploy.mjs +301 -0
  191. package/stack/digest.mjs +16 -0
  192. package/stack/docker-transport.mjs +53 -0
  193. package/stack/edge-alias.mjs +118 -0
  194. package/stack/errors.mjs +21 -0
  195. package/stack/image-scan.mjs +152 -0
  196. package/stack/ingress-create.mjs +114 -0
  197. package/stack/ingress-publish.mjs +273 -0
  198. package/stack/ingress.mjs +706 -0
  199. package/stack/jobs.mjs +51 -0
  200. package/stack/live-restore.mjs +177 -0
  201. package/stack/prepare.mjs +266 -0
  202. package/stack/private-file.mjs +22 -0
  203. package/stack/provision.mjs +90 -0
  204. package/stack/prune.mjs +46 -0
  205. package/stack/release-store.mjs +87 -0
  206. package/stack/remote.mjs +205 -0
  207. package/stack/render.mjs +149 -0
  208. package/stack/retire.mjs +47 -0
  209. package/stack/sealed.mjs +149 -0
  210. package/stack/task.mjs +42 -0
  211. package/stack/verify.mjs +99 -0
  212. package/sync/ci-commands.mjs +13 -0
  213. package/sync/index.mjs +108 -52
  214. package/sync/stack-images.mjs +127 -0
  215. package/templates/app/ci-workflows/github/workflows/ci.yml +5 -1
  216. package/templates/app/ci-workflows/github/workflows/images.yml +15 -3
  217. package/templates/app/ci-workflows/github/workflows/stack-deploy.yml +148 -0
  218. package/templates/app/ci-workflows-lite/github/workflows/ci.yml +7 -3
  219. package/templates/app/ci-workflows-lite/github/workflows/db-deploy.yml +38 -6
  220. package/templates/app/ci-workflows-lite/github/workflows/images.yml +14 -2
  221. package/templates/app/ci-workflows-lite/github/workflows/stack-deploy.yml +148 -0
  222. package/templates/app/codegen-graphql.mjs.partial +13 -2
  223. package/templates/app/codegen-lite/scripts/codegen.mjs +6 -1
  224. package/templates/app/codegen-prelude.mjs.partial +8 -0
  225. package/templates/app/gitignore +3 -0
  226. package/templates/app/gitignore-lite +3 -0
  227. package/templates/app/package-scripts/package.json +1 -0
  228. package/templates/app/package-scripts/supabase.json +7 -0
  229. package/templates/app/package-scripts-lite/package.json +1 -7
  230. package/templates/app/skeleton/.editorconfig +1 -0
  231. package/templates/app/skeleton/.gitattributes +7 -1
  232. package/templates/app/skeleton/.github/dependabot.yml +17 -0
  233. package/templates/app/skeleton/.nvmrc +1 -1
  234. package/templates/app/skeleton/.starcistacks/application-stacks.yaml +134 -1
  235. package/templates/app/skeleton/.starcistacks/dev/README.md +16 -0
  236. package/templates/app/skeleton/.starcistacks/dev/environment.json +5 -0
  237. package/templates/app/skeleton/.starcistacks/dev/infra/compose/stack.yaml +71 -0
  238. package/templates/app/skeleton/.starcistacks/dev/runtime/env/KEYS.md +14 -0
  239. package/templates/app/skeleton/.starcistacks/production/README.md +37 -0
  240. package/templates/app/skeleton/.starcistacks/production/environment.json +5 -0
  241. package/templates/app/skeleton/.starcistacks/production/infra/swarm/Caddyfile +15 -0
  242. package/templates/app/skeleton/.starcistacks/production/infra/swarm/owner-actions.md +11 -0
  243. package/templates/app/skeleton/.starcistacks/production/infra/swarm/recovery/restore-proof.md +7 -0
  244. package/templates/app/skeleton/.starcistacks/production/infra/swarm/releases/current.yaml +6 -0
  245. package/templates/app/skeleton/.starcistacks/production/infra/swarm/releases/images.lock.yaml +9 -0
  246. package/templates/app/skeleton/.starcistacks/production/infra/swarm/releases/previous.yaml +6 -0
  247. package/templates/app/skeleton/.starcistacks/production/infra/swarm/stack.yaml +142 -0
  248. package/templates/app/skeleton/.starcistacks/production/runtime/env/KEYS.md +14 -0
  249. package/templates/app/skeleton/README.md +6 -6
  250. package/templates/app/skeleton/scripts/codegen.mjs +1 -2
  251. package/templates/app/skeleton-lite/.editorconfig +16 -0
  252. package/templates/app/skeleton-lite/.gitattributes +8 -0
  253. package/templates/app/skeleton-lite/.github/dependabot.yml +17 -0
  254. package/templates/app/skeleton-lite/.nvmrc +1 -0
  255. package/templates/app/skeleton-lite/.sops.yaml +3 -2
  256. package/templates/app/skeleton-lite/.starcistacks/application-stacks.yaml +178 -1
  257. package/templates/app/skeleton-lite/.starcistacks/dev/README.md +20 -0
  258. package/templates/app/skeleton-lite/.starcistacks/dev/environment.json +2 -1
  259. package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/Caddyfile +16 -0
  260. package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/owner-actions.md +11 -0
  261. package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/recovery/restore-proof.md +7 -0
  262. package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/releases/current.yaml +6 -0
  263. package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/releases/images.lock.yaml +8 -0
  264. package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/releases/previous.yaml +6 -0
  265. package/templates/app/skeleton-lite/.starcistacks/dev/infra/swarm/stack.yaml +120 -0
  266. package/templates/app/skeleton-lite/.starcistacks/dev/runtime/env/KEYS.md +18 -0
  267. package/templates/app/skeleton-lite/.starcistacks/production/README.md +38 -0
  268. package/templates/app/skeleton-lite/.starcistacks/production/environment.json +6 -0
  269. package/templates/app/skeleton-lite/.starcistacks/production/infra/swarm/Caddyfile +11 -0
  270. package/templates/app/skeleton-lite/.starcistacks/production/infra/swarm/owner-actions.md +11 -0
  271. package/templates/app/skeleton-lite/.starcistacks/production/infra/swarm/recovery/restore-proof.md +7 -0
  272. package/templates/app/skeleton-lite/.starcistacks/production/infra/swarm/releases/current.yaml +6 -0
  273. package/templates/app/skeleton-lite/.starcistacks/production/infra/swarm/releases/images.lock.yaml +8 -0
  274. package/templates/app/skeleton-lite/.starcistacks/production/infra/swarm/releases/previous.yaml +6 -0
  275. package/templates/app/skeleton-lite/.starcistacks/production/infra/swarm/stack.yaml +121 -0
  276. package/templates/app/skeleton-lite/.starcistacks/production/runtime/env/KEYS.md +21 -0
  277. package/templates/app/skeleton-lite/.starciwork/brand/index.yaml +12 -0
  278. package/templates/app/skeleton-lite/README.md +5 -3
  279. package/templates/app/skeleton-lite/supabase/config.toml +8 -1
  280. package/templates/app/skeleton-lite/supabase/migrations/__timestamp___baseline.sql +15 -6
  281. package/templates/app/upgrade-full/.starcistacks/dev/README.md +13 -7
  282. package/templates/be/image/worker/Dockerfile +1 -1
  283. package/templates/be/inbox/database-client.spec.ts.tpl +17 -18
  284. package/templates/be/inbox/database-client.ts.tpl +12 -11
  285. package/templates/be/inbox/delivery-recorded.event.ts.tpl +36 -0
  286. package/templates/be/inbox/event-index.ts.tpl +1 -0
  287. package/templates/be/inbox/events.json.tpl +10 -0
  288. package/templates/be/inbox/migration.sql.tpl +1 -0
  289. package/templates/be/inbox/platform/event-bus/event-bus.contracts.ts.tpl +42 -0
  290. package/templates/be/inbox/platform/event-bus/event-bus.decorators.ts.tpl +9 -0
  291. package/templates/be/inbox/platform/event-bus/event-bus.module-definition.ts.tpl +6 -0
  292. package/templates/be/inbox/platform/event-bus/event-bus.module.ts.tpl +19 -0
  293. package/templates/be/inbox/platform/event-bus/event-bus.options.ts.tpl +2 -0
  294. package/templates/be/inbox/platform/event-bus/event-bus.port.ts.tpl +16 -0
  295. package/templates/be/inbox/platform/event-bus/event-bus.service.spec.ts.tpl +45 -0
  296. package/templates/be/inbox/platform/event-bus/event-bus.service.ts.tpl +15 -0
  297. package/templates/be/inbox/platform/event-bus/index.ts.tpl +5 -0
  298. package/templates/be/inbox/platform/event-bus/persistence/event-bus.sql.ts.tpl +5 -0
  299. package/templates/be/inbox/platform/inbox/inbox.decorators.ts.tpl +9 -0
  300. package/templates/be/inbox/platform/inbox/inbox.module-definition.ts.tpl +6 -0
  301. package/templates/be/inbox/platform/inbox/inbox.module.ts.tpl +19 -0
  302. package/templates/be/inbox/platform/inbox/inbox.options.ts.tpl +2 -0
  303. package/templates/be/inbox/platform/inbox/inbox.port.ts.tpl +10 -0
  304. package/templates/be/inbox/platform/inbox/inbox.service.spec.ts.tpl +41 -0
  305. package/templates/be/inbox/platform/inbox/inbox.service.ts.tpl +19 -0
  306. package/templates/be/inbox/platform/inbox/index.ts.tpl +3 -0
  307. package/templates/be/inbox/platform/inbox/persistence/inbox.sql.ts.tpl +7 -0
  308. package/templates/be/inbox/platform/migration.sql.tpl +48 -0
  309. package/templates/be/inbox/service.spec.ts.tpl +42 -9
  310. package/templates/be/inbox/service.ts.tpl +23 -8
  311. package/templates/be/inbox/webhook-door.spec.ts.tpl +1 -1
  312. package/templates/be/inbox/webhook-door.ts.tpl +2 -2
  313. package/templates/be/patterns/api/feature.module-definition.ts.tpl +6 -0
  314. package/templates/be/patterns/api/feature.module.ts.tpl +2 -1
  315. package/templates/be/patterns/api/feature.options.ts.tpl +2 -0
  316. package/templates/be/patterns/api/graphql.module-definition.ts.tpl +6 -0
  317. package/templates/be/patterns/api/graphql.module.ts.tpl +3 -2
  318. package/templates/be/patterns/api/graphql.options.ts.tpl +2 -0
  319. package/templates/be/patterns/cli/group.module-definition.ts.tpl +6 -0
  320. package/templates/be/patterns/cli/group.module.ts.tpl +2 -1
  321. package/templates/be/patterns/cli/group.options.ts.tpl +2 -0
  322. package/templates/be/patterns/event-bus/platform/errors/event-bus.error.ts.tpl +0 -3
  323. package/templates/be/patterns/event-bus/platform/messages/event-bus.messages.ts.tpl +0 -2
  324. package/templates/be/patterns/jobs/job.module-definition.ts.tpl +6 -0
  325. package/templates/be/patterns/jobs/job.module.ts.tpl +2 -1
  326. package/templates/be/patterns/jobs/job.options.ts.tpl +2 -0
  327. package/templates/be/patterns/jobs/job.queue.module-definition.ts.tpl +6 -0
  328. package/templates/be/patterns/jobs/job.queue.module.ts.tpl +6 -3
  329. package/templates/be/patterns/jobs/job.queue.options.ts.tpl +2 -0
  330. package/templates/be/patterns/jobs/platform/fenced.processor.spec.ts.tpl +3 -1
  331. package/templates/be/patterns/outbox/platform/outbox-relay.policy.spec.ts.tpl +25 -9
  332. package/templates/be/patterns/queues/platform/queue.config.ts.tpl +1 -1
  333. package/templates/be/patterns/queues/platform/queue.index.ts.tpl +1 -1
  334. package/templates/be/patterns/queues/platform/queue.module-definition.ts.tpl +2 -2
  335. package/templates/be/patterns/queues/platform/queue.module.ts.tpl +11 -0
  336. package/templates/be/patterns/queues/platform/queue.options.ts.tpl +10 -0
  337. package/templates/be/patterns/queues/queue.module-definition.ts.tpl +6 -0
  338. package/templates/be/patterns/queues/queue.module.ts.tpl +2 -1
  339. package/templates/be/patterns/queues/queue.options.ts.tpl +2 -0
  340. package/templates/be/patterns/reactors/message.module-definition.ts.tpl +6 -0
  341. package/templates/be/patterns/reactors/message.module.ts.tpl +6 -3
  342. package/templates/be/patterns/reactors/message.options.ts.tpl +2 -0
  343. package/templates/be/patterns/reactors/reactor.module-definition.ts.tpl +6 -0
  344. package/templates/be/patterns/reactors/reactor.module.ts.tpl +2 -1
  345. package/templates/be/patterns/reactors/reactor.options.ts.tpl +2 -0
  346. package/templates/be/patterns/realtime/channel.gateway.spec.ts.tpl +188 -0
  347. package/templates/be/patterns/realtime/channel.gateway.ts.tpl +22 -0
  348. package/templates/be/patterns/realtime/channel.graphql.module-definition.ts.tpl +6 -0
  349. package/templates/be/patterns/realtime/channel.graphql.module.ts.tpl +2 -1
  350. package/templates/be/patterns/realtime/channel.graphql.options.ts.tpl +2 -0
  351. package/templates/be/patterns/realtime/channel.websocket.module-definition.ts.tpl +6 -0
  352. package/templates/be/patterns/realtime/channel.websocket.module.ts.tpl +110 -0
  353. package/templates/be/patterns/realtime/channel.websocket.options.ts.tpl +7 -0
  354. package/templates/be/patterns/saga/saga.message.module-definition.ts.tpl +6 -0
  355. package/templates/be/patterns/saga/saga.message.module.ts.tpl +6 -3
  356. package/templates/be/patterns/saga/saga.message.options.ts.tpl +2 -0
  357. package/templates/be/patterns/saga/saga.module-definition.ts.tpl +6 -0
  358. package/templates/be/patterns/saga/saga.module.ts.tpl +2 -1
  359. package/templates/be/patterns/saga/saga.options.ts.tpl +2 -0
  360. package/templates/be/patterns/webhooks/webhook.http.module-definition.ts.tpl +6 -0
  361. package/templates/be/patterns/webhooks/webhook.http.module.ts.tpl +2 -1
  362. package/templates/be/patterns/webhooks/webhook.http.options.ts.tpl +2 -0
  363. package/templates/be/platform/graphql/localize-errors.mapper.spec.ts +80 -26
  364. package/templates/be/platform/graphql/localize-errors.mapper.ts +2 -5
  365. package/templates/be/skeleton/apps/__app__/src/app.module.ts +11 -9
  366. package/templates/be/skeleton/apps/__app__/src/compose/api.ts +4 -0
  367. package/templates/be/skeleton/apps/__app__/src/compose/domain.ts +11 -0
  368. package/templates/be/skeleton/apps/__app__/src/compose/platform.ts +2 -0
  369. package/templates/be/skeleton/apps/__app__/src/compose/system.ts +5 -0
  370. package/templates/be/skeleton/apps/__app__/src/compose/webhooks.ts +4 -0
  371. package/templates/be/skeleton/apps/__app__/src/main.ts +15 -1
  372. package/templates/be/skeleton/apps/cli/src/app.module.ts +3 -13
  373. package/templates/be/skeleton/apps/cli/src/cli.options.ts +6 -14
  374. package/templates/be/skeleton/apps/cli/src/compose/cli.ts +5 -0
  375. package/templates/be/skeleton/apps/cli/src/compose/platform.ts +24 -0
  376. package/templates/be/skeleton/src/features/api/system-health/application/check-readiness.contracts.ts +2 -0
  377. package/templates/be/skeleton/src/features/api/system-health/application/check-readiness.handler.ts +22 -0
  378. package/templates/be/skeleton/src/features/api/system-health/application/check-readiness.query.ts +11 -0
  379. package/templates/be/skeleton/src/features/api/system-health/system-health.module-definition.ts +6 -0
  380. package/templates/be/skeleton/src/features/api/system-health/system-health.module.ts +5 -3
  381. package/templates/be/skeleton/src/features/api/system-health/system-health.options.ts +2 -0
  382. package/templates/be/skeleton/src/features/api/system-health/transport/http/ready.controller.ts +19 -0
  383. package/templates/be/skeleton/src/features/api/system-health/transport/http/system-health-http.module-definition.ts +6 -0
  384. package/templates/be/skeleton/src/features/api/system-health/transport/http/system-health-http.module.ts +8 -3
  385. package/templates/be/skeleton/src/features/api/system-health/transport/http/system-health-http.options.ts +2 -0
  386. package/templates/be/skeleton/src/features/cli/cli.module-definition.ts +6 -0
  387. package/templates/be/skeleton/src/features/cli/cli.module.ts +3 -2
  388. package/templates/be/skeleton/src/features/cli/cli.options.ts +2 -0
  389. package/templates/be/skeleton/src/features/cli/migrate/migrate.module-definition.ts +6 -0
  390. package/templates/be/skeleton/src/features/cli/migrate/migrate.module.ts +2 -1
  391. package/templates/be/skeleton/src/features/cli/migrate/migrate.options.ts +2 -0
  392. package/templates/be/skeleton/src/features/cli/seed/seed.module-definition.ts +6 -0
  393. package/templates/be/skeleton/src/features/cli/seed/seed.module.ts +2 -1
  394. package/templates/be/skeleton/src/features/cli/seed/seed.options.ts +2 -0
  395. package/templates/be/skeleton/src/features/cli/seed/subs/run.cli.spec.ts +5 -6
  396. package/templates/be/skeleton/src/modules/domain/identity/admission.policy.spec.ts +56 -4
  397. package/templates/be/skeleton/src/modules/domain/identity/admission.policy.ts +36 -5
  398. package/templates/be/skeleton/src/modules/domain/identity/auth.guard.spec.ts +39 -8
  399. package/templates/be/skeleton/src/modules/domain/identity/auth.guard.ts +14 -8
  400. package/templates/be/skeleton/src/modules/domain/identity/credential.contracts.ts +26 -0
  401. package/templates/be/skeleton/src/modules/domain/identity/credential.guard.spec.ts +119 -0
  402. package/templates/be/skeleton/src/modules/domain/identity/credential.guard.ts +43 -0
  403. package/templates/be/skeleton/src/modules/domain/identity/identity.contracts.ts +16 -0
  404. package/templates/be/skeleton/src/modules/domain/identity/identity.decorators.ts +18 -1
  405. package/templates/be/skeleton/src/modules/domain/identity/identity.module.ts +22 -4
  406. package/templates/be/skeleton/src/modules/domain/identity/identity.options.ts +8 -2
  407. package/templates/be/skeleton/src/modules/domain/identity/index.ts +1 -0
  408. package/templates/be/skeleton/src/modules/platform/database/connection-source.client.spec.ts +11 -4
  409. package/templates/be/skeleton/src/modules/platform/database/database-probe.service.spec.ts +32 -0
  410. package/templates/be/skeleton/src/modules/platform/database/database-probe.service.ts +14 -0
  411. package/templates/be/skeleton/src/modules/platform/database/database.module.ts +7 -1
  412. package/templates/be/skeleton/src/modules/platform/database/index.ts +2 -0
  413. package/templates/be/skeleton/src/modules/platform/database/primary.decorators.ts +8 -0
  414. package/templates/be/skeleton/src/modules/platform/database/seed-connections.client.spec.ts +25 -6
  415. package/templates/be/skeleton/src/modules/platform/errors/errors.filter.spec.ts +16 -6
  416. package/templates/be/skeleton/src/modules/platform/http-security/edge-security.service.spec.ts +130 -0
  417. package/templates/be/skeleton/src/modules/platform/http-security/edge-security.service.ts +58 -0
  418. package/templates/be/skeleton/src/modules/platform/http-security/errors/http-security.error.ts +3 -0
  419. package/templates/be/skeleton/src/modules/platform/http-security/execution-request.mapper.spec.ts +16 -4
  420. package/templates/be/skeleton/src/modules/platform/http-security/http-security.config.ts +16 -10
  421. package/templates/be/skeleton/src/modules/platform/http-security/http-security.module.ts +3 -1
  422. package/templates/be/skeleton/src/modules/platform/http-security/http-security.options.ts +2 -0
  423. package/templates/be/skeleton/src/modules/platform/http-security/index.ts +5 -0
  424. package/templates/be/skeleton/src/modules/platform/http-security/messages/http-security.messages.ts +12 -5
  425. package/templates/be/skeleton/src/modules/platform/http-security/rate-limit.guard.spec.ts +13 -8
  426. package/templates/be/skeleton/src/modules/platform/http-security/readiness.contracts.ts +5 -0
  427. package/templates/be/skeleton/src/modules/platform/http-security/readiness.decorators.ts +7 -0
  428. package/templates/be/skeleton/src/modules/platform/http-security/readiness.service.spec.ts +86 -0
  429. package/templates/be/skeleton/src/modules/platform/http-security/readiness.service.ts +39 -0
  430. package/templates/be/skeleton/src/modules/platform/http-security/webhook-signature.service.spec.ts +139 -131
  431. package/templates/be/skeleton/src/modules/platform/i18n/index.ts +1 -1
  432. package/templates/be/skeleton/src/modules/platform/logging/json-logger.service.spec.ts +91 -67
  433. package/templates/be/skeleton/src/modules/platform/logging/json-logger.service.ts +5 -4
  434. package/templates/be/skeleton/src/modules/platform/logging/logging.log-events.ts +0 -2
  435. package/templates/be/skeleton/src/modules/platform/primitives/outcome.mapper.spec.ts +20 -6
  436. package/templates/be/skeleton/src/modules/platform/primitives/sequence.policy.spec.ts +16 -4
  437. package/templates/be/skeleton/src/modules/platform/retry/index.ts +3 -0
  438. package/templates/be/skeleton/src/modules/platform/retry/retry.decorators.ts +9 -0
  439. package/templates/be/skeleton/src/modules/platform/retry/retry.module-definition.ts +7 -0
  440. package/templates/be/skeleton/src/modules/platform/retry/retry.module.ts +19 -0
  441. package/templates/be/skeleton/src/modules/platform/retry/retry.options.ts +2 -0
  442. package/templates/be/skeleton/src/modules/platform/retry/retry.port.ts +11 -0
  443. package/templates/be/skeleton/src/modules/platform/retry/system-timer.service.spec.ts +36 -0
  444. package/templates/be/skeleton/src/modules/platform/retry/system-timer.service.ts +10 -0
  445. package/templates/be/skeleton/src/tests/contract/http-security/http-security.contract-spec.ts +54 -0
  446. package/templates/be/skeleton-lite/apps/api/src/app.module.ts +14 -18
  447. package/templates/be/skeleton-lite/apps/api/src/compose/api.ts +4 -0
  448. package/templates/be/skeleton-lite/apps/api/src/compose/domain.ts +10 -0
  449. package/templates/be/skeleton-lite/apps/api/src/compose/integrations.ts +16 -0
  450. package/templates/be/skeleton-lite/apps/api/src/compose/platform.ts +2 -0
  451. package/templates/be/skeleton-lite/apps/api/src/compose/system.ts +5 -0
  452. package/templates/be/skeleton-lite/apps/api/src/compose/webhooks.ts +4 -0
  453. package/templates/be/skeleton-lite/apps/api/src/main.ts +2 -3
  454. package/templates/be/skeleton-lite/apps/cli/src/compose/platform.ts +12 -0
  455. package/templates/be/skeleton-lite/src/modules/domain/identity/admission.policy.spec.ts +71 -5
  456. package/templates/be/skeleton-lite/src/modules/domain/identity/admission.policy.ts +29 -2
  457. package/templates/be/skeleton-lite/src/modules/domain/identity/auth.guard.spec.ts +68 -15
  458. package/templates/be/skeleton-lite/src/modules/domain/identity/auth.guard.ts +14 -9
  459. package/templates/be/skeleton-lite/src/modules/domain/identity/identity.contracts.ts +9 -2
  460. package/templates/be/skeleton-lite/src/modules/domain/identity/identity.decorators.ts +16 -1
  461. package/templates/be/skeleton-lite/src/modules/domain/identity/identity.module.ts +19 -4
  462. package/templates/be/skeleton-lite/src/modules/domain/identity/identity.options.ts +5 -1
  463. package/templates/be/skeleton-lite/src/modules/domain/identity/index.ts +2 -1
  464. package/templates/be/skeleton-lite/src/modules/integrations/supabase/errors/supabase.error.ts +0 -3
  465. package/templates/be/skeleton-lite/src/modules/integrations/supabase/supabase.client.spec.ts +17 -5
  466. package/templates/be/skeleton-lite/src/modules/platform/database/database.module.ts +24 -13
  467. package/templates/be/skeleton-lite/src/modules/platform/database/database.port.ts +5 -0
  468. package/templates/be/skeleton-lite/src/modules/platform/database/errors/database.error.ts +16 -0
  469. package/templates/be/skeleton-lite/src/modules/platform/database/index.ts +3 -0
  470. package/templates/be/skeleton-lite/src/modules/platform/http-security/http-security.module.ts +5 -2
  471. package/templates/be/skeleton-lite/src/modules/platform/http-security/index.ts +4 -1
  472. package/templates/be/skeleton-lite/src/modules/platform/i18n/index.ts +1 -1
  473. package/templates/be/skeleton-lite/src/modules/platform/logging/logging.log-events.ts +7 -0
  474. package/templates/be/skeleton-lite/src/tests/fixtures/builders/api.builder.ts +29 -0
  475. package/templates/be/skeleton-lite/src/tests/fixtures/builders/cli.builder.ts +16 -0
  476. package/templates/be/table/api/feature.module-definition.ts.tpl +6 -0
  477. package/templates/be/table/api/feature.module.ts.tpl +2 -1
  478. package/templates/be/table/api/feature.options.ts.tpl +2 -0
  479. package/templates/be/table/api/graphql.module-definition.ts.tpl +6 -0
  480. package/templates/be/table/api/graphql.module.ts.tpl +3 -2
  481. package/templates/be/table/api/graphql.options.ts.tpl +2 -0
  482. package/templates/be/table/migration.sql.tpl +1 -0
  483. package/templates/be/upgrade-full/apps/cli/src/app.module.ts +4 -6
  484. package/templates/be/upgrade-full/apps/cli/src/cli.env.ts +7 -0
  485. package/templates/be/upgrade-full/apps/cli/src/compose/cli.ts +5 -0
  486. package/templates/be/upgrade-full/apps/cli/src/compose/platform.ts +9 -0
  487. package/templates/be/upgrade-full/apps/cli/src/main.ts +3 -0
  488. package/templates/be/upgrade-full/src/features/cli/cli.module.ts +3 -2
  489. package/templates/fe/auth/supabase-client.ts.tpl +34 -0
  490. package/templates/fe/skeleton/apps/__app__/src/app/auth/callback/route.ts +11 -0
  491. package/templates/fe/skeleton/apps/__app__/src/components/blocks/SignInForm/index.tsx +6 -1
  492. package/templates/fe/skeleton/apps/__app__/src/components/leaves/Icon/index.tsx +4 -0
  493. package/templates/fe/skeleton/apps/__app__/src/features/overlays/AppGuideOverlay/component.tsx +31 -0
  494. package/templates/fe/skeleton/apps/__app__/src/features/overlays/AppGuideOverlay/index.tsx +24 -0
  495. package/templates/fe/skeleton/apps/__app__/src/hooks/auth/useSession.ts +6 -1
  496. package/templates/fe/skeleton/apps/__app__/src/hooks/auth/useSignOut.ts +3 -4
  497. package/templates/fe/skeleton/apps/__app__/src/hooks/zustand/appGuide/index.ts +1 -0
  498. package/templates/fe/skeleton/apps/__app__/src/hooks/zustand/appGuide/store.ts +15 -0
  499. package/templates/fe/skeleton/apps/__app__/src/modules/auth/pkce-client.ts +18 -0
  500. package/templates/fe/skeleton/apps/__app__/src/modules/auth/server.ts +19 -0
  501. package/templates/fe/skeleton/apps/app/src/features/pages/AppHomePage/index.tsx +7 -1
  502. package/templates/fe/skeleton/apps/app/src/modules/i18n/messages/en.json +57 -0
  503. package/templates/fe/skeleton/apps/app/src/modules/i18n/messages/vi.json +6 -0
  504. package/templates/fe/skeleton/apps/landing/src/modules/i18n/messages/en.json +31 -0
  505. package/templates/fe/skeleton/packages/__project__-i18n/src/routing.ts +1 -1
  506. package/templates/fe/skeleton/packages/__project__-ui/src/index.ts +1 -0
  507. package/templates/fe/skeleton-lite/apps/__app__/src/modules/auth/client.ts +1 -34
  508. package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppHomePage/index.tsx +7 -1
  509. package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/messages/en.json +57 -0
  510. package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/messages/vi.json +30 -24
  511. package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/routing.ts +1 -1
  512. package/templates/fe/transport/graphql/clients/clients/auth.ts +1 -1
  513. package/templates/fe/transport/graphql/clients/links/locale.ts +2 -2
  514. package/upgrade/check.mjs +3 -1
  515. package/upgrade/index.mjs +53 -40
@@ -212,6 +212,50 @@ ARCH_TYPESCRIPT_MISSING:
212
212
  owner: op-retry
213
213
  kind: check-finding
214
214
 
215
+ BACKUP_PROOF_MISSING:
216
+ title: "A declared backup has no tracked restore proof"
217
+ title_vi: "Bản sao lưu được khai báo thiếu bằng chứng phục hồi được theo dõi"
218
+ meaning_vi: "Backup chưa có restoreProof hoặc tham chiếu của nó không trỏ tới tệp bằng chứng phục hồi hợp lệ được Git theo dõi."
219
+ causes_vi:
220
+ - "Vi phạm R267: khai báo sao lưu hoặc tệp không được theo dõi chưa chứng minh được khả năng phục hồi dữ liệu."
221
+ nextStep_vi: "Thử phục hồi trong môi trường cách ly, ghi bằng chứng vào tệp được theo dõi và khai báo đường dẫn đó tại backup.restoreProof."
222
+ owner: op-retry
223
+ kind: check-finding
224
+
225
+ BACKUP_UNDECLARED:
226
+ title: "A stateful component has no complete backup declaration"
227
+ title_vi: "Component có trạng thái thiếu khai báo sao lưu đầy đủ"
228
+ meaning_vi: "Component có dữ liệu bền vững hoặc backing được quản lý không có backup tương ứng nêu component, target, retention và owner của môi trường."
229
+ causes_vi:
230
+ - "Vi phạm R267: nơi lưu bản sao, thời gian giữ và người chịu trách nhiệm phục hồi chưa được khai báo."
231
+ nextStep_vi: "Thêm backup cho từng component có trạng thái với nơi lưu, chính sách giữ và chủ sở hữu; nêu rõ miền lỗi của bản sao."
232
+ owner: op-retry
233
+ kind: check-finding
234
+
235
+ BE_AMBIENT_CLOCK:
236
+ title: "Ambient clock read directly"
237
+ title_vi: "Đọc đồng hồ hệ thống trực tiếp"
238
+ meaning_vi: "Một tệp (kể cả spec) ngoài platform/clock tham chiếu tới đồng hồ hệ thống: Date.now, new Date() không tham số, performance.now, process.hrtime hoặc Temporal.Now, dù gọi hay chỉ tham chiếu, nên không có cách nào thay đồng hồ trong một spec hoặc lái một job theo lịch tới một thời điểm chọn trước."
239
+ causes_vi:
240
+ - "const startedAt = Date.now()"
241
+ - "const at = new Date()"
242
+ - "const mark = performance.now()"
243
+ - "const read = Date.now, hoặc const { now } = performance"
244
+ nextStep_vi: "Tiêm cổng Clock (đọc thời gian qua clock.now()) thay vì đọc đồng hồ hệ thống trực tiếp; một spec lái thời gian bằng FakeClock của @starci/jest-preset. 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."
245
+ owner: op-retry
246
+ kind: check-finding
247
+
248
+ BE_AMBIENT_ID:
249
+ title: "An id is minted from the ambient random source"
250
+ title_vi: "Mã định danh được sinh trực tiếp từ nguồn ngẫu nhiên"
251
+ meaning_vi: "Mã ở `<file>` gọi `randomUUID` của Node crypto (import tên, import namespace hoặc `crypto` toàn cục), một bộ sinh `v1`, `v4`, `v6`, `v7` của `uuid`, hoặc `nanoid`/`ulid` thay vì lấy từ cổng `Ids`; spec khi đó không điều khiển được mã nào được sinh ra và một lần chạy lỗi không tái hiện được."
252
+ causes_vi:
253
+ - "Vi phạm luật R142: dịch vụ gọi `randomUUID()` trực tiếp để tạo khóa hoặc mã sự kiện"
254
+ - "Spec tự sinh mã bằng `randomUUID` thay vì dùng `fakeIds()`"
255
+ nextStep_vi: "Tiêm cổng bằng `@InjectIds() private readonly ids: Ids` và gọi `ids.next()`; trong spec cấp `fakeIds([...])` qua token của `platform/ids`. 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."
256
+ owner: op-retry
257
+ kind: check-finding
258
+
215
259
  BE_APPLICATION_IMPORTS_TRANSPORT:
216
260
  title: "Application imports transport layer"
217
261
  title_vi: "Lớp ứng dụng import lớp giao vận"
@@ -232,6 +276,18 @@ BE_APPLICATION_TRANSPORT_FRAMEWORK:
232
276
  owner: op-retry
233
277
  kind: check-finding
234
278
 
279
+ BE_APP_BOOT_PROOF_MISSING:
280
+ title: "Lite app has no successful Nest dependency wiring proof"
281
+ title_vi: "Ứng dụng lite chưa chứng minh ghép nối phụ thuộc Nest thành công"
282
+ meaning_vi: "AppModule thật của ứng dụng lite không biên dịch được đồ thị phụ thuộc với dữ liệu mẫu và các đối tượng thay thế hạ tầng; bản full tiếp tục dùng bằng chứng e2e hiện có."
283
+ causes_vi:
284
+ - "Thiếu provider, thiếu export của module hoặc có phụ thuộc vòng"
285
+ - "Thiếu AppModule, dữ liệu mẫu cho đăng ký động hoặc đối tượng thay thế hạ tầng"
286
+ - "Tiến trình chứng minh ghép nối thất bại hoặc hết thời gian"
287
+ nextStep_vi: "Sửa đăng ký và phạm vi provider trong AppModule thật, cung cấp dữ liệu mẫu cùng đối tượng thay thế hạ tầng rồi chạy lại starci app check --wiring."
288
+ owner: op-retry
289
+ kind: check-finding
290
+
235
291
  BE_APP_BUSINESS_ROLE:
236
292
  title: "Composition root holds business role"
237
293
  title_vi: "Gốc ghép nối chứa vai trò nghiệp vụ"
@@ -242,6 +298,17 @@ BE_APP_BUSINESS_ROLE:
242
298
  owner: op-retry
243
299
  kind: check-finding
244
300
 
301
+ BE_APP_COMPOSITION_BUDGET:
302
+ title: "An app composition file is over its line budget"
303
+ title_vi: "Tệp ghép nối của ứng dụng vượt ngân sách dòng"
304
+ meaning_vi: "Một tệp ghép nối của ứng dụng vượt ngân sách dòng mà slot `be.app.*` đặt ra (`app.module.ts` 250, `<app>.env.ts` 150, `<app>.options.ts` 200, `compose/<area>.ts` 120, `main.ts` 80). Ngân sách là tuyệt đối, không phải kiểu không-được-tăng."
305
+ causes_vi:
306
+ - "Vi phạm luật R246: mọi module của ứng dụng được liệt kê trực tiếp trong app.module.ts"
307
+ - "Một nhóm compose/<area>.ts gom nhiều khu vực vào một tệp"
308
+ nextStep_vi: "Tách một khu vực ghép nối ra nhóm mới `apps/<app>/src/compose/<area>.ts` và spread nó trong `app.module.ts`; tệp options chỉ giữ giá trị từ môi trường và ràng buộc cổng. 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."
309
+ owner: op-retry
310
+ kind: check-finding
311
+
245
312
  BE_APP_COMPOSITION_ONLY:
246
313
  title: "Composition root has stray source"
247
314
  title_vi: "Gốc ghép nối có tệp lạc chỗ"
@@ -252,6 +319,49 @@ BE_APP_COMPOSITION_ONLY:
252
319
  owner: op-retry
253
320
  kind: check-finding
254
321
 
322
+ BE_APP_ENV_REQUIRED:
323
+ title: "A back-end app lacks its typed environment boundary"
324
+ title_vi: "Ứng dụng back end thiếu ranh giới môi trường có kiểu"
325
+ meaning_vi: "Ứng dụng không có tệp <app>.env.ts được theo dõi với interface và read<App>Env(env: EnvSource), hoặc main.ts tự đọc và phân tích môi trường thay vì gọi hàm đó."
326
+ causes_vi:
327
+ - "Ứng dụng thiếu tệp môi trường hoặc dùng hàm đọc không có hợp đồng kiểu"
328
+ - "main.ts gọi parseConfig hoặc đọc EnvSource trực tiếp"
329
+ nextStep_vi: "Tạo và theo dõi apps/<app>/src/<app>.env.ts, xuất interface cùng read<App>Env nhận EnvSource; main.ts chỉ tạo EnvSource một lần rồi gọi hàm đọc đó để khởi động."
330
+ owner: op-retry
331
+ kind: check-finding
332
+
333
+ BE_APP_MODULE_LIST_INLINE:
334
+ title: "An app root holds an inline composition list"
335
+ title_vi: "Module gốc ứng dụng chứa danh sách ghép nối tại chỗ"
336
+ meaning_vi: "app.module.ts liệt kê module, queue hoặc scheduler tại chỗ thay vì spread nhóm có kiểu từ compose/<area>.ts."
337
+ causes_vi:
338
+ - "Module gốc nhập trực tiếp các module tính năng hoặc đăng ký capability"
339
+ - "Danh sách ghép nối được đặt trong biến của module gốc để tránh nhóm compose"
340
+ nextStep_vi: "Chuyển danh sách vào nhóm có kiểu trong compose/<area>.ts rồi spread nhóm trong app.module.ts; chỉ giữ guard và filter toàn ứng dụng tại module gốc."
341
+ owner: op-retry
342
+ kind: check-finding
343
+
344
+ BE_APP_OPTIONS_COMPOSITION:
345
+ title: "App env or options holds module, queue or scheduler composition"
346
+ title_vi: "Tệp options của ứng dụng chứa danh sách module, queue hoặc scheduler"
347
+ meaning_vi: "`apps/<app>/src/<app>.env.ts` or `apps/<app>/src/<app>.options.ts` chứa một mảng module Nest (lớp `*Module`, lời gọi `X.register(...)`, `forRoot(...)`, `registerQueue(...)`), một mục queue hoặc scheduler (`*SchedulerOf(...)`), hoặc một danh sách `imports`, `modules`, `schedulers`, `processors`. Tệp options chỉ giữ các giá trị đọc từ môi trường và các ràng buộc cổng (map provider như `{ approvalGate: X }`); danh sách ghép nối thuộc về `compose/<area>.ts`. The env file allows typed, fail-fast parsing through EnvSource and capability parse helpers only and also refuses standalone module or queue registration calls; the options file holds env-derived values and port bindings with no parsing."
348
+ causes_vi:
349
+ - "Vi phạm luật R245: dồn danh sách module, queue hoặc scheduler vào <app>.options.ts để app.module.ts ngắn lại"
350
+ nextStep_vi: "Chuyển danh sách vào `apps/<app>/src/compose/<area>.ts` (mỗi khu vực một nhóm, xuất mảng module của khu vực đó) và spread nhóm trong `app.module.ts`; 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."
351
+ owner: op-retry
352
+ kind: check-finding
353
+
354
+ BE_ASYNC_NO_AWAIT:
355
+ title: "Async function that never awaits"
356
+ title_vi: "Hàm async không có await"
357
+ meaning_vi: "Một hàm async không chứa await nào, nên nó hứa việc bất đồng bộ mà không làm, biến lỗi ném ra thành promise bị từ chối vô ích và khiến người đọc tìm một điểm chờ không tồn tại."
358
+ causes_vi:
359
+ - "async method chỉ return giá trị đồng bộ"
360
+ - "async arrow chỉ trả về promise của hàm khác"
361
+ nextStep_vi: "Bỏ async và trả giá trị (hoặc promise thân hàm đã có); nếu chữ ký phải trả Promise thì trả Promise.resolve(giá trị). 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."
362
+ owner: op-retry
363
+ kind: check-finding
364
+
255
365
  BE_ASYNC_SPEC_MISSING:
256
366
  title: "Every message consumer and saga has an e2e spec"
257
367
  title_vi: "Consumer hoặc saga chưa có spec e2e"
@@ -272,6 +382,36 @@ BE_BACKGROUND_UNOWNED:
272
382
  owner: op-retry
273
383
  kind: check-finding
274
384
 
385
+ BE_CLI_BOOTSTRAP:
386
+ title: "The cli app does not boot with CommandFactory.run, or serves"
387
+ title_vi: "App cli không khởi động bằng CommandFactory.run hoặc lại chạy như server"
388
+ meaning_vi: "`main.ts` của app cli khởi động bằng `CommandFactory.run(...)` của nest-commander và không bao giờ phục vụ HTTP hay microservice: `NestFactory.create*`, `.listen()`, adapter `@nestjs/platform-*` hay `@nestjs/microservices` trong app cli đều bị từ chối."
389
+ causes_vi:
390
+ - "Vi phạm luật R148: app cli chạy một lệnh rồi thoát; một server trong app cli là một app api đội lốt."
391
+ 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: viết `main.ts` của app cli là `CommandFactory.run(AppModule, logger)` và bỏ mọi phần tạo server khỏi app cli."
392
+ owner: op-retry
393
+ kind: check-finding
394
+
395
+ BE_CLI_COMMAND_SHAPE:
396
+ title: "A cli command is misplaced or has no spec"
397
+ title_vi: "Lệnh cli đặt sai chỗ hoặc thiếu spec"
398
+ meaning_vi: "Lệnh nest-commander (`@Command` hoặc `@SubCommand`) chỉ được khai báo trong `src/features/cli/`: nhóm `@Command` ở `<group>/<group>.cli.ts`, `@SubCommand` kế thừa `CommandRunner` ở `<group>/subs/<name>.cli.ts`, mỗi tệp một lệnh, và mỗi lệnh con có unit spec `<name>.cli.spec.ts` bên cạnh."
399
+ causes_vi:
400
+ - "Vi phạm luật R149: lệnh cli là nơi chạy hành động (không phải transport mỏng), nên phải nằm đúng chỗ và được chứng minh bằng spec như một service."
401
+ 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: chuyển lệnh về `src/features/cli/<group>/subs/<name>.cli.ts` (nhóm về `<group>/<group>.cli.ts`), cho lệnh con kế thừa `CommandRunner` và viết `<name>.cli.spec.ts` bên cạnh."
402
+ owner: op-retry
403
+ kind: check-finding
404
+
405
+ BE_CLI_OWNER:
406
+ title: "A command line outside the cli"
407
+ title_vi: "Có dòng lệnh nằm ngoài cli"
408
+ meaning_vi: "`nest-commander` chỉ được import trong app cli và gốc feature cli; không mã back end nào tự đọc `process.argv` hay import commander, yargs, minimist."
409
+ causes_vi:
410
+ - "Vi phạm luật R150: một cửa hành động một lần ở app khác (ví dụ cờ `migrate` trong api) lách khỏi app cli, image cli và spec của lệnh."
411
+ 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: viết hành động thành một lệnh `src/features/cli/<group>/subs/<name>.cli.ts` do app cli chạy và bỏ việc đọc tham số dòng lệnh ở nơi khác."
412
+ owner: op-retry
413
+ kind: check-finding
414
+
275
415
  BE_CLI_REQUIRED:
276
416
  title: "The back end has no cli app, or no cli image"
277
417
  title_vi: "Back end thiếu app cli hoặc image của nó"
@@ -282,6 +422,29 @@ BE_CLI_REQUIRED:
282
422
  owner: op-retry
283
423
  kind: check-finding
284
424
 
425
+ BE_COMPOSITION_UNTYPED:
426
+ title: "App composition lives outside an explicitly typed area group"
427
+ title_vi: "Ghép nối ứng dụng nằm ngoài nhóm khu vực có kiểu tường minh"
428
+ meaning_vi: "Danh sách hoặc map module, provider, queue hay scheduler được đặt trong main, env hoặc options, hoặc nhóm compose không khai báo kiểu mảng hay kiểu trả về tường minh. Kiểm tra dựa trên kiểu đã phân giải, không dựa trên tên lớp hoặc hàm."
429
+ causes_vi:
430
+ - "Đổi tên module hoặc helper, dùng configure hay cron-table để giấu danh sách ghép nối"
431
+ - "Xuất nhóm compose với kiểu suy luận, any hoặc phần tử không phải thành phần ghép nối"
432
+ nextStep_vi: "Chuyển danh sách sang compose/<area>.ts, khai báo kiểu mảng hoặc kiểu trả về như Array<DynamicModule | Type<unknown>>, rồi spread nhóm trong app.module.ts; chỉ để giá trị môi trường và ràng buộc cổng trong options."
433
+ owner: op-retry
434
+ kind: check-finding
435
+
436
+ BE_CONFIG_OWNER:
437
+ title: "Environment read outside platform/config"
438
+ title_vi: "Đọc biến môi trường ngoài platform/config"
439
+ meaning_vi: "Một tệp đọc process.env (truy cập, destructuring, Reflect.get hay import env của node:process), import @nestjs/config hoặc dotenv, gọi một hàm đọc cấu hình của <capability>.config.ts ngoài main.ts (tệp config, spec của nó hoặc test world), hoặc ghép đường dẫn từ process.cwd() với src hay .starcistacks, ngoài tệp khai báo lớp EnvSource của platform/config, nên cấu hình của capability không đi qua bộ đọc có kiểu duy nhất."
440
+ causes_vi:
441
+ - "Đọc process.env trực tiếp trong service, module hoặc factory"
442
+ - "Import @nestjs/config, dotenv hoặc gọi platformConfig()/parse<C>Config() trong service, ở phạm vi module hay trong factory thay vì nhận options do main.ts truyền vào AppModule.register"
443
+ - "path.join(process.cwd(), \"src\", ...)"
444
+ nextStep_vi: "Thêm khóa vào <capability>.config.ts (parse<C>Config(env: EnvSource)) và lấy giá trị qua <capability>.options.ts; 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."
445
+ owner: op-retry
446
+ kind: check-finding
447
+
285
448
  BE_CONNECTION_DUPLICATE:
286
449
  title: "One database reached through more than one connection or injector"
287
450
  title_vi: "Một cơ sở dữ liệu có hơn một kết nối hoặc injector"
@@ -365,6 +528,42 @@ BE_CONTRACT_UNGUARDED:
365
528
  owner: op-retry
366
529
  kind: check-finding
367
530
 
531
+ BE_COOKIE_ATTRIBUTES:
532
+ title: "A cookie a door writes leaves its attributes to the defaults"
533
+ title_vi: "Cookie do cửa backend ghi ra không nêu rõ thuộc tính"
534
+ meaning_vi: "Một lời gọi `cookie` hoặc `setCookie` trên phản hồi của Express hay Fastify ở `<file>` không nêu `httpOnly: true`, `secure: true` hoặc `sameSite` là `lax`/`strict` bằng giá trị cố định, hoặc `Set-Cookie` được ghi tay thành header; cookie đó khi đó đọc được từ script của trang, đi qua HTTP thường hoặc đi theo request chéo trang."
535
+ causes_vi:
536
+ - "Vi phạm luật R141: gọi `res.cookie(name, value)` không có options, hoặc options thiếu một trong ba thuộc tính"
537
+ - "`sameSite: \"none\"`, `secure: false` hoặc `httpOnly` lấy từ một biến boolean không cố định"
538
+ - "Ghi header `Set-Cookie` bằng `setHeader`, `append` hoặc `header`"
539
+ nextStep_vi: "Khai báo một hằng số options `as const` trong module sở hữu cookie với `httpOnly: true`, `secure: true` và `sameSite: \"lax\"` hoặc `\"strict\"`, rồi truyền nó vào mọi lần `res.cookie`; bỏ mọi header `Set-Cookie` viết tay. 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."
540
+ owner: op-retry
541
+ kind: check-finding
542
+
543
+ BE_CORS_ALLOWLIST:
544
+ title: "API CORS has no exact origin allowlist"
545
+ title_vi: "CORS của API thiếu danh sách origin cụ thể"
546
+ meaning_vi: "API chưa giới hạn các origin của trình duyệt bằng danh sách cấu hình rõ ràng."
547
+ causes_vi:
548
+ - "Thiếu enableCors hoặc origin là wildcard, true hay danh sách rỗng"
549
+ nextStep_vi: "Áp dụng allowedOrigins của http-security hoặc danh sách origin cụ thể; cấu hình phải từ chối wildcard và origin không hợp lệ."
550
+ owner: op-retry
551
+ kind: check-finding
552
+
553
+ BE_CQRS_SHAPE:
554
+ title: "Application layer is not in CQRS shape"
555
+ title_vi: "Tầng ứng dụng sai hình CQRS"
556
+ meaning_vi: "Tầng application phải là CQRS: message Command<R>/Query<R> chỉ mang một trường readonly params kiểu ExecuteParams hoặc PublicExecuteParams, handler kế thừa ICQRSHandler và override process (không override execute). Bộ kiểm tra thấy tệp *.use-case.ts, service chuyển tiếp, message không khai kiểu kết quả, EventBus, @EventsHandler hoặc EventEmitter2, cũng như new EventEmitter() / extends EventEmitter, new Subject() (RxJS) dùng làm kênh, hoặc lớp giữ danh sách callback do nơi khác đăng ký (như *TransitionEmitter)."
557
+ causes_vi:
558
+ - "Còn lớp use case kiểu cũ"
559
+ - "Message thiếu kiểu kết quả Command<R>"
560
+ - "Handler override execute"
561
+ - "Dùng sự kiện trong tiến trình thay cho outbox"
562
+ - "class XTransitionEmitter { private emitter = new EventEmitter() } hoặc lớp tự giữ Set<callback> rồi gọi từng callback"
563
+ nextStep_vi: "Tách mỗi thao tác thành <action>.command.ts hoặc <action>.query.ts, <action>.handler.ts và <action>.contracts.ts; transport gọi bus trực tiếp; hiệu ứng phụ bắt buộc đi qua outbox trong transaction. 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 và mục của luật trong BE-CONVENTION; không thêm ngoại lệ, không tắt luật."
564
+ owner: op-retry
565
+ kind: check-finding
566
+
368
567
  BE_DEFAULT_DENY:
369
568
  title: "Open or untyped door"
370
569
  title_vi: "Cửa mở hoặc nhận dữ liệu không kiểu"
@@ -380,6 +579,19 @@ BE_DEFAULT_DENY:
380
579
  owner: op-retry
381
580
  kind: check-finding
382
581
 
582
+ BE_ENTITY_IN_CONTRACT:
583
+ title: "ORM entity leaks into a contract"
584
+ title_vi: "Entity ORM lộ ra hợp đồng"
585
+ meaning_vi: "Một kiểu có khai báo @Entity của ORM với nguồn gốc từ typeorm nằm trong tầm với của một chữ ký transport, hàm execute của handler, hợp đồng (*.contracts.ts) hoặc thông điệp (*.command.ts, *.query.ts), trực tiếp hay qua tham số kiểu và thuộc tính, nên cấu trúc bảng bị lộ ra ngoài capability; hoặc một tệp entity nhập @nestjs/graphql hay class-validator."
586
+ causes_vi:
587
+ - "Trả nguyên entity từ resolver hoặc controller"
588
+ - "Truyền entity xuyên tầng thay vì mã người dùng và vai trò"
589
+ - "Một thuộc tính của kiểu hợp đồng có kiểu là Array<OrderEntity>"
590
+ - "Entity gắn decorator @Field hoặc @IsString"
591
+ nextStep_vi: "Trả một projection (kiểu thường của hợp đồng) và truyền principal id cùng role; 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."
592
+ owner: op-retry
593
+ kind: check-finding
594
+
383
595
  BE_ENTRYPOINT_ONLY_IN_APPS:
384
596
  title: "Entrypoints only in `apps/*/src`"
385
597
  title_vi: "Entrypoint nằm ngoài apps"
@@ -392,6 +604,18 @@ BE_ENTRYPOINT_ONLY_IN_APPS:
392
604
  owner: op-retry
393
605
  kind: check-finding
394
606
 
607
+ BE_ERROR_CAUSE_DROPPED:
608
+ title: "A catch replaces the failure without carrying it"
609
+ title_vi: "Catch thay lỗi mà không giữ lỗi gốc"
610
+ meaning_vi: "Một `throw` thoát ra khỏi `catch` (hoặc handler `.catch`) ở `<file>` ném một lỗi mới mà không ném lại giá trị đã bắt và không mang nó theo; lỗi gốc cùng stack của nó mất hẳn. Lỗi thay thế phải nhận đúng giá trị đã bắt làm `cause` (luật R108, BE-ERROR-4)."
611
+ causes_vi:
612
+ - "catch (error) { throw new PlanError({ code: PlanErrorCode.Failed }) } thiếu `cause: error`"
613
+ - "Chỉ đọc `error.message` rồi dựng lỗi mới từ chuỗi đó"
614
+ - "catch { throw new PlanError(...) } không đặt tên cho giá trị đã bắt"
615
+ nextStep_vi: "Đặt tên cho giá trị bắt được (`catch (error)`) và truyền đúng nó làm `cause` của lỗi capability (`new <C>Error({ code, cause: error })`), hoặc ném lại chính nó. 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."
616
+ owner: op-retry
617
+ kind: check-finding
618
+
395
619
  BE_ERROR_HOME:
396
620
  title: "Error declared outside its capability"
397
621
  title_vi: "Lỗi không nằm ở capability sở hữu"
@@ -430,6 +654,17 @@ BE_EVENT_CLASS_CONTRACT:
430
654
  owner: op-retry
431
655
  kind: check-finding
432
656
 
657
+ BE_EVENT_CONSUMER_SHAPE:
658
+ title: "A message consumer is a transport door named after its event"
659
+ title_vi: "Consumer sự kiện sai chỗ, sai tên hoặc không chuyển eventId"
660
+ meaning_vi: "Một lớp implements `EventConsumer` nằm ngoài tệp `.consumer.ts` của `transport/message`, tên tệp không theo `eventName` của lớp sự kiện, `handle` không đọc `delivery.eventId`, hoặc một tệp `.consumer.ts` chứa lớp không phải consumer sự kiện."
661
+ causes_vi:
662
+ - "Vi phạm luật R153: giao hàng ít nhất một lần nên mỗi lần nhận lại phải dedupe; consumer chuyển `eventId` vào command để domain service claim trong `platform/inbox` cùng transaction với tác dụng của nó."
663
+ - "Consumer được đặt ở handler hoặc nơi khác, hoặc đặt tên theo ý riêng khiến bên phát và bên nhận không cùng một tên sự kiện."
664
+ 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: đặt consumer tại `transport/message/<event>.consumer.ts` (tên theo eventName, dấu chấm thành gạch ngang), khai báo `readonly event = <Lớp>Event`, và truyền `delivery.eventId` vào command được dispatch."
665
+ owner: op-retry
666
+ kind: check-finding
667
+
433
668
  BE_FEATURE_IMPORTS_FEATURE:
434
669
  title: "A feature never imports a feature"
435
670
  title_vi: "Feature import feature khác"
@@ -474,6 +709,89 @@ BE_FEATURE_SHAPE:
474
709
  owner: op-retry
475
710
  kind: check-finding
476
711
 
712
+ BE_FEATURE_THIN:
713
+ title: "A feature file holds logic"
714
+ title_vi: "Tệp feature chứa logic thay vì chỉ ủy quyền"
715
+ meaning_vi: "Mọi tệp thuộc tầng feature có vai trò trong ruleParams.be.thinRoles (handler, resolver, controller, consumer, processor, step, saga, saga-step, compensation, webhook, gateway, subscription, cli, mapper, service của saga) chỉ ánh xạ tham số rồi thực hiện MỘT lệnh gọi ủy quyền vào modules/ (service, command bus, query bus, event bus hoặc queue producer): không if/switch/vòng lặp/toán tử ba ngôi/&&/||/??, không try/catch hay throw, không tính toán (so sánh, phép toán, chuỗi mẫu có biểu thức), không gọi hàm nào khác ngoài lệnh ủy quyền duy nhất, hàm ánh xạ thuần (*.mapper.ts) và logger. Logic nghiệp vụ nằm ở modules/** nơi được đo độ phủ 100% từng tệp; điều phối (thứ tự bước, bù trừ, claim, fencing, retry) nằm ở platform/*; tầng feature được kiểm chứng bằng integration và e2e, không bằng unit spec. A webhook POST delegates only to its durable inbox recording intake and may return the declared literal PROVIDER_ACK; its optional GET challenge delegates once to the domain verifier and echoes only that result. Webhook doors and HTTP modules import or inject no CommandBus, QueryBus or feature command handler; the reactor or job alone dispatches the processing command after commit."
716
+ causes_vi:
717
+ - "Handler, consumer hoặc processor có if/switch/vòng lặp/try để quyết định nghiệp vụ thay vì để service quyết định"
718
+ - "Step hoặc saga service gọi hai service liên tiếp (điều phối nằm sai chỗ, phải thuộc platform/*)"
719
+ - "Mapper tính toán hoặc rẽ nhánh (giá trị mặc định bằng ||, so sánh, nối chuỗi) thay vì chỉ đổi tên và sao chép trường"
720
+ - "Lệnh cli chứa vòng lặp, try/catch hoặc phép gán thay vì gọi một service của platform"
721
+ nextStep_vi: "Chuyển phần logic vào service/policy/mapper thuộc modules/** (domain hoặc platform tùy loại), viết unit spec đạt 100% từng tệp ở đó, rồi để tệp feature chỉ gọi đúng một phương thức. Điều phối nhiều bước chuyển vào platform/* (saga, jobs, event-bus, queue). 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 và knowledge/patterns/be/*.yaml; không thêm ngoại lệ, không tắt luật."
722
+ owner: op-retry
723
+ kind: check-finding
724
+
725
+ BE_HAND_ROLLED_RETRY:
726
+ title: "Hand-rolled retry loop"
727
+ title_vi: "Tự viết vòng lặp thử lại thay vì dùng bộ trợ giúp chung"
728
+ meaning_vi: "Một vòng lặp bắt lỗi (catch hoặc .catch) rồi chờ trước khi thử lại được viết tay tại chỗ; chờ nghĩa là gọi setTimeout, hàm của node:timers/promises, hoặc một hàm khai báo ngoài platform/retry mà thân hàm chờ, dù tên hàm là gì. Vòng lặp như vậy không có giới hạn số lần thử mà bộ kiểm tra nhìn thấy được, không có jitter, và không có cách nào tôn trọng một abort signal."
729
+ causes_vi:
730
+ - "for (let i = 0; i < max; i++) { try { return await fn() } catch (e) { await sleep(backoffMs(i)) } }"
731
+ - "while (true) { try { return await fn() } catch (e) { await new Promise((r) => setTimeout(r, delayMs)) } }"
732
+ nextStep_vi: "Thử lại qua bộ trợ giúp chung platform/retry (giới hạn số lần thử, backoff theo cấp số nhân có jitter, nhận abort signal) thay vì viết vòng lặp tại chỗ; 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."
733
+ owner: op-retry
734
+ kind: check-finding
735
+
736
+ BE_HEALTH_SIGNALS:
737
+ title: "API does not expose both live and ready health signals"
738
+ title_vi: "API chưa cung cấp đủ tín hiệu live và ready"
739
+ meaning_vi: "Đồ thị module của API chưa đăng ký cả endpoint liveness và readiness."
740
+ causes_vi:
741
+ - "Thiếu route live hoặc ready hoặc controller chưa được đăng ký trong module của API"
742
+ nextStep_vi: "Đăng ký controller live và ready trong đồ thị module được API compose."
743
+ owner: op-retry
744
+ kind: check-finding
745
+
746
+ BE_HTTP_TIMEOUT:
747
+ title: "Outbound HTTP call without a timeout"
748
+ title_vi: "Gọi HTTP ra ngoài không có timeout"
749
+ meaning_vi: "Một lời gọi fetch, axios hoặc HttpService không nêu timeout hay signal huỷ, nên một bên ngoài im lặng giữ yêu cầu, socket và một chỗ worker vô hạn."
750
+ causes_vi:
751
+ - "fetch(url) không có { signal }"
752
+ - "axios.get hoặc post không có timeout"
753
+ - "axios.create không có timeout"
754
+ nextStep_vi: "Thêm signal: AbortSignal.timeout(<ms có tên>) cho fetch, hoặc timeout: <ms có tên> cho axios và HttpService. 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."
755
+ owner: op-retry
756
+ kind: check-finding
757
+
758
+ BE_INBOX_DEDUPE_MISSING:
759
+ title: "Webhook or consumer with no inbox dedupe"
760
+ title_vi: "Thiếu chống trùng lặp qua inbox"
761
+ meaning_vi: "Một handler của consumer (tệp *.consumer.ts trong transport/message) hoặc một handler mang @Public({ reason: PublicReason.SignedWebhook }) không bắt đầu bằng await inbox.claim(source, eventId) trên một receiver có kiểu Inbox của platform/inbox, hoặc có claim nhưng không return sớm khi claim trả false, nên khi bên gửi gửi lại (webhook không thấy phản hồi kịp, hàng đợi giao lại một thông điệp chưa được xác nhận) hành động sẽ chạy lại lần nữa thay vì là một no-op."
762
+ causes_vi:
763
+ - "@Public({ reason: PublicReason.SignedWebhook }) handle() { return this.service.charge() }"
764
+ - "class PaymentCapturedConsumer { handle() { return this.service.capture() } }"
765
+ - "await this.inbox.claim(source, id) rồi chạy tiếp mà không kiểm tra kết quả"
766
+ nextStep_vi: "Gọi await this.inbox.claim(source, eventId) trước mọi await khác và return khi nó trả false (if (!(await this.inbox.claim(source, eventId))) return), để một lượt giao lại trở thành no-op; 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."
767
+ owner: op-retry
768
+ kind: check-finding
769
+
770
+ BE_INFRA_OWNER:
771
+ title: "Infrastructure library used outside its owning capability"
772
+ title_vi: "Thư viện hạ tầng dùng ngoài năng lực sở hữu"
773
+ meaning_vi: "Mỗi thư viện hạ tầng thô có đúng một năng lực sở hữu: fetch/axios/got/undici/node:http chỉ ở platform/http; cache-manager/redis/ioredis chỉ ở integrations/cache hoặc integrations/redis; bullmq/kafkajs chỉ ở platform/messaging; setTimeout/setInterval/node:timers chỉ ở platform/{scheduling,retry,messaging,http}; winston chỉ ở platform/logging; dayjs/moment chỉ ở platform/{clock,primitives}; @nestjs/config, dotenv, @nestjs/event-emitter và @nestjs/schedule không được dùng."
774
+ causes_vi:
775
+ - "Gọi fetch thẳng trong client tích hợp"
776
+ - "Hẹn giờ bằng setTimeout trong service"
777
+ - "Dùng dayjs trong miền"
778
+ - "Gọi cổng HttpClient của platform/http ngoài một năng lực integrations, ví dụ service miền gọi thẳng endpoint token hay admin của Keycloak hoặc API gửi thư"
779
+ nextStep_vi: "Gọi qua cổng của năng lực sở hữu (HttpClient, Cache, MessagePublisher, Clock, Logger) được tiêm bằng Inject*(). 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 và mục của luật trong BE-CONVENTION; không thêm ngoại lệ, không tắt luật."
780
+ owner: op-retry
781
+ kind: check-finding
782
+
783
+ BE_INPUT_BOUNDED:
784
+ title: "Bounded input, depth limits, rate limits"
785
+ title_vi: "Input không giới hạn"
786
+ meaning_vi: "Một thuộc tính của lớp input ở `<file>` không có class-validator đúng với kiểu của nó (string thiếu @MaxLength, enum thiếu @IsEnum, mảng thiếu @ArrayMaxSize, object lồng thiếu @ValidateNested cùng @Type), hoặc phân trang bằng skip, offset, OFFSET hay số trang thay vì cursor."
787
+ causes_vi:
788
+ - "Thuộc tính của lớp input không có decorator class-validator nào, hoặc thiếu ràng buộc mà kiểu của nó đòi hỏi"
789
+ - "skip hoặc offset trong tùy chọn find*, OFFSET trong câu SQL, hoặc trường page, pageNumber, offset trong DTO"
790
+ - "Cửa mở bằng PublicReason.AuthHandshake hoặc PublicReason.SignedWebhook (đăng nhập, đăng ký, đặt lại mật khẩu, callback, webhook) không có @RateLimit(RateTier.Strict) của platform/http-security trên phương thức hay lớp của nó"
791
+ 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 và mục của luật trong tiêu chuẩn HFS; nợ cũ cần quyết định thiết kế nên op không thêm ngoại lệ, chỉ tách hoặc chuyển về đúng chủ."
792
+ owner: op-retry
793
+ kind: check-finding
794
+
477
795
  BE_INTEGRATION_SPEC_MISSING:
478
796
  title: "Every integration has an integration spec that proves its client through the test world"
479
797
  title_vi: "Tích hợp thiếu spec integration chứng minh client qua test world"
@@ -484,6 +802,72 @@ BE_INTEGRATION_SPEC_MISSING:
484
802
  owner: op-retry
485
803
  kind: check-finding
486
804
 
805
+ BE_JOB_FENCE_REQUIRED:
806
+ title: "Every write to a claimed job carries its required fencing token"
807
+ title_vi: "Ghi vào job đã claim không bắt buộc fencing token"
808
+ meaning_vi: "Một phương thức của `JobClaims` nhận job mà kiểu tham số không bắt buộc `expectedFencingToken: number`, một lời gọi dùng `as`, `!` hoặc type assertion để lách, hoặc một `catch` quanh lệnh ghi có guard nuốt lỗi `JobFencedOut`."
809
+ causes_vi:
810
+ - "Vi phạm luật R158: lease hết hạn chỉ cho phép claimant mới, không dừng được tiến trình cũ; chỉ token đơn điệu được kiểm tra nguyên tử tại nơi nhận lệnh ghi mới từ chối worker zombie, nên token phải là bắt buộc trong kiểu."
811
+ - "Token để optional, ép kiểu `as number`, hoặc `catch` ghi log rồi tiếp tục làm worker cũ vẫn gây tác dụng sau khi bị fence."
812
+ 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: khai báo `expectedFencingToken: number` bắt buộc trong kiểu, truyền đúng token trả về từ claim không ép kiểu, và để `FencedProcessor` xử lý lỗi (không `catch` quanh lệnh ghi trong step hay processor)."
813
+ owner: op-retry
814
+ kind: check-finding
815
+
816
+ BE_JOB_RUN_KEY:
817
+ title: "An external effect of a job step carries a run key with the fencing token"
818
+ title_vi: "Tác dụng ngoài của step không mang run key chứa fencing token"
819
+ meaning_vi: "Một lời gọi tới integration trong `steps/<step>.step.ts` không truyền đối số kiểu `RunKey`, hoặc `RunKey` được tạo bằng ép kiểu thay vì `JobClaims.runKey`."
820
+ causes_vi:
821
+ - "Vi phạm luật R159: một worker zombie có thể lặp lại tác dụng ngoài; nhà cung cấp chỉ chặn được lặp nếu khoá idempotency gồm `jobId`, bước và fencing token của lần claim."
822
+ - "Step truyền chuỗi cố định, hoặc `as RunKey` để tránh lỗi kiểu."
823
+ 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: tạo khoá bằng `this.claims.runKey(job, \"<step>\")` và truyền cho integration; việc đọc dữ liệu đưa về lớp application."
824
+ owner: op-retry
825
+ kind: check-finding
826
+
827
+ BE_JOB_SHAPE:
828
+ title: "A job processor extends the fenced base and a step implements JobStep"
829
+ title_vi: "Processor của job không kế thừa lớp fenced hoặc step đặt sai chỗ"
830
+ meaning_vi: "Một tệp `.processor.ts` nằm ngoài thư mục job của nó, tên khác job, không extends `FencedProcessor`; hoặc một tệp `.step.ts` nằm ngoài `steps/` hay không implements `JobStep`."
831
+ causes_vi:
832
+ - "Vi phạm luật R160: lớp cơ sở `FencedProcessor` claim job với token được tăng và kết thúc job qua các lệnh ghi có guard; một processor không kế thừa nó không có fence."
833
+ - "Processor hoặc step được đặt cạnh handler hay nơi khác, làm loại jobs không còn một chỗ duy nhất."
834
+ 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: đặt `<job>.processor.ts` trong `features/jobs/<job>/transport/queue/` với class extends `FencedProcessor`, và mỗi step trong `steps/<step>.step.ts` implements `JobStep`."
835
+ owner: op-retry
836
+ kind: check-finding
837
+
838
+ BE_JOB_WRITE_OUTSIDE_OWNER:
839
+ title: "The job row is written only by platform/jobs"
840
+ title_vi: "Dòng job bị ghi ngoài platform/jobs"
841
+ meaning_vi: "Một lời gọi `update`, `increment`, `save`, `insert`, `delete` hoặc query builder ghi vào entity của job nằm ngoài `platform/jobs`."
842
+ causes_vi:
843
+ - "Vi phạm luật R157: fencing token và trạng thái của job chỉ đổi qua `JobClaims` (claim, advance, complete, fail), mỗi hàm đều bắt buộc `expectedFencingToken`; ghi thẳng cho phép một worker zombie ghi đè job mà worker mới đang sở hữu."
844
+ - "Một handler, step hoặc domain service tự `manager.update(JobEntity, ...)` để tiện thay vì gọi cổng `JobClaims`."
845
+ 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: bỏ lệnh ghi trực tiếp, gọi `JobClaims.advance`, `complete` hoặc `fail` với `expectedFencingToken` của lần claim; việc chỉ đọc thì không bị cấm."
846
+ owner: op-retry
847
+ kind: check-finding
848
+
849
+ BE_JSON_PARSE_UNGUARDED:
850
+ title: "JSON.parse outside a try"
851
+ title_vi: "JSON.parse không nằm trong try"
852
+ meaning_vi: "Một lời gọi JSON.parse không nằm trong try trong cùng hàm, nên văn bản hỏng từ cơ sở dữ liệu, webhook hoặc nhà cung cấp gây ngoại lệ không được che và trả về 500."
853
+ causes_vi:
854
+ - "JSON.parse cột văn bản đã lưu"
855
+ - "JSON.parse thân webhook hay phản hồi nhà cung cấp"
856
+ nextStep_vi: "Bọc trong try và trả outcome có kiểu (hoặc DomainError) nêu rõ đang phân tích cái gì. 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."
857
+ owner: op-retry
858
+ kind: check-finding
859
+
860
+ BE_JWT_ALG_PINNED:
861
+ title: "JWT verification has no pinned asymmetric algorithm policy"
862
+ title_vi: "Xác minh JWT thiếu chính sách thuật toán bất đối xứng cố định"
863
+ meaning_vi: "Bộ xác minh chưa cố định thuật toán an toàn, nguồn cấu hình của issuer hoặc vai trò authenticated của principal Supabase."
864
+ causes_vi:
865
+ - "Danh sách algorithms rỗng, động hoặc chứa none hay HS256"
866
+ - "Issuer viết trực tiếp hoặc principal chấp nhận vai trò khác authenticated"
867
+ nextStep_vi: "Dùng danh sách literal gồm ES256, RS256 hoặc EdDSA, lấy issuer từ cấu hình và từ chối vai trò khác authenticated sau xác minh."
868
+ owner: op-retry
869
+ kind: check-finding
870
+
487
871
  BE_KIND_DECLARATION:
488
872
  title: "The trigger kinds in use are declared, whole and never unused"
489
873
  title_vi: "Loại kích hoạt đang dùng chưa khai báo đủ trong hfs.json"
@@ -517,6 +901,79 @@ BE_KIND_ISOLATION:
517
901
  owner: op-retry
518
902
  kind: check-finding
519
903
 
904
+ BE_LITE_PROOF_LANE_MISSING:
905
+ title: "A lite back end declares a pattern without an integration proof lane"
906
+ title_vi: "Back end lite khai báo pattern nhưng thiếu nhánh kiểm thử integration"
907
+ meaning_vi: "Ứng dụng lite khai báo event-bus, queue, webhooks, realtime, fenced-job, projection hoặc saga nhưng không có spec integration để chứng minh hành vi của pattern."
908
+ causes_vi:
909
+ - "Vi phạm luật R284: chỉ có unit spec, spec e2e hoặc contract, hoặc chưa có spec dưới src/tests/integration."
910
+ nextStep_vi: "Thêm spec integration dưới src/tests/integration với test chạy được mang tiêu đề `<pattern>/<kịch bản>: ...` cho từng kịch bản bắt buộc của pattern đã khai báo."
911
+ owner: op-retry
912
+ kind: check-finding
913
+
914
+ BE_LOGGER_REQUIRED:
915
+ title: "Catch swallows the failure"
916
+ title_vi: "Catch nuốt lỗi"
917
+ meaning_vi: "Một catch (hoặc .catch) rỗng, hoặc không ném lại, không gọi phương thức nào của Logger (từ platform/logging), cũng không trả outcome có kiểu mang cause, nên lỗi thật biến mất không để lại dấu vết; hoặc một lời gọi Logger đặt tên sự kiện bằng chuỗi dựng tại chỗ thay vì một thành viên enum trong <owner>.log-events.ts, hoặc dùng Logger của framework thay cho cổng Logger của platform/logging."
918
+ causes_vi:
919
+ - "catch {} rỗng"
920
+ - "catch chỉ trả null, false hoặc undefined"
921
+ - "catch chỉ gọi console hoặc một đối tượng không có kiểu Logger, vốn không phải logger port"
922
+ - "logger.info(`Checkout ${id} failed`) thay vì logger.info(OrderLogEvent.CheckoutFailed, { id })"
923
+ - "new Logger('X') của @nestjs/common"
924
+ nextStep_vi: "Ném lại lỗi, ghi log bằng logger của platform/logging, hoặc trả outcome có kiểu mang cause; 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."
925
+ owner: op-retry
926
+ kind: check-finding
927
+
928
+ BE_LOG_SECRET:
929
+ title: "Secret or personal data in a log call"
930
+ title_vi: "Bí mật hoặc dữ liệu cá nhân bị ghi vào log"
931
+ meaning_vi: "Một lời gọi trên cổng Logger của platform/logging truyền vào một giá trị có kiểu Secret (platform/config) hoặc Pii (identity), hay đọc ra từ một giá trị như vậy; hoặc một đối số có tên là thông tin xác thực (password, secret, token, apiKey, authorization, cookie, otp) hay định danh cá nhân (email, phone, fullName, idCard, birthDate), nên giá trị đó tồn tại lâu hơn yêu cầu và bị sao chép sang bên thứ ba."
932
+ causes_vi:
933
+ - "Ghi một giá trị kiểu Secret hoặc Pii, dù đặt tên là gì"
934
+ - "Ghi token hoặc mật khẩu vào log để gỡ lỗi"
935
+ - "Ghi email hoặc số điện thoại vào thông điệp lỗi"
936
+ - "Tạo một lỗi (lớp kế thừa Error, kể cả DomainError) mang giá trị kiểu Secret hoặc chuỗi có tên là thông tin xác thực (token, mật khẩu, authorization, clientSecret) trong thông điệp hay params"
937
+ nextStep_vi: "Ghi id của chủ sở hữu, độ dài, cờ có hay không, hoặc dạng đã che qua mask(...), không ghi giá trị thật. 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."
938
+ owner: op-retry
939
+ kind: check-finding
940
+
941
+ BE_MEMBER_DOC_MISSING:
942
+ title: "A public member has no doc block"
943
+ title_vi: "Thành viên công khai không có JSDoc"
944
+ meaning_vi: "Một thành viên công khai (phương thức, accessor, field hoặc signature) của class, interface hoặc type object được export ở `<file>` không có khối JSDoc ngay phía trên; người gọi chạm tới thành viên chứ không đọc doc của kiểu (luật R109, BE-COMMENT-3)."
945
+ causes_vi:
946
+ - "Phương thức public của service hoặc handler không có /** ... */"
947
+ - "Field của interface Request/Result được export không có doc"
948
+ - "Comment dòng `//` thay cho khối JSDoc"
949
+ nextStep_vi: "Thêm một khối JSDoc tiếng Anh ngay trên thành viên, nói nó dùng để làm gì hoặc gọi nó gây ra điều gì; private, protected, constructor và property gán hằng không cần. 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."
950
+ owner: op-retry
951
+ kind: check-finding
952
+
953
+ BE_MIGRATION_REVERSIBLE:
954
+ title: "Migration that cannot be undone"
955
+ title_vi: "Migration không có down đảo ngược được"
956
+ meaning_vi: "Một migration có up() nhưng thiếu down(), down() rỗng hoặc chỉ ném lỗi, nên bản phát hành cần quay lui không có đường đi và bảng migration ghi đã hoàn tác trong khi lược đồ vẫn đổi."
957
+ causes_vi:
958
+ - "Không có phương thức down"
959
+ - "down() rỗng"
960
+ - "down() chỉ throw"
961
+ nextStep_vi: "Viết down() là phép đảo ngược đúng của up(); khi up() xoá dữ liệu thì down() dựng lại cấu trúc và ghi rõ phần dữ liệu mất trong chú thích migration. 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."
962
+ owner: op-retry
963
+ kind: check-finding
964
+
965
+ BE_MODULE_DEFINITION_MISSING:
966
+ title: "A Nest module has no module definition or is registered without register"
967
+ title_vi: "Module Nest thiếu module-definition hoặc được đăng ký không qua register"
968
+ meaning_vi: "Mọi module Nest của sản phẩm (capability, module con, feature, module transport, nhóm cli) phải có tệp <x>.module-definition.ts và <x>.options.ts cạnh <x>.module.ts, lớp module kế thừa ConfigurableModuleClass của chính tệp đó, và khi được import thì chỉ được viết dạng XModule.register({...}), không import trần tên lớp."
969
+ causes_vi:
970
+ - "Có <x>.module.ts nhưng thiếu <x>.module-definition.ts hoặc <x>.options.ts bên cạnh"
971
+ - "Lớp module không kế thừa ConfigurableModuleClass lấy từ ./<x>.module-definition"
972
+ - "Module sản phẩm nằm trong mảng imports (hoặc nhóm compose của app) như một tên lớp trần thay vì XModule.register({...})"
973
+ nextStep_vi: "Tạo <x>.module-definition.ts (new ConfigurableModuleBuilder<XOptions>().setClassMethodName(\"register\").build()) và <x>.options.ts (export type XOptions = Record<string, never> nếu chưa có tùy chọn), cho lớp kế thừa ConfigurableModuleClass, và đăng ký bằng XModule.register({}); 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."
974
+ owner: op-retry
975
+ kind: check-finding
976
+
520
977
  BE_MODULE_HANDLER_REGISTRATION:
521
978
  title: "Handler not registered exactly once"
522
979
  title_vi: "Handler chưa đăng ký đúng một lần"
@@ -549,7 +1006,7 @@ BE_MODULE_SHAPE:
549
1006
  - "new ConfigurableModuleBuilder() không có kiểu options"
550
1007
  - "register không static, hoặc có forRoot, forFeature, registerAsync"
551
1008
  - "new XService() hoặc let ở cấp module tạo trạng thái toàn cục"
552
- - "Feature có hai module trong một thư mục transport (một module cho mỗi thao tác), có module-definition, hoặc @Module ngoài `<f>.module.ts` và `<f>-<protocol>.module.ts`; app import module ứng dụng của feature hoặc module transport không thuộc loại app cho phép (api: graphql/http/websocket; worker: schedule/message; cli: cli)"
1009
+ - "Feature có hai module trong một thư mục transport (một module cho mỗi thao tác), module-definition không nằm cạnh module của nó, hoặc @Module ngoài `<f>.module.ts` và `<f>-<protocol>.module.ts`; app import module ứng dụng của feature hoặc module transport không thuộc loại app cho phép (api: graphql/http/websocket; worker: schedule/message; cli: cli)"
553
1010
  - "Bộ máy đồ thị module: module đại diện của capability được đăng ký hai lần trong một app, thiếu isGlobal: true ở gốc app, hoặc bị module khác import"
554
1011
  - "Module con có hơn một nơi import, hoặc vừa nằm ở gốc app vừa được module khác import"
555
1012
  - "isGlobal: true xuất hiện ngoài gốc app apps/<app>/src/app.module.ts"
@@ -557,6 +1014,29 @@ BE_MODULE_SHAPE:
557
1014
  owner: op-retry
558
1015
  kind: check-finding
559
1016
 
1017
+ BE_OPERATION_CONTRACT:
1018
+ title: "A versioned operation has no typed contract"
1019
+ title_vi: "Thao tác có phiên bản không có hợp đồng kiểu"
1020
+ meaning_vi: "Mỗi thao tác `<tên>@<phiên bản>` của một app được khai báo đúng một lần trong bảng thao tác có kiểu `apps/<app>/src/operations.ts` (`OperationContract<Input, Output, RefusalCode>` đăng ký bằng `defineOperations`). Bộ kiểm tra thấy đầu vào hoặc đầu ra là `any`, `unknown`, `Record<string, unknown>`, kiểu generic chưa gán, hàm hoặc `Date`; mã từ chối không phải hợp các chuỗi literal đóng; hoặc route phục vụ bảng không nhận `OperationRequest<Table>` và trả `Promise<OperationReply<Table>>` của cùng một bảng."
1021
+ causes_vi:
1022
+ - "Đầu vào hoặc kết quả của thao tác khai báo là `unknown` nên client phải tự gõ kiểu tay"
1023
+ - "Mã từ chối khai báo là `string` thay vì hợp các literal như `\"DENIED\" | \"INVALID\"`"
1024
+ - "Route nhận `@Body() body: unknown` hoặc chỉ dùng một nửa `OperationRequest` / `OperationReply`"
1025
+ nextStep_vi: "Khai báo kiểu có tên, đóng cho đầu vào và đầu ra của từng thao tác, liệt kê mã từ chối bằng hợp literal, và cho route nhận `OperationRequest<typeof OPERATIONS>` và trả `Promise<OperationReply<typeof OPERATIONS>>`; chạy `starci app emit` để sinh `contracts/<app>/openapi.json`. 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 và mục BE-OPERATIONS-1; không thêm ngoại lệ, không tắt luật."
1026
+ owner: op-retry
1027
+ kind: check-finding
1028
+
1029
+ BE_OUTBOX_WRITE_TX:
1030
+ title: "The outbox is written by a domain service inside its transaction"
1031
+ title_vi: "Outbox bị ghi ngoài domain hoặc không trong transaction"
1032
+ meaning_vi: "`eventBus.publish(event, tx)` hoặc `enqueueX(payload, tx)` của một queue được gọi ngoài `modules/domain`, hoặc đối số `tx` không phải manager của transaction đang chạy (manager dùng chung được inject, một bí danh, manager ở ngoài hoặc thiếu đối số)."
1033
+ causes_vi:
1034
+ - "Vi phạm luật R152: một sự kiện hoặc job là hệ quả của thay đổi miền; dòng outbox phải cùng commit hoặc cùng rollback với thay đổi đó, nên chỉ domain service ghi và chỉ trong transaction của nó."
1035
+ - "Handler, consumer, integration hoặc platform tự phát sự kiện, hoặc gọi `publish` bằng manager ngoài transaction làm sự kiện bị mất hoặc phát dù giao dịch đã rollback."
1036
+ 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: đưa việc phát sự kiện vào domain service, gọi trong `this.entityManager.transaction(async (manager) => { ... })` và truyền đúng `manager` đó; handler hay consumer chỉ gọi domain service."
1037
+ owner: op-retry
1038
+ kind: check-finding
1039
+
560
1040
  BE_PATTERN_SPEC_MISSING:
561
1041
  title: "A declared pattern has its proof scenarios on the test world"
562
1042
  title_vi: "Pattern đã khai báo thiếu kịch bản chứng minh trên test world"
@@ -567,6 +1047,28 @@ BE_PATTERN_SPEC_MISSING:
567
1047
  owner: op-retry
568
1048
  kind: check-finding
569
1049
 
1050
+ BE_PROJECTION_SHAPE:
1051
+ title: "A projection speaks recompute and get, hides its entity, and the api only reads"
1052
+ title_vi: "Projection có phương thức ngoài recompute/get, lộ entity hoặc api gọi recompute"
1053
+ meaning_vi: "Một phương thức public của projection không phải `recompute*` hay `get*`, `index.ts` của projection export entity, hoặc một feature loại api gọi `recompute*`."
1054
+ causes_vi:
1055
+ - "Vi phạm luật R162: projection chỉ làm hai việc, tính lại bảng từ sự kiện nguồn hoặc đọc nó; entity không được lộ để nơi khác không phụ thuộc hình dạng bảng; api chỉ đọc, còn reactor hoặc job giữ bảng luôn mới."
1056
+ - "Thêm một phương thức `set` hay `update` vào projection, hoặc export entity qua index để tiện đọc."
1057
+ 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: đổi tên phương thức theo `recompute*`/`get*` (hoặc chuyển logic về domain service), bỏ export entity khỏi `index.ts`, và để reactor hoặc job gọi `recompute*`."
1058
+ owner: op-retry
1059
+ kind: check-finding
1060
+
1061
+ BE_PROJECTION_WRITE_OWNER:
1062
+ title: "A read-model table is written only by its own projection"
1063
+ title_vi: "Bảng read model bị ghi ngoài projection của nó"
1064
+ meaning_vi: "Một lệnh ghi vào entity của projection nằm ngoài tệp `<name>.projection.ts` cùng thư mục."
1065
+ causes_vi:
1066
+ - "Vi phạm luật R161: read model chỉ có một người ghi là `recompute*` của projection, idempotent và có thể replay từ sự kiện nguồn; ghi từ nơi khác làm bảng lệch khỏi nguồn mà replay không sửa được."
1067
+ - "Một domain service hoặc handler cập nhật thẳng bảng projection để nhanh."
1068
+ 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: bỏ lệnh ghi, để reactor hoặc job gọi `recompute*` của projection, và đọc qua `get*`."
1069
+ owner: op-retry
1070
+ kind: check-finding
1071
+
570
1072
  BE_PUBLIC_CONTRACT_FORM:
571
1073
  title: "Public contract form invalid"
572
1074
  title_vi: "Dạng hợp đồng công khai sai"
@@ -577,6 +1079,62 @@ BE_PUBLIC_CONTRACT_FORM:
577
1079
  owner: op-retry
578
1080
  kind: check-finding
579
1081
 
1082
+ BE_PUBLIC_REASON_CLOSED:
1083
+ title: "PublicReason differs from the closed public vocabulary"
1084
+ title_vi: "PublicReason khác danh sách lý do công khai cố định"
1085
+ meaning_vi: "Enum PublicReason của domain/identity hoặc lý do trên cửa công khai không thuộc đúng bốn tên Health, AuthHandshake, SignedWebhook và CatalogRead."
1086
+ causes_vi:
1087
+ - "Vi phạm R290: ứng dụng thêm lý do riêng như ToolToken hoặc ApiKey, hoặc bỏ một lý do chuẩn khỏi enum PublicReason."
1088
+ nextStep_vi: "Giữ đúng bốn thành viên chuẩn của PublicReason; xác thực route dùng API key hoặc tool token bằng guard riêng thay vì thêm lý do công khai."
1089
+ owner: op-retry
1090
+ kind: check-finding
1091
+
1092
+ BE_PUBLIC_SURFACE:
1093
+ title: "Owner public surface violated"
1094
+ title_vi: "Bề mặt công khai của owner bị vi phạm"
1095
+ meaning_vi: "Một import chui vào tệp bên trong owner khác thay vì đi qua index.ts của owner, một import cùng owner lại đi vòng qua index.ts của chính nó, một index.ts lồng nhau (không nằm ở gốc owner), hoặc index.ts không chỉ gồm các dòng export { } / export type { } có tên (export *, khai báo, import, export default) hoặc export quá nhiều tên."
1096
+ causes_vi:
1097
+ - "Import theo đường dẫn sâu như @modules/domain/plan/plan.service"
1098
+ - "Import cùng owner theo ./index hoặc ../index thay vì tệp khai báo ký hiệu"
1099
+ - "index.ts nằm trong thư mục con của owner, ví dụ persistence/index.ts"
1100
+ - "index.ts dùng export * hoặc chứa khai báo, hoặc vượt số export cho phép"
1101
+ nextStep_vi: "Import owner khác qua index.ts của nó, import cùng owner bằng đường dẫn tương đối tới tệp khai báo, mỗi owner chỉ một index.ts ở gốc và liệt kê tường minh những gì owner cung cấp; 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."
1102
+ owner: op-retry
1103
+ kind: check-finding
1104
+
1105
+ BE_QUERY_IN_LOOP:
1106
+ title: "Query inside a loop (N+1)"
1107
+ title_vi: "Truy vấn chạy trong vòng lặp (N+1)"
1108
+ meaning_vi: "Một lệnh đọc trên EntityManager (find, findOne, count, exists...) chạy một lần cho mỗi phần tử của vòng lặp for hoặc của map, forEach, flatMap, nên số lượt gọi cơ sở dữ liệu tăng theo đầu vào và một yêu cầu lớn giữ kết nối rất lâu."
1109
+ causes_vi:
1110
+ - "for (const id of ids) { await manager.findOne(...) }"
1111
+ - "Promise.all(ids.map((id) => manager.findOneBy(Entity, { id })))"
1112
+ nextStep_vi: "Đọc một lần trước vòng lặp bằng find({ where: { khóa: In(danhSachKhoa) }, take }) hoặc WHERE khóa = ANY($1), dựng Map theo khóa rồi tra trong vòng lặp; 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."
1113
+ owner: op-retry
1114
+ kind: check-finding
1115
+
1116
+ BE_QUERY_UNBOUNDED:
1117
+ title: "Read without a row bound"
1118
+ title_vi: "Truy vấn đọc không có giới hạn số dòng"
1119
+ meaning_vi: "Một lệnh find, findBy hoặc findAndCount trên EntityManager có thể trả về mọi dòng khớp mà không nêu take trong đối tượng tùy chọn, nên một khách hàng lớn biến nó thành quét toàn bảng nằm trong bộ nhớ."
1120
+ causes_vi:
1121
+ - "manager.find(Entity, { where }) không có take"
1122
+ - "findBy không thể mang giới hạn"
1123
+ - "Tùy chọn find là biến, không viết take ngay tại chỗ gọi"
1124
+ nextStep_vi: "Viết take với hằng có tên ngay trong đối tượng tùy chọn (manager.find(Entity, { where, take: LIST_ROWS_MAX })), hoặc phân trang theo con trỏ; câu SELECT thô do máy kiểm tra R86 xét; việc cần đủ mọi dòng (batch, xuất) lặp theo trang keyset chứ không cắt cụt kết quả âm thầm. 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."
1125
+ owner: op-retry
1126
+ kind: check-finding
1127
+
1128
+ BE_QUEUE_PRODUCER_SHAPE:
1129
+ title: "A queue producer writes the outbox with its own tx parameter"
1130
+ title_vi: "Producer của queue ghi outbox không bằng tham số tx của nó"
1131
+ meaning_vi: "Một hàm hoặc phương thức export trong `<queue>.queue.ts` ghi vào `QueueOutbox` mà không khai báo tham số `tx: EntityManager` hoặc không truyền chính tham số đó làm đối số đầu."
1132
+ causes_vi:
1133
+ - "Vi phạm luật R154: job được enqueue trong transaction của người gọi (`enqueueX(payload, tx)`), nếu không job có thể chạy dù giao dịch rollback hoặc mất khi commit thành công."
1134
+ 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: thêm tham số `tx: EntityManager` vào producer và gọi `outbox.write(tx, ...)`."
1135
+ owner: op-retry
1136
+ kind: check-finding
1137
+
580
1138
  BE_RAW_INJECT:
581
1139
  title: "Infrastructure dependency injected without its Inject*() decorator"
582
1140
  title_vi: "Phụ thuộc hạ tầng không được tiêm qua Inject*()"
@@ -603,6 +1161,86 @@ BE_READONLY_BOUNDARY:
603
1161
  owner: op-retry
604
1162
  kind: check-finding
605
1163
 
1164
+ BE_READY_REAL:
1165
+ title: "Readiness does not probe a required dependency"
1166
+ title_vi: "Readiness chưa kiểm tra phụ thuộc bắt buộc"
1167
+ meaning_vi: "Endpoint readiness có thể trả thành công khi phụ thuộc bắt buộc không sẵn sàng."
1168
+ causes_vi:
1169
+ - "Readiness dùng lại liveness hoặc không có probe phụ thuộc với timeout hữu hạn"
1170
+ nextStep_vi: "Gọi probe của phụ thuộc bắt buộc, giới hạn thời gian chờ và trả unavailable khi probe lỗi hoặc hết hạn."
1171
+ owner: op-retry
1172
+ kind: check-finding
1173
+
1174
+ BE_REALTIME_SHAPE:
1175
+ title: "A realtime file is not exactly one push door"
1176
+ title_vi: "Tệp realtime không phải đúng một cửa đẩy tin"
1177
+ meaning_vi: "Mỗi tệp realtime là đúng một cửa theo vai trò của tên tệp: `<kênh>.gateway.ts` có một lớp `@WebSocketGateway` với các handler `@SubscribeMessage`, còn `<x>.subscription.ts` có một lớp `@Resolver` với các thao tác `@Subscription`. Không có query, mutation, field resolver, route REST hoặc giao thức còn lại trong cùng tệp."
1178
+ causes_vi:
1179
+ - "Tệp có hai lớp cửa hoặc không có lớp cửa nào"
1180
+ - "Cửa không có handler @SubscribeMessage hoặc @Subscription nào"
1181
+ - "Resolver của subscription chứa thêm @Query hoặc @Mutation"
1182
+ - "Gateway và subscription bị trộn trong một tệp"
1183
+ nextStep_vi: "Tách thành đúng một cửa cho mỗi tệp và đặt tên theo vai trò (.gateway.ts cho socket, .subscription.ts cho GraphQL subscription); chuyển query, mutation hoặc route REST sang một feature api. 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 và knowledge/patterns/be/realtime.yaml; không thêm ngoại lệ, không tắt luật."
1184
+ owner: op-retry
1185
+ kind: check-finding
1186
+
1187
+ BE_REALTIME_TOPIC_SCOPE:
1188
+ title: "A realtime topic is not scoped to the principal"
1189
+ title_vi: "Chủ đề realtime không gắn với chủ thể đăng nhập"
1190
+ meaning_vi: "Chủ đề mà handler realtime truyền cho `RealtimeHub.subscribe` phải được dựng từ tham số `@CurrentPrincipal()` của domain/identity, để một client chỉ nghe được kênh thuộc về chủ thể của nó. Chủ đề lấy hoàn toàn từ đầu vào client là lỗ hổng rò rỉ dữ liệu của người khác."
1191
+ causes_vi:
1192
+ - "Chủ đề là một chuỗi cố định dùng chung cho mọi client"
1193
+ - "Chủ đề chỉ ghép từ tham số do client gửi"
1194
+ - "Handler không nhận @CurrentPrincipal() hoặc dùng decorator trùng tên của owner khác"
1195
+ nextStep_vi: "Thêm tham số `@CurrentPrincipal() principal` vào handler và dựng chủ đề từ principal.id (cộng với mã đã kiểm tra quyền sở hữu nếu cần). 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 và knowledge/patterns/be/realtime.yaml; không thêm ngoại lệ, không tắt luật."
1196
+ owner: op-retry
1197
+ kind: check-finding
1198
+
1199
+ BE_REALTIME_WRITES:
1200
+ title: "A realtime door can write"
1201
+ title_vi: "Cửa realtime có khả năng ghi dữ liệu"
1202
+ meaning_vi: "Cửa realtime chỉ đọc và đẩy tin: giá trị duy nhất được tiêm vào là `RealtimeHub` của platform/realtime. EntityManager, bus lệnh, hàng đợi, event bus hoặc service miền (có thể ghi) không bao giờ được tiêm vào cửa này; tin đẩy do một reactor gọi hub, không do cửa."
1203
+ causes_vi:
1204
+ - "Gateway tiêm EntityManager hoặc CommandBus để cập nhật dữ liệu khi nhận tin của client"
1205
+ - "Subscription tiêm service miền để đọc, nhưng service đó có phương thức ghi"
1206
+ - "Cửa tiêm một lớp giả danh RealtimeHub do owner khác khai báo"
1207
+ nextStep_vi: "Bỏ mọi thứ khác ngoài RealtimeHub khỏi cửa; việc ghi thuộc về một feature api, và nguồn dữ liệu đẩy đi do một reactor (transport/message) gọi RealtimeHub.publish sau khi sự kiện đến. 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 và knowledge/patterns/be/realtime.yaml; không thêm ngoại lệ, không tắt luật."
1208
+ owner: op-retry
1209
+ kind: check-finding
1210
+
1211
+ BE_REST_DOOR_EVIDENCE:
1212
+ title: "A REST route has no route-level reason"
1213
+ title_vi: "Route REST thiếu lý do ngay tại phương thức"
1214
+ meaning_vi: "Mỗi route REST trong cả lite và full phải chứng minh mục đích được phép của chính phương thức: probe, bắt tay xác thực, webhook đã ký, luồng byte thật hoặc bảng thao tác công khai có phiên bản. Import StreamableFile hoặc lý do ở cấp lớp không chứng minh route trả JSON."
1215
+ causes_vi:
1216
+ - "Controller trả JSON nhưng không có bằng chứng tại route"
1217
+ - "Một route khác hoặc import không dùng được lấy làm lý do cho cả controller"
1218
+ nextStep_vi: "Chuyển thao tác dữ liệu sang GraphQL hoặc khai báo bằng chứng hợp lệ ngay tại route; với API công khai có phiên bản, phục vụ bảng defineOperations có kiểu."
1219
+ owner: op-retry
1220
+ kind: check-finding
1221
+
1222
+ BE_REST_ROUTE_UNDECLARED:
1223
+ title: "A public REST route is absent from the operation table"
1224
+ title_vi: "Route REST công khai chưa thuộc bảng thao tác"
1225
+ meaning_vi: "Route REST công khai phải nhận OperationRequest và trả OperationReply của cùng một bảng defineOperations có thao tác đã khai báo. Một bảng rỗng, bảng không liên quan hoặc chỉ một nửa kiểu hợp đồng không đăng ký route."
1226
+ causes_vi:
1227
+ - "Route API công khai được viết riêng ngoài defineOperations"
1228
+ - "Đầu vào và đầu ra không dùng cùng một bảng thao tác"
1229
+ nextStep_vi: "Khai báo thao tác có kiểu trong bảng của ứng dụng và cho route nhận OperationRequest<typeof TABLE>, trả Promise<OperationReply<typeof TABLE>> của chính bảng đó."
1230
+ owner: op-retry
1231
+ kind: check-finding
1232
+
1233
+ BE_RETURN_TYPE:
1234
+ title: "Handler or public method without a return type"
1235
+ title_vi: "Handler hoặc phương thức công khai thiếu kiểu trả về"
1236
+ meaning_vi: "Một handler (Query, Mutation, Get, Post...) hoặc phương thức công khai của Injectable, Resolver, Controller không khai báo kiểu trả về, nên hợp đồng ra ngoài là thứ thân hàm tình cờ trả về hôm nay."
1237
+ causes_vi:
1238
+ - "Resolver hoặc controller method không có : Promise<T>"
1239
+ - "Phương thức công khai của service để kiểu suy luận"
1240
+ nextStep_vi: "Khai báo kiểu trả về tường minh (Promise<Type>). 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."
1241
+ owner: op-retry
1242
+ kind: check-finding
1243
+
606
1244
  BE_SAGA_CONSUMER_DEDUPE:
607
1245
  title: "A saga consumer passes the delivery id on"
608
1246
  title_vi: "Consumer của saga không chuyển id lần giao đi tiếp"
@@ -653,6 +1291,23 @@ BE_SAGA_STEP_COMPENSATION:
653
1291
  owner: op-retry
654
1292
  kind: check-finding
655
1293
 
1294
+ BE_SCHEMA_AUTHORITY:
1295
+ title: "Schema defined at runtime"
1296
+ title_vi: "Schema được định nghĩa lúc chạy"
1297
+ meaning_vi: "Một tệp để tiến trình đang chạy quyết định schema: một đối tượng tùy chọn của DataSource hoặc TypeORM không ghi literal synchronize: false, synchronize khác false, gọi dataSource.synchronize() hoặc dropDatabase(), có khóa migrationsRun (cấm dưới mọi dạng), gọi runMigrations() ngoài cli (lệnh migrate của apps/cli) và test world, dropSchema khác false, dựng schema bằng createSchemaBuilder()/build(), khóa synchronize trong tùy chọn của @Entity, hook onModuleInit/onApplicationBootstrap ghi dữ liệu qua EntityManager hoặc DataSource (seed chỉ chạy bằng một lệnh cli), chuỗi DDL (CREATE/ALTER/DROP TABLE, INDEX, TYPE, SCHEMA, EXTENSION, TRIGGER, FUNCTION, ADD/DROP CONSTRAINT...) ngoài persistence/migrations/, hoặc tìm entity và migration bằng glob. Schema chỉ đổi bằng migration do lệnh cli migrate chạy."
1298
+ causes_vi:
1299
+ - "new DataSource({ type: 'postgres' }) không ghi synchronize: false"
1300
+ - "synchronize: true, hoặc đọc từ biến môi trường"
1301
+ - "Chuỗi DDL trong .query ngoài migration"
1302
+ - "dropSchema: true, hoặc dataSource.createSchemaBuilder().build() lúc chạy"
1303
+ - "@Entity({ synchronize: false }): công tắc riêng cho từng entity"
1304
+ - "onModuleInit ghi dữ liệu bằng entityManager.save/insert/query INSERT: seed lúc khởi động ở mọi tiến trình"
1305
+ - "dataSource.runMigrations() gọi ngoài cli (lệnh migrate của apps/cli)"
1306
+ - "entities hoặc migrations khai bằng đường dẫn có ký tự *"
1307
+ nextStep_vi: "Viết một migration trong persistence/migrations/ của capability sở hữu bảng và liệt kê entity, migration tường minh; 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."
1308
+ owner: op-retry
1309
+ kind: check-finding
1310
+
656
1311
  BE_SCHEMA_OWNER:
657
1312
  title: "Entities and migrations live in the owning capability's `persistence/`"
658
1313
  title_vi: "Entity hoặc migration sai chủ"
@@ -663,6 +1318,38 @@ BE_SCHEMA_OWNER:
663
1318
  owner: op-retry
664
1319
  kind: check-finding
665
1320
 
1321
+ BE_SECRET_DEFAULT:
1322
+ title: "Literal default for a secret or URL"
1323
+ title_vi: "Giá trị mặc định cố định cho bí mật hoặc URL"
1324
+ meaning_vi: "Một giá trị kiểu Secret hoặc Url của platform/config có giá trị mặc định: đối số mặc định cho env.secret/url/host, toán tử ??, || hay ??= trên giá trị đó, hoặc chuỗi mặc định (\"\", localhost, 127.0.0.1, 0.0.0.0, http(s)://...) đưa vào hàm đọc trả về Secret hay Url. Thiếu biến thì app phải dừng khi boot, nêu tên khóa."
1325
+ causes_vi:
1326
+ - "env.secret(\"K\", \"dev\") hoặc env.url(\"K\", \"http://localhost\")"
1327
+ - "secret ?? new Secret(\"dev\") hoặc url || \"http://localhost:3000\""
1328
+ - "Hàm đọc trả về Secret hay Url được gọi với giá trị mặc định viết sẵn"
1329
+ nextStep_vi: "Bỏ giá trị mặc định, để phần kiểm tra cấu hình báo lỗi nêu tên khóa khi thiếu; 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."
1330
+ owner: op-retry
1331
+ kind: check-finding
1332
+
1333
+ BE_SECURITY_HEADERS:
1334
+ title: "API security headers are missing or x-powered-by is enabled"
1335
+ title_vi: "API thiếu header bảo mật hoặc còn bật x-powered-by"
1336
+ meaning_vi: "Entrypoint chưa áp dụng middleware header bảo mật của platform/http-security và tắt thông tin framework."
1337
+ causes_vi:
1338
+ - "Thiếu middleware bảo mật hoặc chưa tắt x-powered-by"
1339
+ nextStep_vi: "Áp dụng middleware của platform/http-security và disable x-powered-by trước khi phục vụ yêu cầu."
1340
+ owner: op-retry
1341
+ kind: check-finding
1342
+
1343
+ BE_SERVICE_ISOLATION:
1344
+ title: "A service app imports no sibling service app"
1345
+ title_vi: "Ứng dụng dịch vụ import trực tiếp ứng dụng dịch vụ anh em"
1346
+ meaning_vi: "Một tệp dưới `be/apps/<service>/` import (đường dẫn tương đối hoặc alias) một tệp dưới `be/apps/<other>/`, nên hai dịch vụ dính vào nhau thành một khối triển khai."
1347
+ causes_vi:
1348
+ - "Vi phạm luật R168: các dịch vụ chỉ dùng chung thư viện `be/src` và gặp nhau qua đường truyền (client API của dịch vụ kia, hoặc sự kiện theo hợp đồng đã vendor dưới `be/contracts/<service>/`)."
1349
+ 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: bỏ import, đưa phần dùng chung vào `be/src` hoặc gọi dịch vụ kia qua client/sự kiện của hợp đồng."
1350
+ owner: op-retry
1351
+ kind: check-finding
1352
+
666
1353
  BE_SOURCE_FORM:
667
1354
  title: "Back-end source file breaks the naming or form canon"
668
1355
  title_vi: "Tệp nguồn BE sai quy ước tên hoặc hình thức"
@@ -680,6 +1367,17 @@ BE_SOURCE_FORM:
680
1367
  owner: op-retry
681
1368
  kind: check-finding
682
1369
 
1370
+ BE_SPEC_FORM_ALL_ROLES:
1371
+ title: "A logic-role unit spec does not prove behavior through its declared subject"
1372
+ title_vi: "Spec của vai trò logic không chứng minh hành vi qua đối tượng đã khai báo"
1373
+ meaning_vi: "Spec của policy, mapper, guard, client và các vai trò logic phải lấy đối tượng từ Nest testing module đã compile với provider rõ ràng, không new đối tượng hay mock module. Chỉ expect(x).toBeDefined() không chứng minh kết quả hay trạng thái."
1374
+ causes_vi:
1375
+ - "Spec new đối tượng kiểm thử hoặc mock module của ứng dụng"
1376
+ - "Spec chỉ kiểm tra đối tượng tồn tại"
1377
+ nextStep_vi: "Dựng đối tượng bằng Test.createTestingModule với provider và double phù hợp, lấy bằng moduleRef.get, gọi hành vi rồi so sánh kết quả hoặc trạng thái cụ thể."
1378
+ owner: op-retry
1379
+ kind: check-finding
1380
+
683
1381
  BE_SPEC_PLACEMENT:
684
1382
  title: "A back-end spec lives in one of the four test layers"
685
1383
  title_vi: "Spec đặt ngoài bốn tầng test"
@@ -705,6 +1403,30 @@ BE_SPEC_QUALITY:
705
1403
  owner: op-retry
706
1404
  kind: check-finding
707
1405
 
1406
+ BE_SQL_INTERPOLATED:
1407
+ title: "SQL text built from a runtime value"
1408
+ title_vi: "Giá trị chạy lúc chạy bị ghép vào chuỗi SQL"
1409
+ meaning_vi: "Một phép thế trong thẻ sql không có kiểu SqlIdent, hoặc một lời gọi .query(...) có chuỗi SQL dựng bằng template hoặc phép cộng chuỗi, nên đầu vào có thể trở thành câu lệnh (SQL injection) và kế hoạch truy vấn không được dùng lại."
1410
+ causes_vi:
1411
+ - "Template literal có ${biến} trong câu SQL"
1412
+ - "Ghép chuỗi SQL bằng dấu +"
1413
+ - "Thẻ sql nhận một phép thế không có kiểu SqlIdent (một chuỗi thường, một số)"
1414
+ nextStep_vi: "Truyền giá trị bằng tham số đánh số ($1, $2) ở mảng đối số; tên cột hoặc bảng động đi qua ident(tên, danhSáchChoPhép), phép thế duy nhất thẻ sql chấp nhận; câu lệnh là hằng SqlText trong persistence/<tên>.sql.ts chứ không ghép ở nơi gọi. 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."
1415
+ owner: op-retry
1416
+ kind: check-finding
1417
+
1418
+ BE_SQL_OUTSIDE_PERSISTENCE:
1419
+ title: "Raw SQL outside persistence/<name>.sql.ts"
1420
+ title_vi: "SQL thô ngoài persistence/<name>.sql.ts"
1421
+ meaning_vi: "Có câu SQL viết ngoài hằng `sql` trong tệp persistence/<name>.sql.ts của năng lực sở hữu, hoặc `.query()` nhận một chuỗi không phải SqlText, hoặc có createQueryBuilder. SQL rải trong service và handler không được kiểm tra bảng sở hữu và giới hạn đọc."
1422
+ causes_vi:
1423
+ - "manager.query(\"SELECT ...\") viết thẳng trong handler hoặc service"
1424
+ - "createQueryBuilder dùng cho truy vấn phức tạp"
1425
+ - "SQL còn nằm trong *.repository.ts kiểu cũ"
1426
+ nextStep_vi: "Chuyển câu SQL thành hằng `sql` trong src/modules/<tầng>/<năng lực>/persistence/<tên>.sql.ts, kiểu dòng trả về và hàm ánh xạ ở <tên>.rows.ts, rồi gọi entityManager.query(HẰNG, [tham số]); thay QueryBuilder bằng tùy chọn find hoặc hằng sql. 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 và mục của luật trong BE-CONVENTION; không thêm ngoại lệ, không tắt luật."
1427
+ owner: op-retry
1428
+ kind: check-finding
1429
+
708
1430
  BE_SQL_RETURNING_SHAPE:
709
1431
  title: "UPDATE or DELETE with RETURNING is not wrapped in a SELECT"
710
1432
  title_vi: "UPDATE hoặc DELETE có RETURNING chưa được bọc trong SELECT"
@@ -749,9 +1471,55 @@ BE_SUPABASE_JWT_VERIFIED:
749
1471
  owner: op-retry
750
1472
  kind: check-finding
751
1473
 
752
- BE_TEST_TOPOLOGY:
753
- title: "One jest config, four projects live by folder; the world is the only test infrastructure and runs the repository's own stack for real"
754
- title_vi: "Cấu hình test lệch chuẩn"
1474
+ BE_TEST_BUILDER_ARRANGES:
1475
+ title: "A test data builder asserts or has a non-deterministic default"
1476
+ title_vi: "Builder dữ liệu test khẳng định hoặc có giá trị mặc định không ổn định"
1477
+ meaning_vi: "Builder chỉ sắp xếp dữ liệu: không import expect, jest hay @jest/globals, và giá trị mặc định cố định (id từ hằng hoặc fakeIds, ngày cố định) để test lỗi vẫn tái hiện được."
1478
+ causes_vi:
1479
+ - "expect, jest hoặc mô tả test được dùng trong tệp builder"
1480
+ - "Date.now(), new Date() không tham số, Math.random(), randomUUID() hoặc crypto.random* trong builder"
1481
+ nextStep_vi: "Trả về đối tượng hoặc dòng và để spec khẳng định; dùng id cố định hoặc fakeIds() và ngày cố định. 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."
1482
+ owner: op-retry
1483
+ kind: check-finding
1484
+
1485
+ BE_TEST_BUILDER_HOME:
1486
+ title: "A test data builder is outside the builders folder"
1487
+ title_vi: "Builder dữ liệu test nằm ngoài thư mục builders"
1488
+ meaning_vi: "Mọi hàm dựng dữ liệu test (hàm được export và tạo dòng trong cơ sở dữ liệu qua EntityManager, DataSource hoặc QueryRunner) chỉ được nằm trong src/tests/fixtures/builders/<khu>.builder.ts; SQL đi kèm nằm trong <khu>.sql.ts cạnh nó. Một mô-đun khác của cây test làm việc này là builder đặt sai chỗ, dù tên nó là gì."
1489
+ causes_vi:
1490
+ - "Mô-đun trong src/tests/world hoặc src/tests/fixtures (ngoài builders/) export hàm gọi save, insert hoặc upsert, hoặc chạy INSERT, qua EntityManager"
1491
+ - "Hàm seed hoặc anOrder viết ngay cạnh spec thay vì trong builders/"
1492
+ - "Tên tệp *.repository.ts, *.factory.ts hoặc *.fixture.ts trong src/tests (bị BE_SOURCE_FORM chặn cùng lúc)"
1493
+ nextStep_vi: "Chuyển hàm sang src/tests/fixtures/builders/<khu>.builder.ts, chuyển chuỗi SQL sang <khu>.sql.ts cạnh nó, sửa import trong spec. 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 thêm ngoại lệ, không tắt luật."
1494
+ owner: op-retry
1495
+ kind: check-finding
1496
+
1497
+ BE_TEST_CONSTRAINTS_ON:
1498
+ title: "A test switches database constraints off"
1499
+ title_vi: "Test tắt hoặc gỡ ràng buộc cơ sở dữ liệu"
1500
+ meaning_vi: "Cây src/tests luôn chạy với ràng buộc bật: không tắt khóa ngoại, trigger hay ràng buộc, không hoãn ràng buộc. Builder tạo đủ chuỗi cha thay vì bỏ qua khóa ngoại."
1501
+ causes_vi:
1502
+ - "session_replication_role, SET CONSTRAINTS ... DEFERRED, DEFERRABLE INITIALLY DEFERRED, ALTER TABLE ... DISABLE hoặc DISABLE TRIGGER trong một câu SQL của cây test"
1503
+ - "DROP CONSTRAINT trong SQL, hoặc QueryRunner.dropForeignKey(s), dropCheckConstraint(s), dropUniqueConstraint(s)"
1504
+ nextStep_vi: "Bỏ câu lệnh tắt ràng buộc và tạo đủ dòng cha bằng builder. 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 thêm ngoại lệ."
1505
+ owner: op-retry
1506
+ kind: check-finding
1507
+
1508
+ BE_TEST_ROW_BY_BUILDER:
1509
+ title: "A spec hand-builds rows instead of using a builder"
1510
+ title_vi: "Spec tự dựng dòng dữ liệu thay vì dùng builder"
1511
+ meaning_vi: "Spec đơn vị, integration hoặc e2e không tự chèn hay ghi dòng: không có INSERT/UPDATE/DELETE thô, không gọi save, insert, upsert, update, delete, remove trên EntityManager, DataSource hoặc QueryRunner, và không dựng cùng một entity bằng object literal nhiều hơn một lần. Dòng dữ liệu đến từ builder của khu vực đó."
1512
+ causes_vi:
1513
+ - "em.query với chuỗi INSERT INTO ... hoặc chuỗi INSERT/UPDATE/DELETE được truyền vào query trong spec"
1514
+ - "em.save({...}), em.insert(...) hoặc upsert trong spec"
1515
+ - "Cùng một entity được viết thành object literal từ lần thứ hai trở đi trong một spec"
1516
+ nextStep_vi: "Thêm hoặc mở rộng builder của khu vực (orderRow(overrides) cho spec đơn vị, orderBuilder(db).pending().build(overrides) cho integration và e2e) rồi gọi nó trong spec. 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."
1517
+ owner: op-retry
1518
+ kind: check-finding
1519
+
1520
+ BE_TEST_TOPOLOGY:
1521
+ title: "One jest config, four projects live by folder; the world is the only test infrastructure and runs the repository's own stack for real"
1522
+ title_vi: "Cấu hình test lệch chuẩn"
755
1523
  meaning_vi: "Cấu hình test lệch chuẩn: `<detail>`."
756
1524
  causes_vi:
757
1525
  - "Vi phạm luật R47: một `jest.config.js` (preset `@starci/jest-preset`) với bốn project `unit`, `integration`, `e2e`, `contract`; thư mục và hậu tố spec luôn khớp nhau; `diagnostics: false` + `isolatedModules: true`; `test:integration`, `test:e2e`, `test:contract` chạy qua `starci gate run --root <app> --tests <tên>`; `src/tests/world/` là nơi duy nhất dựng hạ tầng test và chỉ chứa `global-setup.ts`, `global-teardown.ts`, `use-test-world.ts`, `fakes/` và file gốc có hậu tố vai trò (kể cả `test-world.config.ts`)."
@@ -760,6 +1528,17 @@ BE_TEST_TOPOLOGY:
760
1528
  owner: op-retry
761
1529
  kind: check-finding
762
1530
 
1531
+ BE_THIRD_PARTY_OWNER:
1532
+ title: "A third-party SDK is imported outside its single integration owner"
1533
+ title_vi: "SDK bên thứ ba dùng ngoài chủ sở hữu tích hợp duy nhất"
1534
+ meaning_vi: "Các dependency của package.json ngoài danh sách thư viện thuần và framework phải có đúng một năng lực integrations/<provider> nhập chúng. Service miền và tính năng gọi API công khai của năng lực đó thay vì nhập SDK mạng hoặc IO trực tiếp."
1535
+ causes_vi:
1536
+ - "Service miền nhập nodemailer, openai, googleapis hoặc SDK mới"
1537
+ - "Hai năng lực tích hợp cùng nhập một SDK"
1538
+ nextStep_vi: "Chuyển import và giao thức nhà cung cấp vào một integrations/<provider> client, rồi tiêm cổng công khai của tích hợp vào service."
1539
+ owner: op-retry
1540
+ kind: check-finding
1541
+
763
1542
  BE_TIER_DIRECTION:
764
1543
  title: "Backend import against the tier direction"
765
1544
  title_vi: "Backend import ngược chiều tầng"
@@ -771,6 +1550,211 @@ BE_TIER_DIRECTION:
771
1550
  owner: op-retry
772
1551
  kind: check-finding
773
1552
 
1553
+ BE_TRANSACTION_EXTERNAL_CALL:
1554
+ title: "Transaction spans an external call"
1555
+ title_vi: "Giao dịch cơ sở dữ liệu bao trùm một lệnh gọi ra ngoài"
1556
+ meaning_vi: "Một lệnh gọi fetch, HTTP client hoặc SDK tích hợp chạy bên trong .transaction(async (tx) => ...), nên giao dịch giữ kết nối mở trong suốt thời gian lệnh gọi ra ngoài chạy; nếu lệnh gọi thất bại sau khi giao dịch đã ghi một phần, hoặc thành công nhưng giao dịch sau đó rollback, hai hệ thống lệch nhau và không có đường quay lại."
1557
+ causes_vi:
1558
+ - "this.manager.transaction(async (tx) => { await tx.save(Entity, row); await fetch(url) })"
1559
+ - "this.manager.transaction(async (tx) => { await this.paymentSdk.charge(id) })"
1560
+ nextStep_vi: "Commit giao dịch trước rồi mới gọi ra ngoài, hoặc ghi một thông điệp outbox bên trong giao dịch để một worker gửi đi sau khi commit; 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."
1561
+ owner: op-retry
1562
+ kind: check-finding
1563
+
1564
+ BE_TRANSPORT_SHAPE:
1565
+ title: "Transport does more than map and dispatch"
1566
+ title_vi: "Tầng giao tiếp làm nhiều hơn chuyển lệnh"
1567
+ meaning_vi: "Mỗi resolver, controller, gateway, consumer, job hoặc lệnh CLI chỉ ánh xạ đầu vào, gửi đúng một command hoặc query qua bus được tiêm và ánh xạ kết quả. Bộ kiểm tra thấy transport tiêm thứ khác ngoài InjectCommandBus/InjectQueryBus (và InjectRequestLocale cho REST), tiêm EntityManager, bọc phản hồi trong envelope, dùng GraphQLJSON hoặc cửa REST không có lý do."
1568
+ causes_vi:
1569
+ - "Resolver gọi service miền trực tiếp"
1570
+ - "Interceptor biến đổi phản hồi thành {success, message, data}"
1571
+ - "Đầu vào kiểu GraphQLJSON không kiểm tra"
1572
+ nextStep_vi: "Đưa logic vào handler của tính năng, để transport chỉ gọi commandBus.execute(new XCommand({ request, principal })) và trả kiểu kết quả; xóa envelope và thay GraphQLJSON bằng input có kiểu. 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 và mục của luật trong BE-CONVENTION; không thêm ngoại lệ, không tắt luật."
1573
+ owner: op-retry
1574
+ kind: check-finding
1575
+
1576
+ BE_TRUST_PROXY:
1577
+ title: "API proxy trust has no declared hop count"
1578
+ title_vi: "API chưa khai báo số hop proxy được tin cậy"
1579
+ meaning_vi: "API chưa nêu ranh giới proxy bằng số hop nguyên không âm nên địa chỉ khách có thể sai hoặc bị giả mạo."
1580
+ causes_vi:
1581
+ - "Thiếu trust proxy hoặc dùng true hay giá trị động chưa được giới hạn"
1582
+ nextStep_vi: "Khai báo số hop nguyên không âm phù hợp với ingress trong cấu hình khởi động API."
1583
+ owner: op-retry
1584
+ kind: check-finding
1585
+
1586
+ BE_TYPE_ESCAPE:
1587
+ title: "Type escape: cast, non-null assertion, any, Function or eval"
1588
+ title_vi: "Thoát kiểu: ép kiểu, non-null, any, Function hoặc eval"
1589
+ meaning_vi: "Có x as T (kể cả as never, as unknown as T), <T>x, x!, any, kiểu Function hoặc eval trong bất kỳ tệp nào, kể cả spec, nên trình biên dịch ngừng kiểm tra một giá trị mà thực tế có thể sai kiểu hoặc vắng mặt. Ép kiểu, x! và any do các luật mượn của typescript-eslint mà nhà máy cấu hình bật; Function và eval do luật no-function-or-eval của canon chặn."
1590
+ causes_vi:
1591
+ - "as never hoặc as unknown as X để lọt kiểm tra tham số, trong mã sản phẩm hay trong spec"
1592
+ - "x! thay cho việc kiểm tra tồn tại"
1593
+ - "Kiểu Function, eval hoặc new Function thay cho chữ ký có kiểu"
1594
+ nextStep_vi: "Cho giá trị đúng kiểu tham số cần (mock<T>() cho double trong spec), thu hẹp bằng guard trả về hoặc ném lỗi có kiểu, hoặc sửa chữ ký để sự vắng mặt không thể xảy ra. 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."
1595
+ owner: op-retry
1596
+ kind: check-finding
1597
+
1598
+ BE_UNNAMED_DATA_ACCESS:
1599
+ title: "Database reached without the named shared EntityManager"
1600
+ title_vi: "Truy cập cơ sở dữ liệu không qua EntityManager dùng chung có tên"
1601
+ meaning_vi: "Tham số hoặc thuộc tính kiểu EntityManager không mang bộ tiêm Inject<Kết nối>EntityManager của một kết nối đã khai báo trong hfs.json (tên na ná cũng không đạt), hoặc tiêm bằng thuộc tính, hoặc gọi .getRepository(...), hoặc dùng @InjectRepository / Repository<T> / TypeOrmModule.forFeature, hoặc tiêm DataSource / QueryRunner ngoài platform/database, cli (apps/cli, src/features/cli) và test world (kể cả viết kiểu DataSource làm kiểu trả về, giá trị await, token provide: hay lớp bọc trả ra kết nối); lớp nào ngoài handler ứng dụng, domain service và năng lực persistence của platform mà nhận, giữ hoặc trả EntityManager, Repository, DataSource hay QueryRunner đều là repository dù đặt tên gì (Store, Dao, Gateway, Persistence, hoặc module hàm export nhận EntityManager làm tham số đầu như authoring.statements.ts); ứng dụng đăng ký nhiều kết nối nên cách viết này trỏ vào kết nối mặc định hoặc gắn một handle vào đúng một entity, và giao dịch không đi qua được sang bảng thứ hai."
1602
+ causes_vi:
1603
+ - "constructor(@InjectFooEntityManager() private readonly manager: EntityManager) {} với Foo không phải kết nối khai báo"
1604
+ - "@InjectPrimaryEntityManager() private readonly manager: EntityManager (tiêm bằng thuộc tính)"
1605
+ - "this.dataSource.getRepository(CourseEntity).find()"
1606
+ - "constructor(private readonly dataSource: DataSource) {} ngoài module database của platform"
1607
+ - "constructor(@InjectRepository(CourseEntity) private readonly repo: Repository<CourseEntity>) {}"
1608
+ - "class AccountingLedgerRepository { appendPosting(manager: EntityManager, ...) {} } đặt ngoài handler và domain service (repository trá hình, kể cả khi tên không có hậu tố Repository)"
1609
+ nextStep_vi: "Nhận EntityManager dùng chung qua tham số constructor với bộ tiêm Inject<Kết nối>EntityManager() của kết nối khai báo trong hfs.json (ví dụ InjectPrimaryEntityManager()) rồi gọi trực tiếp manager.find(Entity, ...), manager.save(Entity, ...); DataSource chỉ do module database của platform dựng; 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."
1610
+ owner: op-retry
1611
+ kind: check-finding
1612
+
1613
+ BE_USER_COPY_LITERAL:
1614
+ title: "User-facing copy written in source"
1615
+ title_vi: "Chữ hiển thị cho người dùng viết thẳng trong mã nguồn"
1616
+ meaning_vi: "Một chuỗi chữ cố định xuất hiện ở nơi người dùng đọc được nó - tham số message của một *Error/*Exception, thuộc tính subject/title/body/text/message truyền vào notify/send/publish, hoặc thuộc tính message/description/title của một object trả về trong tầng transport - thay vì được tra theo khóa từ bộ danh mục messages theo từng capability qua cổng MessageCatalog kiểu platform/i18n."
1617
+ causes_vi:
1618
+ - "throw new PlanNotFoundError('Plan not found for this account')"
1619
+ - "this.notifier.send({ subject: 'Your plan has changed', templateId: 'plan-x' })"
1620
+ - "return { message: 'Plan updated successfully' }"
1621
+ nextStep_vi: "Thêm khóa vào catalog messages (vi và en) của capability hoặc feature liên quan, rồi gọi qua cổng MessageCatalog (messages.get('<khóa>', { ...tham số })) thay vì viết chuỗi tại chỗ; 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."
1622
+ owner: op-retry
1623
+ kind: check-finding
1624
+
1625
+ BE_WEBHOOK_INBOX_KEY:
1626
+ title: "A webhook intake does not claim its stable delivery key"
1627
+ title_vi: "Intake webhook không claim khóa giao hàng ổn định"
1628
+ meaning_vi: "Phương thức intake nhận tham số đầu hoặc thuộc tính DTO kết thúc bằng Id, hoặc thân thô cùng chữ ký, nhưng không claim Inbox bằng khóa giao hàng ổn định và dừng ngay khi nhận bản trùng; hoặc xử lý giao hàng ngay trong giao dịch ghi nhận."
1629
+ causes_vi:
1630
+ - "Đổi tên tham số hoặc dùng thuộc tính DTO kết thúc bằng Id để tránh kiểm tra Inbox"
1631
+ - "Claim dùng khóa khác, chạy sau xử lý hoặc không dừng khi nhận bản trùng"
1632
+ - "Callback process, lời gọi domain hoặc dispatch command xử lý giao hàng trong giao dịch ghi nhận"
1633
+ nextStep_vi: "Dùng Inbox của platform/inbox, claim khóa giao hàng ổn định và trả về ngay khi claim báo bản trùng; ghi nhận cùng sự kiện outbox có kiểu trong giao dịch, rồi để reactor hoặc job xử lý sau commit."
1634
+ owner: op-retry
1635
+ kind: check-finding
1636
+
1637
+ BE_WEBHOOK_SCOPE:
1638
+ title: "Public webhook traffic bypasses the webhook door"
1639
+ title_vi: "Lưu lượng webhook công khai đi ngoài cửa webhook"
1640
+ meaning_vi: "PublicReason.SignedWebhook được dùng ngoài tệp cửa webhook hoặc phương thức xác thực domain được khai báo chính xác trong webhookVerifiers; một route công khai có đường dẫn webhook, callback hoặc events cũng phải nằm trong cửa webhook."
1641
+ causes_vi:
1642
+ - "Controller API dùng nhãn SignedWebhook để bỏ qua xác thực của cửa webhook"
1643
+ - "Route công khai nhận callback hoặc sự kiện ở một transport khác"
1644
+ nextStep_vi: "Chuyển route vào cửa <provider>.webhook.ts của slot HTTP webhook, xác thực trước rồi chuyển giao cho intake domain và Inbox; chỉ khai báo phương thức xác thực domain bằng symbol chính xác trong webhookVerifiers."
1645
+ owner: op-retry
1646
+ kind: check-finding
1647
+
1648
+ BE_WEBHOOK_SHAPE:
1649
+ title: "A webhook door breaks the verified inbox handoff or provider response contract"
1650
+ title_vi: "Cửa webhook làm nhiều hơn xác thực và chuyển giao"
1651
+ meaning_vi: "Webhook của nhà cung cấp là một cửa ký số: A provider webhook door (`src/features/webhooks/<provider>/transport/http/<provider>.webhook.ts`) is one `@Controller` with exactly one `@Post` handler and optionally one `@Get` subscription challenge handler, each declared `@Public({ reason: PublicReason.SignedWebhook })`. It injects only the `WebhookSignatureService` of `platform/http-security` and services of `modules/domain`, holds no branch, loop or `try`, and its POST calls proof then exactly one domain intake that durably records the inbox receipt and outbox event in its transaction. A combined domain proof and intake call is allowed. POST returns nothing or exactly the own-feature exported `PROVIDER_ACK`, a literal object or string declared with `as const` or a readonly literal type; computed values, inline return objects and cross-feature constants are refused. GET starts with a declared domain verifier call and returns exactly that result, with no intake. The door and its HTTP module import or inject no CommandBus, QueryBus of @nestjs/cqrs or feature command handler; the resolved called intake dispatches no command/query bus or feature handler. A reactor or job consumes the durable receipt and dispatches after commit; unresolved helpers and dynamic receivers require semantic review."
1652
+ causes_vi:
1653
+ - "The webhook door or its HTTP module imports or injects CommandBus, QueryBus or a feature command handler, or the resolved called domain intake dispatches on a command/query bus or feature handler"
1654
+ - "The handler branches, loops, parses, maps or publishes instead of proving first and recording one durable receipt"
1655
+ - "The POST returns an inline object, computed value, input-derived constant or another feature's PROVIDER_ACK"
1656
+ - "The door declares two POST routes, more than one GET, or a GET that echoes anything except its declared domain verifier result"
1657
+ nextStep_vi: "Đưa mọi quyết định và mọi lần ghi vào một phương thức nhận của service trong modules/domain: verify first, then call one domain recordProviderDelivery-style method that claims the shared Inbox and records the receipt with its outbox event in one transaction. Let a reactor or job consume the durable receipt and dispatch the command. POST may return nothing or the own-feature exported literal PROVIDER_ACK; one optional GET echoes only its first declared domain verifier result. Keep the signed-webhook public reason and strict rate tier; follow knowledge/patterns/be/webhooks.yaml."
1658
+ owner: op-retry
1659
+ kind: check-finding
1660
+
1661
+ BE_WEBHOOK_UNVERIFIED:
1662
+ title: "A webhook handler runs code before its declared delivery or challenge proof"
1663
+ title_vi: "Handler webhook chạy mã trước khi xác thực giao hàng"
1664
+ meaning_vi: "Câu lệnh đầu tiên của handler webhook: The first statement of a webhook POST is an unconditional verification call to `WebhookSignatureService.verify` or a domain verifier, awaited when async, with no data access or other call before proof. A domain verifier is a method on a domain service whose declared class name ends in `Verifier` or `verify`, or a domain service method declared by symbol in the adjacent HTTP module's exported `webhookVerifiers` array. A domain method may verify with per-connection secrets before intake in the same call; invalid proof throws before intake. A fixed PROVIDER_ACK never replaces proof. The optional GET challenge handler starts with a declared domain verifier call (directly returned or bound to a const and then returned), awaited when async, and echoes only its exact result."
1665
+ causes_vi:
1666
+ - "The handler reads or assigns data before its first unconditional proof call"
1667
+ - "Proof is conditional, unawaited when async, or a call nested around other work"
1668
+ - "The verifier is a local lookalike or lacks a resolved domain service or declared verification method symbol"
1669
+ - "The GET returns the unverified challenge field or transforms its verifier result"
1670
+ nextStep_vi: "Đặt `this.<signature>.verify({ rawBody, signature, timestamp })` làm câu lệnh đầu tiên của handler, không điều kiện, dùng WebhookSignatureService của platform/http-security và thân yêu cầu thô (không phải bản đã serialize lại). POST must start with WebhookSignatureService.verify or the declared domain verifier, awaited when async; a combined proof and intake must refuse invalid proof before recording. GET must start with its declared domain challenge verifier and echo exactly that call result. Returning PROVIDER_ACK after POST intake never removes this first-statement requirement; follow knowledge/patterns/be/webhooks.yaml."
1671
+ owner: op-retry
1672
+ kind: check-finding
1673
+
1674
+ CI_TRIGGERS_RELEASE_ONLY:
1675
+ title: "Workflow trigger other than a release tag, a person or the runtime's main"
1676
+ title_vi: "Workflow có trigger không phải tag phát hành, người chạy tay hay main của runtime"
1677
+ meaning_vi: "Một workflow CI (gốc, ví dụ hoặc template của packages/hfs) khai báo trigger ngoài các loại được phép: push của tag v*, workflow_dispatch và, chỉ ở workflow gốc của runtime, push của nhánh main (không kèm paths hay bộ lọc ignore). Push nhánh khác, push main trong ví dụ hoặc template, pull_request, schedule hay workflow_call làm CI chạy sai thời điểm, và mọi app scaffold từ template sẽ thừa hưởng lỗi đó."
1678
+ causes_vi:
1679
+ - "Giữ trigger push hoặc pull_request của mô hình CI cũ"
1680
+ - "Thêm bộ lọc branches khác [main], hoặc paths, vào push; hoặc thêm push main vào ví dụ hoặc template của app"
1681
+ nextStep_vi: "Người giám sát đặt on của workflow gốc thành push với tags: ['v*'] và/hoặc branches: [main] cùng workflow_dispatch; ví dụ và template trong packages/hfs/templates/app/ci-workflows chỉ giữ tags: ['v*'] và workflow_dispatch (render lại ví dụ bằng starci app sync --write); không thêm trigger khác (docs/git-governance.md)."
1682
+ owner: supervisor
1683
+ kind: check-finding
1684
+
1685
+ CI_UPLOAD_NOT_SILENT:
1686
+ title: "Coverage upload can skip or fail silently"
1687
+ title_vi: "Bước tải coverage có thể bị bỏ qua hoặc lỗi mà không báo"
1688
+ meaning_vi: "Một bước codecov/codecov-action trong workflow CI (gốc, ví dụ hoặc template của packages/hfs) không xác thực bằng OIDC (use_oidc: true cùng permissions id-token: write), không đặt fail_ci_if_error: true, có continue-on-error: true, hoặc bị chặn bởi điều kiện đọc một secret hay CODECOV_TOKEN. Khi đó thiếu secret hay tải lỗi chỉ làm bước bị bỏ qua và job vẫn xanh, coverage thôi được báo mà không ai hay."
1689
+ causes_vi:
1690
+ - "Dùng token trong repository secret và đặt điều kiện if theo secret đó"
1691
+ - "Để continue-on-error hoặc quên fail_ci_if_error cho bước tải"
1692
+ nextStep_vi: "Người giám sát đặt use_oidc: true và fail_ci_if_error: true cho bước, cấp permissions id-token: write cho job, bỏ continue-on-error và điều kiện theo secret, rồi render lại workflow bằng starci app sync --write."
1693
+ owner: supervisor
1694
+ kind: check-finding
1695
+
1696
+ CLI_ONLY_ENTRY:
1697
+ title: "A tracked file runs a runtime script directly"
1698
+ title_vi: "Một file được theo dõi chạy trực tiếp script của runtime"
1699
+ meaning_vi: "Một prompt, tài liệu, skill, file knowledge, cấu hình, CI, hook hoặc đăng ký tác vụ chạy node scripts/..., chạy trực tiếp ui/harness-verbs.mjs hoặc npm run bọc script runtime thay vì starci <group> <verb>."
1700
+ causes_vi:
1701
+ - "Vi phạm luật R201: mọi hành động phải đi qua starci <group> <verb>."
1702
+ nextStep_vi: "Đổi lời gọi thành lệnh starci mà bộ kiểm tra gợi ý; nếu script chưa có verb thì thêm file catalog hoặc khai báo nội bộ trong modules/cli/commands/_internal.yaml."
1703
+ owner: op-retry
1704
+ kind: check-finding
1705
+
1706
+ DB_API_EXPOSED:
1707
+ title: "The database API never exposes pgmq and lite data access is closed"
1708
+ title_vi: "API cơ sở dữ liệu mở schema bị cấm"
1709
+ meaning_vi: "config.toml mở pgmq hoặc pgmq_public, hoặc ứng dụng lite mở API dữ liệu mà không có policy public_read rõ ràng."
1710
+ causes_vi:
1711
+ - "Vi phạm R259: cửa dữ liệu không khớp quy tắc front end chỉ dùng Supabase Auth."
1712
+ nextStep_vi: "Loại pgmq và pgmq_public khỏi schema API; đóng API dữ liệu lite hoặc khai báo policy public_read giới hạn cần thiết."
1713
+ owner: op-retry
1714
+ kind: check-finding
1715
+
1716
+ DB_APP_ROLE:
1717
+ title: "The back-end database role has explicit least privileges"
1718
+ title_vi: "Role cơ sở dữ liệu của back end có quyền quá rộng"
1719
+ meaning_vi: "Migration tại <path> tạo hoặc sửa role của ứng dụng với superuser, bypassrls hay thuộc tính thiếu an toàn, hoặc cấp all thay cho các quyền cần thiết; role mặc định là app_be và có thể khai báo bằng supabase.appRole."
1720
+ causes_vi:
1721
+ - "Vi phạm R255: app role vượt qua RLS hoặc được cấp quyền rộng hơn công việc của back end."
1722
+ - "Role không khai báo rõ noinherit và nobypassrls, hoặc nhận grant all trên schema hay bảng."
1723
+ nextStep_vi: "Khai báo app role với noinherit, nobypassrls và các thuộc tính không đặc quyền; bỏ superuser, bypassrls và grant all, rồi cấp từng quyền cần thiết."
1724
+ owner: op-retry
1725
+ kind: check-finding
1726
+
1727
+ DB_ARCHIVE_MUTATED:
1728
+ title: "Archived migrations are immutable and retire only with a new baseline"
1729
+ title_vi: "Archive migration bị thay đổi ngoài lần cắt baseline"
1730
+ meaning_vi: "Migration đã lưu trữ bị sửa hoặc xóa, file mới được đưa thẳng vào archive, hoặc migration đang hoạt động được lưu trữ mà không thêm baseline trong cùng thay đổi."
1731
+ causes_vi:
1732
+ - "Vi phạm R257: archive không giữ nguyên nội dung lịch sử của chuỗi migration."
1733
+ nextStep_vi: "Khôi phục file lưu trữ nguyên vẹn và tạo baseline thay thế trong cùng thay đổi khi cần lưu trữ chuỗi đang hoạt động."
1734
+ owner: op-retry
1735
+ kind: check-finding
1736
+
1737
+ DB_BASELINE_SHAPE:
1738
+ title: "An archived migration chain needs exactly one valid replacement baseline"
1739
+ title_vi: "Baseline của chuỗi migration lưu trữ sai hình dạng"
1740
+ meaning_vi: "Archive có migration nhưng thiếu hoặc thừa baseline đang hoạt động, marker không nêu stamp cuối của archive, hoặc stamp baseline không hợp lệ và không có bản đồ restamp rõ ràng."
1741
+ causes_vi:
1742
+ - "Vi phạm R257: chuyển chuỗi migration vào archive mà không tạo baseline đúng marker và stamp."
1743
+ nextStep_vi: "Chạy app db baseline để xem kế hoạch, kiểm tra marker và bản đồ restamp, rồi thi hành với --apply khi kế hoạch đã đúng."
1744
+ owner: op-retry
1745
+ kind: check-finding
1746
+
1747
+ DB_BE_ONLY_ACCESS:
1748
+ title: "Exposed product data is accessed through the back end"
1749
+ title_vi: "Dữ liệu exposed được mở trực tiếp cho trình duyệt"
1750
+ meaning_vi: "Migration tại <path> cấp quyền bảng hoặc sequence cho authenticated, khai báo policy cho authenticated, hoặc mở quyền anon ngoài policy select có tên kết thúc _public_read; đối tượng chưa được khai báo ngoại lệ supabase.directAccess."
1751
+ causes_vi:
1752
+ - "Vi phạm R254: Supabase phía front end chỉ phục vụ Auth; dữ liệu sản phẩm đi qua back end."
1753
+ - "Grant hoặc policy mới mở lại cửa dữ liệu trong schema exposed mà không có ngoại lệ rõ ràng."
1754
+ nextStep_vi: "Gỡ grant và policy truy cập trực tiếp; cho back end truy cập bằng app role. Nếu chủ ý mở đối tượng, khai báo bảng trong hfs.json supabase.directAccess; anon chỉ đọc qua policy select *_public_read."
1755
+ owner: op-retry
1756
+ kind: check-finding
1757
+
774
1758
  DB_CONFIG_POLICY:
775
1759
  title: "`supabase/config.toml` holds no literal credential (`env(NAME)` only), `auth.jwt_expiry <= 3600`, and signup/site_url/redirects equal the Supabase block of hfs.json"
776
1760
  title_vi: "config.toml lệch policy"
@@ -781,6 +1765,26 @@ DB_CONFIG_POLICY:
781
1765
  owner: op-retry
782
1766
  kind: check-finding
783
1767
 
1768
+ DB_CRON_OWNED:
1769
+ title: "A uniquely owned migration cron job only enqueues to a declared queue"
1770
+ title_vi: "Lịch pg_cron thiếu chủ sở hữu hoặc thực hiện nghiệp vụ"
1771
+ meaning_vi: "Tên lịch bị trùng, đích hàng đợi hoặc hàm tick không tồn tại, lệnh thực hiện DML nghiệp vụ hay HTTP, hoặc cùng tick còn được lập lịch ở ứng dụng."
1772
+ causes_vi:
1773
+ - "Vi phạm R258: pg_cron chỉ đưa việc vào hàng đợi, mỗi tick có đúng một nơi lập lịch và được hủy lịch khi loại bỏ."
1774
+ nextStep_vi: "Đặt lịch trong migration với tên duy nhất, dùng pgmq.send hoặc một hàm *_tick chỉ enqueue, bỏ lịch trùng và gọi cron.unschedule khi xóa tick."
1775
+ owner: op-retry
1776
+ kind: check-finding
1777
+
1778
+ DB_DEFINER_REVOKE:
1779
+ title: "Exposed security definer functions revoke browser execution"
1780
+ title_vi: "Function security definer chưa thu hồi quyền trình duyệt"
1781
+ meaning_vi: "Function security definer tại <path>, được khai báo bằng create hoặc alter function trong schema exposed, vẫn chưa revoke execute từ authenticated sau toàn bộ tập migration."
1782
+ causes_vi:
1783
+ - "Vi phạm R256: authenticated hoặc role công khai vẫn có thể gọi function chạy bằng quyền của chủ sở hữu."
1784
+ nextStep_vi: "Chuyển function sang schema không exposed hoặc thêm migration revoke execute on function <signature> from authenticated; giữ các yêu cầu search_path, revoke public và anon cùng các kiểm tra an toàn của R215."
1785
+ owner: op-retry
1786
+ kind: check-finding
1787
+
784
1788
  DB_DEFINER_SAFE:
785
1789
  title: "A `security definer` function pins `search_path`, lives outside exposed schemas (or is revoked from public in the same migration), declares a return type, and interpolates parameters only through `format('%I/%L')`"
786
1790
  title_vi: "Function security definer không an toàn"
@@ -801,6 +1805,36 @@ DB_DYNAMIC_DDL:
801
1805
  owner: op-retry
802
1806
  kind: check-finding
803
1807
 
1808
+ DB_EXTENSION_ALLOWED:
1809
+ title: "Database extensions belong to the declared allowlist"
1810
+ title_vi: "Extension cơ sở dữ liệu nằm ngoài danh sách cho phép"
1811
+ meaning_vi: "Migration tạo extension không thuộc danh sách extension mà chuẩn cơ sở dữ liệu cho phép."
1812
+ causes_vi:
1813
+ - "Vi phạm R259: thêm extension chưa được ruleParams.db.extensions cho phép."
1814
+ nextStep_vi: "Dùng extension trong danh sách chuẩn hoặc khai báo chính sách extension được phê duyệt trước khi thêm migration."
1815
+ owner: op-retry
1816
+ kind: check-finding
1817
+
1818
+ DB_FORCE_RLS_DEFAULT:
1819
+ title: "Tables owned by the app role force row level security"
1820
+ title_vi: "Bảng do app role sở hữu chưa bắt buộc RLS"
1821
+ meaning_vi: "Bảng tại <path> thuộc app role nhưng thiếu force row level security và không có ngoại lệ forceRls kèm lý do không rỗng trong hfs.json."
1822
+ causes_vi:
1823
+ - "Vi phạm R255: chủ sở hữu bảng có thể bỏ qua RLS khi không có force row level security."
1824
+ nextStep_vi: "Thêm alter table <schema>.<table> force row level security trong migration; nếu cần miễn, khai báo đúng bảng và lý do trong hfs.json supabase.forceRls."
1825
+ owner: op-retry
1826
+ kind: check-finding
1827
+
1828
+ DB_MIGRATION_DESTRUCTIVE:
1829
+ title: "Destructive DDL requires a contract migration with retired application references"
1830
+ title_vi: "Migration phá hủy chưa thuộc bước contract"
1831
+ meaning_vi: "DDL xóa hoặc thu hẹp schema nằm ngoài migration *_contract_*, hoặc đối tượng bị thay đổi vẫn được SQL hay entity của back end tham chiếu."
1832
+ causes_vi:
1833
+ - "Vi phạm R259: thực hiện bước phá hủy trước khi ứng dụng ngừng dùng đối tượng cũ."
1834
+ nextStep_vi: "Tách thay đổi thành expand và contract, bỏ tham chiếu cũ trong ứng dụng, rồi tạo migration <stamp>_contract_<name>.sql."
1835
+ owner: op-retry
1836
+ kind: check-finding
1837
+
804
1838
  DB_MIGRATION_SHAPE:
805
1839
  title: "A migration is named `<ts14>_<kebab>.sql`, stamped in valid UTC, appended after HEAD's active migrations, and immutable once on the base"
806
1840
  title_vi: "Migration sai hình dạng hoặc bị sửa"
@@ -811,6 +1845,16 @@ DB_MIGRATION_SHAPE:
811
1845
  owner: op-retry
812
1846
  kind: check-finding
813
1847
 
1848
+ DB_PGMQ_DECLARED:
1849
+ title: "Queue constants, migration-created pgmq queues and queue grants must agree"
1850
+ title_vi: "Khai báo hàng đợi pgmq lệch migration"
1851
+ meaning_vi: "Hằng hàng đợi không có pgmq.create trong migration đang hoạt động, hàng đợi đã tạo không có hằng khai báo, hoặc quyền trên đối tượng pgmq vượt vai trò ứng dụng."
1852
+ causes_vi:
1853
+ - "Vi phạm R258: mã nguồn, migration và quyền truy cập không khai báo cùng hàng đợi."
1854
+ nextStep_vi: "Đối chiếu hằng hàng đợi với pgmq.create và chỉ cấp quyền trên q_<queue>, a_<queue> cho vai trò ứng dụng."
1855
+ owner: op-retry
1856
+ kind: check-finding
1857
+
814
1858
  DB_POLICY_SHAPE:
815
1859
  title: "Policies are named `<table>_<role>_<action>`, write/FOR ALL never uses (true), anon/public only on `for select` `*_public_read`, no grant to anon without that policy, never `service_role`"
816
1860
  title_vi: "Policy sai hình dạng"
@@ -821,6 +1865,27 @@ DB_POLICY_SHAPE:
821
1865
  owner: op-retry
822
1866
  kind: check-finding
823
1867
 
1868
+ DB_POLICY_TAUTOLOGY:
1869
+ title: "Access policies use a meaningful principal predicate"
1870
+ title_vi: "Policy dữ liệu có điều kiện luôn đúng"
1871
+ meaning_vi: "Policy tại <path> dùng true, is not null hoặc so sánh một biểu thức với chính nó để mở dữ liệu, hoặc policy cho role trình duyệt không chứng minh điều kiện theo principal."
1872
+ causes_vi:
1873
+ - "Vi phạm R256: điều kiện luôn đúng không tạo ranh giới truy cập dữ liệu."
1874
+ - "Policy không gắn hàng dữ liệu với auth.uid(), auth.jwt() hoặc nguồn principal tương đương."
1875
+ nextStep_vi: "Viết điều kiện quyền sở hữu hoặc tenant dựa trên principal cho using và with check; bỏ các điều kiện true, is not null và x = x dùng thay cho kiểm soát truy cập."
1876
+ owner: op-retry
1877
+ kind: check-finding
1878
+
1879
+ DB_REALTIME_UNLISTED:
1880
+ title: "Realtime publication tables require declared direct access"
1881
+ title_vi: "Bảng Realtime chưa được khai báo truy cập trực tiếp"
1882
+ meaning_vi: "Migration tại <path> thêm bảng vào publication supabase_realtime nhưng bảng không có trong hfs.json supabase.directAccess."
1883
+ causes_vi:
1884
+ - "Vi phạm R254: publication Realtime mở một cửa dữ liệu trực tiếp chưa được khai báo."
1885
+ nextStep_vi: "Gỡ bảng khỏi publication supabase_realtime hoặc khai báo ngoại lệ có chủ ý trong hfs.json supabase.directAccess."
1886
+ owner: op-retry
1887
+ kind: check-finding
1888
+
824
1889
  DB_RLS_REQUIRED:
825
1890
  title: "Every `create table` in an exposed schema is followed by `enable row level security` in the same migration"
826
1891
  title_vi: "Bảng trong schema public thiếu RLS"
@@ -841,6 +1906,16 @@ DB_STORAGE_POLICY:
841
1906
  owner: op-retry
842
1907
  kind: check-finding
843
1908
 
1909
+ DB_STORAGE_PREFIX:
1910
+ title: "Private storage policies scope an ownership prefix"
1911
+ title_vi: "Policy storage thiếu prefix theo chủ sở hữu"
1912
+ meaning_vi: "Policy trên storage.objects tại <path> không ràng buộc storage.foldername(name) với principal và bucket không có id kết thúc _public."
1913
+ causes_vi:
1914
+ - "Vi phạm R256: chỉ kiểm tra bucket_id không giới hạn đối tượng theo chủ sở hữu."
1915
+ nextStep_vi: "Thêm điều kiện prefix sở hữu bằng storage.foldername(name) và principal trong using hoặc with check theo thao tác; chỉ dùng ngoại lệ cho bucket id kết thúc _public."
1916
+ owner: op-retry
1917
+ kind: check-finding
1918
+
844
1919
  DB_TYPES_DRIFT:
845
1920
  title: "`supabase/types/database.types.ts` is committed and equals what `contract:emit` regenerates from the migrations"
846
1921
  title_vi: "database.types.ts lệch với migrations"
@@ -851,6 +1926,92 @@ DB_TYPES_DRIFT:
851
1926
  owner: op-retry
852
1927
  kind: check-finding
853
1928
 
1929
+ DB_VAULT_WRITE:
1930
+ title: "Migrations contain no vault writes or role passwords"
1931
+ title_vi: "Migration ghi vault hoặc mật khẩu vai trò"
1932
+ meaning_vi: "SQL ghi bí mật vào vault hoặc khai báo PASSWORD khi tạo hay đổi vai trò; bí mật phải nằm trong luồng lưu giữ sops, không trong migration."
1933
+ causes_vi:
1934
+ - "Vi phạm R259: migration được dùng để tạo hoặc thay đổi giá trị bí mật."
1935
+ nextStep_vi: "Bỏ thao tác ghi bí mật khỏi migration và dùng luồng app secret; chỉ giữ khai báo vai trò không chứa mật khẩu."
1936
+ owner: op-retry
1937
+ kind: check-finding
1938
+
1939
+ DB_VIEW_UNSAFE:
1940
+ title: "Exposed views preserve the caller's access boundary"
1941
+ title_vi: "View exposed chưa bảo vệ quyền của người gọi"
1942
+ meaning_vi: "View hoặc materialized view tại <path> nằm trong schema exposed mà không có security_invoker và tập migration kết thúc khi quyền của anon cùng authenticated chưa được thu hồi."
1943
+ causes_vi:
1944
+ - "Vi phạm R256: view có thể mở dữ liệu qua quyền của chủ sở hữu thay vì quyền của người gọi."
1945
+ nextStep_vi: "Đặt security_invoker = true cho view hỗ trợ tùy chọn này hoặc thêm migration revoke quyền trên view từ anon và authenticated."
1946
+ owner: op-retry
1947
+ kind: check-finding
1948
+
1949
+ ENV_DUPLICATE:
1950
+ title: "One env key is sealed in two members"
1951
+ title_vi: "Một khoá env bị niêm phong trong hai member"
1952
+ meaning_vi: "Cùng một khoá môi trường xuất hiện trong hai member `.enc` khác nhau, nên giá trị nào được dùng không còn rõ."
1953
+ causes_vi:
1954
+ - "Vi phạm luật R248: một khoá chỉ sống trong một member; hai bản sao lệch giá trị là lỗi vận hành chờ xảy ra."
1955
+ - "Khoá được di chuyển sang member mới nhưng quên gỡ khỏi member cũ."
1956
+ nextStep_vi: "Giữ khoá ở đúng một member và gỡ nó khỏi member còn lại (`starci app secret set` rồi chỉnh lại member thừa)."
1957
+ owner: op-retry
1958
+ kind: check-finding
1959
+
1960
+ ENV_EXAMPLE_DRIFT:
1961
+ title: "An env example names a key nothing reads"
1962
+ title_vi: "File env mẫu nêu một khoá không ai đọc"
1963
+ meaning_vi: "Một file `*.env.example` liệt kê khoá mà không có EnvSource reader nào dùng: ví dụ đã lệch với các khoá code thật sự đọc."
1964
+ causes_vi:
1965
+ - "Vi phạm luật R248: file example là hợp đồng tài liệu của môi trường; khoá trong đó phải là khoá code đọc."
1966
+ - "Đổi tên hoặc xoá reader mà quên cập nhật file example (D2-23 `QUEUE_RELAY_*`)."
1967
+ nextStep_vi: "Sửa file example cho khớp các khoá `<app>.env.ts`/`*.config.ts` đọc, hoặc thêm reader nếu khoá thật sự cần."
1968
+ owner: op-retry
1969
+ kind: check-finding
1970
+
1971
+ ENV_PUBLIC_SEALED:
1972
+ title: "A NEXT_PUBLIC_* key is in sealed custody"
1973
+ title_vi: "Khoá NEXT_PUBLIC_* nằm trong khu vực niêm phong"
1974
+ meaning_vi: "Một member `.enc` giữ khoá `NEXT_PUBLIC_*`: giá trị public của front end là cấu hình công khai, không bao giờ vào custody bí mật."
1975
+ causes_vi:
1976
+ - "Vi phạm luật R248: đưa cấu hình public vào `.enc` làm người đọc tưởng nó là bí mật và kéo theo quy trình custody không cần thiết."
1977
+ - "`web.enc` chứa `NEXT_PUBLIC_*` (D2-11)."
1978
+ nextStep_vi: "Gỡ khoá `NEXT_PUBLIC_*` khỏi member và khai báo nó như public config (stack/`KEYS.md`)."
1979
+ owner: op-retry
1980
+ kind: check-finding
1981
+
1982
+ ENV_REQUIRED_UNSET:
1983
+ title: "A required env key is neither sealed nor declared"
1984
+ title_vi: "Khoá môi trường bắt buộc chưa được niêm phong hay khai báo"
1985
+ meaning_vi: "Mã nguồn đọc một khoá môi trường bắt buộc qua EnvSource nhưng khoá đó không nằm trong member `.enc` nào của `.starcistacks/<env>/secrets` cũng không được stack hay `KEYS.md` khai báo."
1986
+ causes_vi:
1987
+ - "Vi phạm luật R248: khoá mà code đọc, khoá được niêm phong và khoá stack truyền vào phải là cùng một tập; khoá bắt buộc thiếu cả chỗ niêm phong lẫn khai báo sẽ làm app không boot được."
1988
+ - "Khoá được thêm vào `<app>.env.ts` hoặc `*.config.ts` nhưng quên `starci app secret set` và quên khai báo trong `KEYS.md` hoặc stack."
1989
+ nextStep_vi: "Niêm phong khoá bằng `starci app secret set <env> <slug> <KEY>` hoặc khai báo nó trong `.starcistacks/<env>/runtime/env/KEYS.md` / phần `environment:` của stack."
1990
+ owner: op-retry
1991
+ kind: check-finding
1992
+
1993
+ ENV_SEALED_UNREAD:
1994
+ title: "A sealed key is read by nobody"
1995
+ title_vi: "Khoá niêm phong không ai đọc"
1996
+ meaning_vi: "Một member `.enc` trong `.starcistacks/<env>/secrets` giữ một khoá mà không có EnvSource reader nào đọc và không có consumer nào được khai báo (stack hay `KEYS.md`)."
1997
+ causes_vi:
1998
+ - "Vi phạm luật R248: khoá niêm phong thừa là custody chết — tên sai (như D2-06 `OPENCLAW_TURN_TIMEOUT_MS`), khoá cũ đã bỏ (D2-07/08) hoặc member sai."
1999
+ - "Code đổi tên khoá nhưng member niêm phong vẫn giữ tên cũ."
2000
+ nextStep_vi: "Gỡ khoá khỏi member, sửa tên cho khớp khoá code đọc, hoặc khai báo consumer của nó trong `KEYS.md` / stack."
2001
+ owner: op-retry
2002
+ kind: check-finding
2003
+
2004
+ ENV_VALUE_SHAPE:
2005
+ title: "A stored env value does not parse as its reader's type"
2006
+ title_vi: "Giá trị env đã lưu không đúng kiểu reader khai báo"
2007
+ meaning_vi: "Một giá trị trong member niêm phong (kiểm bằng `starci app secret verify <env>`) hoặc trong `*.env.example` không parse được theo kiểu reader EnvSource khai báo (int, bool `true`/`false`, url tuyệt đối, duration `250`/`30s`/`5m`/`2h`, hoặc một giá trị enum đã khai báo)."
2008
+ causes_vi:
2009
+ - "Vi phạm luật R249: app boot sẽ fail hoặc đọc sai ngầm khi giá trị không khớp parser — như `OPENCLAW_MANAGE_AGENTS=1` cho `env.bool` (D2-03)."
2010
+ - "Giá trị được set bằng định dạng khác (`1` thay `true`, `1000` thay `1s`, URL thiếu scheme)."
2011
+ nextStep_vi: "Sửa giá trị qua `starci app secret set <env> <slug> <KEY>` cho đúng kiểu reader khai báo, hoặc sửa example; verify in ra tên khoá và kiểu bị vi phạm, không in giá trị."
2012
+ owner: op-retry
2013
+ kind: check-finding
2014
+
854
2015
  FE_APP_ISOLATION:
855
2016
  title: "App imports another app"
856
2017
  title_vi: "Một app import app khác"
@@ -873,6 +2034,27 @@ FE_BLOCK_PRODUCT_HOOK_DEFINITION:
873
2034
  owner: other-op:interface.implement
874
2035
  kind: check-finding
875
2036
 
2037
+ FE_CALLBACK_SHAPE:
2038
+ title: "An auth callback performs more than code exchange and redirect"
2039
+ title_vi: "Callback xác thực làm thêm việc ngoài đổi mã và chuyển hướng"
2040
+ meaning_vi: "Handler của fe.route.callback chứa operation dữ liệu, logic sản phẩm, handler bổ sung hoặc phản hồi JSON thay vì chỉ đổi mã xác thực rồi chuyển hướng."
2041
+ causes_vi:
2042
+ - "Callback gọi operation API hoặc xử lý dữ liệu sản phẩm"
2043
+ - "Callback trả JSON hoặc định nghĩa thêm handler"
2044
+ nextStep_vi: "Giữ callback ở việc đọc mã, tạo client xác thực, đổi mã và chuyển hướng; chuyển logic sản phẩm về back end hoặc hook phù hợp."
2045
+ owner: op-retry
2046
+ kind: check-finding
2047
+
2048
+ FE_CLIENT_BOUNDARY:
2049
+ title: "Server first; `\"use client\"` only where allowed"
2050
+ title_vi: "use client sai chỗ"
2051
+ meaning_vi: "`\\\"use client\\\"` ở `<file>` (slot `<slot>` không được là client component)."
2052
+ causes_vi:
2053
+ - "Vi phạm luật R55: Server-first: `\"use client\"` chỉ ở `index.tsx` của block có tương tác, leaf có tương tác nội tại, và `error.tsx`/`global-error.tsx` (Next bắt buộc); layout/page là server component."
2054
+ 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."
2055
+ owner: op-retry
2056
+ kind: check-finding
2057
+
876
2058
  FE_CLIENT_REACHES_SERVER:
877
2059
  title: "Client module reaches a server-only import"
878
2060
  title_vi: "Mô-đun client với tới import chỉ dành cho máy chủ"
@@ -884,6 +2066,28 @@ FE_CLIENT_REACHES_SERVER:
884
2066
  owner: op-retry
885
2067
  kind: check-finding
886
2068
 
2069
+ FE_CLIENT_SERVER_IMPORT:
2070
+ title: "A client component imports nothing server-only"
2071
+ title_vi: "Client component nhập mã chỉ chạy ở server"
2072
+ meaning_vi: "Tệp `\"use client\"` ở `<file>` nhập `<what>` (`server-only`, `next/headers`, module Node), thứ chỉ tồn tại ở server."
2073
+ causes_vi:
2074
+ - "Vi phạm luật R55: Client component không nhập `server-only`, `next/headers` hay module Node; dữ liệu đến từ hook SWR trong `hooks/swr` gọi `modules/api`."
2075
+ 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; nợ cũ được chia thành các lane sửa cơ học, op không thêm ngoại lệ và không tắt luật."
2076
+ owner: op-retry
2077
+ kind: check-finding
2078
+
2079
+ FE_COMPONENT_API:
2080
+ title: "Front-end component exposes a non-canon surface"
2081
+ title_vi: "Component FE sai bề mặt công khai"
2082
+ meaning_vi: "Component phải có props và slot có kiểu được đặt tên, mỗi khối kết nối có bản trình bày tương ứng, trạng thái đi qua slot view, không mở cửa className hay thuộc tính CSS, không có bản sao trạng thái nghỉ hay prop placeholder, tên lớp nằm ở tệp đi kèm."
2083
+ causes_vi:
2084
+ - "Component nhận className từ ngoài"
2085
+ - "Khối kết nối không có bản trình bày thuần"
2086
+ - "Trạng thái dữ liệu tự chế thay vì qua SlotView"
2087
+ nextStep_vi: "Sửa chữ ký component theo luật drawing và tách phần trình bày; 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."
2088
+ owner: op-retry
2089
+ kind: check-finding
2090
+
887
2091
  FE_COMPONENT_DEEP_HOOK_IMPORT:
888
2092
  title: "Component imports a hook too deeply"
889
2093
  title_vi: "Thành phần nhập hook quá sâu"
@@ -917,6 +2121,38 @@ FE_CONNECTED_BLOCK_RENDER_PAIR:
917
2121
  owner: other-op:interface.implement
918
2122
  kind: check-finding
919
2123
 
2124
+ FE_CONSOLE_CALL:
2125
+ title: "No console in product source"
2126
+ title_vi: "console.* trong mã sản phẩm"
2127
+ meaning_vi: "`console.<method>` ở `<file>` không đi vào pipeline log nào, lộ chi tiết nội bộ trong devtools của người dùng và che mất việc xử lý thật."
2128
+ causes_vi:
2129
+ - "Vi phạm luật R65: Không `console.*` trong mã sản phẩm; trả outcome có kiểu, hiển thị trạng thái lỗi, hoặc để lỗi tới `error.tsx`."
2130
+ 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; nợ cũ được chia thành các lane sửa cơ học, op không thêm ngoại lệ và không tắt luật."
2131
+ owner: op-retry
2132
+ kind: check-finding
2133
+
2134
+ FE_COOKIE_ATTRIBUTES:
2135
+ title: "A written cookie leaves its attributes to the defaults"
2136
+ title_vi: "Cookie được ghi mà không nêu rõ thuộc tính"
2137
+ meaning_vi: "Một lời gọi `set` trên cookie phản hồi của Next (`response.cookies.set`, `cookies().set`) ở `<file>` không nêu `httpOnly` bằng một giá trị cố định, thiếu `secure`, hoặc có `sameSite` khác `lax`/`strict`; cookie phiên khi đó đọc được từ script của trang hoặc đi theo request chéo trang."
2138
+ causes_vi:
2139
+ - "Vi phạm luật R107: ghi cookie phiên bằng `set(name, value)` không có options, hoặc options thiếu `httpOnly: true`"
2140
+ - "`sameSite: \"none\"`, `secure: false` hoặc `httpOnly` lấy từ một biến boolean không cố định"
2141
+ nextStep_vi: "Khai báo một hằng số options `as const` trong module sở hữu cookie với `httpOnly: true` (chỉ `false` cho cookie tùy chọn mà script của trang cần đọc), `secure` lấy từ `modules/config` và `sameSite: \"lax\"` hoặc `\"strict\"`, rồi truyền nó vào mọi lần `set`. 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."
2142
+ owner: op-retry
2143
+ kind: check-finding
2144
+
2145
+ FE_COPY_IN_CALL:
2146
+ title: "A validation or toast call contains literal display copy"
2147
+ title_vi: "Lời gọi kiểm tra dữ liệu hoặc toast chứa chữ hiển thị gõ trực tiếp"
2148
+ meaning_vi: "Tệp <file> truyền chữ hiển thị trực tiếp vào thông báo Zod hoặc toast; chữ người dùng đọc phải được lấy từ catalog next-intl."
2149
+ causes_vi:
2150
+ - "Lời gọi z.string().email chứa câu báo lỗi gõ trực tiếp"
2151
+ - "Toast chứa tiêu đề hoặc mô tả gõ trực tiếp"
2152
+ nextStep_vi: "Đưa chữ hiển thị vào catalog ngôn ngữ và truyền kết quả t() vào thông báo kiểm tra dữ liệu hoặc toast."
2153
+ owner: op-retry
2154
+ kind: check-finding
2155
+
920
2156
  FE_CROSS_APP_DUPLICATE:
921
2157
  title: "File copied between apps"
922
2158
  title_vi: "Tệp chép nguyên giữa các app"
@@ -939,6 +2175,46 @@ FE_CUSTOM_HOOK_LOCATION:
939
2175
  owner: other-op:interface.implement
940
2176
  kind: check-finding
941
2177
 
2178
+ FE_DANGEROUS_HTML:
2179
+ title: "dangerouslySetInnerHTML only on script"
2180
+ title_vi: "dangerouslySetInnerHTML trên phần tử hiển thị"
2181
+ meaning_vi: "`<tag>` ở `<file>` dùng `dangerouslySetInnerHTML`: chuỗi bất kỳ (của người dùng, nhà cung cấp, catalog) trở thành mã HTML chạy được."
2182
+ causes_vi:
2183
+ - "Vi phạm luật R55: Chỉ được `dangerouslySetInnerHTML` trên `<script>` mang JSON-LD hoặc mã khởi tạo theme từ hằng số do app viết; nội dung khác render thành phần tử."
2184
+ 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; nợ cũ được chia thành các lane sửa cơ học, op không thêm ngoại lệ và không tắt luật."
2185
+ owner: op-retry
2186
+ kind: check-finding
2187
+
2188
+ FE_EFFECT_CLEANUP:
2189
+ title: "What an effect starts, its cleanup releases"
2190
+ title_vi: "Effect khởi động thứ không được dọn"
2191
+ meaning_vi: "`<what>` ở `<file>` (timer, frame, listener, observer, socket hoặc subscription) do effect hoặc `subscribe` khởi động, nhưng cleanup trả về không giải phóng đúng handle, target và listener đó; nó chạy tiếp sau khi component đã gỡ và set state vào chỗ không còn."
2192
+ causes_vi:
2193
+ - "Vi phạm luật R65: mọi thứ effect hoặc `subscribe` khởi động được cleanup trả về giải phóng cùng handle, target và listener (`clearTimeout(id)`, `cancelAnimationFrame(id)`, `removeEventListener` cùng type và listener, `.disconnect()`, `.close()`, `.unsubscribe()`)."
2194
+ 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; nợ cũ được chia thành các lane sửa cơ học, op không thêm ngoại lệ và không tắt luật."
2195
+ owner: op-retry
2196
+ kind: check-finding
2197
+
2198
+ FE_EFFECT_FETCH:
2199
+ title: "No data loading inside useEffect"
2200
+ title_vi: "Tải dữ liệu trong useEffect"
2201
+ meaning_vi: "`useEffect` ở `<file>` gọi `await`, `fetch` hoặc `.then`. Effect không có cache, không gộp request, không có trạng thái loading/lỗi và không huỷ được."
2202
+ causes_vi:
2203
+ - "Vi phạm luật R50: Đọc dữ liệu bằng hook SWR trong `hooks/swr` gọi operation của `modules/api`; component chỉ gọi hook."
2204
+ 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; nợ cũ được chia thành các lane sửa cơ học, op không thêm ngoại lệ và không tắt luật."
2205
+ owner: op-retry
2206
+ kind: check-finding
2207
+
2208
+ FE_ENV_OWNER:
2209
+ title: "Only `modules/config` reads the environment; no localhost fallback"
2210
+ title_vi: "Đọc biến môi trường ngoài modules/config"
2211
+ meaning_vi: "`<file>` đọc env (hoặc fallback localhost). Chỉ `modules/config` được đọc env."
2212
+ causes_vi:
2213
+ - "Vi phạm luật R49: `process.env`/`NEXT_PUBLIC_*` chỉ trong `modules/config`; không fallback `?? \"http://localhost…\"`; thiếu biến ở production thì ném lỗi khi load."
2214
+ 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 (nợ hàng loạt có codemod của HFS); không cần ai can thiệp thêm."
2215
+ owner: op-retry
2216
+ kind: check-finding
2217
+
942
2218
  FE_ERROR_BOUNDARY_MISSING:
943
2219
  title: "Next app lacks a required error or loading boundary"
944
2220
  title_vi: "Ứng dụng Next thiếu trang lỗi hoặc trang đang tải bắt buộc"
@@ -983,6 +2259,16 @@ FE_HREF_RESOLVES:
983
2259
  owner: op-retry
984
2260
  kind: check-finding
985
2261
 
2262
+ FE_HTTP_STATUS_COLLAPSE:
2263
+ title: "One `GraphQLResponse<T>` envelope; 401 and 403 become `refused`"
2264
+ title_vi: "Gộp mọi lỗi thành một nhánh"
2265
+ meaning_vi: "`<file>` gộp mọi lỗi thành một nhánh hoặc biến lỗi thành dữ liệu rỗng. UNAUTHENTICATED/FORBIDDEN (401/403) phải thành `refused`."
2266
+ causes_vi:
2267
+ - "Vi phạm luật R51: Một envelope `GraphQLResponse<T>` (`success`, `message`, `error`, `data`) trong `modules/api/graphql/types.ts`; 401/403 → `refused` (error link làm mới token một lần rồi đưa về đăng nhập); cấm `return null` khi lỗi; không hiển thị nguyên văn lỗi server làm lý do."
2268
+ 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 và mục của luật trong tiêu chuẩn HFS; nợ cũ cần quyết định thiết kế nên op không thêm ngoại lệ, chỉ tách hoặc chuyển về đúng chủ."
2269
+ owner: op-retry
2270
+ kind: check-finding
2271
+
986
2272
  FE_I18N_CATALOG:
987
2273
  title: "Same keys in every catalog, real catalog in specs, `pick` for clients"
988
2274
  title_vi: "Catalog ngôn ngữ lệch key"
@@ -993,6 +2279,16 @@ FE_I18N_CATALOG:
993
2279
  owner: op-retry
994
2280
  kind: check-finding
995
2281
 
2282
+ FE_I18N_FORMATTER:
2283
+ title: "Numbers, money and dates through the next-intl formatter"
2284
+ title_vi: "Định dạng số, tiền, ngày ngoài formatter của next-intl"
2285
+ meaning_vi: "`<file>` định dạng bằng `<what>` (`toLocale*String`, `new Intl.*`, `toFixed` hiển thị, ký hiệu tiền dán vào template hoặc thư viện ngày) thay vì formatter của next-intl, nên locale và múi giờ lệch giữa server và trình duyệt."
2286
+ causes_vi:
2287
+ - "Vi phạm luật R59: Số, tiền, ngày dùng `useFormatter()` / `getFormatter()` của next-intl; định dạng dùng chung khai báo một lần trong `modules/i18n`."
2288
+ 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; nợ cũ được chia thành các lane sửa cơ học, op không thêm ngoại lệ và không tắt luật."
2289
+ owner: op-retry
2290
+ kind: check-finding
2291
+
996
2292
  FE_I18N_KEYS:
997
2293
  title: "Catalog keys and the source that reads them agree"
998
2294
  title_vi: "Khóa catalog và mã nguồn đọc chúng không khớp"
@@ -1003,6 +2299,26 @@ FE_I18N_KEYS:
1003
2299
  owner: op-retry
1004
2300
  kind: check-finding
1005
2301
 
2302
+ FE_I18N_LITERAL:
2303
+ title: "No display text at any tier, no escape comment"
2304
+ title_vi: "Chữ hiển thị không đi qua t()"
2305
+ meaning_vi: "Chữ `\\\"<text>\\\"` ở `<file>:<line>` không đi qua `t()`."
2306
+ causes_vi:
2307
+ - "Vi phạm luật R58: Không chữ hiển thị (JSX text, `aria-label`, `title`, `placeholder`, `alt`, object copy) ở MỌI tầng, mọi ngôn ngữ; `<html lang>` lấy từ `[locale]`."
2308
+ 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 (nợ hàng loạt có codemod của HFS); không cần ai can thiệp thêm."
2309
+ owner: op-retry
2310
+ kind: check-finding
2311
+
2312
+ FE_I18N_NAVIGATION:
2313
+ title: "Navigation from modules/i18n/navigation"
2314
+ title_vi: "Điều hướng không đi qua next-intl"
2315
+ meaning_vi: "`<file>` dùng `next/link`, `useRouter`, `usePathname`, `redirect` của Next hoặc thẻ `<a>` cho route nội bộ: mất tiền tố `[locale]`, mất prefetch, tải lại cả trang."
2316
+ causes_vi:
2317
+ - "Vi phạm luật R59: `Link`, `useRouter`, `usePathname`, `redirect` lấy từ `modules/i18n/navigation`; liên kết ngoài mở tab mới phải có `rel=\"noopener noreferrer\"`."
2318
+ 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; nợ cũ được chia thành các lane sửa cơ học, op không thêm ngoại lệ và không tắt luật."
2319
+ owner: op-retry
2320
+ kind: check-finding
2321
+
1006
2322
  FE_I18N_PLACEMENT:
1007
2323
  title: "`next-intl`, `[locale]`, `modules/i18n/`"
1008
2324
  title_vi: "i18n sai chuẩn next-intl"
@@ -1013,49 +2329,156 @@ FE_I18N_PLACEMENT:
1013
2329
  owner: op-retry
1014
2330
  kind: check-finding
1015
2331
 
1016
- FE_NO_TESTS:
1017
- title: "Front end holds a test file, test tooling or a test dependency"
1018
- title_vi: "Front end có tệp test, công cụ test hoặc thư viện test"
1019
- meaning_vi: "Chuẩn của front end là không có test: repository có spec, e2e, cấu hình vitest, Playwright hay jest, script test, hoặc khai báo thư viện chạy test, dựng môi trường test, testing-library, axe trong package.json."
2332
+ FE_ICON_INLINE_SVG:
2333
+ title: "An inline SVG is outside a declared brand-mark leaf"
2334
+ title_vi: "SVG viết trực tiếp nằm ngoài leaf nhãn hiệu được khai báo"
2335
+ meaning_vi: "Tệp <file> vẽ SVG trực tiếp ngoài leaf nhãn hiệu được khai báo; glyph thông thường phải đến từ họ Heroicons thông qua leaf Icon."
1020
2336
  causes_vi:
1021
- - "Viết spec hoặc e2e cho component, hook hay trang"
1022
- - "Thêm vitest, Playwright, jest, testing-library hoặc axe vào package.json"
1023
- - "Thêm script test hoặc cấu hình test vào repository front end"
1024
- nextStep_vi: "Xóa tệp, cấu hình, script và thư viện test đó; front end không có ngoại lệ, kể cả thư mục scripts/. Kiểm tra giao diện bằng lint, typecheck và starci app check."
2337
+ - "Leaf Icon tự vẽ path hoặc SVG thay vì dùng Heroicons"
2338
+ - "Component vẽ SVG trực tiếp mà không phải leaf nhãn hiệu được khai báo"
2339
+ nextStep_vi: "Thay glyph bằng Heroicons qua leaf Icon; nếu là nhãn hiệu, đặt nó trong leaf nhãn hiệu và khai báo đúng chủ của leaf."
1025
2340
  owner: op-retry
1026
2341
  kind: check-finding
1027
2342
 
1028
- FE_OWNER_REACHABLE:
1029
- title: "Page feature not mounted by a route"
1030
- title_vi: "Feature trang chưa được route nào gắn vào"
1031
- meaning_vi: "Một feature trang, layout hoặc overlay của frontend không có đường import chạy thật từ bất kỳ tệp nào trong cây route của ứng dụng (thư mục src/app). Người dùng sẽ không bao giờ thấy nó vì không route nào hiển thị."
2343
+ FE_LIST_KEY:
2344
+ title: "List rows have a stable key and stable props"
2345
+ title_vi: "Danh sách thiếu key ổn định"
2346
+ meaning_vi: "`.map` ở `<file>` trả phần tử không có `key`, dùng chỉ số hoặc giá trị ngẫu nhiên làm `key`, hoặc truyền object/array literal cho component trong danh sách; hàng bị đổi state khi xoá hoặc sắp xếp lại, và memo không bao giờ trúng."
1032
2347
  causes_vi:
1033
- - "Quên import feature vào page.tsx hoặc layout.tsx tương ứng"
1034
- - "Route chỉ import kiểu của feature chứ không render nó"
1035
- - "Feature đã bị thay thế nhưng thư mục cũ vẫn còn"
1036
- nextStep_vi: "Op vừa tạo ra bản này sẽ được chạy lại để gắn feature vào route đúng hoặc xoá feature không dùng; không cần ai can thiệp thêm."
2348
+ - "Vi phạm luật R65: Phần tử trong `.map` có `key` lấy từ id của dữ liệu (không dùng index, `Math.random`, `Date.now`), và không truyền object/array literal cho component trong danh sách; hằng số được nâng ra ngoài."
2349
+ 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; nợ cũ được chia thành các lane sửa cơ học, op không thêm ngoại lệ và không tắt luật."
1037
2350
  owner: op-retry
1038
2351
  kind: check-finding
1039
2352
 
1040
- FE_PACKAGE_SHAPE:
1041
- title: "Packages build to `dist` with explicit exports and no dead unit"
1042
- title_vi: "Gói dùng chung sai hình dạng"
1043
- meaning_vi: "Package `<p>` export source chưa build (hoặc `export *`, hoặc unit không ai dùng)."
2353
+ FE_NATIVE_FORM_CONTROL:
2354
+ title: "No raw form controls, native images or raw structural tags in product tiers; the grammar renders them"
2355
+ title_vi: "Control form thô"
2356
+ meaning_vi: "`<tag>` thô ở `<file>`. Dùng renderer của grammar."
1044
2357
  causes_vi:
1045
- - "Vi phạm luật R63: `packages/*` build ra `dist`, `exports` tường minh, không `export *`, không unit chết; package UI chỉ chứa composites/branches/leaves + CSS token của family."
2358
+ - "Vi phạm luật R62: Không `<select>/<input>/<textarea>/<button>`, `<img>` thô và không thẻ cấu trúc thô (`<section>`, `<p>`, `<ul>`, `<h1>`...) ở tầng sản phẩm; dùng renderer grammar."
1046
2359
  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 và mục của luật trong tiêu chuẩn HFS; nợ cũ cần quyết định thiết kế nên op không thêm ngoại lệ, chỉ tách hoặc chuyển về đúng chủ."
1047
2360
  owner: op-retry
1048
2361
  kind: check-finding
1049
2362
 
1050
- FE_PURE_REACHES_DATA:
1051
- title: "Pure component reaches hooks or API"
1052
- title_vi: "Thành phần thuần chạm tới hook hoặc API"
1053
- meaning_vi: "Tệp component.tsx thuần lại nhập được hook hoặc lớp truyền tải API, kể cả qua alias hay barrel."
2363
+ FE_NATIVE_IMAGE:
2364
+ title: "next/image with a reserved size"
2365
+ title_vi: "Ảnh thô hoặc next/image thiếu kích thước"
2366
+ meaning_vi: "`<file>` dùng `<img>` thô, hoặc `Image` không có `width` và `height` (hay `fill` mà không có `sizes`): trình duyệt không giữ chỗ nên trang nhảy khi ảnh tải xong."
1054
2367
  causes_vi:
1055
- - "Nhập hook dữ liệu vào thành phần thuần"
1056
- - "Đường nhập gián tiếp đưa hook vào"
1057
- nextStep_vi: "Op interface.implement đưa phần dữ liệu lên index.tsx kết nối và giữ component.tsx thuần."
1058
- owner: other-op:interface.implement
2368
+ - "Vi phạm luật R62: Ảnh dùng `Image` của `next/image` với `width`+`height` hoặc `fill`+`sizes`; alt lấy từ catalog thông điệp."
2369
+ 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; nợ cũ được chia thành các lane sửa cơ học, op không thêm ngoại lệ và không tắt luật."
2370
+ owner: op-retry
2371
+ kind: check-finding
2372
+
2373
+ FE_NEXT_CONVENTIONS:
2374
+ title: "A Next convention of HFS is broken"
2375
+ title_vi: "Vi phạm quy ước Next của HFS"
2376
+ meaning_vi: "Một tệp dùng tên hoặc cách viết cũ của Next: middleware thay vì proxy.ts (từ Next 16), thiếu generateMetadata trên page, turbopack.root sai, hoặc dev và build dùng hai bundler khác nhau. Đây là kiểm tra con của R54 và R02."
2377
+ causes_vi:
2378
+ - "Đặt tên tệp middleware.ts trên Next 16 trở lên"
2379
+ - "Page không có generateMetadata"
2380
+ - "turbopack.root trỏ sai gốc workspace"
2381
+ 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."
2382
+ owner: op-retry
2383
+ kind: check-finding
2384
+
2385
+ FE_NO_ALERT:
2386
+ title: "The front end calls a browser alert"
2387
+ title_vi: "Front end gọi hộp thoại alert của trình duyệt"
2388
+ meaning_vi: "Tệp <file> gọi alert() hoặc biến thể toàn cục của nó, tạo hộp thoại trình duyệt ngoài hệ thống thông báo của sản phẩm."
2389
+ causes_vi:
2390
+ - "Dùng alert() để báo lỗi hoặc báo thao tác hoàn tất"
2391
+ - "Gọi window.alert hoặc globalThis.alert thay cho toast"
2392
+ nextStep_vi: "Dùng mô-đun toast của sản phẩm hoặc overlay được khai báo, với chữ hiển thị lấy từ catalog next-intl."
2393
+ owner: op-retry
2394
+ kind: check-finding
2395
+
2396
+ FE_NO_TESTS:
2397
+ title: "Front end holds a test file, test tooling or a test dependency"
2398
+ title_vi: "Front end có tệp test, công cụ test hoặc thư viện test"
2399
+ meaning_vi: "Chuẩn của front end là không có test: repository có spec, e2e, cấu hình vitest, Playwright hay jest, script test, hoặc khai báo thư viện chạy test, dựng môi trường test, testing-library, axe trong package.json."
2400
+ causes_vi:
2401
+ - "Viết spec hoặc e2e cho component, hook hay trang"
2402
+ - "Thêm vitest, Playwright, jest, testing-library hoặc axe vào package.json"
2403
+ - "Thêm script test hoặc cấu hình test vào repository front end"
2404
+ nextStep_vi: "Xóa tệp, cấu hình, script và thư viện test đó; front end không có ngoại lệ, kể cả thư mục scripts/. Kiểm tra giao diện bằng lint, typecheck và starci app check."
2405
+ owner: op-retry
2406
+ kind: check-finding
2407
+
2408
+ FE_OP_OUTSIDE_SWR_HOOK:
2409
+ title: "An API operation is used outside its SWR wrapper hook"
2410
+ title_vi: "Operation API được dùng ngoài hook bọc SWR"
2411
+ meaning_vi: "Tệp <file> import hoặc gọi operation của modules/api trực tiếp ở component, feature hoặc hook miền, bỏ qua hook bọc trong hooks/swr."
2412
+ causes_vi:
2413
+ - "Hook miền import operation API trực tiếp"
2414
+ - "Component hoặc trang máy chủ gọi operation thay vì dùng hook bọc"
2415
+ nextStep_vi: "Đặt import và lời gọi operation trong hook bọc tương ứng ở hooks/swr; các tệp tiêu thụ chỉ dùng hook đó."
2416
+ owner: op-retry
2417
+ kind: check-finding
2418
+
2419
+ FE_OUTCOME_KIND_UNHANDLED:
2420
+ title: "A switch over an Outcome names every kind"
2421
+ title_vi: "Switch trên Outcome thiếu nhánh"
2422
+ meaning_vi: "`switch` trên `kind` ở `<file>` có nhánh `ok` nhưng thiếu `<kinds>`. Mỗi kind là một màn người dùng có thể gặp: refused, invalid, not-found, unavailable."
2423
+ causes_vi:
2424
+ - "Vi phạm luật R51: Mọi `switch` trên `Outcome<T>` liệt kê đủ `ok`, `refused`, `invalid`, `not-found`, `unavailable`; không dựa vào `default`."
2425
+ 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; nợ cũ được chia thành các lane sửa cơ học, op không thêm ngoại lệ và không tắt luật."
2426
+ owner: op-retry
2427
+ kind: check-finding
2428
+
2429
+ FE_OWNER_REACHABLE:
2430
+ title: "Page feature not mounted by a route"
2431
+ title_vi: "Feature trang chưa được route nào gắn vào"
2432
+ meaning_vi: "Một feature trang, layout hoặc overlay của frontend không có đường import chạy thật từ bất kỳ tệp nào trong cây route của ứng dụng (thư mục src/app). Người dùng sẽ không bao giờ thấy nó vì không route nào hiển thị."
2433
+ causes_vi:
2434
+ - "Quên import feature vào page.tsx hoặc layout.tsx tương ứng"
2435
+ - "Route chỉ import kiểu của feature chứ không render nó"
2436
+ - "Feature đã bị thay thế nhưng thư mục cũ vẫn còn"
2437
+ nextStep_vi: "Op vừa tạo ra bản này sẽ được chạy lại để gắn feature vào route đúng hoặc xoá feature không dùng; không cần ai can thiệp thêm."
2438
+ owner: op-retry
2439
+ kind: check-finding
2440
+
2441
+ FE_PACKAGE_SHAPE:
2442
+ title: "Packages build to `dist` with explicit exports and no dead unit"
2443
+ title_vi: "Gói dùng chung sai hình dạng"
2444
+ meaning_vi: "Package `<p>` export source chưa build (hoặc `export *`, hoặc unit không ai dùng)."
2445
+ causes_vi:
2446
+ - "Vi phạm luật R63: `packages/*` build ra `dist`, `exports` tường minh, không `export *`, không unit chết; package UI chỉ chứa composites/branches/leaves + CSS token của family."
2447
+ 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 và mục của luật trong tiêu chuẩn HFS; nợ cũ cần quyết định thiết kế nên op không thêm ngoại lệ, chỉ tách hoặc chuyển về đúng chủ."
2448
+ owner: op-retry
2449
+ kind: check-finding
2450
+
2451
+ FE_PAGE_METADATA_MISSING:
2452
+ title: "Every page exports metadata"
2453
+ title_vi: "Trang không khai báo metadata"
2454
+ meaning_vi: "`<file>` là `page.tsx` nhưng không export `metadata` hoặc `generateMetadata`, nên mọi trang cùng một tiêu đề trên tab, lịch sử, liên kết chia sẻ và kết quả tìm kiếm."
2455
+ causes_vi:
2456
+ - "Vi phạm luật R54: Mỗi `page.tsx` export `metadata` (hoặc `generateMetadata` khi tiêu đề lấy từ dữ liệu), tiêu đề và mô tả lấy từ catalog thông điệp."
2457
+ 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; nợ cũ được chia thành các lane sửa cơ học, op không thêm ngoại lệ và không tắt luật."
2458
+ owner: op-retry
2459
+ kind: check-finding
2460
+
2461
+ FE_PROPS_MUTABLE:
2462
+ title: "Component props can be written"
2463
+ title_vi: "Props của component có thể bị ghi"
2464
+ meaning_vi: "Kiểu props của một hàm render được export ở `<file>` có field hoặc index signature thiếu `readonly`, hoặc giữ một collection có thể thay đổi (`T[]`, `[A, B]`, `Array<T>`), trực tiếp hay trong object lồng nhau (luật R110, FE-TYPING-2)."
2465
+ causes_vi:
2466
+ - "interface XProps { title: string } thiếu `readonly`"
2467
+ - "readonly items: Item[] thay vì `readonly Item[]`"
2468
+ - "Field của kiểu phần tử khai báo cùng file thiếu `readonly`"
2469
+ nextStep_vi: "Đánh dấu `readonly` cho mọi field và index signature của props, viết collection là `readonly T[]`, `readonly [A, B]` hoặc `ReadonlyArray<T>`. 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."
2470
+ owner: op-retry
2471
+ kind: check-finding
2472
+
2473
+ FE_PURE_REACHES_DATA:
2474
+ title: "Pure component reaches hooks or API"
2475
+ title_vi: "Thành phần thuần chạm tới hook hoặc API"
2476
+ meaning_vi: "Tệp component.tsx thuần lại nhập được hook hoặc lớp truyền tải API, kể cả qua alias hay barrel."
2477
+ causes_vi:
2478
+ - "Nhập hook dữ liệu vào thành phần thuần"
2479
+ - "Đường nhập gián tiếp đưa hook vào"
2480
+ nextStep_vi: "Op interface.implement đưa phần dữ liệu lên index.tsx kết nối và giữ component.tsx thuần."
2481
+ owner: other-op:interface.implement
1059
2482
  kind: check-finding
1060
2483
 
1061
2484
  FE_PURE_WORLD_HOOK:
@@ -1144,6 +2567,16 @@ FE_ROUTE_HANDLER_FORBIDDEN:
1144
2567
  owner: op-retry
1145
2568
  kind: check-finding
1146
2569
 
2570
+ FE_ROUTE_HARDCODED:
2571
+ title: "A route is built by modules/routes"
2572
+ title_vi: "Đường dẫn route viết tay"
2573
+ meaning_vi: "`<path>` ở `<file>` là route viết thẳng ở nơi gọi. Route đổi chỗ thì mọi bản sao trỏ vào 404 mà không kiểm tra kiểu nào bắt được."
2574
+ causes_vi:
2575
+ - "Vi phạm luật R57: Mọi href và lời gọi điều hướng (`router.push`, `redirect`) dùng hàm dựng href của `modules/routes`, không viết chuỗi đường dẫn ở nơi gọi."
2576
+ 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; nợ cũ được chia thành các lane sửa cơ học, op không thêm ngoại lệ và không tắt luật."
2577
+ owner: op-retry
2578
+ kind: check-finding
2579
+
1147
2580
  FE_ROUTE_ONE_PAGE:
1148
2581
  title: "Route must mount exactly one page"
1149
2582
  title_vi: "Route phải mount đúng một thành phần trang"
@@ -1165,6 +2598,16 @@ FE_SERVER_ACTION_FORBIDDEN:
1165
2598
  owner: op-retry
1166
2599
  kind: check-finding
1167
2600
 
2601
+ FE_SERVER_ONLY_MARK:
2602
+ title: "A module that uses the server starts with import server-only"
2603
+ title_vi: "Module dùng API server không có dấu server-only"
2604
+ meaning_vi: "Module `<file>` nhập `<what>` (`next/headers`, `next/server`, `next-intl/server`, module Node hoặc một module đã là server-only) nhưng không mở đầu bằng `import \"server-only\"`, nên không gì chặn client component nhập nó trực tiếp hoặc qua hook."
2605
+ causes_vi:
2606
+ - "Vi phạm luật R55: Module dùng API chỉ có ở server mở đầu bằng `import \"server-only\"` (tệp route `page`/`layout`/`route`/`proxy` là server component sẵn, không cần)."
2607
+ 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; nợ cũ được chia thành các lane sửa cơ học, op không thêm ngoại lệ và không tắt luật."
2608
+ owner: op-retry
2609
+ kind: check-finding
2610
+
1168
2611
  FE_SERVICE_ROLE_FORBIDDEN:
1169
2612
  title: "A service-role or credential-shaped environment value enters the front end"
1170
2613
  title_vi: "Service role hoặc biến môi trường bí mật đi vào front end"
@@ -1175,6 +2618,16 @@ FE_SERVICE_ROLE_FORBIDDEN:
1175
2618
  owner: op-retry
1176
2619
  kind: check-finding
1177
2620
 
2621
+ FE_SIZE_AND_STATE_BUDGET:
2622
+ title: "Hook and state budgets; no hand-written poll loop"
2623
+ title_vi: "Vượt ngân sách hook và state"
2624
+ meaning_vi: "`<unit>` có `<n>` hook dữ liệu / `<m>` useState / vòng poll tự viết."
2625
+ causes_vi:
2626
+ - "Vi phạm luật R65: Component ≤ 300 dòng, connected unit ≤ 6 hook dữ liệu và ≤ 6 `useState`; không vòng polling tự viết; một cơ chế làm tươi mỗi resource. (Phần kích thước dùng `HFS_SIZE_GROWTH`.)"
2627
+ 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 và mục của luật trong tiêu chuẩn HFS; nợ cũ cần quyết định thiết kế nên op không thêm ngoại lệ, chỉ tách hoặc chuyển về đúng chủ."
2628
+ owner: op-retry
2629
+ kind: check-finding
2630
+
1178
2631
  FE_SLOT_FILE_ROLE:
1179
2632
  title: "A front-end file is owned by a slot and named by it"
1180
2633
  title_vi: "Tệp không đúng vai trò trong slot"
@@ -1185,6 +2638,19 @@ FE_SLOT_FILE_ROLE:
1185
2638
  owner: op-retry
1186
2639
  kind: check-finding
1187
2640
 
2641
+ FE_SOURCE_FORM:
2642
+ title: "Front-end source file breaks the layout, naming or comment canon"
2643
+ title_vi: "Tệp nguồn FE sai quy ước tên, vị trí hoặc chú thích"
2644
+ meaning_vi: "Tệp FE phải nằm đúng thư mục tầng với tên cố định, export hàm mũi tên trùng tên thư mục, có JSDoc tiếng Anh, không emoji và không có chữ tiếng Việt trong định danh, chuỗi ký tự, chú thích hay tên test (trừ danh mục i18n và slot fixture i18n); luật ESLint nêu trong lời nhắn chỉ rõ phần sai."
2645
+ causes_vi:
2646
+ - "Tệp đặt sai tầng hoặc thư mục phụ trợ trong components"
2647
+ - "Export không khớp tên thư mục"
2648
+ - "Thiếu JSDoc hoặc có emoji"
2649
+ - "Có chữ tiếng Việt trong mã nguồn ngoài danh mục i18n hoặc slot fixture i18n"
2650
+ nextStep_vi: "Chuyển tệp về đúng thư mục tầng, đổi tên export theo thư mục và thêm JSDoc tiếng Anh. 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 thêm ngoại lệ."
2651
+ owner: op-retry
2652
+ kind: check-finding
2653
+
1188
2654
  FE_SOURCE_LAYOUT_INVALID:
1189
2655
  title: "Frontend source in wrong folder"
1190
2656
  title_vi: "Mã giao diện nằm sai thư mục"
@@ -1196,6 +2662,16 @@ FE_SOURCE_LAYOUT_INVALID:
1196
2662
  owner: other-op:interface.implement
1197
2663
  kind: check-finding
1198
2664
 
2665
+ FE_STACK_CORE_MISSING:
2666
+ title: "A product front end lacks a core stack package"
2667
+ title_vi: "Ứng dụng front end thiếu thư viện cốt lõi"
2668
+ meaning_vi: "Ứng dụng Next phục vụ sản phẩm thiếu một thư viện tier core trong knowledge/hfs/fe-stack.yaml; công cụ dùng chung có thể đặt tại gốc ứng dụng."
2669
+ causes_vi:
2670
+ - "Manifest của workspace sản phẩm chưa khai báo đủ thư viện cốt lõi của cùng chuẩn lite và full."
2671
+ nextStep_vi: "Khai báo thư viện còn thiếu trong workspace sản phẩm; đặt công cụ codegen, lint, TypeScript và Tailwind tại gốc nếu dùng chung."
2672
+ owner: op-retry
2673
+ kind: check-finding
2674
+
1199
2675
  FE_STACK_FORBIDDEN:
1200
2676
  title: "A front end depends on or imports a package that knowledge/hfs/fe-stack.yaml forbids"
1201
2677
  title_vi: "Front end dùng thư viện bị fe-stack cấm"
@@ -1206,6 +2682,53 @@ FE_STACK_FORBIDDEN:
1206
2682
  owner: op-retry
1207
2683
  kind: check-finding
1208
2684
 
2685
+ FE_STACK_UNLISTED:
2686
+ title: "A front-end dependency is absent from the stack catalog"
2687
+ title_vi: "Thư viện front end chưa được khai báo trong danh mục"
2688
+ meaning_vi: "Một package.json của front end khai báo thư viện không có trong knowledge/hfs/fe-stack.yaml và không có ngoại lệ fe.extraPackages kèm lý do trong hfs.json."
2689
+ causes_vi:
2690
+ - "Thêm thư viện ngoài danh mục mà chưa ghi rõ tên và lý do sử dụng."
2691
+ nextStep_vi: "Dùng thư viện chuẩn của concern hoặc khai báo tên và lý do cụ thể trong fe.extraPackages của hfs.json."
2692
+ owner: op-retry
2693
+ kind: check-finding
2694
+
2695
+ FE_STORAGE_OUTSIDE_MODULES:
2696
+ title: "Web storage only inside modules"
2697
+ title_vi: "Dùng localStorage/sessionStorage ngoài modules/"
2698
+ meaning_vi: "`<file>` đọc hoặc ghi `localStorage`/`sessionStorage` ngoài `modules/`. Storage không tồn tại ở server và ném lỗi khi đầy hạn mức, nên render server và hydrate lệch nhau."
2699
+ causes_vi:
2700
+ - "Vi phạm luật R55: Chỉ `modules/**` được chạm web storage, sau một hook hoặc module có bảo vệ (`typeof window`, try/catch); block gọi hook đó."
2701
+ 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; nợ cũ được chia thành các lane sửa cơ học, op không thêm ngoại lệ và không tắt luật."
2702
+ owner: op-retry
2703
+ kind: check-finding
2704
+
2705
+ FE_STYLE_SOURCE_UNRESOLVED:
2706
+ title: "Tailwind @source path does not resolve"
2707
+ title_vi: "Đường dẫn @source của Tailwind không tồn tại"
2708
+ meaning_vi: "Một dòng @source trong CSS trỏ tới thư mục hoặc tệp không có thật, nên Tailwind không quét được gì và lặng lẽ làm mất các utility mà component dùng."
2709
+ causes_vi:
2710
+ - "Đường dẫn tới node_modules tính sai so với vị trí của tệp CSS"
2711
+ - "Thư mục được trỏ tới đã đổi tên hoặc bị chuyển đi"
2712
+ nextStep_vi: "Op interface.implement sửa đường dẫn @source cho trỏ tới thư mục có thật, tính từ chính tệp CSS, rồi kiểm tra lại."
2713
+ owner: other-op:interface.implement
2714
+ kind: check-finding
2715
+
2716
+ FE_STYLE_TOKEN_ONLY:
2717
+ title: "CSS bypasses grammar tokens"
2718
+ title_vi: "CSS không đi qua token của grammar"
2719
+ meaning_vi: "CSS của app dùng giá trị màu, khoảng cách, bo góc hoặc chữ viết thẳng, giá trị thô ngoài brand.css, !important, selector class, CSS module, khai lại token của grammar, breakpoint ngoài thang, hoặc globals.css sai hình dạng và sai thứ tự import; hoặc màu trạng thái không theo cặp mềm HeroUI (thiếu --<tông>-soft/--<tông>-soft-foreground ở sáng hoặc tối, độ tương phản dưới ngưỡng, chữ hay icon tô bằng tông trạng thái đặc)."
2720
+ causes_vi:
2721
+ - "Hex, rgb, hsl, oklch hoặc px viết trong CSS ngoài modules/brand/brand.css"
2722
+ - "Biến --tên-riêng thay cho token grammar, hoặc khai lại token của grammar ngoài brand.css"
2723
+ - "Có tệp *.module.css hoặc selector class trong CSS của app"
2724
+ - "globals.css chứa thứ ngoài @import, @source và khối token, hoặc import sai thứ tự"
2725
+ - "@media dùng breakpoint ngoài thang của grammar"
2726
+ - "brand.css thiếu cặp mềm của một tông trạng thái ở sáng hoặc tối, chữ mềm dưới 3:1 trên nền nhạt hoặc nền trang, --foreground dưới 4.5:1, hoặc giá trị màu không phân giải được"
2727
+ - "Component tô chữ hoặc icon bằng text-success, text-danger, fill-warning, stroke-info (tông đặc) thay vì text-<tông>-soft-foreground"
2728
+ nextStep_vi: "Op interface.implement bỏ CSS riêng, dùng component và token của grammar; giá trị màu thương hiệu chỉ khai ở modules/brand/brand.css cho cả sáng và tối, đủ cặp mềm cho mọi tông trạng thái; chữ và icon trạng thái dùng text-<tông>-soft-foreground."
2729
+ owner: other-op:interface.implement
2730
+ kind: check-finding
2731
+
1209
2732
  FE_SUPABASE_CLIENT_OWNER:
1210
2733
  title: "A front-end Supabase import or call is outside `fe.modules.auth`, or reaches past the auth API"
1211
2734
  title_vi: "Supabase phía front end ngoài modules/auth hoặc dùng ngoài API auth"
@@ -1216,6 +2739,26 @@ FE_SUPABASE_CLIENT_OWNER:
1216
2739
  owner: op-retry
1217
2740
  kind: check-finding
1218
2741
 
2742
+ FE_SUSPENSE_NULL_FALLBACK:
2743
+ title: "Suspense fallback renders something"
2744
+ title_vi: "Suspense có fallback rỗng"
2745
+ meaning_vi: "`<Suspense>` ở `<file>` có `fallback={null}` hoặc không có fallback: người dùng thấy một khoảng trống, không biết trang đang tải."
2746
+ causes_vi:
2747
+ - "Vi phạm luật R53: `Suspense fallback={null}` bị cấm; fallback là trạng thái loading (skeleton) của đúng thứ đang tải."
2748
+ 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; nợ cũ được chia thành các lane sửa cơ học, op không thêm ngoại lệ và không tắt luật."
2749
+ owner: op-retry
2750
+ kind: check-finding
2751
+
2752
+ FE_SWALLOWED_ERROR:
2753
+ title: "No empty catch"
2754
+ title_vi: "catch rỗng nuốt lỗi"
2755
+ meaning_vi: "`catch` hoặc `.catch(() => {})` ở `<file>` không làm gì: lỗi biến mất, người dùng không thấy thông báo, đội không có dấu vết."
2756
+ causes_vi:
2757
+ - "Vi phạm luật R65: Mỗi `catch` trả một outcome có kiểu mang nguyên nhân, hiển thị trạng thái lỗi hoặc ném lại."
2758
+ 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; nợ cũ được chia thành các lane sửa cơ học, op không thêm ngoại lệ và không tắt luật."
2759
+ owner: op-retry
2760
+ kind: check-finding
2761
+
1219
2762
  FE_SWR_KEY_IDENTITY:
1220
2763
  title: "SWR key misses an identity"
1221
2764
  title_vi: "Khóa SWR thiếu thông tin định danh"
@@ -1238,6 +2781,17 @@ FE_SWR_MUTATION_RESOURCE_IDENTITY:
1238
2781
  owner: other-op:interface.implement
1239
2782
  kind: check-finding
1240
2783
 
2784
+ FE_SWR_OUTSIDE_FAMILY:
2785
+ title: "An SWR runtime call is outside its hook family"
2786
+ title_vi: "Lời gọi SWR nằm ngoài họ hook được khai báo"
2787
+ meaning_vi: "Tệp <file> gọi SWR trực tiếp ở component, feature hoặc hook miền thay vì dùng hook thuộc họ SWR chịu trách nhiệm lấy dữ liệu."
2788
+ causes_vi:
2789
+ - "Component tự gọi useSWR hoặc useSWRMutation"
2790
+ - "Hook miền tự sở hữu bộ nhớ đệm thay vì dùng hook bọc của họ SWR"
2791
+ nextStep_vi: "Chuyển lời gọi SWR về hook bọc trong hooks/swr rồi để component hoặc hook miền dùng hook đó."
2792
+ owner: op-retry
2793
+ kind: check-finding
2794
+
1241
2795
  FE_TIER_DIRECTION:
1242
2796
  title: "Frontend import against the tier direction"
1243
2797
  title_vi: "Frontend import ngược chiều tầng"
@@ -1260,6 +2814,29 @@ FE_TRANSPORT_OWNER:
1260
2814
  owner: op-retry
1261
2815
  kind: check-finding
1262
2816
 
2817
+ FE_TYPE_ESCAPE:
2818
+ title: "No type assertion, non-null assertion or any in product source"
2819
+ title_vi: "Ép kiểu, `!` hoặc `any` trong mã sản phẩm"
2820
+ meaning_vi: "`<file>` dùng `as T`, `x!` hoặc `any`: trình biên dịch bị bảo tin một điều nó không kiểm được, và lỗi dời sang trình duyệt của người dùng."
2821
+ causes_vi:
2822
+ - "Vi phạm luật R22: Không ép kiểu (`as T`, `<T>x`), không `!`, không `any` trong mã sản phẩm; thu hẹp bằng type guard, discriminant hoặc parser tại nơi dữ liệu đi vào. Ngoại lệ duy nhất là `as const` và `as unknown`."
2823
+ 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; nợ cũ được chia thành các lane sửa cơ học, op không thêm ngoại lệ và không tắt luật."
2824
+ owner: op-retry
2825
+ kind: check-finding
2826
+
2827
+ FE_VENDOR_BOUNDARY:
2828
+ title: "Vendor primitive or icon used outside its owner"
2829
+ title_vi: "Thư viện giao diện bên ngoài dùng ngoài chủ sở hữu"
2830
+ meaning_vi: "Biểu tượng của bộ icon ứng dụng, họ heroicons (@heroicons/react trước, @starci/heroicons cho glyph heroicons còn thiếu, ở hai nhánh 24/outline và 16/solid), chỉ đến component qua lá icon với kích thước theo thang glyph; mỗi primitive của thư viện ngoài nằm sau một chủ sở hữu có tên."
2831
+ causes_vi:
2832
+ - "Import thẳng @heroicons/react hoặc @starci/heroicons trong component thay vì qua lá icon"
2833
+ - "Dùng bộ glyph thứ hai ngoài họ heroicons (Phosphor, Lucide, Iconify...)"
2834
+ - "Dùng kích thước biểu tượng ngoài thang"
2835
+ - "Primitive thư viện ngoài dùng trực tiếp ở nhiều nơi"
2836
+ nextStep_vi: "Dùng lá icon và chủ sở hữu primitive có sẵn; 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."
2837
+ owner: op-retry
2838
+ kind: check-finding
2839
+
1263
2840
  FE_WIRE_GENERATED:
1264
2841
  title: "Wire types generated from the be contract snapshots the fe side reads"
1265
2842
  title_vi: "Kiểu wire gõ tay"
@@ -1281,6 +2858,37 @@ FE_WORLD_OWNER_RENDER_BOUNDARY:
1281
2858
  owner: other-op:interface.implement
1282
2859
  kind: check-finding
1283
2860
 
2861
+ GENERATED_UNTRACKED:
2862
+ title: "Tracked path under a generated root"
2863
+ title_vi: "Đường dẫn được git theo dõi dưới một gốc sinh tự động"
2864
+ meaning_vi: "Một tệp được git theo dõi nằm dưới một gốc sinh tự động của ruleParams.runtime.generated (knowledge/hfs/runtime-slots.yaml). Một gốc như vậy chỉ chứa thứ generatedBy của nó ghi ra (scripts/hfs/sync-runtime.mjs cho các bản sao runtime/ của gói) và đã được gitignore, nên index không bao giờ được giữ một bản sao cũ."
2865
+ causes_vi:
2866
+ - "Commit các tệp runtime/ của gói như mã nguồn thay vì để sync-runtime.mjs tái tạo"
2867
+ - "git add -f qua một mục .gitignore"
2868
+ nextStep_vi: "Người giám sát chạy git rm -r --cached trên gốc sinh tự động đó và giữ mục .gitignore của nó; chỉ generatedBy ghi vào đó."
2869
+ owner: supervisor
2870
+ kind: check-finding
2871
+
2872
+ HFS_AGENT_DATA_TRACKED:
2873
+ title: "`.starciwork` holds product records only"
2874
+ title_vi: "Dữ liệu của agent bị track"
2875
+ meaning_vi: "`<path>` là dữ liệu của agent, không phải record sản phẩm. Nó thuộc blob store (trích bằng `{name, sha256}`)."
2876
+ causes_vi:
2877
+ - "Vi phạm luật R07: `.starciwork` chỉ chứa record sản phẩm; evidence, log, report, capture, run, sqlite, `_derived/` không được track."
2878
+ 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 (nợ hàng loạt có codemod của HFS); không cần ai can thiệp thêm."
2879
+ owner: op-retry
2880
+ kind: check-finding
2881
+
2882
+ HFS_AGE_KEY_CUSTODY:
2883
+ title: "A tracked file holds the sops/age identity"
2884
+ title_vi: "Khoá age/sops bị commit trong kho"
2885
+ meaning_vi: "`<path>` là tệp định danh sops/age được track (`.claude/secret.env`, `keys.txt`, `*.agekey`). Khoá age chỉ nằm trong `.claude/secret.env` nội bộ của host hoặc `keys.txt` của sops, không bao giờ trong kho."
2886
+ causes_vi:
2887
+ - "Vi phạm luật R253: định danh age chỉ là custody trên host; `.claude/secret.env` (SOPS_AGE_KEY) thắng `keys.txt` và cả hai đều không được commit."
2888
+ nextStep_vi: "Không đọc và không in giá trị khoá. Gỡ tệp khỏi index (git rm), đặt khoá vào `.claude/secret.env` hoặc `keys.txt` của sops ngoài kho, rồi xoay identity vì nó đã lộ trong lịch sử."
2889
+ owner: owner
2890
+ kind: check-finding
2891
+
1284
2892
  HFS_ALIAS_REEXPORT:
1285
2893
  title: "A declaration re-exported under a second name"
1286
2894
  title_vi: "Xuất lại một khai báo dưới tên thứ hai"
@@ -1314,6 +2922,26 @@ HFS_APP_LAYOUT_INVALID:
1314
2922
  owner: other-op:backend.scaffold
1315
2923
  kind: check-finding
1316
2924
 
2925
+ HFS_APP_LEXICON:
2926
+ title: "App source or documentation uses a removed product word"
2927
+ title_vi: "Mã hoặc tài liệu dùng từ sản phẩm đã bỏ"
2928
+ meaning_vi: "Tệp nguồn hoặc tài liệu dùng từ nằm trong hfs.json lexicon.removed, kể cả trong tên định danh, chú thích và nội dung giao diện."
2929
+ causes_vi:
2930
+ - "Đổi thuật ngữ sản phẩm nhưng chưa cập nhật toàn bộ mã và tài liệu."
2931
+ nextStep_vi: "Thay từ đã bỏ bằng thuật ngữ sản phẩm hiện hành tại đường dẫn và dòng được báo."
2932
+ owner: op-retry
2933
+ kind: check-finding
2934
+
2935
+ HFS_APP_WORKFLOW_TRIGGER:
2936
+ title: "App workflows start on release tags or manual dispatch"
2937
+ title_vi: "Workflow ứng dụng có sự kiện khởi chạy không hợp lệ"
2938
+ meaning_vi: "Workflow tại <path> chạy theo push nhánh hoặc sự kiện khác; ứng dụng chỉ chạy CI theo release tag v* hoặc workflow_dispatch."
2939
+ causes_vi:
2940
+ - "Vi phạm R251: áp dụng ngoại lệ push main của runtime cho workflow ứng dụng."
2941
+ nextStep_vi: "Đặt on.push.tags thành [v*] và dùng workflow_dispatch khi cần chạy thủ công; bỏ nhánh và sự kiện khác."
2942
+ owner: op-retry
2943
+ kind: check-finding
2944
+
1317
2945
  HFS_ARCH_CONFIG_UNREAD:
1318
2946
  title: "The machine reads `hfs.json`; zero files analysed is red"
1319
2947
  title_vi: "Máy kiến trúc không đọc được hfs.json"
@@ -1324,6 +2952,47 @@ HFS_ARCH_CONFIG_UNREAD:
1324
2952
  owner: op-retry
1325
2953
  kind: check-finding
1326
2954
 
2955
+ HFS_ASSET_KIND:
2956
+ title: "A back-end asset has a forbidden kind or location"
2957
+ title_vi: "Loại hoặc vị trí tài nguyên back end bị cấm"
2958
+ meaning_vi: "Back end chỉ giữ tài nguyên dựng nhỏ thuộc danh sách phần mở rộng cho phép. Media nghiệp vụ ở kho đối tượng; trọng số mô hình và bộ dữ liệu không được đưa vào Git. Media tài liệu theo slot vẫn hợp lệ."
2959
+ causes_vi:
2960
+ - "Đưa ảnh, âm thanh hoặc video nghiệp vụ vào mã back end."
2961
+ - "Đưa trọng số mô hình, bộ dữ liệu hoặc loại tệp ngoài danh sách vào tài nguyên dựng."
2962
+ nextStep_vi: "Chuyển media sang kho đối tượng và bỏ trọng số mô hình, bộ dữ liệu khỏi cây back end; giữ tài nguyên dựng nhỏ đúng slot và loại tệp."
2963
+ owner: op-retry
2964
+ kind: check-finding
2965
+
2966
+ HFS_ASSET_OVERSIZE:
2967
+ title: "A back-end build asset exceeds its per-file byte budget"
2968
+ title_vi: "Tệp tài nguyên back end vượt giới hạn dung lượng"
2969
+ meaning_vi: "Tệp tài nguyên dùng lúc dựng back end lớn hơn giới hạn từng tệp; mặc định là 512000 byte. Tệp lớn phải nằm trong kho đối tượng và được tải lúc chạy."
2970
+ causes_vi:
2971
+ - "Đưa tệp lớn vào be/src/assets thay vì kho đối tượng."
2972
+ nextStep_vi: "Giảm dung lượng tài nguyên dựng hoặc chuyển tệp lớn sang kho đối tượng rồi tải lúc chạy."
2973
+ owner: op-retry
2974
+ kind: check-finding
2975
+
2976
+ HFS_AUDIT_STEP_MISSING:
2977
+ title: "CI gates releases on a production dependency audit"
2978
+ title_vi: "CI thiếu bước kiểm tra lỗ hổng phụ thuộc production"
2979
+ meaning_vi: "Workflow CI thiếu bước npm audit --omit=dev hoạt động và dừng job khi kiểm tra thất bại."
2980
+ causes_vi:
2981
+ - "Vi phạm R269: audit không có, bị vô hiệu hoá, chỉ nằm trong chú thích hoặc bỏ qua kết quả."
2982
+ nextStep_vi: "Thêm npm audit --omit=dev --audit-level=high vào job CI hoạt động; không dùng continue-on-error hoặc bỏ qua mã lỗi."
2983
+ owner: op-retry
2984
+ kind: check-finding
2985
+
2986
+ HFS_CANON_COPY_DRIFT:
2987
+ title: "A hash-sensitive canon file differs from its template"
2988
+ title_vi: "Tệp canon nhạy hash lệch khỏi template"
2989
+ meaning_vi: "`<path>` khác bytes với bản canon trong `packages/hfs/templates/be/skeleton` (sha256 của hai bên được báo trong finding). `env-source.config.ts`, `origin.guard.ts` và `webhook-signature.service.ts` là canon nhạy hash, không phải tệp để sửa."
2990
+ causes_vi:
2991
+ - "Vi phạm luật R253: ba tệp custody bí mật phải bằng đúng bản template scaffold; mọi chỉnh sửa là drift."
2992
+ nextStep_vi: "Khôi phục đúng bytes của template `be/skeleton` cho tệp đó (scaffold lại hoặc checkout lại); thay đổi cần thiết phải đi qua template và canon pins, không sửa bản copy."
2993
+ owner: owner
2994
+ kind: check-finding
2995
+
1327
2996
  HFS_CANON_PIN_DRIFT:
1328
2997
  title: "Dependency version differs from the canon pin"
1329
2998
  title_vi: "Phiên bản phụ thuộc lệch khỏi bản đã ghim"
@@ -1346,6 +3015,16 @@ HFS_CI_MISSING_CANON:
1346
3015
  owner: op-retry
1347
3016
  kind: check-finding
1348
3017
 
3018
+ HFS_CI_TOOL_INSTALL:
3019
+ title: "CI tool installation verifies immutable artifacts"
3020
+ title_vi: "Công cụ CI được cài mà chưa xác minh checksum"
3021
+ meaning_vi: "Workflow tại <path> tải hoặc cài công cụ bằng curl|sh, go install, npm global hoặc pip mà không xác minh checksum hay digest."
3022
+ causes_vi:
3023
+ - "Vi phạm R251: phiên bản hoặc URL riêng lẻ không chứng minh nội dung công cụ đã được xác minh."
3024
+ nextStep_vi: "Dùng action đã ghim SHA hoặc tải artifact được ghim và xác minh checksum/digest trước khi thực thi; không pipe bản tải vào shell."
3025
+ owner: op-retry
3026
+ kind: check-finding
3027
+
1349
3028
  HFS_CONTRACT_SNAPSHOT_DRIFT:
1350
3029
  title: "The be side commits its contract snapshot and it equals the emit"
1351
3030
  title_vi: "Bản hợp đồng lệch bản thật"
@@ -1356,6 +3035,17 @@ HFS_CONTRACT_SNAPSHOT_DRIFT:
1356
3035
  owner: op-retry
1357
3036
  kind: check-finding
1358
3037
 
3038
+ HFS_COVERAGE_BELOW:
3039
+ title: "Measured back-end logic file is below 100 percent coverage"
3040
+ title_vi: "Tệp logic back-end trong phạm vi đo chưa đạt độ bao phủ 100 phần trăm"
3041
+ meaning_vi: "Báo cáo lcov được cung cấp không chứng minh mỗi tệp logic trong phạm vi đo của edition đạt 100 phần trăm dòng, hàm và nhánh; phạm vi cùng các tệp ngoài phạm vi được liệt kê riêng. Độ bao phủ câu lệnh do trình chạy unit test kiểm tra."
3042
+ causes_vi:
3043
+ - "Có dòng, hàm hoặc nhánh chưa được chạy trong một tệp logic thuộc phạm vi đo"
3044
+ - "Báo cáo thiếu tệp nguồn, thiếu số liệu, sai định dạng hoặc không đọc được"
3045
+ nextStep_vi: "Bổ sung unit test cho tệp được nêu, tạo lại báo cáo lcov đầy đủ rồi chạy starci app check --coverage-from <lcov>; features, cli và webhooks ngoài phạm vi đo vẫn được liệt kê."
3046
+ owner: op-retry
3047
+ kind: check-finding
3048
+
1359
3049
  HFS_COVERAGE_SCOPE_DRIFT:
1360
3050
  title: "A file states a coverage scope the slot manifest does not derive"
1361
3051
  title_vi: "Tệp nêu phạm vi độ phủ khác với phạm vi suy ra từ slot manifest"
@@ -1368,6 +3058,16 @@ HFS_COVERAGE_SCOPE_DRIFT:
1368
3058
  owner: op-retry
1369
3059
  kind: check-finding
1370
3060
 
3061
+ HFS_DB_DEPLOY_UNGUARDED:
3062
+ title: "Database deployment accepts a non-tag ref"
3063
+ title_vi: "Triển khai cơ sở dữ liệu chấp nhận ref không phải tag"
3064
+ meaning_vi: "Mỗi đường chạy triển khai migration phải từ chối ref không phải tag phát hành trước khi tác động cơ sở dữ liệu."
3065
+ causes_vi:
3066
+ - "Workflow db-deploy thiếu chốt kiểm tag hoặc chốt không chặn đường chạy migration theo luật R271."
3067
+ nextStep_vi: "Thêm chốt kiểm ref tag trước mỗi bước triển khai migration, rồi chứng minh ref nhánh bị từ chối."
3068
+ owner: op-retry
3069
+ kind: check-finding
3070
+
1371
3071
  HFS_DECLARATION_INVALID:
1372
3072
  title: "hfs.json missing or invalid"
1373
3073
  title_vi: "hfs.json thiếu hoặc sai định dạng"
@@ -1381,6 +3081,56 @@ HFS_DECLARATION_INVALID:
1381
3081
  owner: op-retry
1382
3082
  kind: check-finding
1383
3083
 
3084
+ HFS_DEPENDABOT_MISSING:
3085
+ title: "Apps declare scheduled dependency updates"
3086
+ title_vi: "Ứng dụng thiếu cấu hình cập nhật phụ thuộc"
3087
+ meaning_vi: "Kho thiếu .github/dependabot.yml hợp lệ với lịch cập nhật npm, github-actions và docker."
3088
+ causes_vi:
3089
+ - "Vi phạm R269: một hệ sinh thái phụ thuộc chưa được theo dõi cập nhật định kỳ."
3090
+ nextStep_vi: "Khai báo Dependabot version 2 với directory và schedule cho npm, github-actions và docker."
3091
+ owner: op-retry
3092
+ kind: check-finding
3093
+
3094
+ HFS_DEPLOY_COMPOSE_ON_VPS:
3095
+ title: "Docker compose lifecycle commands are dev-only"
3096
+ title_vi: "Docker Compose được dùng ngoài môi trường dev"
3097
+ meaning_vi: "Tệp tại <path> chạy docker compose up, down hoặc pull cho môi trường không phải dev; triển khai VPS dùng Docker Swarm."
3098
+ causes_vi:
3099
+ - "Vi phạm R250: dùng vòng đời Compose trong production, staging hoặc trên VPS."
3100
+ nextStep_vi: "Dùng stack Swarm đã khai báo để triển khai VPS; chỉ giữ Compose cho môi trường dev được xác định rõ."
3101
+ owner: op-retry
3102
+ kind: check-finding
3103
+
3104
+ HFS_DEPLOY_NO_LOGIC:
3105
+ title: "Deploy shell scripts delegate to the app CLI"
3106
+ title_vi: "Script triển khai chứa logic của ứng dụng"
3107
+ meaning_vi: "Script triển khai tại <path> chạy node -e, psql, SQL nội tuyến hoặc tải và thực thi shell; logic phải nằm trong ứng dụng và có spec."
3108
+ causes_vi:
3109
+ - "Vi phạm R250: đặt logic hoặc truy cập cơ sở dữ liệu trực tiếp trong deploy/*.sh."
3110
+ nextStep_vi: "Đưa logic vào lệnh be/apps/cli có spec rồi để script triển khai gọi docker, starci hoặc CLI đã biên dịch."
3111
+ owner: op-retry
3112
+ kind: check-finding
3113
+
3114
+ HFS_DEPLOY_SCHEDULER:
3115
+ title: "Workflow scheduling does not own app work"
3116
+ title_vi: "Workflow tự lập lịch cho tác vụ ứng dụng"
3117
+ meaning_vi: "Workflow tại <path> có lịch chạy thay cho scheduler của ứng dụng; tác vụ định kỳ phải thuộc back end hoặc job đã khai báo."
3118
+ causes_vi:
3119
+ - "Vi phạm R250: dùng sự kiện schedule hoặc lịch trong workflow để chạy tác vụ ứng dụng."
3120
+ nextStep_vi: "Bỏ lịch khỏi workflow và đưa tác vụ sang scheduler của ứng dụng hoặc replicated-job đã khai báo."
3121
+ owner: op-retry
3122
+ kind: check-finding
3123
+
3124
+ HFS_DEPLOY_WORKFLOW:
3125
+ title: "Deployment workflows declare protected jobs and sealed custody"
3126
+ title_vi: "Workflow triển khai thiếu kiểm soát hoặc sai nơi giữ bí mật"
3127
+ meaning_vi: "Workflow triển khai tại <path> thiếu environment, permissions, concurrency hoặc bước materialize; hoặc dùng dotenv hay bí mật ngoài danh sách cho phép."
3128
+ causes_vi:
3129
+ - "Vi phạm R251: workflow bỏ qua môi trường bảo vệ hoặc lấy bí mật ứng dụng từ GitHub thay cho kho mã hoá."
3130
+ nextStep_vi: "Khai báo environment, permissions và concurrency; materialize bí mật đã mã hoá với SOPS_AGE_KEY; chỉ cho phép khoá SSH và known_hosts phục vụ vận chuyển."
3131
+ owner: op-retry
3132
+ kind: check-finding
3133
+
1384
3134
  HFS_DEP_VERSION_SKEW:
1385
3135
  title: "One version per dependency in the workspace"
1386
3136
  title_vi: "Một gói nhiều phiên bản"
@@ -1421,6 +3171,16 @@ HFS_DOCKER_ENTRY:
1421
3171
  owner: op-retry
1422
3172
  kind: check-finding
1423
3173
 
3174
+ HFS_DOCKER_HEALTHCHECK_FAKE:
3175
+ title: "API Docker healthcheck cannot reach its live route"
3176
+ title_vi: "Healthcheck Docker của API không tới được route live"
3177
+ meaning_vi: "Healthcheck không chứng minh API còn phục vụ liveness vì lệnh luôn thành công hoặc gọi route không tồn tại."
3178
+ causes_vi:
3179
+ - "HEALTHCHECK CMD true hoặc lệnh không kiểm tra thành công của route live đã đăng ký"
3180
+ nextStep_vi: "Dùng healthcheck HTTP kiểm tra thành công của route liveness thật của API và giữ lỗi probe thành exit code khác không."
3181
+ owner: op-retry
3182
+ kind: check-finding
3183
+
1424
3184
  HFS_DOCKER_SECRETS:
1425
3185
  title: "A secret enters an image"
1426
3186
  title_vi: "Bí mật bị đưa vào image"
@@ -1441,6 +3201,26 @@ HFS_DOCKER_STAGES:
1441
3201
  owner: op-retry
1442
3202
  kind: check-finding
1443
3203
 
3204
+ HFS_DOC_COMMAND_UNKNOWN:
3205
+ title: "Product documentation cites an unknown command"
3206
+ title_vi: "Tài liệu sản phẩm dẫn lệnh không tồn tại"
3207
+ meaning_vi: "Lệnh npm run không có script tương ứng trong package.json hoặc verb starci app không có trong catalog CLI."
3208
+ causes_vi:
3209
+ - "Script hoặc verb được đổi hay bỏ nhưng tài liệu chưa đổi theo luật R270."
3210
+ nextStep_vi: "Dùng lệnh hiện có trong package.json hoặc catalog CLI, hoặc khai báo script sản phẩm còn thiếu."
3211
+ owner: op-retry
3212
+ kind: check-finding
3213
+
3214
+ HFS_DOC_ENV_UNKNOWN:
3215
+ title: "Product documentation cites an undeclared environment key"
3216
+ title_vi: "Tài liệu sản phẩm dẫn khóa môi trường chưa khai báo"
3217
+ meaning_vi: "Tên khóa được tài liệu dẫn không có trong bộ đọc môi trường có kiểu hoặc KEYS.md; kiểm tra chỉ đọc tên, không đọc giá trị bí mật."
3218
+ causes_vi:
3219
+ - "Tên khóa cũ còn trong tài liệu hoặc bộ đọc chưa khai báo khóa theo luật R270."
3220
+ nextStep_vi: "Cập nhật tên khóa hoặc khai báo khóa trong bộ đọc môi trường và KEYS.md, không đưa giá trị bí mật vào tài liệu."
3221
+ owner: op-retry
3222
+ kind: check-finding
3223
+
1444
3224
  HFS_DOC_NOT_ENGLISH:
1445
3225
  title: "Document is not English"
1446
3226
  title_vi: "Tài liệu không viết bằng tiếng Anh"
@@ -1452,6 +3232,26 @@ HFS_DOC_NOT_ENGLISH:
1452
3232
  owner: op-retry
1453
3233
  kind: check-finding
1454
3234
 
3235
+ HFS_DOC_PATH_MISSING:
3236
+ title: "Product documentation cites a missing path"
3237
+ title_vi: "Tài liệu sản phẩm dẫn đường dẫn không còn tồn tại"
3238
+ meaning_vi: "README, tài liệu hoặc README của stack dẫn một tệp hay thư mục mà cây sản phẩm không có, trong cả lite và full."
3239
+ causes_vi:
3240
+ - "Đổi hoặc xóa đường dẫn nhưng chưa cập nhật tài liệu theo luật R270."
3241
+ nextStep_vi: "Sửa đường dẫn được dẫn hoặc khôi phục tệp đúng trong sản phẩm rồi chạy lại kiểm tra."
3242
+ owner: op-retry
3243
+ kind: check-finding
3244
+
3245
+ HFS_DOC_RULE_UNKNOWN:
3246
+ title: "Product documentation cites an unknown HFS rule"
3247
+ title_vi: "Tài liệu sản phẩm dẫn luật HFS không tồn tại"
3248
+ meaning_vi: "Mã R được dẫn không có trong catalog luật HFS hiện hành của runtime."
3249
+ causes_vi:
3250
+ - "Dẫn sai mã luật hoặc dùng mã chưa được đăng ký theo luật R270."
3251
+ nextStep_vi: "Dùng đúng mã luật hiện có trong knowledge/hfs/rules.yaml và cập nhật đoạn tài liệu liên quan."
3252
+ owner: op-retry
3253
+ kind: check-finding
3254
+
1455
3255
  HFS_DUPLICATE_CODE:
1456
3256
  title: "Duplicated code block"
1457
3257
  title_vi: "Mã trùng lặp"
@@ -1529,6 +3329,28 @@ HFS_EVENT_CONTRACT:
1529
3329
  owner: op-retry
1530
3330
  kind: check-finding
1531
3331
 
3332
+ HFS_FEATURE_FILE_BUDGET:
3333
+ title: "A feature exceeds its file budget"
3334
+ title_vi: "Tính năng vượt hạn mức số tệp"
3335
+ meaning_vi: "Một thư mục sở hữu tính năng có nhiều tệp được theo dõi hơn hạn mức ruleParams.featureFiles (250 tệp), tính cả các thư mục con. Luật áp dụng cho cả lite và full ở back end và front end."
3336
+ causes_vi:
3337
+ - "Một tính năng gộp nhiều khả năng sản phẩm vào cùng một thư mục"
3338
+ - "Các tệp mã nguồn, kiểm thử hoặc tài liệu trong thư mục con làm tổng số tệp vượt hạn mức"
3339
+ nextStep_vi: "Tách tính năng theo khả năng sản phẩm vào các thư mục sở hữu riêng, rồi chạy lại starci app check để mỗi tính năng nằm trong hạn mức."
3340
+ owner: op-retry
3341
+ kind: check-finding
3342
+
3343
+ HFS_FEATURE_LOGIC_ROLE:
3344
+ title: "A feature support role hides executable logic"
3345
+ title_vi: "Vai trò hỗ trợ của feature chứa logic thực thi"
3346
+ meaning_vi: "Tệp không phải cửa mỏng trong tầng feature chứa rẽ nhánh, vòng lặp, phép tính hoặc lời gọi thực thi thay vì chỉ khai báo dữ liệu, kiểu và metadata DTO."
3347
+ causes_vi:
3348
+ - "Đặt policy, guard, client hoặc projection có logic trong application/support"
3349
+ - "Đổi hậu tố handler thành vai trò hỗ trợ để tránh luật feature-thin"
3350
+ nextStep_vi: "Chuyển logic vào modules/domain hoặc modules/platform phù hợp và viết unit spec ở đó; giữ tệp feature dưới dạng khai báo dữ liệu hoặc một cửa mỏng ủy quyền."
3351
+ owner: op-retry
3352
+ kind: check-finding
3353
+
1532
3354
  HFS_FORBIDDEN_PRESENT:
1533
3355
  title: "Tracked path in a forbidden slot"
1534
3356
  title_vi: "Có tệp bị cấm trong cây mã nguồn"
@@ -1562,6 +3384,17 @@ HFS_FORMAT_TOOL_MISSING:
1562
3384
  owner: op-retry
1563
3385
  kind: input-invalid
1564
3386
 
3387
+ HFS_FUNCTION_BUDGET:
3388
+ title: "A function body exceeds its line budget"
3389
+ title_vi: "Thân hàm vượt hạn mức dòng"
3390
+ meaning_vi: "Thân một hàm, phương thức hoặc hàm mũi tên của back end hoặc front end vượt hạn mức ruleParams.functionLines (80 dòng), kể cả dòng trống và chú thích. Luật áp dụng cho cả lite và full, không cho phép giữ nguyên hàm cũ quá dài."
3391
+ causes_vi:
3392
+ - "Một hàm gộp nhiều trách nhiệm hoặc nhiều bước xử lý vào cùng một thân hàm"
3393
+ - "Thêm mã, chú thích hoặc dòng trống làm thân hàm vượt hạn mức"
3394
+ nextStep_vi: "Tách các trách nhiệm thành hàm nhỏ có tên rõ ràng trong đúng nơi sở hữu, rồi chạy lại lint để mỗi thân hàm nằm trong hạn mức."
3395
+ owner: op-retry
3396
+ kind: check-finding
3397
+
1565
3398
  HFS_GHOST_TREE:
1566
3399
  title: "No empty directory beside a near-identical sibling"
1567
3400
  title_vi: "Cây thư mục ma cạnh thư mục gần trùng tên"
@@ -1592,6 +3425,26 @@ HFS_HOOKS_PATH_REDIRECTED:
1592
3425
  owner: op-retry
1593
3426
  kind: check-finding
1594
3427
 
3428
+ HFS_HOST_CRON:
3429
+ title: "Apps do not install host cron or timer jobs"
3430
+ title_vi: "Ứng dụng cài lịch chạy trên máy chủ"
3431
+ meaning_vi: "Tệp triển khai hoặc script tại <path> khai báo crontab, cron.d, systemd timer, @reboot hoặc at; lịch chạy phải do ứng dụng sở hữu."
3432
+ causes_vi:
3433
+ - "Vi phạm R250: cài lịch chạy ngoài scheduler của ứng dụng hoặc replicated-job đã khai báo."
3434
+ nextStep_vi: "Chuyển tác vụ sang scheduler của back end, pgmq/pg_cron hoặc replicated-job đã khai báo và gọi lệnh CLI của ứng dụng."
3435
+ owner: op-retry
3436
+ kind: check-finding
3437
+
3438
+ HFS_IDENTITY_CUSTODY:
3439
+ title: "An identity points its secret at `secrets/identity-<slug>.enc`; UAT chooses by role"
3440
+ title_vi: "Bí mật của identity sai chỗ"
3441
+ meaning_vi: "Identity `<id>` trỏ bí mật tới `<path>`; chỉ `.starcistacks/<env>/secrets/identity-<slug>.enc` (đường dẫn tính từ gốc app) hợp lệ; dạng `be/.starcistacks/...` bị từ chối. UAT chọn tài khoản theo role."
3442
+ causes_vi:
3443
+ - "Vi phạm luật R09: Identity record (`_resources/identities/<slug>/resource.yaml`) trỏ `custody.sealed` tới `.starcistacks/<env>/secrets/identity-<slug>.enc` ở gốc app; UAT `accounts.yaml` chọn identity theo role, không theo flow."
3444
+ 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 (nợ hàng loạt có codemod của HFS); không cần ai can thiệp thêm."
3445
+ owner: op-retry
3446
+ kind: check-finding
3447
+
1595
3448
  HFS_IMAGE_UNPINNED:
1596
3449
  title: "Every image of a multi-service stack is pinned"
1597
3450
  title_vi: "Image của stack nhiều dịch vụ chưa được ghim phiên bản"
@@ -1602,6 +3455,18 @@ HFS_IMAGE_UNPINNED:
1602
3455
  owner: op-retry
1603
3456
  kind: check-finding
1604
3457
 
3458
+ HFS_INLINE_SUPPRESSION:
3459
+ title: "Inline lint or type suppression"
3460
+ title_vi: "Chú thích tắt luật tại chỗ"
3461
+ meaning_vi: "Có chú thích tắt luật hoặc miễn trừ trong mã: eslint-disable, eslint-enable, eslint-env, /* global */, stylelint-disable, @ts-ignore, @ts-expect-error, @ts-nocheck (ở bất kỳ vị trí nào trong chú thích), istanbul, c8 hay v8 ignore, NOSONAR, sonar-disable hoặc prettier-ignore. HFS không cho tắt luật, bỏ kiểm tra kiểu, giấu độ phủ hay giấu phát hiện Sonar tại chỗ."
3462
+ causes_vi:
3463
+ - "eslint-disable hoặc stylelint-disable (dòng, dòng kế tiếp hoặc cả tệp) để qua một luật"
3464
+ - "@ts-ignore hoặc @ts-expect-error để qua lỗi kiểu"
3465
+ - "istanbul, c8 hay v8 ignore để giấu mã khỏi độ phủ; NOSONAR hay sonar-disable để giấu phát hiện; prettier-ignore để bỏ định dạng"
3466
+ nextStep_vi: "Sửa mã cho đúng luật, hoặc đề xuất đổi luật trong canon; 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."
3467
+ owner: op-retry
3468
+ kind: check-finding
3469
+
1605
3470
  HFS_LINT_SUPPRESSION_FILE:
1606
3471
  title: "A repository keeps no lint-suppression file, script or option"
1607
3472
  title_vi: "Kho có tệp hoặc script ghi nhận bỏ qua lỗi lint"
@@ -1846,6 +3711,16 @@ HFS_README_WORK_POINTER_MISSING:
1846
3711
  owner: other-op:docs.author
1847
3712
  kind: check-finding
1848
3713
 
3714
+ HFS_RELEASE_TAG_MISMATCH:
3715
+ title: "Product release tag does not match its version or image tag"
3716
+ title_vi: "Tag phát hành sản phẩm không khớp phiên bản hoặc tag image"
3717
+ meaning_vi: "Tag phát hành phải có dạng v cộng phiên bản package.json; workflow images.yml phải dùng tag phát hành đó cho image trong lite và full."
3718
+ causes_vi:
3719
+ - "Phiên bản package và tag khác nhau hoặc image dùng tag khác theo luật R271."
3720
+ nextStep_vi: "Đồng bộ phiên bản package và tag phát hành, rồi dùng cùng tag trong workflow xây image."
3721
+ owner: op-retry
3722
+ kind: check-finding
3723
+
1849
3724
  HFS_REPO_LOCAL_CHECK:
1850
3725
  title: "A repository keeps no check or lint rule of its own"
1851
3726
  title_vi: "Kho tự giữ bộ kiểm tra riêng"
@@ -1933,20 +3808,100 @@ HFS_RULE_OFF_WITHOUT_REPLACEMENT:
1933
3808
  owner: op-retry
1934
3809
  kind: check-finding
1935
3810
 
1936
- HFS_SERVICE_PLACEMENT:
1937
- title: "A back-end service lives only at be/apps/<service>"
1938
- title_vi: "Dịch vụ back end nằm ngoài be/apps/<service>"
1939
- meaning_vi: "Một thư mục có Dockerfile hoặc package.json riêng nằm ngoài `be/apps/<service>/` (và không phải Dockerfile của front end hay package workspace của `fe/packages`), nên nó trở thành một gốc dịch vụ thứ hai bên cạnh `be/` và `fe/`."
3811
+ HFS_SBOM_MISSING:
3812
+ title: "Release images carry SBOM and provenance evidence"
3813
+ title_vi: "Image phát hành thiếu SBOM hoặc provenance"
3814
+ meaning_vi: "Workflow images thiếu bước hoạt động tạo SBOM và chứng thực nguồn gốc cho image phát hành."
1940
3815
  causes_vi:
1941
- - "Vi phạm luật R163: mỗi dịch vụ back end là một ứng dụng Nest tại `be/apps/<service>/` dùng chung thư viện `be/src` và một `package.json` duy nhất của app; image của nó được build từ `be/apps/<service>/Dockerfile`."
1942
- - "Op đã tạo thư mục dịch vụ riêng (ví dụ `services/<name>/` hoặc `be/<name>/`) thay vì thêm ứng dụng dưới `be/apps/`."
1943
- 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: chuyển dịch vụ vào `be/apps/<service>/` (main.ts, app.module.ts, Dockerfile) và xoá thư mục cũ; không thêm ngoại lệ."
3816
+ - "Vi phạm R269: bản dựng không tạo cả danh mục thành phần và bằng chứng nguồn gốc."
3817
+ nextStep_vi: "Bật sbom và provenance trong bước build image hoặc tạo SBOM cùng chứng thực nguồn gốc bằng công cụ/action đã ghim."
1944
3818
  owner: op-retry
1945
3819
  kind: check-finding
1946
3820
 
1947
- HFS_SERVICE_STACK_DECLARATION:
1948
- title: "Each service app is a service component of the stack"
1949
- title_vi: "Ứng dụng dịch vụ chưa được khai báo trong stack"
3821
+ HFS_SCHEDULE_UNDECLARED:
3822
+ title: "A scheduled component has no declared CLI job"
3823
+ title_vi: "Component chạy theo lịch thiếu job CLI được khai báo"
3824
+ meaning_vi: "Component có lịch hoặc replicated-job trong mô hình chưa có entry jobs khớp với schedule và lệnh gọi action có tên của CLI ứng dụng."
3825
+ causes_vi:
3826
+ - "Vi phạm R268: lịch chỉ nằm trong cấu hình triển khai hoặc gọi lệnh ngoài quyền sở hữu CLI của ứng dụng."
3827
+ nextStep_vi: "Khai báo jobs.<id> với schedule, command của back end CLI và component khi cần; giữ lập lịch trong ứng dụng hoặc replicated-job được khai báo."
3828
+ owner: op-retry
3829
+ kind: check-finding
3830
+
3831
+ HFS_SCRIPT_PLAINTEXT_LOAD:
3832
+ title: "Executable scripts do not load plaintext credential files"
3833
+ title_vi: "Script đọc tệp bí mật dạng rõ"
3834
+ meaning_vi: "Script tại <path> đọc secrets.env, secret.env, .env bí mật hoặc tệp thông tin xác thực trong thư mục nhà; bộ kiểm chỉ báo đường dẫn và không đọc giá trị."
3835
+ causes_vi:
3836
+ - "Vi phạm R252: nạp bí mật từ tệp dạng rõ thay cho bí mật đã mã hoá và cơ chế materialize."
3837
+ nextStep_vi: "Bỏ cách nạp tệp rõ; dùng starci app secret materialize và cơ chế bí mật của stack, không in giá trị."
3838
+ owner: op-retry
3839
+ kind: check-finding
3840
+
3841
+ HFS_SCRIPT_XTRACE:
3842
+ title: "Scripts do not enable xtrace or reveal secrets"
3843
+ title_vi: "Script bật truy vết hoặc hiện bí mật"
3844
+ meaning_vi: "Script tại <path> bật set -x, xtrace hoặc yêu cầu --reveal, có thể đưa giá trị bí mật vào log."
3845
+ causes_vi:
3846
+ - "Vi phạm R252: bật truy vết shell hoặc yêu cầu hiển thị bí mật trong lệnh tự động."
3847
+ nextStep_vi: "Tắt xtrace và bỏ --reveal; chỉ ghi thông tin thao tác không chứa giá trị bí mật."
3848
+ owner: op-retry
3849
+ kind: check-finding
3850
+
3851
+ HFS_SECRET_OUTSIDE_ENC:
3852
+ title: "A sealed secret sits outside the sealed home"
3853
+ title_vi: "File `.enc` nằm ngoài thư mục custody duy nhất"
3854
+ meaning_vi: "`<path>` là file `.enc` ngoài `.starcistacks/<env>/secrets/`. Chỉ đúng một đường custody được chấp nhận cho envelope bí mật trong kho sản phẩm."
3855
+ causes_vi:
3856
+ - "Vi phạm luật R277: chỉ `.starcistacks/<env>/secrets/**.enc` được giữ envelope; `.enc` ở chỗ khác là custody sai (với lite lỗi này đã là HFS_LITE_SECRET_CUSTODY)."
3857
+ nextStep_vi: "Chuyển envelope về `.starcistacks/<env>/secrets/<slug>.enc` (hoặc niêm phong lại bằng `starci app secret set`) rồi gỡ bản `.enc` lạc chỗ khỏi index."
3858
+ owner: owner
3859
+ kind: check-finding
3860
+
3861
+ HFS_SECRET_PLAINTEXT_FILE:
3862
+ title: "A tracked plaintext secret file is refused"
3863
+ title_vi: "Tệp bí mật dạng rõ bị track trong kho"
3864
+ meaning_vi: "`<path>` là tệp bí mật dạng rõ được track (`secrets.env`, `*.env` không dấu chấm chứa khoá bí mật, hoặc tệp trong thư mục `secrets/`). Trong mọi edition bí mật chỉ được commit dưới dạng sops envelope tại `.starcistacks/<env>/secrets/**.enc`."
3865
+ causes_vi:
3866
+ - "Vi phạm luật R277: custody duy nhất cho secret được track là `.starcistacks/<env>/secrets/**.enc`; mọi file plaintext secret đều bị từ chối."
3867
+ nextStep_vi: "Không đọc và không in giá trị. Niêm phong giá trị bằng `starci app secret set` vào `.starcistacks/<env>/secrets/<slug>.enc` rồi gỡ tệp plaintext khỏi index; xoay secret nếu nó đã lộ."
3868
+ owner: owner
3869
+ kind: check-finding
3870
+
3871
+ HFS_SEED_PII:
3872
+ title: "Seed SQL contains no real email or phone literals"
3873
+ title_vi: "Seed SQL chứa dữ liệu định danh thật"
3874
+ meaning_vi: "File seed chứa literal có dạng email hoặc số điện thoại thật thay vì dữ liệu mẫu an toàn. Finding không hiển thị giá trị đó."
3875
+ causes_vi:
3876
+ - "Vi phạm R259: sao chép dữ liệu người dùng vào seed theo dõi trong repository."
3877
+ nextStep_vi: "Thay bằng địa chỉ @example.test và dữ liệu mẫu không định danh; không đưa dữ liệu người dùng thật vào seed."
3878
+ owner: op-retry
3879
+ kind: check-finding
3880
+
3881
+ HFS_SERVICE_PLACEMENT:
3882
+ title: "A back-end service lives only at be/apps/<service>"
3883
+ title_vi: "Dịch vụ back end nằm ngoài be/apps/<service>"
3884
+ meaning_vi: "Một thư mục có Dockerfile hoặc package.json riêng nằm ngoài `be/apps/<service>/` (và không phải Dockerfile của front end hay package workspace của `fe/packages`), nên nó trở thành một gốc dịch vụ thứ hai bên cạnh `be/` và `fe/`."
3885
+ causes_vi:
3886
+ - "Vi phạm luật R163: mỗi dịch vụ back end là một ứng dụng Nest tại `be/apps/<service>/` dùng chung thư viện `be/src` và một `package.json` duy nhất của app; image của nó được build từ `be/apps/<service>/Dockerfile`."
3887
+ - "Op đã tạo thư mục dịch vụ riêng (ví dụ `services/<name>/` hoặc `be/<name>/`) thay vì thêm ứng dụng dưới `be/apps/`."
3888
+ 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: chuyển dịch vụ vào `be/apps/<service>/` (main.ts, app.module.ts, Dockerfile) và xoá thư mục cũ; không thêm ngoại lệ."
3889
+ owner: op-retry
3890
+ kind: check-finding
3891
+
3892
+ HFS_SERVICE_ROLE_SCOPE:
3893
+ title: "Service-role access belongs to the Supabase integration"
3894
+ title_vi: "Service role được dùng ngoài integration Supabase"
3895
+ meaning_vi: "Mã tại <path> tham chiếu service_role ngoài integration Supabase của back end, kể cả trong script triển khai."
3896
+ causes_vi:
3897
+ - "Vi phạm R252: sử dụng quyền service role ngoài thành phần sở hữu kết nối Supabase."
3898
+ nextStep_vi: "Đưa quyền service role vào be/src/modules/integrations/supabase và truy cập qua port của integration; script gọi CLI của ứng dụng."
3899
+ owner: op-retry
3900
+ kind: check-finding
3901
+
3902
+ HFS_SERVICE_STACK_DECLARATION:
3903
+ title: "Each service app is a service component of the stack"
3904
+ title_vi: "Ứng dụng dịch vụ chưa được khai báo trong stack"
1950
3905
  meaning_vi: "Một ứng dụng api hoặc worker dưới `be/apps/<service>/` của sản phẩm nhiều dịch vụ không có thành phần `role: service` cùng tên trong `.starcistacks/application-stacks.yaml`."
1951
3906
  causes_vi:
1952
3907
  - "Vi phạm luật R165: mọi dịch vụ của sản phẩm nhiều dịch vụ là một thành phần `role: service` của stack (image riêng `<repo>/<service>:<x.y.z>`, build từ Dockerfile của app)."
@@ -1954,6 +3909,18 @@ HFS_SERVICE_STACK_DECLARATION:
1954
3909
  owner: op-retry
1955
3910
  kind: check-finding
1956
3911
 
3912
+ HFS_SIZE_GROWTH:
3913
+ title: "Source file over the size budget grew"
3914
+ title_vi: "Tệp mã nguồn vượt hạn mức dòng vẫn tiếp tục dài thêm"
3915
+ meaning_vi: "Một tệp mã nguồn đã dài hơn hạn mức mềm của ruleParams.fileLines nhưng lại dài thêm so với phiên bản gốc của nhánh, hoặc một tệp mới tạo đã vượt hạn mức ngay từ đầu. Tệp quá dài khó đọc, khó sửa và là nơi lỗi dễ ẩn; quy tắc này cho phép tệp cũ đang quá dài được giữ nguyên hoặc thu nhỏ dần, nhưng không cho phép nó phình thêm."
3916
+ causes_vi:
3917
+ - "Op thêm mã mới vào một tệp vốn đã quá hạn mức thay vì tách phần mới sang tệp khác"
3918
+ - "Một tệp đang dưới hạn mức được thêm mã cho đến khi vượt hạn mức"
3919
+ - "Một tệp mới tạo dài hơn hạn mức, hoặc một tệp bị di chuyển sang đường dẫn mới nên bị coi là tệp mới"
3920
+ nextStep_vi: "Op vừa tạo ra bản này sẽ được chạy lại để tách phần mã mới (hoặc bớt phần cũ) sang tệp riêng đúng ô của nó, sao cho tệp không dài hơn phiên bản gốc và tệp mới nằm trong hạn mức."
3921
+ owner: op-retry
3922
+ kind: check-finding
3923
+
1957
3924
  HFS_SIZE_SOFT_BACKLOG:
1958
3925
  title: "Source file above the soft size"
1959
3926
  title_vi: "Tệp mã vượt cỡ mềm (chỉ báo cáo)"
@@ -2131,6 +4098,27 @@ HFS_UNUSED_FILE:
2131
4098
  owner: op-retry
2132
4099
  kind: check-finding
2133
4100
 
4101
+ HFS_UNUSED_SYMBOL:
4102
+ title: "An enum member, queue constant, error code or catalog key has no production consumer"
4103
+ title_vi: "Ký hiệu không có nơi dùng trong mã chạy thực tế"
4104
+ meaning_vi: "Thành viên enum, hằng hàng đợi, mã lỗi hoặc khóa danh mục được khai báo nhưng không được mã chạy thực tế sử dụng; bảng mô tả lỗi và tham chiếu chỉ trong kiểm thử không đủ."
4105
+ causes_vi:
4106
+ - "Ký hiệu còn sót lại sau khi xóa hoặc đổi luồng nghiệp vụ."
4107
+ - "Import namespace hoặc bảng ánh xạ che mất ký hiệu không còn dùng."
4108
+ nextStep_vi: "Xóa ký hiệu và ánh xạ thừa, hoặc nối nó vào luồng chạy thực tế nếu nghiệp vụ vẫn cần."
4109
+ owner: op-retry
4110
+ kind: check-finding
4111
+
4112
+ HFS_WORKFLOW_PIN:
4113
+ title: "Remote workflow actions use immutable commit SHAs"
4114
+ title_vi: "Action của workflow chưa được ghim SHA"
4115
+ meaning_vi: "Workflow tại <path> dùng action từ xa với tag hoặc nhánh thay cho commit SHA gồm 40 ký tự hex."
4116
+ causes_vi:
4117
+ - "Vi phạm R251: action có thể đổi nội dung khi tag hoặc nhánh thay đổi."
4118
+ nextStep_vi: "Ghim uses của action từ xa bằng commit SHA gồm 40 ký tự hex; action cục bộ vẫn dùng đường dẫn trong kho."
4119
+ owner: op-retry
4120
+ kind: check-finding
4121
+
2134
4122
  HFS_WORK_IN_FE:
2135
4123
  title: "Work tree inside the front end (fe/)"
2136
4124
  title_vi: "Hồ sơ Work nằm trong repo giao diện"
@@ -2141,3 +4129,1085 @@ HFS_WORK_IN_FE:
2141
4129
  nextStep_vi: "Bộ kiểm kiến trúc từ chối; op phải chuyển hồ sơ sang repo backend rồi chạy lại."
2142
4130
  owner: op-retry
2143
4131
  kind: check-finding
4132
+
4133
+ RELEASE_NOTES:
4134
+ title: "Release tag without finished release notes"
4135
+ title_vi: "Tag phát hành chưa có ghi chú phát hành hoàn chỉnh"
4136
+ meaning_vi: "HEAD mang tag phát hành v<phiên bản> nhưng CHANGELOG.md không có mục ## [<phiên bản>], hoặc mục đó còn TODO, PENDING, TBD hay dấu in preparation. Thông điệp của tag và GitHub Release chính là mục này nên không được cắt tag khi nó chưa xong."
4137
+ causes_vi:
4138
+ - "Tạo tag trước khi viết xong mục CHANGELOG"
4139
+ - "Còn sót dấu PENDING hoặc TBD của lane chưa land"
4140
+ nextStep_vi: "Người giám sát hoàn tất mục CHANGELOG của phiên bản (xóa mọi dấu TODO, PENDING, TBD), commit rồi mới tạo lại tag chú thích trên commit phát hành; không dời hay xóa tag đã đẩy."
4141
+ owner: supervisor
4142
+ kind: check-finding
4143
+
4144
+ RIGHTS_GIT_COMMIT:
4145
+ title: "Raw git commit write refused"
4146
+ title_vi: "Từ chối ghi commit git trực tiếp"
4147
+ meaning_vi: "Agent đã gọi trực tiếp git commit, add, rm hoặc mv, nhưng mọi thay đổi lịch sử và vùng staged phải đi qua động từ runtime để quyền, bằng chứng và bước settle được giữ nhất quán."
4148
+ causes_vi:
4149
+ - "Agent tự stage hoặc commit thay vì để runtime tạo commit của phần việc đã qua cổng"
4150
+ - "Agent dùng git rm hoặc git mv trực tiếp cho một thay đổi trong cây làm việc"
4151
+ nextStep_vi: "Lệnh bị từ chối; dùng starci git commit, hoặc để nguyên thay đổi và báo cáo để runtime commit phần việc đã qua cổng khi settle."
4152
+ owner: op-retry
4153
+ kind: verb-refusal
4154
+
4155
+ RIGHTS_GIT_PUSH:
4156
+ title: "Git push refused by role rights"
4157
+ title_vi: "Quyền theo vai trò từ chối git push"
4158
+ meaning_vi: "Worker, lead, Supervisor hoặc coordinator không được đẩy remote theo ý mình: remote chỉ di chuyển một lần trong mỗi bản phát hành, khi main và thẻ phát hành được đẩy cùng nhau; coordinator hoặc Supervisor chỉ được đẩy bản sao lưu bằng refspec tường minh vào refs/backup/*."
4159
+ causes_vi:
4160
+ - "Agent định đẩy main, một nhánh thường, mọi thẻ hoặc một refspec quá rộng"
4161
+ - "Coordinator hoặc Supervisor đẩy bản sao lưu nhưng đích không nằm trong refs/backup/* hoặc không có refspec tường minh"
4162
+ nextStep_vi: "Lệnh bị từ chối; để thay đổi đi qua starci release cut, hoặc nếu là coordinator hay Supervisor thì dùng starci git backup với đích refs/backup/* tường minh."
4163
+ owner: op-retry
4164
+ kind: verb-refusal
4165
+
4166
+ RIGHTS_GIT_SYNC:
4167
+ title: "Raw git synchronization write refused"
4168
+ title_vi: "Từ chối lệnh git đồng bộ trực tiếp"
4169
+ meaning_vi: "Agent đã gọi một lệnh git ghi lịch sử, ref, cấu hình hoặc cây làm việc ngoài commit, push và tag; các thao tác merge, rebase, pull, fetch, checkout, switch, reset, restore, clean, stash, worktree, branch và tương tự chỉ do runtime điều phối."
4170
+ causes_vi:
4171
+ - "Agent tự merge hoặc rebase main vào lane"
4172
+ - "Agent tự đổi nhánh, reset, restore, clean, stash, sửa ref, cấu hình hoặc worktree"
4173
+ nextStep_vi: "Lệnh bị từ chối; dùng starci git sync để cập nhật lane hoặc starci git land khi coordinator đưa thay đổi vào main."
4174
+ owner: op-retry
4175
+ kind: verb-refusal
4176
+
4177
+ RIGHTS_GIT_TAG:
4178
+ title: "Git tag write refused"
4179
+ title_vi: "Từ chối ghi thẻ git"
4180
+ meaning_vi: "Thẻ là dấu của một bản phát hành nên chỉ release cut được tạo thẻ; agent không được tạo, di chuyển hoặc xóa thẻ, và dấu giữ chỗ không phải thẻ."
4181
+ causes_vi:
4182
+ - "Agent định tạo hoặc ký một thẻ phát hành"
4183
+ - "Agent định di chuyển, ép ghi hoặc xóa thẻ, hoặc dùng thẻ làm dấu giữ chỗ"
4184
+ nextStep_vi: "Lệnh bị từ chối; dùng starci release cut cho thẻ phát hành, còn dấu giữ chỗ dùng refs/backup/* hoặc refs/salvage/*."
4185
+ owner: op-retry
4186
+ kind: verb-refusal
4187
+
4188
+ RIGHTS_NPM_CI_UNLOCKED:
4189
+ title: "Unlocked clean install refused"
4190
+ title_vi: "Từ chối cài sạch khi chưa giữ khóa máy"
4191
+ meaning_vi: "npm ci xóa và dựng lại node_modules nên là một lần chạy nặng; lệnh chỉ được chạy sau khi runtime đã lấy host lock cho đúng tác vụ."
4192
+ causes_vi:
4193
+ - "Agent gọi npm ci hoặc một biến thể clean install mà không có host lock"
4194
+ - "Host lock đang do terminal khác giữ hoặc chủ khóa đã stale"
4195
+ nextStep_vi: "Lệnh bị từ chối; dùng động từ starci của cổng hoặc đồng bộ ứng dụng để runtime lấy host lock rồi cài phụ thuộc, không chạy npm ci bằng tay."
4196
+ owner: op-retry
4197
+ kind: verb-refusal
4198
+
4199
+ RIGHTS_NPM_PUBLISH:
4200
+ title: "Package publish refused"
4201
+ title_vi: "Từ chối phát hành package"
4202
+ meaning_vi: "Package chỉ được publish một lần trong mỗi bản phát hành và từ release commit, nên agent ở lane không được publish, unpublish, deprecate hoặc đổi dist-tag."
4203
+ causes_vi:
4204
+ - "Agent gọi npm, pnpm hoặc yarn publish"
4205
+ - "Agent định unpublish, deprecate hoặc sửa dist-tag ngoài release cut"
4206
+ nextStep_vi: "Lệnh bị từ chối; hoàn tất lane và để chủ chạy starci release cut từ release commit."
4207
+ owner: op-retry
4208
+ kind: verb-refusal
4209
+
4210
+ RIGHTS_OP_RUNTIME_WRITE:
4211
+ title: "Op runtime write refused"
4212
+ title_vi: "Từ chối op ghi vào runtime"
4213
+ meaning_vi: "Op chỉ sửa ứng dụng trong worktree của workflow và không được ghi vào checkout .claude của runtime; lỗi của harness phải đi qua đường nâng cấp runtime."
4214
+ causes_vi:
4215
+ - "Op định sửa mã, cấu hình hoặc tri thức bên trong checkout .claude"
4216
+ - "Op phát hiện lỗi harness rồi cố sửa ngay trong phần việc của ứng dụng"
4217
+ nextStep_vi: "Lệnh bị từ chối; ghi lesson hoặc upgrade request trong báo cáo để đường owner hoặc supervisor self-upgrade xử lý."
4218
+ owner: op-retry
4219
+ kind: verb-refusal
4220
+
4221
+ RIGHTS_PROTECTED_ZONE:
4222
+ title: "Protected-zone write refused"
4223
+ title_vi: "Từ chối ghi vào vùng được bảo vệ"
4224
+ meaning_vi: "Supervisor self-upgrade đã định sửa vùng được bảo vệ của runtime, nơi chỉ đường owner được thay đổi guard và quyền vai trò, release cut và git hook, trigger CI, chính sách test, host lock, rule catalog liên quan hoặc cài đặt quyền và model."
4225
+ causes_vi:
4226
+ - "Một self-upgrade sửa trực tiếp tệp nằm trong vùng được khai báo ở modules/kernel/protected-zone.yaml"
4227
+ - "Một self-upgrade sửa mục rule hoặc failure code bảo vệ vùng đó"
4228
+ nextStep_vi: "Lệnh bị từ chối; Supervisor nộp đề xuất cho owner, nêu thay đổi cần làm và lý do, để đường owner thực hiện."
4229
+ owner: supervisor
4230
+ kind: verb-refusal
4231
+
4232
+ RIGHTS_PUSH_NOT_RELEASE:
4233
+ title: "Pre-push gate refused the push"
4234
+ title_vi: "Cổng pre-push từ chối lần đẩy"
4235
+ meaning_vi: "Hook pre-push đã chặn một lần đẩy main hoặc thẻ v* của runtime vì commit được đẩy chưa phải commit phát hành: phiên bản chưa vượt phiên bản ở main trên remote, thiếu thẻ annotated v<version>, thiếu tiêu đề CHANGELOG có ngày, hoặc thiếu bản ghi L4 của đúng commit đó với suite gốc, suite các package và check đều xanh."
4236
+ causes_vi:
4237
+ - "Thao tác xóa main hoặc xóa thẻ phát hành"
4238
+ - "Đẩy main không phải fast-forward của main trên remote (force push, rewind hoặc nhánh rẽ)"
4239
+ - "Commit được đẩy không phải commit phát hành: version trùng version ở main remote, thiếu thẻ annotated v<version>, CHANGELOG chưa có tiêu đề có ngày, hoặc bản ghi starci-release của đúng commit đó thiếu hay có dòng đỏ"
4240
+ nextStep_vi: "Không đẩy main giữa hai bản phát hành; chạy starci release cut --tag v<version> (thêm --plan để xem trước) đã được owner duyệt, lệnh này ghi bản ghi L4 rồi đẩy main và thẻ v* cùng nhau."
4241
+ owner: supervisor
4242
+ kind: verb-refusal
4243
+
4244
+ RIGHTS_RAW_TOOL:
4245
+ title: "Raw side-effecting tool refused"
4246
+ title_vi: "Từ chối công cụ thô có tác động phụ"
4247
+ meaning_vi: "Agent đã gọi trực tiếp một công cụ có tác động phụ trong khi runtime có động từ starci sở hữu thao tác đó; chính sách chỉ cho phép các lệnh chỉ đọc được khai báo một lần trong modules/kernel/command-policy.yaml."
4248
+ causes_vi:
4249
+ - "Agent gọi npm run, npm test, npm install, node scripts, docker, supabase, gh, schtasks, shutdown, một test runner hoặc Orca mutation trực tiếp"
4250
+ - "Agent gọi một chương trình chưa được khai báo trong bảng chính sách lệnh"
4251
+ nextStep_vi: "Lệnh bị từ chối; dùng đúng starci <nhóm> <động từ> mà lời từ chối nêu, hoặc nhờ owner khi không có động từ được phép."
4252
+ owner: op-retry
4253
+ kind: verb-refusal
4254
+
4255
+ RIGHTS_RELEASE_CUT:
4256
+ title: "Release cut refused"
4257
+ title_vi: "Từ chối cắt bản phát hành"
4258
+ meaning_vi: "Release cut là quyết định phát hành của owner và chạy toàn bộ cổng L4 một lần; worker, lead, Supervisor và coordinator không được tự khởi động nó."
4259
+ causes_vi:
4260
+ - "Agent gọi starci release cut hoặc đường script tương đương"
4261
+ - "Agent định publish bản phát hành mà chưa có phê duyệt của owner"
4262
+ nextStep_vi: "Lệnh bị từ chối; báo lane đã sẵn sàng để owner phê duyệt và khởi động starci release cut."
4263
+ owner: op-retry
4264
+ kind: verb-refusal
4265
+
4266
+ RIGHTS_ROLE_DENIED:
4267
+ title: "Action denied by role rights"
4268
+ title_vi: "Quyền theo vai trò từ chối thao tác"
4269
+ meaning_vi: "Command guard đã từ chối một thao tác vì vai trò của caller không sở hữu tác động đó; modules/kernel/command-policy.yaml là bảng chính sách duy nhất mà PreToolUse guard và PATH shim cùng đọc."
4270
+ causes_vi:
4271
+ - "Agent gọi một lệnh ghi hoặc công cụ có tác động phụ ngoài quyền của vai trò"
4272
+ - "Agent bỏ qua động từ starci sở hữu thao tác"
4273
+ nextStep_vi: "Không chạy đường thô; dùng động từ starci được lời từ chối nêu hoặc báo lại cho vai trò sở hữu thao tác."
4274
+ owner: op-retry
4275
+ kind: verb-refusal
4276
+
4277
+ RIGHTS_SUITE_RUN:
4278
+ title: "Whole-suite run refused"
4279
+ title_vi: "Từ chối chạy toàn bộ bộ kiểm thử"
4280
+ meaning_vi: "Toàn bộ suite chỉ chạy một lần trong mỗi release hoặc theo yêu cầu trong unit.verify hay e2e.verify; một worker chỉ chạy tệp spec vừa thay đổi hoặc để op gate chọn phạm vi."
4281
+ causes_vi:
4282
+ - "Agent gọi npm test, một script test toàn bộ hoặc node --test trên cả cây tests"
4283
+ - "Agent chạy suite ngoài release cut và ngoài op verify được yêu cầu"
4284
+ nextStep_vi: "Lệnh bị từ chối; chạy riêng tệp spec đã thay đổi, để op gate chọn spec liên quan, hoặc dùng unit.verify/e2e.verify khi owner hay mục tiêu yêu cầu."
4285
+ owner: op-retry
4286
+ kind: verb-refusal
4287
+
4288
+ RT_ABSOLUTE_PATH:
4289
+ title: "Hard-coded absolute host path"
4290
+ title_vi: "Đường dẫn tuyệt đối của máy bị ghi cứng"
4291
+ meaning_vi: "Một tệp được git theo dõi của runtime chứa đường dẫn tuyệt đối của máy chủ: đường dẫn có ổ đĩa (chữ cái ổ đĩa, dấu hai chấm và dấu gạch), đường dẫn hồ sơ người dùng (/Users/<tên>/, /home/<tên>/) hoặc đường dẫn AppData đã khai triển. Kho .claude dùng đường dẫn tương đối; đường dẫn ghi cứng chỉ chạy trên một máy và làm lộ tên người dùng."
4292
+ causes_vi:
4293
+ - "Một lane ghi đường dẫn máy của nó vào mã, tài liệu, tri thức, ví dụ hoặc dữ liệu thử"
4294
+ - "Một spec dựng fixture từ ổ đĩa ghi cứng thay vì thư mục tạm của nó"
4295
+ nextStep_vi: "Người giám sát đổi đường dẫn thành đường dẫn tương đối hoặc tên giữ chỗ (<runtime>/..., %LOCALAPPDATA% chưa khai triển), cho mã lấy đường dẫn từ gốc runtime, os.tmpdir(), cấu hình hoặc hàm gốc trạng thái, và cho spec dựng fixture từ thư mục tạm."
4296
+ owner: supervisor
4297
+ kind: check-finding
4298
+
4299
+ RT_API_SHAPE:
4300
+ title: "api folder breaks its shape"
4301
+ title_vi: "Thư mục api sai hình dạng"
4302
+ meaning_vi: "Một tệp lệnh trong scripts/api/<hệ thống>/ export nhiều hơn một hàm hoặc không export hàm mang tên của tệp, một tệp bên ngoài hệ thống import lib.mjs của hệ thống đó, một hệ thống api import hệ thống khác, hoặc một tệp lệnh không phải là id trong hợp đồng calls của hệ thống."
4303
+ causes_vi:
4304
+ - "Thêm hằng số hay hàm phụ vào một tệp lệnh"
4305
+ - "Gọi thẳng trình chạy lib.mjs thay vì tệp lệnh"
4306
+ nextStep_vi: "Người giám sát tách mỗi lệnh thành một tệp riêng mang tên lệnh, chuyển hằng dùng chung vào lib.mjs của hệ thống và để nơi gọi dùng tệp lệnh."
4307
+ owner: supervisor
4308
+ kind: check-finding
4309
+
4310
+ RT_BASE_IMPURE:
4311
+ title: "Base tier writes or reads the environment"
4312
+ title_vi: "Tầng cơ sở ghi tệp hoặc đọc biến môi trường"
4313
+ meaning_vi: "Một tệp của tầng cơ sở (engine nền tảng, scripts/lib) gọi một hàm ghi của node:fs (writeFileSync, rmSync, mkdirSync...) hoặc đọc process.env ngoài các điểm khai báo trong ruleParams.runtime.baseEnvSeams. Tầng cơ sở chỉ chứa hàm thuần."
4314
+ causes_vi:
4315
+ - "Một hàm tiện ích tự ghi tệp thay vì trả dữ liệu cho nơi gọi"
4316
+ - "Đọc cấu hình từ biến môi trường bên trong thư viện"
4317
+ nextStep_vi: "Người giám sát chuyển thao tác ghi vào chủ sở hữu của nó (scripts/api/fs, engine/db hoặc một module nghiệp vụ) và truyền giá trị môi trường vào dưới dạng tham số."
4318
+ owner: supervisor
4319
+ kind: check-finding
4320
+
4321
+ RT_CITED_PATH_MISSING:
4322
+ title: "Cited runtime path does not exist"
4323
+ title_vi: "Đường dẫn được trích dẫn không tồn tại"
4324
+ meaning_vi: "Văn bản đang dùng (modules ngoài lịch sử hợp đồng, docs, skills, knowledge, CONTEXT.md, README.md, CONTRIBUTING.md, init, tài liệu ui) trích dẫn một đường dẫn runtime không tồn tại."
4325
+ causes_vi:
4326
+ - "Di chuyển hay xóa tệp mà không cập nhật tài liệu"
4327
+ - "Gõ sai đường dẫn trong tài liệu"
4328
+ nextStep_vi: "Người giám sát sửa trích dẫn thành đường dẫn hiện tại hoặc xóa câu đó."
4329
+ owner: supervisor
4330
+ kind: check-finding
4331
+
4332
+ RT_CLI_APP_ONLY_TEMPLATES:
4333
+ title: "Managed app template invokes the wrong CLI"
4334
+ title_vi: "Template ứng dụng gọi sai bề mặt CLI"
4335
+ meaning_vi: "Template do StarCi quản lý hoặc bản sao runtime đã sinh gọi lệnh hfs cũ, test-stack cũ, hoặc một nhóm starci không phải app; repository sản phẩm chỉ được gọi starci app <verb>."
4336
+ causes_vi:
4337
+ - "Vi phạm luật R200: mọi hành động sản phẩm trong template phải đi qua CLI thống nhất starci app."
4338
+ nextStep_vi: "Đổi lời gọi thành starci app <verb>, sinh lại các bản sao runtime và chạy lại kiểm tra."
4339
+ owner: op-retry
4340
+ kind: check-finding
4341
+
4342
+ RT_CLI_CATALOG_DRIFT:
4343
+ title: "Generated CLI catalog outputs drifted"
4344
+ title_vi: "Các đầu ra catalog CLI sinh tự động bị lệch"
4345
+ meaning_vi: "Một đầu ra sinh từ catalog của modules/cli/commands (packages/cli/src/catalog.generated.mjs, docs/cli.md, packages/cli/completions/*) khác với nội dung mà scripts/cli/gen-catalog.mjs sinh ra: thiếu, cũ, hoặc đã sửa tay."
4346
+ causes_vi:
4347
+ - "Sửa một file catalog trong modules/cli/commands mà quên chạy gen-catalog --write"
4348
+ - "Sửa tay một đầu ra sinh tự động thay vì sửa catalog nguồn"
4349
+ nextStep_vi: "Người giám sát chạy starci runtime gen-catalog --write rồi commit cả catalog lẫn các đầu ra; không sửa tay tệp đã sinh."
4350
+ owner: supervisor
4351
+ kind: check-finding
4352
+
4353
+ RT_CLI_VERB_PARITY:
4354
+ title: "CLI catalog and implementation disagree"
4355
+ title_vi: "Catalog CLI và cài đặt không khớp nhau"
4356
+ meaning_vi: "Catalog của modules/cli/commands và code lệch nhau: một verb của catalog không có handler (impl.script thiếu, không có verbs/<verb>.mjs, không có case trong cli.mjs) hoặc không có tài liệu; một verb module của kernel hoặc một case của cli.mjs không có file catalog; một --flag trong usage:/required của module verb hay trong api-boolean-flags.txt không nằm trong flags của catalog."
4357
+ causes_vi:
4358
+ - "Thêm một verb module trong scripts/kernel/verbs mà quên file modules/cli/commands/kernel/<verb>.yaml"
4359
+ - "Đổi usage hoặc required của một verb mà không cập nhật flags của catalog"
4360
+ nextStep_vi: "Người giám sát đồng bộ hai phía: thêm file catalog cho verb mới, sửa flags của catalog theo usage/required, hoặc xóa verb khỏi catalog khi handler đã bị xóa."
4361
+ owner: supervisor
4362
+ kind: check-finding
4363
+
4364
+ RT_CONFIG_DEFAULT_TWICE:
4365
+ title: "A documented default is restated outside its owner"
4366
+ title_vi: "Một giá trị mặc định được khai báo lại ngoài tệp chủ"
4367
+ meaning_vi: "Mỗi mặc định của cấu hình được viết một lần trong tệp chủ của nó: các leaf camelCase hay khóa lowercase của config.example.yaml, và các mặc định mà chủ sở hữu khai báo (DEFAULTS của scripts/machine/home.mjs cho supervisor và ngôn ngữ owner, engine/config.mjs cho connector/ask/UAT/spec/allocation, engine/orca-config.mjs cho độ sâu worker, scripts/reconciler/state.mjs cho mode của controller). Một literal fallback (??, ||, mặc định tham số, hay ternary kiểu language === 'en' ? 'en' : 'vi') ở tệp khác là bản chép lại; phía đọc gọi accessor của chủ (supervisorSettings(), configuredMode(), ownerLanguage()). Spec, test, runtime sinh ra, story và template HFS nằm ngoài luật."
4368
+ causes_vi:
4369
+ - "Viết ?? <literal> hay || <literal> cho một khóa đã có trong config.example.yaml thay vì gọi accessor"
4370
+ - "Khai báo tham số mặc định bằng literal giá trị mà chủ sở hữu đã định"
4371
+ nextStep_vi: "Người giám sát thay literal bằng accessor của chủ sở hữu (import DEFAULTS/supervisorSettings/ownerLanguage...) hoặc chuyển mặc định về tệp chủ rồi đọc qua nó."
4372
+ owner: supervisor
4373
+ kind: check-finding
4374
+
4375
+ RT_CONTROL_CHARACTER:
4376
+ title: "Raw control character in source"
4377
+ title_vi: "Ký tự điều khiển thô trong mã nguồn"
4378
+ meaning_vi: "Một tệp văn bản được git theo dõi (mjs, cjs, js, ts, tsx, yaml, md, json) chứa ký tự điều khiển thô (U+0000 đến U+001F trừ tab, LF, CR, và U+007F). NUL thô làm git và grep coi tệp là nhị phân; backspace thô trong biểu thức chính quy không còn nghĩa \\b như khi gõ."
4379
+ causes_vi:
4380
+ - "Một lần sửa bằng script ghi chuỗi \\b hoặc \\0 thành ký tự thật"
4381
+ nextStep_vi: "Người giám sát thay ký tự đó bằng dạng thoát của nó (\\0, \\b, \\u001b) để tệp giữ đúng nghĩa ban đầu."
4382
+ owner: supervisor
4383
+ kind: check-finding
4384
+
4385
+ RT_COVERAGE_CODE_UNROUTED:
4386
+ title: "A machine finding routed to lint has no applicable plugin enforcer"
4387
+ title_vi: "Mã lỗi chuyển sang lint thiếu bộ kiểm tra phù hợp"
4388
+ meaning_vi: "Mã lỗi máy chuyển sang bề mặt lint nhưng plugin của profile và edition đó không đăng ký bộ kiểm tra phát mã này."
4389
+ causes_vi:
4390
+ - "Vi phạm R247: mã lỗi chỉ được xử lý bởi plugin của profile khác hoặc bộ kiểm tra chưa được đăng ký."
4391
+ nextStep_vi: "Đăng ký bộ kiểm tra phát đúng mã trong plugin của profile và edition được định tuyến, rồi thêm spec chứng minh đường đi."
4392
+ owner: op-retry
4393
+ kind: check-finding
4394
+
4395
+ RT_COVERAGE_EDITION_ASYMMETRY:
4396
+ title: "An edition difference has no ruling justification"
4397
+ title_vi: "Khác biệt phiên bản thiếu căn cứ quyết định"
4398
+ meaning_vi: "Luật, ô hoặc bộ kiểm tra khác nhau giữa lite và full nhưng chưa dẫn quyết định của chủ sở hữu."
4399
+ causes_vi:
4400
+ - "Danh mục hoặc bằng chứng của chuẩn chưa được cập nhật theo hành vi thực tế"
4401
+ nextStep_vi: "Ghi editionWhy với quyết định có thật giải thích khác biệt."
4402
+ owner: runtime-core
4403
+ kind: check-finding
4404
+
4405
+ RT_COVERAGE_GATE_LIE:
4406
+ title: "A catalog gate claim has no corresponding command"
4407
+ title_vi: "Cổng khai báo không có lệnh kiểm tra tương ứng"
4408
+ meaning_vi: "Luật tuyên bố chạy ở một cổng nhưng lệnh thật của cổng không chạy bộ kiểm tra áp dụng."
4409
+ causes_vi:
4410
+ - "Danh mục hoặc bằng chứng của chuẩn chưa được cập nhật theo hành vi thực tế"
4411
+ nextStep_vi: "Đối chiếu hook, cổng và workflow rồi sửa lệnh hoặc khai báo cho đúng."
4412
+ owner: runtime-core
4413
+ kind: check-finding
4414
+
4415
+ RT_COVERAGE_MATRIX:
4416
+ title: "The HFS standard has a missing coverage link"
4417
+ title_vi: "Chuẩn HFS thiếu mắt xích kiểm chứng"
4418
+ meaning_vi: "Bảng bao phủ chưa nối đủ luật, bộ kiểm tra và bằng chứng cho từng phiên bản."
4419
+ causes_vi:
4420
+ - "Danh mục hoặc bằng chứng của chuẩn chưa được cập nhật theo hành vi thực tế"
4421
+ nextStep_vi: "Sửa các phát hiện RT_COVERAGE trong báo cáo rồi chạy lại bộ kiểm tra."
4422
+ owner: runtime-core
4423
+ kind: check-finding
4424
+
4425
+ RT_COVERAGE_NO_DOC:
4426
+ title: "A rule has no documentation evidence"
4427
+ title_vi: "Luật thiếu bằng chứng tài liệu"
4428
+ meaning_vi: "Tài liệu được luật dẫn không tồn tại hoặc không giải thích mã lỗi trong phiên bản áp dụng."
4429
+ causes_vi:
4430
+ - "Danh mục hoặc bằng chứng của chuẩn chưa được cập nhật theo hành vi thực tế"
4431
+ nextStep_vi: "Dẫn tài liệu thật và giải thích từng mã lỗi của luật."
4432
+ owner: runtime-core
4433
+ kind: check-finding
4434
+
4435
+ RT_COVERAGE_NO_ENFORCER:
4436
+ title: "A rule has no enforcer in an applicable edition"
4437
+ title_vi: "Luật thiếu bộ kiểm tra trong phiên bản áp dụng"
4438
+ meaning_vi: "Luật không có bộ kiểm tra đã tồn tại và chạy trong phiên bản được khai báo."
4439
+ causes_vi:
4440
+ - "Danh mục hoặc bằng chứng của chuẩn chưa được cập nhật theo hành vi thực tế"
4441
+ nextStep_vi: "Xây dựng và đăng ký bộ kiểm tra đúng phiên bản của luật."
4442
+ owner: runtime-core
4443
+ kind: check-finding
4444
+
4445
+ RT_COVERAGE_NO_EXAMPLE:
4446
+ title: "A rule or slot has no example"
4447
+ title_vi: "Luật hoặc ô thiếu ví dụ"
4448
+ meaning_vi: "Không có tệp ví dụ đúng phiên bản thể hiện cấu trúc mà luật hoặc ô yêu cầu."
4449
+ causes_vi:
4450
+ - "Danh mục hoặc bằng chứng của chuẩn chưa được cập nhật theo hành vi thực tế"
4451
+ nextStep_vi: "Thêm cấu trúc vào ví dụ đúng phiên bản và dẫn tệp thật trong danh mục."
4452
+ owner: runtime-core
4453
+ kind: check-finding
4454
+
4455
+ RT_COVERAGE_NO_SPEC:
4456
+ title: "A failure code has no edition-specific spec proof"
4457
+ title_vi: "Mã lỗi thiếu đặc tả chứng minh theo phiên bản"
4458
+ meaning_vi: "Mã lỗi chưa có đặc tả được khai báo với nhãn proves cho phiên bản áp dụng."
4459
+ causes_vi:
4460
+ - "Danh mục hoặc bằng chứng của chuẩn chưa được cập nhật theo hành vi thực tế"
4461
+ nextStep_vi: "Thêm ca vi phạm và ca đạt, ghi nhãn proves: CODE@edition và dẫn tệp trong luật."
4462
+ owner: runtime-core
4463
+ kind: check-finding
4464
+
4465
+ RT_COVERAGE_NO_TEMPLATE:
4466
+ title: "A rule or slot has no template"
4467
+ title_vi: "Luật hoặc ô thiếu mẫu chuẩn"
4468
+ meaning_vi: "Luật hoặc ô bắt buộc chưa có mẫu hay đường dẫn scaffold thật trong phiên bản áp dụng."
4469
+ causes_vi:
4470
+ - "Danh mục hoặc bằng chứng của chuẩn chưa được cập nhật theo hành vi thực tế"
4471
+ nextStep_vi: "Bổ sung mẫu đúng phiên bản và nối mẫu với ô hoặc luật."
4472
+ owner: runtime-core
4473
+ kind: check-finding
4474
+
4475
+ RT_COVERAGE_NO_WITNESS:
4476
+ title: "A rule has no mutation witness"
4477
+ title_vi: "Luật thiếu ca đột biến làm chứng"
4478
+ meaning_vi: "Luật chưa có đột biến từ ví dụ đúng làm phát sinh mã lỗi hoặc lý do có ngày cho trường hợp không thể kích hoạt."
4479
+ causes_vi:
4480
+ - "Danh mục hoặc bằng chứng của chuẩn chưa được cập nhật theo hành vi thực tế"
4481
+ nextStep_vi: "Thêm đột biến có mã lỗi mong đợi cho từng phiên bản hoặc ghi lý do không thể kích hoạt kèm ngày."
4482
+ owner: runtime-core
4483
+ kind: check-finding
4484
+
4485
+ RT_COVERAGE_PROOF_VACUOUS:
4486
+ title: "A claimed coverage proof does not exercise its enforcer"
4487
+ title_vi: "Bằng chứng kiểm thử không thực thi bộ kiểm tra"
4488
+ meaning_vi: "Spec khai báo proves nhưng không có ca hợp lệ và vi phạm thực chất, hoặc dùng bằng chứng của một bộ kiểm tra cho bộ khác."
4489
+ causes_vi:
4490
+ - "Vi phạm R247: danh sách valid hoặc invalid rỗng, mã lỗi chỉ được nhắc tên, hoặc bộ kiểm tra chưa được gọi trong bằng chứng."
4491
+ nextStep_vi: "Thêm ca hợp lệ và vi phạm cho từng edition, gọi đúng bộ kiểm tra và xác nhận kết quả bằng assertion."
4492
+ owner: op-retry
4493
+ kind: check-finding
4494
+
4495
+ RT_COVERAGE_REQUIREMENT_UNLEDGERED:
4496
+ title: "A hard documentation requirement is not ledgered"
4497
+ title_vi: "Yêu cầu bắt buộc trong tài liệu chưa vào sổ"
4498
+ meaning_vi: "Một câu yêu cầu bắt buộc không có mục sổ hợp lệ hoặc neo tài liệu được luật dẫn."
4499
+ causes_vi:
4500
+ - "Danh mục hoặc bằng chứng của chuẩn chưa được cập nhật theo hành vi thực tế"
4501
+ nextStep_vi: "Đưa yêu cầu vào sổ cùng luật hoặc trạng thái có lý do và dẫn neo tài liệu."
4502
+ owner: runtime-core
4503
+ kind: check-finding
4504
+
4505
+ RT_COVERAGE_SCHEMA_NO_RECORD:
4506
+ title: "A Work schema has no full-edition example record"
4507
+ title_vi: "Schema Work thiếu bản ghi ví dụ full"
4508
+ meaning_vi: "Một họ bản ghi Work chưa có bản ghi trong ví dụ full và chưa được đánh dấu là dữ liệu sinh."
4509
+ causes_vi:
4510
+ - "Danh mục hoặc bằng chứng của chuẩn chưa được cập nhật theo hành vi thực tế"
4511
+ nextStep_vi: "Thêm bản ghi ví dụ theo schema hoặc khai báo generated cho họ dữ liệu sinh."
4512
+ owner: runtime-core
4513
+ kind: check-finding
4514
+
4515
+ RT_COVERAGE_SUPERSEDED_CITE:
4516
+ title: "A citation uses a superseded ruling"
4517
+ title_vi: "Trích dẫn dùng quyết định đã bị thay thế"
4518
+ meaning_vi: "Tài liệu còn dẫn quyết định cũ mà thiếu quyết định kế tiếp trong cùng đoạn hoặc còn cách diễn đạt đã bị bỏ."
4519
+ causes_vi:
4520
+ - "Danh mục hoặc bằng chứng của chuẩn chưa được cập nhật theo hành vi thực tế"
4521
+ nextStep_vi: "Đổi sang quyết định hiện hành hoặc nêu quyết định kế tiếp trong cùng đoạn."
4522
+ owner: runtime-core
4523
+ kind: check-finding
4524
+
4525
+ RT_COVERAGE_SURFACE_UNMAPPED:
4526
+ title: "A surface has no rule and slot mapping"
4527
+ title_vi: "Bề mặt chưa nối với luật và ô"
4528
+ meaning_vi: "Một ô theo phiên bản hoặc bề mặt khai báo chưa có luật và ô áp dụng, cũng chưa có lý do thiếu."
4529
+ causes_vi:
4530
+ - "Danh mục hoặc bằng chứng của chuẩn chưa được cập nhật theo hành vi thực tế"
4531
+ nextStep_vi: "Nối bề mặt với luật và ô đúng phiên bản hoặc ghi gap với lý do cụ thể."
4532
+ owner: runtime-core
4533
+ kind: check-finding
4534
+
4535
+ RT_DOCUMENTED_DEFAULT_DRIFT:
4536
+ title: "A documented default differs from the value the code reads"
4537
+ title_vi: "Một giá trị mặc định được ghi trong tài liệu khác với giá trị mà code đọc"
4538
+ meaning_vi: "Tài liệu nêu giá trị mặc định bằng cách dẫn khóa sở hữu nó (default <giá trị> = `<khóa>`); khóa là đường dẫn config.yaml (xác định qua hàm settings của bộ kiểm tra) hoặc <tệp yaml trong modules/>:<đường dẫn>. Giá trị ghi khác giá trị khóa đó thực sự cho, hoặc khóa không xác định được, là một phát hiện nêu tệp:dòng."
4539
+ causes_vi:
4540
+ - "Đổi mặc định trong code hoặc yaml mà không sửa số trong tài liệu"
4541
+ - "Gõ sai khóa dẫn hoặc dẫn tới khóa đã bị đổi tên"
4542
+ nextStep_vi: "Người giám sát sửa số trong tài liệu cho khớp giá trị của khóa, hoặc sửa khóa dẫn; không chép lại một số không dẫn khóa."
4543
+ owner: supervisor
4544
+ kind: check-finding
4545
+
4546
+ RT_DOC_NO_OWNER:
4547
+ title: "A document has no single ownership header"
4548
+ title_vi: "Một tài liệu không có đúng một dòng sở hữu"
4549
+ meaning_vi: "Một tệp docs/*.md không mở đầu bằng đúng một dòng sở hữu — \"Owner: <đường dẫn knowledge/ hoặc modules/ tồn tại>\" khi tài liệu giải thích một nguồn do máy sở hữu, hoặc \"Task: <một việc>\" với một tài liệu hướng dẫn — hoặc nó trùng một tài liệu khác: cùng một dòng Task, hoặc hai tiêu đề chung từ hai từ chủ đề trở lên trong khi ít nhất 30% câu của tài liệu ngắn hơn xuất hiện trong tài liệu kia. Một câu chuyện chỉ có một tài liệu; nguồn máy là luật và tài liệu chỉ đọc nó."
4550
+ causes_vi:
4551
+ - "Thêm một tài liệu mà quên dòng Owner:/Task: đầu tiên, hoặc Owner trỏ tới một đường dẫn không tồn tại hay ngoài knowledge/ và modules/"
4552
+ - "Tách một tài liệu thành hai mà không gộp nội dung trùng hoặc không đổi dòng Task của một trong hai"
4553
+ nextStep_vi: "Người giám sát thêm đúng một dòng sở hữu vào dòng đầu của tài liệu (một nguồn knowledge/ hoặc modules/ tồn tại, hoặc một Task duy nhất), hoặc gộp hai tài liệu trùng thành một rồi xóa tệp thừa, qua một lane đã land."
4554
+ owner: supervisor
4555
+ kind: check-finding
4556
+
4557
+ RT_EDGE_CASE_REGISTRY:
4558
+ title: "The edge-case registry is inconsistent"
4559
+ title_vi: "Sổ tình huống biên không nhất quán"
4560
+ meaning_vi: "Mỗi mục covered của modules/reconciler/edge-cases.yaml phải nêu một tệp spec và một quy tắc đều tồn tại; mỗi mục có họ, lần xảy ra thật và trạng thái hợp lệ."
4561
+ causes_vi:
4562
+ - "Mục covered thiếu spec hoặc quy tắc"
4563
+ - "Spec hoặc quy tắc được đổi tên mà sổ không đổi theo"
4564
+ nextStep_vi: "Người giám sát sửa mục cho đúng tệp, hoặc chuyển mục sang open kèm lý do."
4565
+ owner: supervisor
4566
+ kind: check-finding
4567
+
4568
+ RT_EVENT_SPILL_BYPASS:
4569
+ title: "A ledger event payload is spilled by hand"
4570
+ title_vi: "Một payload sự kiện của sổ cái được tự chuyển sang blob"
4571
+ meaning_vi: "Payload sự kiện lớn theo đầu vào (danh sách tệp, manifest, bản tóm tắt, báo cáo, lời phê bình, menu) chỉ đi vào kho blob qua một đường duy nhất là appendEvent: engine/db/event-compact.mjs giữ bản inline tối đa 16 KiB và toàn bộ payload sau events.payload_sha, người đọc giải qua engine/db/event-payload.mjs. Một tệp nguồn khác truyền payloadSha vào lệnh ghi, bắt lỗi payload quá lớn để tự chuyển, hoặc chèn thẳng vào bảng events là đi vòng."
4572
+ causes_vi:
4573
+ - "Người viết gặp STARCI_EVENT_PAYLOAD_TOO_LARGE rồi tự lưu blob và truyền payloadSha thay vì để appendEvent làm"
4574
+ nextStep_vi: "Bỏ đoạn tự chuyển; gọi appendEvent với payload đầy đủ và đọc lại bằng eventPayloadOf."
4575
+ owner: supervisor
4576
+ kind: check-finding
4577
+
4578
+ RT_EXAMPLE_COUPLING:
4579
+ title: "Runtime source names a concrete example tree"
4580
+ title_vi: "Mã nguồn runtime gọi tên một cây examples/ cụ thể"
4581
+ meaning_vi: "Một tệp nguồn runtime được theo dõi (scripts/, engine/, packages/ trừ bản sao runtime đã sinh, ui/src, ui/api, hay một template quản lý dưới packages/hfs/templates/) viết literal examples/<name> — trỏ tới một thư mục ví dụ cụ thể thay vì một khai báo chung. Canon của runtime ship vào mọi sản phẩm, nên một đường dẫn ví dụ cứng chỉ đúng trên một checkout. Spec, test, lịch sử CHANGELOG và các bản ghi .starciwork nằm ngoài phạm vi; một lượt đọc runtime thật sự cần được khai báo một lần trong scripts/lib/example-refs.mjs và mọi nơi dùng gọi tên hằng đó."
4582
+ causes_vi:
4583
+ - "Một script hay gate đọc ví dụ của riêng một sản phẩm (ví dụ một tệp declaration đặt theo tên repo) lúc chạy, thay vì một mẫu chung hay tệp của repo đang được kiểm"
4584
+ - "Comment, tài liệu hay chuỗi trong nguồn trích dẫn đường dẫn examples/<name> cụ thể thay vì nói examples/<app>"
4585
+ nextStep_vi: "Người giám sát khái quát hóa chỗ đó (ví dụ examples/<app>, một mẫu template.*, hay một hằng trong scripts/lib/example-refs.mjs khi runtime thật sự đọc cây đó) hoặc trỏ tới repo đang được kiểm tra, qua một lane đã land."
4586
+ owner: supervisor
4587
+ kind: check-finding
4588
+
4589
+ RT_EXTERNAL_OWNER:
4590
+ title: "External system reached outside its owner"
4591
+ title_vi: "Gọi hệ thống bên ngoài ngoài nơi sở hữu nó"
4592
+ meaning_vi: "Trong mã nguồn runtime, một tệp import một module bên ngoài (node:child_process, node:http, node:sqlite...), gọi một hàm toàn cục (fetch) hoặc khởi chạy một chương trình (git, npm, orca, powershell, node...) mà ruleParams.runtime.infraOwners của knowledge/hfs/runtime-slots.yaml không giao cho thư mục của tệp đó. Chỉ scripts/api/<hệ thống>/ (và engine/db cho SQLite) được gọi ra bên ngoài."
4593
+ causes_vi:
4594
+ - "Gọi spawnSync/execFile hay fetch trực tiếp trong tầng nghiệp vụ hoặc trong scripts/lib"
4595
+ - "Một tệp api của hệ thống này khởi chạy chương trình của hệ thống khác"
4596
+ nextStep_vi: "Người giám sát chuyển lời gọi vào một tệp lệnh của scripts/api/<hệ thống>/ rồi gọi hàm đó, qua một lane đã land."
4597
+ owner: supervisor
4598
+ kind: check-finding
4599
+
4600
+ RT_FACT_FALSE:
4601
+ title: "A machine-evaluated fact is false, or prose contradicts it"
4602
+ title_vi: "Một sự thật máy kiểm được là sai, hoặc văn bản mâu thuẫn với nó"
4603
+ meaning_vi: "Một sự thật trong knowledge/hfs/facts.yaml không đúng với knowledge/hfs/slots.yaml (nguồn duy nhất của hình dạng sản phẩm), hoặc một dòng trong knowledge, docs hay README nói điều ngược lại, ví dụ 'không có package.json' cho một ứng dụng fe vốn có package.json riêng. Văn bản chép lại điều khẳng định bằng lời khác sẽ lệch dần khỏi slots.yaml."
4604
+ causes_vi:
4605
+ - "Sửa slots.yaml mà không sửa sự thật hoặc câu văn tương ứng"
4606
+ - "Một tài liệu chép lại điều khẳng định của slot bằng lời riêng và lời đó cũ đi"
4607
+ nextStep_vi: "Người giám sát xóa câu văn mâu thuẫn và thay bằng mã sự thật hoặc mã slot; nếu sự thật sai thì sửa slots.yaml hoặc facts.yaml, qua một lane đã land."
4608
+ owner: supervisor
4609
+ kind: check-finding
4610
+
4611
+ RT_GATE_LOOSENING:
4612
+ title: "A change loosens a gate without the owner's approval"
4613
+ title_vi: "Một thay đổi nới lỏng cổng kiểm mà chưa có chủ phê duyệt"
4614
+ meaning_vi: "Bỏ một kiểm tra khỏi danh sách của cổng, hạ ngưỡng, thêm một mục vào allowlist, xoá hoặc làm yếu một spec cùng lúc với mã sản phẩm là việc của chủ dự án; commit hoặc lần land đó chỉ qua khi cây gốc đã có mục owner-rulings gate-loosening-<dấu vân> của đúng thay đổi này."
4615
+ causes_vi:
4616
+ - "Một lane gỡ kiểm tra hoặc hạ ngưỡng để cho workflow chạy qua"
4617
+ - "Một spec bị xoá hoặc bớt khẳng định trong cùng thay đổi với mã sản phẩm"
4618
+ nextStep_vi: "Khôi phục cổng; nếu chủ thật sự duyệt, chủ thêm mục owner-rulings gate-loosening-<dấu vân> trước, rồi mới land thay đổi."
4619
+ owner: owner
4620
+ kind: check-finding
4621
+
4622
+ RT_GENERATED_BLOCK_STALE:
4623
+ title: "A generated README block or derived field differs from its source"
4624
+ title_vi: "Khối README sinh tự động khác nguồn của nó"
4625
+ meaning_vi: "Một khối giữa các dấu hfs:generated của knowledge/hfs/README.md (bản đồ ứng dụng, cây mã backend, ma trận tầng, cây mã frontend, danh mục luật) không bằng từng byte với bản dựng từ knowledge/hfs/slots.yaml và rules.yaml. Cùng cách đó, danh sách verification.automated của luật mẫu, slot của mục files: và mã code của mục why phải bằng đúng bản suy ra từ danh mục luật và slots.yaml. Sửa tay hoặc đổi nguồn mà không dựng lại làm văn bản nói khác nguồn."
4626
+ causes_vi:
4627
+ - "Sửa slots.yaml hoặc rules.yaml mà không chạy lại readme-blocks.mjs --write"
4628
+ - "Sửa tay nội dung bên trong một khối sinh tự động"
4629
+ nextStep_vi: "Người giám sát chạy starci runtime readme-blocks --write hoặc starci runtime derived-fields --write rồi commit, qua một lane đã land; không sửa tay bên trong khối."
4630
+ owner: supervisor
4631
+ kind: check-finding
4632
+
4633
+ RT_GENERATED_DRIFT:
4634
+ title: "Generated copy drifted"
4635
+ title_vi: "Bản sao sinh tự động bị lệch"
4636
+ meaning_vi: "Một bản sao sinh tự động trong packages/hfs/runtime, packages/eslint/be/runtime hoặc packages/eslint/fe/runtime khác với nội dung mà scripts/hfs/sync-runtime.mjs ghi ra từ nguồn duy nhất: thiếu, cũ, hoặc thừa tệp."
4637
+ causes_vi:
4638
+ - "Sửa một tệp nguồn được đóng gói mà quên chạy sync-runtime"
4639
+ - "Sửa tay một tệp trong thư mục runtime/ của gói"
4640
+ nextStep_vi: "Người giám sát chạy bước sinh bản sao runtime của kiểm tra (sync-runtime); các bản sao là kết quả sinh tự động đã gitignore nên không commit, và không sửa tay thư mục runtime/ của gói."
4641
+ owner: supervisor
4642
+ kind: check-finding
4643
+
4644
+ RT_HELD_UNSURFACED:
4645
+ title: "A runtime fault of the Supervisor or the owner reaches nobody"
4646
+ title_vi: "Lỗi runtime giao cho Supervisor hoặc chủ sở hữu không đến được tay ai"
4647
+ meaning_vi: "Một mã lỗi loại runtime-fault được giao cho Supervisor hoặc chủ sở hữu nhưng mục của nó không nêu cơ chế báo cho họ, hoặc cơ chế được nêu không có thật trong tệp được trỏ tới. Trạng thái bị giữ như vậy đứng im hàng giờ cho đến khi có người đọc mã nguồn."
4648
+ causes_vi:
4649
+ - "Thêm mã runtime-fault mới mà không khai báo surfacedBy"
4650
+ - "surfacedAt trỏ tới tệp không mở Decision Item, không đặt đồng hồ SLA hoặc không nêu dòng digest như đã khai"
4651
+ - "Mã trạng thái chưa có đường báo thì phải ghi surfacedBy open kèm mục đang mở trong sổ ca biên"
4652
+ nextStep_vi: "Khai báo surfacedBy, surfacedAs và surfacedAt trong mục của mã (decision-item, sla-clock, incident, digest, notifier, caller hoặc open) và làm cho tệp được trỏ tới thật sự báo cho vai trò giữ mã; nếu chưa làm được thì ghi open và thêm mục open vào modules/reconciler/edge-cases.yaml nêu đúng lý do."
4653
+ owner: supervisor
4654
+ kind: check-finding
4655
+
4656
+ RT_HOOK_SHAPE:
4657
+ title: "App hook template breaks the gate model"
4658
+ title_vi: "Template hook của app phá vỡ mô hình cổng"
4659
+ meaning_vi: "Template hook husky của app (packages/hfs/templates/app/hooks/husky) không đúng mô hình: pre-commit chỉ được chạy mức L0 (work-hygiene, eslint, stylelint, prettier trên tệp staged), không typecheck và không chạy test; pre-push chỉ kiểm cổng phát hành (bản ghi L4 trong starci-release, không gian refs/backup/, mẫu thẻ v[0-9]*), không chạy npm test, jest, typecheck hay lint."
4660
+ causes_vi:
4661
+ - "Thêm lại npm run typecheck hoặc test:affected vào pre-commit"
4662
+ - "Đưa typecheck, lint hay test vào pre-push, hoặc xóa các dấu mốc cổng phát hành khỏi pre-push"
4663
+ nextStep_vi: "Người giám sát trả template về đúng mô hình (pre-commit L0, pre-push chỉ kiểm cổng phát hành) rồi render lại hook các ví dụ bằng starci app sync --write, qua một lane đã land."
4664
+ owner: supervisor
4665
+ kind: check-finding
4666
+
4667
+ RT_HOST_PATH_IN_TEMPLATE:
4668
+ title: "A host drive path is hard-coded in source"
4669
+ title_vi: "Một đường dẫn trên host bị viết cứng trong mã nguồn"
4670
+ meaning_vi: "Một tệp nguồn runtime hay một template quản lý viết cứng một đường dẫn của máy host — một ổ đĩa Windows kiểu chữ cái kèm dấu hai chấm, một thư mục người dùng kiểu Users hay home, hay một đường AppData đã bung. Source và template ship sang mọi máy và mọi sản phẩm, nên đường dẫn phải tương đối, đi qua runtime root, os.tmpdir() hay một giá trị cấu hình."
4671
+ causes_vi:
4672
+ - "Sao chép một đường dẫn tuyệt đối từ máy phát triển vào một script, template hay fixture"
4673
+ - "Một chuỗi gỡ lỗi hay ví dụ viết thẳng đường dẫn host thay vì biến môi trường hay đường tương đối"
4674
+ nextStep_vi: "Người giám sát thay đường đó bằng đường tương đối, runtime root, os.tmpdir() hay một cấu hình đã khai báo, qua một lane đã land."
4675
+ owner: supervisor
4676
+ kind: check-finding
4677
+
4678
+ RT_IDENTIFIER_UNDECLARED:
4679
+ title: "A runtime module reads a name nothing declares"
4680
+ title_vi: "Một module runtime đọc một tên mà không nơi nào khai báo"
4681
+ meaning_vi: "Một định danh trong module ES của runtime (scripts/, engine/ trừ gói yaml của bên thứ ba, ui/api/, packages/cli/bin và src) không khớp với khai báo, import, tham số hay biến toàn cục Node nào của các phạm vi bao quanh. Một re-export (export {x} from './y.mjs') không gắn x vào phạm vi cục bộ; require, module, exports và __dirname không phải biến toàn cục của module; biến toàn cục của trình duyệt chỉ hợp lệ trong hàm được truyền cho page.evaluate. Dòng đó sẽ ném ReferenceError khi chạy tới."
4682
+ causes_vi:
4683
+ - "Tách một hàm ra khỏi vòng lặp hay closure nhưng biến cũ không được truyền sang"
4684
+ - "Xóa import cùng với hàm dùng nó trong khi còn một chỗ khác đọc tên đó"
4685
+ - "Dùng tên đã re-export từ module khác như thể nó là tên cục bộ"
4686
+ nextStep_vi: "Người giám sát khai báo hoặc import tên đó, truyền nó qua tham số, hoặc xóa chỗ đọc; không thêm ngoại lệ hay che bằng typeof."
4687
+ owner: supervisor
4688
+ kind: check-finding
4689
+
4690
+ RT_NODE_MODULES_LINK:
4691
+ title: "node_modules linked instead of installed"
4692
+ title_vi: "node_modules được liên kết thay vì cài đặt"
4693
+ meaning_vi: "Mã runtime tạo một junction hoặc symlink tới một thư mục node_modules (qua fs.symlinkSync, fs.promises.symlink, một hàm bọc chúng, hoặc một lệnh mklink /J, New-Item -ItemType Junction, ln -s được khởi chạy). Mỗi bản checkout phải tự cài phụ thuộc bằng npm ci thật từ bộ nhớ đệm; một liên kết vào node_modules của checkout khác bị lệnh xóa đệ quy hoặc npm đi xuyên qua và làm trống cây thật."
4694
+ causes_vi:
4695
+ - "Dùng chung node_modules của checkout chính cho một worktree tạm để tiết kiệm thời gian cài đặt"
4696
+ - "Giữ lại đoạn mã cũ tạo junction cho land scratch hoặc staging"
4697
+ nextStep_vi: "Người giám sát thay liên kết bằng npm ci thật qua scripts/api/npm/ci.mjs; chỉ giữ mã gỡ hoặc phát hiện liên kết đã có."
4698
+ owner: supervisor
4699
+ kind: check-finding
4700
+
4701
+ RT_OP_JUDGE_UNDECLARED:
4702
+ title: "An op does not declare who judges its product, or declares a judge the runtime does not provide"
4703
+ title_vi: "Một op chưa khai báo ai chấm sản phẩm của nó, hoặc khai báo người chấm mà runtime không có"
4704
+ meaning_vi: "Nguyên tắc P2: người làm không bao giờ tự chấm việc của mình. Hợp đồng của mỗi op (modules/ops/ops/<op>.yaml) có trường bắt buộc judge, danh sách ít nhất một người chấm: machine (phép đo độc lập mà runtime sở hữu và chạy lại lúc chốt, gọi tên trong modules/kernel/op-judges.yaml; tệp test do chính lượt làm viết thì không bao giờ tự nó là người chấm), critic (Critic độc lập, đúng cho những loại op mà bảng modules/kernel/critic.yaml đánh dấu covered), owner (chỉ nơi có cổng của chủ) hoặc next-leg (chặng sau có hợp đồng là duyệt sản phẩm này). Danh sách rỗng, thiếu trường, phép đo lạ, phép đo runtime không áp dụng cho op đó, Critic khai lệch bảng, owner không có cổng hoặc next-leg chưa đăng ký đều bị từ chối."
4705
+ causes_vi:
4706
+ - "Thêm một op mới mà chưa khai báo trường judge"
4707
+ - "Khai báo một phép đo hoặc Critic mà runtime không chạy cho op đó, hoặc bảng của runtime đổi mà hợp đồng chưa đổi theo"
4708
+ nextStep_vi: "Khai báo judge trong hợp đồng của op theo modules/kernel/op-judges.yaml, hoặc sửa bảng tương ứng của runtime cho khớp với hành vi thật."
4709
+ owner: supervisor
4710
+ kind: check-finding
4711
+
4712
+ RT_PINNED_PATH_MOVED:
4713
+ title: "Pinned path missing"
4714
+ title_vi: "Đường dẫn được ghim không còn tồn tại"
4715
+ meaning_vi: "Một đường dẫn được ghim (ruleParams.runtime.pinned: được lưu ngoài git trong hook, settings, tác vụ định kỳ, prompt) không còn tồn tại."
4716
+ causes_vi:
4717
+ - "Đổi tên một tệp mà hook hay tác vụ định kỳ đang trỏ tới"
4718
+ - "Chuyển tệp khi các worker còn đang chạy"
4719
+ nextStep_vi: "Người giám sát khôi phục đường dẫn."
4720
+ owner: supervisor
4721
+ kind: check-finding
4722
+
4723
+ RT_PORT_RESTATED:
4724
+ title: "A port literal is spelled outside its owner"
4725
+ title_vi: "Một literal cổng được viết ngoài tệp chủ sở hữu"
4726
+ meaning_vi: "Một cổng của runtime chỉ được viết literal một lần, trong tệp chủ của nó: port và devPort của UI nằm trong modules/models/runtimes.yaml (statusApp) và đọc qua ui/ports.mjs, host SonarQube nội bộ trong DEFAULT_HOST của scripts/gates/sonar-local.mjs. Một literal cổng ở vị trí cổng (authority scheme://host:NNNN, host:NNNN, gán *port*/PORT, lời gọi listen(NNNN)) trong một tệp không phải chủ sở hữu — hoặc cùng một literal không chủ trong từ hai tệp nguồn runtime — là một bản chép lại. Spec, test, bản runtime sinh ra, lịch sử hợp đồng, CHANGELOG, .starciwork và khai báo stack của sản phẩm nằm ngoài luật."
4727
+ causes_vi:
4728
+ - "Hard-code một port của runtime trong script hay tài liệu thay vì import từ tệp chủ"
4729
+ - "Hai tệp nguồn cùng viết literal một port không chủ mà không khai báo hằng số chủ sở hữu"
4730
+ nextStep_vi: "Người giám sát thay literal bằng import từ tệp chủ (ui/ports.mjs, sonar-local DEFAULT_HOST) hoặc khai báo một hằng số chủ duy nhất rồi cho mọi nơi khác tham chiếu."
4731
+ owner: supervisor
4732
+ kind: check-finding
4733
+
4734
+ RT_PRODUCT_NAME_IN_SOURCE:
4735
+ title: "A product name is written in runtime source"
4736
+ title_vi: "Tên một sản phẩm xuất hiện trong mã nguồn runtime"
4737
+ meaning_vi: "Một tệp nguồn runtime được theo dõi viết tên của một sản phẩm mà runtime từng ship kèm (danh sách đóng PRODUCT_NAMES trong scripts/lib/example-refs.mjs) hoặc một mã inc-<hash> có tiền tố là tên sản phẩm. Canon chạy trong mọi repo sản phẩm, nên một tên cứng trong source rò rỉ phán đoán của một sản phẩm sang mọi sản phẩm khác. Spec, test và lịch sử được giữ tên đó; mọi nơi khác phải nói chung chung hoặc qua hằng khai báo trong scripts/lib/example-refs.mjs."
4738
+ causes_vi:
4739
+ - "Một câu chuyện sự cố trong comment hay log viết thẳng tên sản phẩm (ví dụ workflow wf-<product>-… hay một ledger cụ thể) thay vì 'a product's …' hay <product>"
4740
+ - "Một ánh xạ, alias hay fixture đặt theo tên sản phẩm được để trong source thay vì trong tệp khai báo chung"
4741
+ nextStep_vi: "Người giám sát viết lại chỗ đó theo dạng chung (<product>, a product's …, acme cho một ví dụ) hoặc chuyển giá trị thật sự cần vào một hằng trong scripts/lib/example-refs.mjs, qua một lane đã land."
4742
+ owner: supervisor
4743
+ kind: check-finding
4744
+
4745
+ RT_PROSE_COMMAND_UNKNOWN:
4746
+ title: "Instruction text shows a starci command the CLI catalog does not have"
4747
+ title_vi: "Văn bản hướng dẫn nêu một lệnh starci mà danh mục CLI không có"
4748
+ meaning_vi: "Một lệnh `starci <nhóm> <verb>` trong đoạn mã, khối mã hay ví dụ yaml của skills/, docs/, README.md, CONTEXT.md, CONTRIBUTING.md hoặc modules/ nêu một verb không có trong modules/cli/commands, hoặc một cờ --flag mà verb đó và các cờ chung không nhận. Tác tử làm theo sẽ nhận lời từ chối thay vì kết quả. Danh mục CLI là sự thật; phát hiện nêu tệp:dòng và lệnh sai."
4749
+ causes_vi:
4750
+ - "Verb hoặc cờ bị gỡ hay đổi tên nhưng văn bản hướng dẫn vẫn nêu cách viết cũ"
4751
+ - "Gõ sai tên verb hoặc cờ khi viết ví dụ"
4752
+ nextStep_vi: "Người giám sát viết lại lệnh theo danh mục CLI (starci <nhóm> <verb> --help); nếu lệnh đã bị gỡ, thêm mục vào modules/kernel/removed-vocabulary.yaml và sửa mọi chỗ còn dạy nó."
4753
+ owner: supervisor
4754
+ kind: check-finding
4755
+
4756
+ RT_PROSE_PATH_NO_SLOT:
4757
+ title: "A path in prose that no slot owns"
4758
+ title_vi: "Đường dẫn trong văn bản không thuộc slot nào"
4759
+ meaning_vi: "Một đường dẫn sản phẩm (be/... hoặc fe/...) trong knowledge, docs hoặc README không được slot nào của knowledge/hfs/slots.yaml sở hữu và cũng không phải thư mục cha của một slot. Văn bản đã tự nghĩ ra một chỗ không có, hoặc giữ lại một chỗ mà slot không còn. Tên ứng dụng là tự do: tên ghi cứng phải là ứng dụng của ví dụ hoặc của starter, còn <tên> giữ chỗ thay cho mọi ứng dụng."
4760
+ causes_vi:
4761
+ - "Đổi hoặc xóa một slot mà văn bản vẫn nêu đường dẫn cũ"
4762
+ - "Ghi tên ứng dụng theo vai trò (api, web) thay vì giữ chỗ"
4763
+ nextStep_vi: "Người giám sát thay đường dẫn bằng mã slot, bằng <giữ chỗ> hoặc bằng tên ứng dụng của ví dụ, qua một lane đã land."
4764
+ owner: supervisor
4765
+ kind: check-finding
4766
+
4767
+ RT_PROSE_RESTATES_SLOTS:
4768
+ title: "Prose restates the slots"
4769
+ title_vi: "Văn bản chép lại các slot"
4770
+ meaning_vi: "Một khối mã hoặc một dòng trong knowledge, docs hoặc README nêu từ ba đường dẫn sản phẩm mà slot sở hữu trở lên, hoặc một câu liệt kê từ ba tên tệp gốc hay tệp của phía mà các slot khai báo. Bản chép lại cũ đi so với slots.yaml; chỉ khối sinh tự động giữa các dấu hfs:generated mới được phép sao chép."
4771
+ causes_vi:
4772
+ - "Chép cây thư mục hoặc danh sách tệp của slots.yaml vào văn bản"
4773
+ - "Liệt kê các tệp cấu hình của gốc ứng dụng trong một câu"
4774
+ nextStep_vi: "Người giám sát thay bản chép bằng mã slot (slot app.format-config...) hoặc bằng một khối sinh tự động, qua một lane đã land."
4775
+ owner: supervisor
4776
+ kind: check-finding
4777
+
4778
+ RT_REMOVED_VOCABULARY:
4779
+ title: "Instruction text spells a name the runtime refuses"
4780
+ title_vi: "Văn bản hướng dẫn viết một tên mà runtime từ chối"
4781
+ meaning_vi: "modules/kernel/removed-vocabulary.yaml là danh sách duy nhất các tên đã bị gỡ (khóa config, trường bias định tuyến, verb, cờ, mã, từ vựng), mỗi tên kèm thứ thay thế và bản phát hành đã gỡ nó. Một dòng trong skills/, docs/, README.md, CONTEXT.md, CONTRIBUTING.md, modules/ hoặc knowledge/ viết một tên trong danh sách mà không nằm trong CHANGELOG.md, không nằm trong chính danh sách và không mang dấu [removed-list] là dạy tác tử một cách viết mà runtime sẽ từ chối. Phát hiện nêu tệp:dòng, tên bị gỡ và cách viết thay thế."
4782
+ causes_vi:
4783
+ - "Một skill hay tài liệu còn dạy cách viết cũ sau khi runtime đổi"
4784
+ - "Gỡ hoặc đổi tên một khóa, cờ, verb, trường hay mã mà không thêm mục vào danh sách tên đã gỡ"
4785
+ nextStep_vi: "Người giám sát viết lại dòng theo cách thay thế mà phát hiện nêu; nếu dòng là danh sách các tên đã gỡ, đánh dấu [removed-list]; nếu tên mới bị gỡ, thêm mục vào modules/kernel/removed-vocabulary.yaml trong cùng commit."
4786
+ owner: supervisor
4787
+ kind: check-finding
4788
+
4789
+ RT_REVISION_SCOPE:
4790
+ title: "The revision-scope table does not match the runtime tree"
4791
+ title_vi: "Bảng phạm vi bản sửa không khớp với cây runtime"
4792
+ meaning_vi: "Một đường dẫn có trong cây không thuộc dòng nào của bảng phạm vi, bảng gọi tên vai trò hay hành động lạ, một tệp động cơ nạp bị liệt kê tay, tệp dựng lời nhắc khởi động của một ghế không khớp với bộ sinh, hoặc phép so sánh chỉ-sửa-lời-văn cho qua một thay đổi bỏ mục."
4793
+ causes_vi:
4794
+ - "Thêm một tệp hay thư mục mới mà chưa có dòng trong bảng"
4795
+ - "Bộ sinh lời nhắc đọc thêm một tệp mà bảng seatBoot chưa ghi"
4796
+ nextStep_vi: "Thêm dòng cho đường dẫn vào modules/kernel/revision-scope.yaml hoặc sửa seatBoot cho khớp bộ sinh."
4797
+ owner: supervisor
4798
+ kind: check-finding
4799
+
4800
+ RT_ROLES_CONTRACT_DRIFT:
4801
+ title: "A role surface differs from the roles contract"
4802
+ title_vi: "Một bề mặt mô tả vai trò khác với hợp đồng vai trò"
4803
+ meaning_vi: "Hợp đồng vai trò (modules/kernel/roles.yaml) là nơi duy nhất mô tả một vai trò. Một bề mặt Markdown mang khối sinh từ hợp đồng, một bề mặt yaml hoặc script dẫn id của vai trò, và không bề mặt nào dạy điều mà danh sách contradicts của vai trò cấm."
4804
+ causes_vi:
4805
+ - "Sửa hợp đồng mà không sinh lại các khối"
4806
+ - "Viết lại một đoạn mô tả vai trò trong prompt hoặc tài liệu thay vì dẫn hợp đồng"
4807
+ nextStep_vi: "Người giám sát chạy starci runtime check --only roles-contract -- --write, hoặc sửa câu mâu thuẫn theo hợp đồng."
4808
+ owner: supervisor
4809
+ kind: check-finding
4810
+
4811
+ RT_ROOT_ADHOC:
4812
+ title: "A root is resolved outside the root module"
4813
+ title_vi: "Một thư mục gốc được tự phân giải ngoài mô-đun gốc"
4814
+ meaning_vi: "Thư mục mà một thành phần đọc hoặc ghi chỉ được quyết định ở một mô-đun là scripts/lib/roots.mjs: cây runtime, thư mục trạng thái, thư mục tạm, thư mục nơi lệnh được gọi, tên thư mục Work và thứ tự thử hai cây cùng có thể giữ một bản ghi (cây làm việc trước, bản checkout chính sau). Tệp nguồn runtime tự viết process.cwd() hoặc literal tên thư mục Work là tự phân giải thư mục, và đã bốn lần trong hai ngày một thành phần đọc hay ghi nhầm cây vì vậy."
4815
+ causes_vi:
4816
+ - "Tệp mới nối đường dẫn từ process.cwd() hoặc từ literal .starciwork thay vì gọi mô-đun gốc"
4817
+ - "Tệp đã có sẵn ở bản phát hành trước nhưng nay viết nhiều hơn số lần khi đó"
4818
+ nextStep_vi: "Dùng invocationDir, workDirHolding, workRecordPath hoặc toolSearchDirs của scripts/lib/roots.mjs."
4819
+ owner: supervisor
4820
+ kind: check-finding
4821
+
4822
+ RT_RULE_ID_UNKNOWN:
4823
+ title: "A rule id that is not in the catalog"
4824
+ title_vi: "Mã luật không có trong danh mục"
4825
+ meaning_vi: "Một tệp được git theo dõi (tri thức, tài liệu, mã hoặc dữ liệu) nêu một mã luật R<số> mà knowledge/hfs/rules.yaml không có, hoặc một mã luật đã nghỉ hưu mà chỉ tệp lịch sử (CHANGELOG) mới được nêu. Mã luật ma làm người đọc và máy trỏ tới một luật không tồn tại."
4826
+ causes_vi:
4827
+ - "Một luật bị xóa hoặc đánh số lại mà các chỗ trích dẫn không được sửa theo"
4828
+ - "Gõ nhầm mã luật"
4829
+ nextStep_vi: "Người giám sát đổi trích dẫn sang luật hiện có của danh mục hoặc bỏ mã, qua một lane đã land."
4830
+ owner: supervisor
4831
+ kind: check-finding
4832
+
4833
+ RT_RULE_UNCITED:
4834
+ title: "A catalog rule no pattern topic cites"
4835
+ title_vi: "Một luật của danh mục mà không chủ đề pattern nào trích dẫn"
4836
+ meaning_vi: "Một luật của knowledge/hfs/rules.yaml không được nêu trong hfsRules của bất kỳ chủ đề nào dưới knowledge/patterns/** và cũng không mang scope: runtime. Luật của sản phẩm tồn tại để được dạy; một luật không được trích dẫn là luật mà một lane không bao giờ đọc."
4837
+ causes_vi:
4838
+ - "Thêm một luật vào danh mục mà không trích dẫn nó từ chủ đề dạy nó"
4839
+ - "Một luật chỉ quản trị chính kho runtime mà thiếu scope: runtime"
4840
+ nextStep_vi: "Người giám sát thêm mã luật vào hfsRules của chủ đề knowledge/patterns dạy nó, hoặc đánh dấu luật scope: runtime khi nó chỉ quản trị kho runtime, qua một lane đã land."
4841
+ owner: supervisor
4842
+ kind: check-finding
4843
+
4844
+ RT_RULING_STALE_CITE:
4845
+ title: "A live statement cites a superseded or unknown owner ruling"
4846
+ title_vi: "Nội dung hiện hành dẫn quyết định đã bị thay thế hoặc chưa được ghi nhận"
4847
+ meaning_vi: "Một trích dẫn dẫn quyết định cũ mà không dẫn quyết định thay thế trong cùng đoạn, khớp mẫu staleWhen, hoặc nêu ngày quyết định không có trong danh mục. Nội dung lịch sử của case và CHANGELOG được giữ nguyên."
4848
+ causes_vi:
4849
+ - "Tài liệu hoặc chú thích chưa cập nhật sau khi chủ sở hữu sửa quyết định"
4850
+ - "Trích dẫn thiếu id ổn định hoặc nêu ngày không có case tương ứng"
4851
+ nextStep_vi: "Cập nhật câu theo quyết định hiện hành và dẫn id của quyết định thay thế trong cùng đoạn; kiểm tra ngày và liên kết supersedes trong danh mục."
4852
+ owner: supervisor
4853
+ kind: check-finding
4854
+
4855
+ RT_SECRET_TRACKED:
4856
+ title: "A secret is tracked in the public runtime repository"
4857
+ title_vi: "Một bí mật được theo dõi trong repository runtime công khai"
4858
+ meaning_vi: "Một tệp *.enc hoặc secret.env/.env được git theo dõi trong repository runtime, vốn là public và không giữ vật liệu bí mật nào, kể cả bản mã hóa. Mọi bí mật của máy chủ nằm trong .claude/secret.env chưa theo dõi, chỉ đọc qua engine/secrets.mjs; các tên được liệt kê trong secret.env.example."
4859
+ causes_vi:
4860
+ - "Một bí mật mới được niêm phong và commit thay vì thêm tên vào secret.env"
4861
+ - "Tệp bí mật bị gitignore sót nên bị git add"
4862
+ nextStep_vi: "git rm tệp, thêm tên biến vào secret.env.example và danh mục env, owner đặt giá trị vào secret.env; bản mã hóa đã nằm trong lịch sử git chỉ mất khi xoay (rotate) bí mật."
4863
+ owner: owner
4864
+ kind: check-finding
4865
+
4866
+ RT_SLOT_ID_SHAPE:
4867
+ title: "A slot id breaks the manifest's declared grammar"
4868
+ title_vi: "Một slot id vi phạm ngữ pháp mà manifest khai báo"
4869
+ meaning_vi: "Một slot id đi theo ngữ pháp mà chính manifest khai báo: các đoạn lowercase kebab ngăn bằng dấu chấm, đoạn đầu thuộc naming.prefixes của knowledge/hfs/slots.yaml (profiles của runtime-slots.yaml), <side>.app.<kind> chỉ một kind trong appKinds.<side>, và mọi id sâu hơn mở rộng một id đã khai báo — qua trường parent tường minh, hoặc một family mà ít nhất hai slot cùng dùng; một id sâu lẻ loi tự bịa ra family. Từ vựng suffix không chứa cặp đồng nghĩa: suffixes của một profile không trùng bannedSuffixes, không cặp nào trong naming.refusedPairs sống ở cả hai vế, và naming.sameConceptPairs (step + saga-step) là ngoại lệ duy nhất — sống ở cả hai vế hoặc bị bỏ."
4870
+ causes_vi:
4871
+ - "Đặt một slot id mới không bắt đầu bằng prefix khai báo hay không kebab-case"
4872
+ - "Tạo một id sâu không mở rộng slot nào đã khai báo và family chỉ có một thành viên"
4873
+ - "Thêm một suffix đồng nghĩa với một suffix đã có mà không khai báo cặp đó trong naming"
4874
+ nextStep_vi: "Người giám sát sửa id theo ngữ pháp (đổi tên, đặt parent, hay khai báo prefix/family trong naming của manifest) hoặc đưa từ trùng nghĩa về bannedSuffixes / khai báo cặp trong naming.sameConceptPairs/refusedPairs."
4875
+ owner: supervisor
4876
+ kind: check-finding
4877
+
4878
+ RT_SOURCE_NAME:
4879
+ title: "Runtime source name breaks the convention"
4880
+ title_vi: "Tên tệp nguồn runtime sai quy ước"
4881
+ meaning_vi: "Một tệp nguồn của runtime không đặt tên kebab-case, mang tên dùng một lần (_*, tmp-*, fix-*, *-backfill, migrate-*, *.bak*), hoặc trùng tên với một tệp khác trong cùng tầng (trừ lib.mjs và index.mjs)."
4882
+ causes_vi:
4883
+ - "Để lại một script dùng một lần trong cây nguồn"
4884
+ - "Hai module ở hai thư mục cùng tầng mang cùng một tên"
4885
+ nextStep_vi: "Người giám sát đổi tên tệp (bằng script di chuyển để import được viết lại) hoặc xóa script dùng một lần."
4886
+ owner: supervisor
4887
+ kind: check-finding
4888
+
4889
+ RT_SPEC_BUDGET_STALE:
4890
+ title: "A recorded spec duration names a missing file"
4891
+ title_vi: "Một thời lượng spec đã ghi trỏ tới tệp không còn tồn tại"
4892
+ meaning_vi: "Một hàng trong modules/kernel/spec-durations.yaml đặt tên tệp spec không còn tồn tại, nên bản ghi thời lượng của lần land đã cũ và không thể dùng làm ngân sách hiện hành."
4893
+ causes_vi:
4894
+ - "Tệp spec được đổi tên hoặc xóa sau lần làm mới bản ghi gần nhất"
4895
+ - "Bản ghi thời lượng được chép từ một revision khác"
4896
+ nextStep_vi: "Người giám sát làm mới bản ghi từ kết quả land của revision hiện tại để mỗi hàng trỏ tới một tệp spec đang tồn tại."
4897
+ owner: supervisor
4898
+ kind: check-finding
4899
+
4900
+ RT_SPEC_OVER_BUDGET:
4901
+ title: "A spec exceeds the land duration budget"
4902
+ title_vi: "Một tệp spec vượt quá ngân sách thời gian của lần land"
4903
+ meaning_vi: "Thời lượng đã ghi của một tệp spec còn tồn tại vượt quá giới hạn chung trong modules/kernel/spec-durations.yaml. Tệp chậm làm kéo dài lần land và phải giảm chi phí thiết lập mà vẫn giữ nguyên từng test, assertion và đường vào production."
4904
+ causes_vi:
4905
+ - "Mỗi test lặp lại việc cài đặt, khởi động tiến trình hoặc dựng fixture nặng"
4906
+ - "Test chờ đồng hồ thật thay vì dùng đồng hồ được tiêm vào"
4907
+ nextStep_vi: "Người giám sát làm theo speed.md: dùng fixture dùng chung trong một tiến trình spec, bỏ cài đặt hoặc khởi động theo từng test, tiêm đồng hồ và không tăng timeout; sau lần land cuối thì làm mới bản ghi thời lượng."
4908
+ owner: supervisor
4909
+ kind: check-finding
4910
+
4911
+ RT_SPEC_PLACEMENT:
4912
+ title: "Runtime spec out of place"
4913
+ title_vi: "Spec của runtime đặt sai chỗ"
4914
+ meaning_vi: "Một spec của runtime không nằm ở tests/<khu vực>/<module>[.<chủ đề>].spec.mjs, mã dùng chung cho spec không nằm ở tests/helpers/, hoặc một tệp trong packages dùng hậu tố .test. thay vì .spec."
4915
+ causes_vi:
4916
+ - "Spec để phẳng ngay trong tests/"
4917
+ - "Tệp dùng chung có tiền tố _ ở gốc tests/"
4918
+ nextStep_vi: "Người giám sát chuyển tệp vào đúng thư mục bằng script di chuyển (rh-move.mjs) để import được viết lại, qua lane của chunk C7."
4919
+ owner: supervisor
4920
+ kind: check-finding
4921
+
4922
+ RT_STORAGE_UNDECLARED:
4923
+ title: "A text column of a store has no storage class"
4924
+ title_vi: "Một cột văn bản của kho dữ liệu chưa có lớp lưu trữ"
4925
+ meaning_vi: "Nội dung nằm thành tệp trong kho blob dưới thư mục trạng thái runtime còn hàng trong cơ sở dữ liệu chỉ giữ tham chiếu (sha) và các giá trị nhỏ dùng để truy vấn. Mọi cột văn bản của sổ cái sản phẩm và machine.sqlite phải được khai báo trong modules/schemas/storage-convention.yaml với một lớp: scalar, bounded, reference, spill hoặc migrate. Một cột chưa khai báo, một khai báo trỏ vào cột không còn, một lớp lạ, hoặc lớp migrate không khớp danh sách migrations là đi vòng quy ước."
4926
+ causes_vi:
4927
+ - "Thêm bảng hoặc cột văn bản mới mà không khai báo lớp"
4928
+ - "Cột bị đổi tên hoặc xóa nhưng khai báo vẫn còn"
4929
+ nextStep_vi: "Khai báo cột trong modules/schemas/storage-convention.yaml: nội dung dùng reference (lưu blob, giữ sha), giá trị nhỏ dùng scalar hoặc bounded."
4930
+ owner: supervisor
4931
+ kind: check-finding
4932
+
4933
+ RT_SYNTAX_INVALID:
4934
+ title: "Tracked JavaScript has invalid syntax"
4935
+ title_vi: "Tệp JavaScript được theo dõi có cú pháp không hợp lệ"
4936
+ meaning_vi: "Một tệp .mjs, .cjs hoặc .js được git theo dõi không thể được trình phân tích cú pháp JavaScript của TypeScript đọc. Luật áp dụng cả tests/ và nguồn packages/, trừ các gốc được sinh và cây đầu ra build của bên thứ ba. Một tệp không phân tích được không thể chạy tin cậy dù kiểm tra Node trong các thư mục runtime vẫn xanh."
4937
+ causes_vi:
4938
+ - "Biểu thức chính quy chứa xuống dòng thô, hoặc chuỗi hay template chưa đóng"
4939
+ - "Thiếu dấu ngoặc nhọn, ngoặc tròn hoặc ngoặc vuông đóng"
4940
+ nextStep_vi: "Người giám sát sửa cú pháp tại dòng được báo và chạy lại cổng runtime; không bỏ qua tệp hoặc thêm ngoại lệ."
4941
+ owner: supervisor
4942
+ kind: check-finding
4943
+
4944
+ RT_TIER_DIRECTION:
4945
+ title: "Runtime import against the tier direction"
4946
+ title_vi: "Import ngược chiều tầng của runtime"
4947
+ meaning_vi: "Một import tương đối giữa hai chủ sở hữu trong runtime đi từ một tầng tới một tầng không nằm trong mayImport của tầng đó (tiers.runtime trong knowledge/hfs/runtime-slots.yaml), ví dụ kernel import supervisor hay scripts/lib import một miền nghiệp vụ."
4948
+ causes_vi:
4949
+ - "Một dịch vụ dùng chung nằm ở tầng cao hơn nơi dùng nó"
4950
+ - "Thư viện cơ sở import mã nghiệp vụ"
4951
+ nextStep_vi: "Người giám sát chuyển phần dùng chung xuống tầng được phép (machine, lib) hoặc đảo chiều phụ thuộc, qua một lane đã land."
4952
+ owner: supervisor
4953
+ kind: check-finding
4954
+
4955
+ RT_VERSION_RESTATED:
4956
+ title: "A pinned version is spelled outside canon-pins.yaml"
4957
+ title_vi: "Một phiên bản ghim được viết ngoài canon-pins.yaml"
4958
+ meaning_vi: "knowledge/hfs/canon-pins.yaml viết phiên bản của mọi gói @starci và mọi phụ thuộc được ghim đúng một lần; package.json là nơi cài đặt khai báo và lockfile là kết quả resolve. Một literal semver bằng pin — hay specifier name@x.y.z nhắc tới nó — ở bất cứ đâu khác là bản chép lại, trừ các khóa binding mà refresher sở hữu và ghi đè tại chỗ (canon.version của code-pattern bindings do check-canon-pins giữ; provenance.version và identity.version của grammar snapshot do grammar-knowledge.mjs ghi lại). Manifest, lockfile, changelog, lịch sử hợp đồng, spec, test, runtime sinh ra và .starciwork nằm ngoài luật."
4959
+ causes_vi:
4960
+ - "Viết số phiên bản của một pin trong prose hay YAML thay vì nhắc tới tên pin"
4961
+ - "Chép specifier name@x.y.z vào tài liệu hay mẫu scaffold"
4962
+ nextStep_vi: "Người giám sát viết lại theo tên pin (\"the pinned @starci/grammar\", \"knowledge/hfs/canon-pins.yaml\") hoặc để refresher ghi qua khóa binding đã khai báo — không bao giờ viết literal."
4963
+ owner: supervisor
4964
+ kind: check-finding
4965
+
4966
+ SCAN_SMELL:
4967
+ title: "Code carries a smell SonarCloud measures"
4968
+ title_vi: "Mã mang một mùi mã mà SonarCloud đo"
4969
+ meaning_vi: "Tệp `<file>` có một đoạn mã mà SonarCloud sẽ báo thành code smell: `await` trong vòng lặp, `charCodeAt`, chuỗi chỉ thoát dấu gạch chéo ngược, biểu thức điều kiện lồng nhau, `void` trên giá trị không phải lời gọi, import không dùng, import chỉ để chuyển tiếp, prop khai báo mà component không đọc, API đã deprecated, đối tượng bị ép thành chuỗi hoặc hàm `async` không `await`; luật bắt ở máy trước khi bản quét đến."
4970
+ causes_vi:
4971
+ - "Vi phạm luật R235: một vòng lặp `await` từng bước mà không gọi tên thứ tự"
4972
+ - "Import còn sót sau khi mã dùng nó đã bị xóa"
4973
+ - "Dùng API next-intl `requestLocale` hay `setRequestLocale` đã bị thay bằng `next/root-params`"
4974
+ nextStep_vi: "Sửa đúng dòng được chỉ: chạy các bước độc lập bằng `Promise.all`, gọi tên thứ tự bằng `eachInOrder` của `platform/primitives`, bỏ import thừa, viết lại biểu thức lồng thành câu lệnh riêng, đọc locale qua `next/root-params`, trả `Promise.resolve(...)` thay vì `async` không `await`. 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."
4975
+ owner: op-retry
4976
+ kind: check-finding
4977
+
4978
+ SEALED_CUSTODY_LOCATION:
4979
+ title: "Sealed secret path is not under the app root .starcistacks"
4980
+ title_vi: "Đường dẫn bí mật đã mã hoá không nằm dưới .starcistacks"
4981
+ meaning_vi: "Trường custody.sealed của bản ghi danh tính hoặc tài nguyên không trỏ tới .starcistacks/<môi-trường>/secrets/<slug>.enc ở gốc app, nơi duy nhất được phép giữ bí mật đã mã hoá; dạng nằm dưới một phía (be/.starcistacks/...) cũng bị từ chối."
4982
+ causes_vi:
4983
+ - "Đường dẫn trỏ vào .starciwork, ví dụ .starciwork/_resources/identities/<id>/secrets.enc.yaml"
4984
+ - "Đường dẫn dùng thư mục con hoặc đuôi khác thay vì <slug>.enc"
4985
+ - "Ghi 'none' hoặc để trống thay vì nêu đường dẫn sẽ được mã hoá"
4986
+ nextStep_vi: "Đây là lỗi bị từ chối; op work.author hoặc op uat.verify sửa custody.sealed thành .starcistacks/<môi-trường>/secrets/<slug>.enc và chuyển tệp mã hoá (nếu có) tới đó bằng git mv, không đọc nội dung."
4987
+ owner: other-op:work.author
4988
+ kind: check-finding
4989
+
4990
+ SEALED_FILE_IN_WORK:
4991
+ title: "Sealed secret file kept inside .starciwork"
4992
+ title_vi: "Tệp bí mật đã mã hoá nằm trong .starciwork"
4993
+ meaning_vi: "Cây Work chứa một tệp mã hoá (.enc); cây Work chỉ được giữ bản ghi danh tính hoặc tài nguyên, còn tệp bí mật phải nằm ở .starcistacks/<môi-trường>/secrets/<slug>.enc."
4994
+ causes_vi:
4995
+ - "Tệp secrets.enc.yaml được đặt cạnh resource.yaml của danh tính"
4996
+ - "Tệp mã hoá được sao vào cây Work để tiện đọc"
4997
+ nextStep_vi: "Đây là lỗi bị từ chối; op work.author hoặc op uat.verify chuyển tệp bằng git mv tới .starcistacks/<môi-trường>/secrets/<slug>.enc và trỏ custody.sealed vào đó, không đọc hay in nội dung."
4998
+ owner: other-op:work.author
4999
+ kind: check-finding
5000
+
5001
+ # --- HFS rule catalog (knowledge/hfs/rules.yaml): one code per rule, from R01 up ---
5002
+
5003
+ STACKS_DEPLOY_CONFIG_INVALID:
5004
+ title: "A deploy block of application-stacks.yaml is not coherent"
5005
+ title_vi: "Khối deploy trong application-stacks.yaml không nhất quán"
5006
+ meaning_vi: "environments.<env>.deploy có trường lạ, tên hoặc mẫu sai, tệp cấu hình không tồn tại, dịch vụ không có trong stack, thành viên sealed không có tệp .enc, mục verify sai dạng, tên config trùng hoặc ảnh backup chưa ghim digest thật."
5007
+ causes_vi:
5008
+ - "Vi phạm R294: khối deploy được kiểm bằng đúng normalizeDeploy mà các động từ starci app stack dùng, nên động từ sẽ từ chối cùng lỗi đó."
5009
+ - "Tệp, dịch vụ hoặc thành viên sealed được đổi tên hay xóa mà khối deploy chưa cập nhật."
5010
+ nextStep_vi: "Sửa đúng trường nêu trong thông báo (environments.<env>.<đường dẫn>), rồi chạy lại starci app check; mỗi vấn đề là một phát hiện riêng."
5011
+ owner: op-retry
5012
+ kind: check-finding
5013
+
5014
+ STACKS_DNS_UNDECLARED:
5015
+ title: "An ingress host has no DNS ownership receipt"
5016
+ title_vi: "Host của ingress thiếu khai báo chủ sở hữu và bằng chứng DNS"
5017
+ meaning_vi: "Một host được ingress sử dụng không có bản ghi dns với host, owner và receipt đầy đủ trong khai báo môi trường."
5018
+ causes_vi:
5019
+ - "Vi phạm R266: điều kiện DNS chỉ tồn tại ngoài kho mã hoặc chưa có người chịu trách nhiệm rõ ràng."
5020
+ nextStep_vi: "Thêm bản ghi dns cho từng host, nêu chủ sở hữu và tham chiếu bằng chứng DNS; việc thay đổi DNS vẫn do chủ sở hữu thực hiện."
5021
+ owner: op-retry
5022
+ kind: check-finding
5023
+
5024
+ STACKS_HEALTHCHECK_MISSING:
5025
+ title: "A runnable Swarm service has no healthcheck"
5026
+ title_vi: "Dịch vụ Swarm đang chạy thiếu healthcheck"
5027
+ meaning_vi: "Mô hình Swarm thiếu healthcheck hoạt động cho dịch vụ chạy liên tục hoặc đã tắt healthcheck của dịch vụ đó."
5028
+ causes_vi:
5029
+ - "Vi phạm R266: trạng thái container chạy chưa chứng minh được tín hiệu sức khỏe cần để cập nhật và phục hồi dịch vụ."
5030
+ nextStep_vi: "Khai báo healthcheck có lệnh thực sự kiểm tra dịch vụ; tách tín hiệu sống và sẵn sàng theo trách nhiệm của ứng dụng."
5031
+ owner: op-retry
5032
+ kind: check-finding
5033
+
5034
+ STACKS_HOST_UNROUTED:
5035
+ title: "A declared ingress host has no rendered route"
5036
+ title_vi: "Host được khai báo chưa có tuyến trong mô hình ingress"
5037
+ meaning_vi: "Danh sách ingress.hosts chứa host mà cấu hình Traefik hoặc Caddy đã render không định tuyến."
5038
+ causes_vi:
5039
+ - "Vi phạm R266: danh sách host và tuyến thực tế không khớp, nên khai báo chưa chứng minh được cửa vào của ứng dụng."
5040
+ nextStep_vi: "Thêm tuyến cho host trong cấu hình ingress được theo dõi hoặc bỏ host không còn dùng, rồi render lại mô hình."
5041
+ owner: op-retry
5042
+ kind: check-finding
5043
+
5044
+ STACKS_INGRESS_UNDECLARED:
5045
+ title: "A Swarm environment has no declared ingress"
5046
+ title_vi: "Môi trường Swarm chưa khai báo ingress"
5047
+ meaning_vi: "Môi trường chưa có ingress.component và danh sách ingress.hosts hợp lệ để xác định cửa vào của ứng dụng."
5048
+ causes_vi:
5049
+ - "Vi phạm R275: mô hình có thể chạy proxy nhưng khai báo không nêu ingress mà môi trường thực sự chọn."
5050
+ nextStep_vi: "Khai báo một ingress component và danh sách host của nó; dùng Traefik hoặc Caddy được ghim bằng digest."
5051
+ owner: op-retry
5052
+ kind: check-finding
5053
+
5054
+ STACKS_INGRESS_UNKNOWN:
5055
+ title: "The declared ingress is unknown or unsupported"
5056
+ title_vi: "Ingress được khai báo không tồn tại hoặc chưa được hỗ trợ"
5057
+ meaning_vi: "Component ingress không được chọn, sai role, thiếu trong mô hình hoặc không dùng image Traefik hay Caddy được ghim bằng digest."
5058
+ causes_vi:
5059
+ - "Vi phạm R275: tên component hoặc image của cửa vào không khớp hợp đồng ingress được hỗ trợ."
5060
+ nextStep_vi: "Chọn component có role ingress hiện diện trong mô hình và ghim image Traefik hoặc Caddy bằng digest đầy đủ."
5061
+ owner: op-retry
5062
+ kind: check-finding
5063
+
5064
+ STACKS_LITE_SELFHOSTED_DB:
5065
+ title: "A lite deployment runs a self-hosted backing database"
5066
+ title_vi: "Triển khai lite đang chạy cơ sở dữ liệu tự quản"
5067
+ meaning_vi: "Tập component hoặc mô hình triển khai của lite chứa image PostgreSQL, Redis hoặc MongoDB thay vì backing Supabase được quản lý."
5068
+ causes_vi:
5069
+ - "Vi phạm R276: topology lite thêm máy chủ dữ liệu mà chuẩn chỉ dành cho backing được quản lý."
5070
+ nextStep_vi: "Gỡ image backing tự quản khỏi triển khai lite; dùng Supabase cho ứng dụng và SQLite trên volume được khai báo cho sidecar phù hợp."
5071
+ owner: op-retry
5072
+ kind: check-finding
5073
+
5074
+ STACKS_MODEL_UNAVAILABLE:
5075
+ title: "The tracked rendered deployment model cannot be checked"
5076
+ title_vi: "Không kiểm tra được mô hình triển khai đã render được theo dõi"
5077
+ meaning_vi: "Mô hình triển khai được chọn bị thiếu, không được theo dõi, không đọc được hoặc không có cấu trúc dịch vụ hợp lệ."
5078
+ causes_vi:
5079
+ - "Vi phạm R267: checker không có mô hình thực tế để xét topology và không được bỏ qua kiểm tra vì thiếu đầu vào."
5080
+ nextStep_vi: "Theo dõi mô hình đã render dưới cây Swarm của môi trường, khai báo renderedModel hoặc composeFiles đúng và chạy lại kiểm tra."
5081
+ owner: op-retry
5082
+ kind: check-finding
5083
+
5084
+ STACKS_PLACEMENT_MISSING:
5085
+ title: "A stateful Swarm service has no placement constraint"
5086
+ title_vi: "Dịch vụ Swarm có trạng thái thiếu ràng buộc vị trí chạy"
5087
+ meaning_vi: "Dịch vụ có trạng thái hoặc volume bền vững không khai báo deploy.placement.constraints trong mô hình đã render."
5088
+ causes_vi:
5089
+ - "Vi phạm R266: task có thể chuyển sang node không giữ volume và mất quyền truy cập dữ liệu của component."
5090
+ nextStep_vi: "Thêm ràng buộc placement phù hợp chủ sở hữu volume và nêu rõ giới hạn phục hồi khi node hoặc đĩa bị mất."
5091
+ owner: op-retry
5092
+ kind: check-finding
5093
+
5094
+ STACKS_PORT_PUBLISHED:
5095
+ title: "A service outside the declared ingress publishes a port"
5096
+ title_vi: "Dịch vụ ngoài ingress được khai báo mở cổng công khai"
5097
+ meaning_vi: "Mô hình Swarm công bố cổng của dịch vụ không phải ingress duy nhất được môi trường chọn."
5098
+ causes_vi:
5099
+ - "Vi phạm R266: API, worker hoặc dịch vụ dữ liệu có cửa truy cập trực tiếp vượt qua ingress đã khai báo."
5100
+ nextStep_vi: "Gỡ ports khỏi dịch vụ nội bộ và định tuyến lưu lượng công khai qua ingress được môi trường khai báo."
5101
+ owner: op-retry
5102
+ kind: check-finding
5103
+
5104
+ STACKS_REALTIME_SCALED:
5105
+ title: "A lite realtime API does not pin one replica"
5106
+ title_vi: "API Realtime của lite chưa ghim đúng một replica"
5107
+ meaning_vi: "App API của lite có composition chứa feature be.feature.realtime.* nhưng dịch vụ Swarm chạy nhiều replica hoặc không ghim deploy.replicas: 1."
5108
+ causes_vi:
5109
+ - "Vi phạm R266: Realtime của lite chưa có topology chia sẻ trạng thái để phục vụ nhiều replica an toàn."
5110
+ nextStep_vi: "Ghim deploy.replicas: 1 cho API Realtime của lite trong mô hình Swarm; giới hạn này không áp dụng cho bản full."
5111
+ owner: op-retry
5112
+ kind: check-finding
5113
+
5114
+ STACKS_RELEASE_UNDECLARED:
5115
+ title: "A Swarm environment has no complete release declaration"
5116
+ title_vi: "Môi trường Swarm thiếu khai báo release đầy đủ"
5117
+ meaning_vi: "Môi trường chưa khai báo imagesLock, pointer, previous hoặc danh sách healthAssertions không rỗng."
5118
+ causes_vi:
5119
+ - "Vi phạm R267: kho mã chưa gắn image bất biến, release đang chạy, release trước và điều kiện xác nhận triển khai."
5120
+ nextStep_vi: "Bổ sung release với khóa image, con trỏ hiện tại và trước đó cùng healthAssertions; chỉ đổi con trỏ hiện tại sau khi xác minh thành công."
5121
+ owner: op-retry
5122
+ kind: check-finding
5123
+
5124
+ STACKS_SECOND_INGRESS:
5125
+ title: "A Swarm environment selects more than one ingress"
5126
+ title_vi: "Môi trường Swarm chọn nhiều hơn một ingress"
5127
+ meaning_vi: "Tập component được chọn hoặc mô hình đã render chứa ingress thứ hai bên cạnh ingress của môi trường."
5128
+ causes_vi:
5129
+ - "Vi phạm R266: hai ingress tạo hai chủ sở hữu định tuyến, TLS hoặc cổng công khai trong cùng môi trường."
5130
+ nextStep_vi: "Giữ đúng một ingress trong môi trường và mô hình; chuyển mọi host cùng quyền công bố cổng sang component đó."
5131
+ owner: op-retry
5132
+ kind: check-finding
5133
+
5134
+ STACKS_SECRET_ENV_LITERAL:
5135
+ title: "A rendered service puts a secret literal in its environment"
5136
+ title_vi: "Dịch vụ đã render đưa bí mật trực tiếp vào biến môi trường"
5137
+ meaning_vi: "Mô hình triển khai truyền giá trị bí mật qua environment thay vì tham chiếu tệp secret được cấp cho dịch vụ."
5138
+ causes_vi:
5139
+ - "Vi phạm R266: giá trị bí mật có thể xuất hiện trong cấu hình đã render, thông tin dịch vụ hoặc bản ghi vận hành."
5140
+ nextStep_vi: "Bỏ giá trị trực tiếp và dùng adapter đọc tệp secret của consumer; cấp secret bên ngoài cho đúng dịch vụ mà không in giá trị."
5141
+ owner: op-retry
5142
+ kind: check-finding
5143
+
5144
+ STACKS_SECRET_NOT_EXTERNAL:
5145
+ title: "A Swarm secret is not an external engine secret"
5146
+ title_vi: "Bí mật Swarm chưa dùng secret bên ngoài của engine"
5147
+ meaning_vi: "Mô hình Swarm chứa secret không có external: true, dùng file hoặc giá trị trực tiếp, hoặc không có secret tương ứng với khai báo của môi trường."
5148
+ causes_vi:
5149
+ - "Vi phạm R266: mô hình triển khai giữ bí mật trong tệp mount hoặc nội dung stack thay vì cấp secret của engine cho đúng dịch vụ."
5150
+ nextStep_vi: "Khai báo secret bên ngoài trong mô hình Swarm, tạo nó qua stdin riêng tư và cấp quyền cho đúng dịch vụ; chỉ theo dõi bản mã hóa trong kho mã."
5151
+ owner: op-retry
5152
+ kind: check-finding
5153
+
5154
+ STACKS_SECRET_UNVERSIONED:
5155
+ title: "A Swarm secret has no matching immutable version"
5156
+ title_vi: "Bí mật Swarm thiếu phiên bản bất biến khớp khai báo"
5157
+ meaning_vi: "Secret thiếu runtimeName hoặc version dương, tên không kết thúc bằng -v<version>, hoặc tên secret đã render khác với tên được khai báo."
5158
+ causes_vi:
5159
+ - "Vi phạm R266: tên secret có thể bị tái sử dụng khi xoay khóa hoặc khai báo không khớp secret của engine."
5160
+ nextStep_vi: "Khai báo runtimeName và version dương khớp hậu tố -v<version>; dùng đúng tên đó trong mô hình, đổi quyền của consumer rồi mới thu hồi phiên bản cũ."
5161
+ owner: op-retry
5162
+ kind: check-finding
5163
+
5164
+ STACKS_SIDECAR_DB_UNSUPPORTED:
5165
+ title: "A lite sidecar requires an unsupported database adapter"
5166
+ title_vi: "Sidecar của lite yêu cầu adapter cơ sở dữ liệu không được hỗ trợ"
5167
+ meaning_vi: "Sidecar của lite khai báo hoặc cấu hình database khác SQLite, bao gồm PostgreSQL, Redis và MongoDB."
5168
+ causes_vi:
5169
+ - "Vi phạm R276: sidecar kéo thêm backing bị cấm vào topology lite thay vì dùng SQLite trên volume bền vững."
5170
+ nextStep_vi: "Chọn chế độ SQLite được sidecar hỗ trợ và volume được khai báo; bỏ sidecar nếu nó bắt buộc dùng backing không được lite hỗ trợ."
5171
+ owner: op-retry
5172
+ kind: check-finding
5173
+
5174
+ STACKS_SIDECAR_VOLUME_UNDECLARED:
5175
+ title: "A lite SQLite sidecar has no declared persistent volume"
5176
+ title_vi: "Sidecar SQLite của lite thiếu volume bền vững được khai báo"
5177
+ meaning_vi: "Sidecar dùng SQLite nhưng chưa khai báo database.volume, chưa liệt kê volume đó trong component hoặc chưa định nghĩa và mount volume trong mô hình."
5178
+ causes_vi:
5179
+ - "Vi phạm R276: tệp cơ sở dữ liệu sidecar không gắn với volume có chủ sở hữu rõ ràng và có thể mất khi tạo lại container."
5180
+ nextStep_vi: "Khai báo database.provider: sqlite, database.volume và components.<id>.volumes, rồi định nghĩa và mount đúng named volume trong mô hình."
5181
+ owner: op-retry
5182
+ kind: check-finding
5183
+
5184
+ STACKS_TLS_UNDECLARED:
5185
+ title: "A Swarm environment has no TLS ownership receipt"
5186
+ title_vi: "Môi trường Swarm thiếu chủ sở hữu và bằng chứng TLS"
5187
+ meaning_vi: "Môi trường có ingress nhưng chưa khai báo tls.owner và tls.receipt cho việc cấp chứng chỉ cùng nơi lưu giữ bền vững."
5188
+ causes_vi:
5189
+ - "Vi phạm R266: quyền cấp hoặc phục hồi chứng chỉ chưa có chủ sở hữu và bằng chứng được khai báo."
5190
+ nextStep_vi: "Khai báo chủ sở hữu TLS và tham chiếu bằng chứng cấp, lưu giữ chứng chỉ; giữ thao tác của chủ sở hữu là điều kiện triển khai rõ ràng."
5191
+ owner: op-retry
5192
+ kind: check-finding
5193
+
5194
+ STACKS_UPDATE_POLICY_MISSING:
5195
+ title: "A Swarm service has no explicit rollout and recovery policy"
5196
+ title_vi: "Dịch vụ Swarm thiếu chính sách cập nhật và phục hồi rõ ràng"
5197
+ meaning_vi: "Dịch vụ chạy liên tục thiếu chính sách deploy.update_config, rollback_config hoặc restart_policy cần cho vòng đời triển khai."
5198
+ causes_vi:
5199
+ - "Vi phạm R266: triển khai dựa vào mặc định thay vì nêu cách cập nhật, xử lý lỗi và phục hồi dịch vụ."
5200
+ nextStep_vi: "Khai báo chính sách cập nhật, rollback và restart của từng dịch vụ trong mô hình, rồi kiểm tra cùng các tín hiệu sức khỏe."
5201
+ owner: op-retry
5202
+ kind: check-finding
5203
+
5204
+ STACK_TOOLING_REIMPLEMENTED:
5205
+ title: "An app re-implements deploy tooling the canon ships"
5206
+ title_vi: "Ứng dụng tự cài lại công cụ triển khai mà canon đã cung cấp"
5207
+ meaning_vi: "Một tệp triển khai gọi lệnh docker mà một động từ starci app stack đã đảm nhiệm, hoặc thư mục deploy chứa tệp mang tên một động từ như render, converge hay prepare."
5208
+ causes_vi:
5209
+ - "Vi phạm R293: kho mã giữ bản sao công cụ Swarm riêng (docker stack deploy, docker secret create, docker swarm init...) thay vì gọi động từ của canon."
5210
+ - "Một workflow hoặc script trong deploy/, scripts/ hay package.json gọi trực tiếp docker cho việc canon đã làm."
5211
+ nextStep_vi: "Xóa tệp hoặc lệnh đó và gọi động từ được nêu trong thông báo (starci app stack render, deploy, converge, verify, prune, rollback, prepare, doctor), đặt hằng số của ứng dụng vào khối deploy của application-stacks.yaml."
5212
+ owner: op-retry
5213
+ kind: check-finding