@tenkara/afm-linux-arm64 0.0.0-stage → 4.0.0-alpha.10

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 (277) hide show
  1. package/bin/afm +4 -0
  2. package/content/.coupling-baseline.tsv +1 -0
  3. package/content/.pending-commands.txt +2 -0
  4. package/content/CHANGELOG-archive.md +806 -0
  5. package/content/CHANGELOG.md +4320 -0
  6. package/content/PRINCIPLES.md +119 -0
  7. package/content/README-light.md +158 -0
  8. package/content/README.md +150 -0
  9. package/content/code-style/_any/file-size.md +62 -0
  10. package/content/code-style/_any/imports.md +68 -0
  11. package/content/code-style/_any/naming.md +72 -0
  12. package/content/code-style/_any/test-placement.md +57 -0
  13. package/content/code-style/nextjs-app-router/imports.md +50 -0
  14. package/content/code-style/nextjs-app-router/naming.md +50 -0
  15. package/content/code-style/prisma-postgres/error-boundary.md +55 -0
  16. package/content/code-style/prisma-postgres/imports.md +61 -0
  17. package/content/code-style/shadcn-ui/naming.md +48 -0
  18. package/content/code-style/stripe-billing/naming.md +76 -0
  19. package/content/code-style/trigger-dev-v4/imports.md +58 -0
  20. package/content/code-style/trigger-dev-v4/naming.md +49 -0
  21. package/content/code-style/trigger-dev-v4/test-placement.md +61 -0
  22. package/content/code-style/trpc-v11/error-boundary.md +51 -0
  23. package/content/code-style/trpc-v11/file-size.md +44 -0
  24. package/content/code-style/trpc-v11/naming.md +60 -0
  25. package/content/code-style/trpc-v11/test-placement.md +52 -0
  26. package/content/core/ach.md +260 -0
  27. package/content/core/afm.md +514 -0
  28. package/content/core/gotchas.md +18 -0
  29. package/content/core/prd.md +141 -0
  30. package/content/core/protocol.md +158 -0
  31. package/content/core/ust.md +65 -0
  32. package/content/guardrails/README.md +55 -0
  33. package/content/guardrails/dsl-com-ordem-obrigatoria.md +47 -0
  34. package/content/guardrails/exaustividade-de-union.md +48 -0
  35. package/content/guardrails/fronteira-de-import.md +57 -0
  36. package/content/hooks/README.md +70 -0
  37. package/content/interview/bootstrap.md +417 -0
  38. package/content/interview/promotion.md +115 -0
  39. package/content/interview/refactor.md +379 -0
  40. package/content/interview/triggers.md +107 -0
  41. package/content/modules/nextjs-app-router/ach.patch.md +44 -0
  42. package/content/modules/nextjs-app-router/gotchas.seed.md +78 -0
  43. package/content/modules/nextjs-app-router/rules.patch.md +35 -0
  44. package/content/modules/prisma-postgres/ach.patch.md +65 -0
  45. package/content/modules/prisma-postgres/rules.patch.md +28 -0
  46. package/content/modules/shadcn-ui/ach.patch.md +42 -0
  47. package/content/modules/stripe-billing/ach.patch.md +46 -0
  48. package/content/modules/stripe-billing/gotchas.seed.md +73 -0
  49. package/content/modules/stripe-billing/rules.patch.md +30 -0
  50. package/content/modules/trigger-dev-v4/ach.patch.md +64 -0
  51. package/content/modules/trigger-dev-v4/gotchas.seed.md +53 -0
  52. package/content/modules/trigger-dev-v4/rules.patch.md +35 -0
  53. package/content/modules/trpc-v11/ach.patch.md +56 -0
  54. package/content/modules/trpc-v11/rules.patch.md +34 -0
  55. package/content/modules/vireya-ui/ach.patch.md +85 -0
  56. package/content/modules/vireya-ui/gotchas.seed.md +98 -0
  57. package/content/modules/vireya-ui/rules.patch.md +36 -0
  58. package/content/ops/adr.md +54 -0
  59. package/content/ops/analyze.md +130 -0
  60. package/content/ops/clarify.md +86 -0
  61. package/content/ops/deliver.md +209 -0
  62. package/content/ops/diagnose.md +143 -0
  63. package/content/ops/evolve.md +366 -0
  64. package/content/ops/export-template.md +97 -0
  65. package/content/ops/generalize.md +117 -0
  66. package/content/ops/gotcha.md +46 -0
  67. package/content/ops/plan.md +61 -0
  68. package/content/ops/policy.md +61 -0
  69. package/content/ops/promote.md +72 -0
  70. package/content/ops/protocol.md +51 -0
  71. package/content/ops/reconcile.md +44 -0
  72. package/content/ops/reflect.md +182 -0
  73. package/content/ops/remediate.md +143 -0
  74. package/content/ops/restructure.md +142 -0
  75. package/content/ops/rule.md +170 -0
  76. package/content/ops/specify.md +62 -0
  77. package/content/ops/tasks.md +67 -0
  78. package/content/ops/update.md +252 -0
  79. package/content/ops/verify.md +67 -0
  80. package/content/patterns/adapter.md +46 -0
  81. package/content/patterns/facade.md +47 -0
  82. package/content/patterns/factory.md +46 -0
  83. package/content/patterns/null-object.md +55 -0
  84. package/content/patterns/result-vs-exception.md +52 -0
  85. package/content/patterns/strategy.md +45 -0
  86. package/content/patterns/template-method-vs-composition.md +50 -0
  87. package/content/questions/adr.questions.yaml +14 -0
  88. package/content/questions/analyze.questions.yaml +12 -0
  89. package/content/questions/bootstrap-mapping.questions.yaml +14 -0
  90. package/content/questions/bootstrap-rules.questions.yaml +10 -0
  91. package/content/questions/bootstrap-stack.questions.yaml +23 -0
  92. package/content/questions/clarify.questions.yaml +10 -0
  93. package/content/questions/code-style.questions.yaml +12 -0
  94. package/content/questions/confirm.questions.yaml +12 -0
  95. package/content/questions/deliver.questions.yaml +23 -0
  96. package/content/questions/evolve.questions.yaml +15 -0
  97. package/content/questions/export-template-apply.questions.yaml +11 -0
  98. package/content/questions/export-template.questions.yaml +23 -0
  99. package/content/questions/gates.questions.yaml +27 -0
  100. package/content/questions/generalize.questions.yaml +13 -0
  101. package/content/questions/gotcha.questions.yaml +11 -0
  102. package/content/questions/init-template.questions.yaml +10 -0
  103. package/content/questions/init.questions.yaml +41 -0
  104. package/content/questions/plan.questions.yaml +14 -0
  105. package/content/questions/refactor.questions.yaml +23 -0
  106. package/content/questions/reflect.questions.yaml +13 -0
  107. package/content/questions/remediate.questions.yaml +27 -0
  108. package/content/questions/restructure.questions.yaml +15 -0
  109. package/content/questions/rf-mapping.questions.yaml +12 -0
  110. package/content/questions/rule-scope.questions.yaml +12 -0
  111. package/content/questions/rule.questions.yaml +12 -0
  112. package/content/questions/specify.questions.yaml +23 -0
  113. package/content/questions/update-artifacts.questions.yaml +10 -0
  114. package/content/questions/update-strings.questions.yaml +14 -0
  115. package/content/questions/update.questions.yaml +10 -0
  116. package/content/questions/verify.questions.yaml +9 -0
  117. package/content/roles/executor.md +18 -0
  118. package/content/roles/explorer.md +22 -0
  119. package/content/roles/reviewer.md +23 -0
  120. package/content/rubrics/enum-vs-union-vs-branded.md +40 -0
  121. package/content/rubrics/episodic-vs-semantic-boundary.md +37 -0
  122. package/content/rubrics/error-classification.md +98 -0
  123. package/content/rubrics/failure-classification.md +111 -0
  124. package/content/rubrics/negative-filters.md +38 -0
  125. package/content/rubrics/solid-triggers.md +119 -0
  126. package/content/rubrics/template-vs-streaming-precedence.md +34 -0
  127. package/content/rubrics/validation-boundary.md +94 -0
  128. package/content/rubrics/when-to-create-dsl.md +65 -0
  129. package/content/rubrics/when-to-create-lib.md +49 -0
  130. package/content/rubrics/when-to-create-module.md +39 -0
  131. package/content/rubrics/when-to-evolve-methodology.md +62 -0
  132. package/content/skills/adr.md +42 -0
  133. package/content/skills/analyze.md +28 -0
  134. package/content/skills/clarify.md +28 -0
  135. package/content/skills/deliver.md +26 -0
  136. package/content/skills/diagnose.md +42 -0
  137. package/content/skills/evolve.md +43 -0
  138. package/content/skills/export-template.md +37 -0
  139. package/content/skills/generalize.md +31 -0
  140. package/content/skills/gotcha.md +40 -0
  141. package/content/skills/init.md +77 -0
  142. package/content/skills/plan.md +28 -0
  143. package/content/skills/policy.md +25 -0
  144. package/content/skills/promote.md +39 -0
  145. package/content/skills/protocol.md +41 -0
  146. package/content/skills/reconcile.md +45 -0
  147. package/content/skills/refactor.md +79 -0
  148. package/content/skills/reflect.md +43 -0
  149. package/content/skills/remediate.md +33 -0
  150. package/content/skills/restructure.md +46 -0
  151. package/content/skills/rule.md +43 -0
  152. package/content/skills/specify.md +28 -0
  153. package/content/skills/tasks.md +24 -0
  154. package/content/skills/update.md +58 -0
  155. package/content/skills/verify.md +29 -0
  156. package/content/templates/adr.template.md +51 -0
  157. package/content/templates/evolution-report.template.md +132 -0
  158. package/content/templates/feature-plan.template.md +84 -0
  159. package/content/templates/feature-research.template.md +56 -0
  160. package/content/templates/feature-spec.template.md +86 -0
  161. package/content/templates/feature-tasks.induced.template.md +91 -0
  162. package/content/templates/feature-tasks.template.md +82 -0
  163. package/content/templates/focus.template.md +30 -0
  164. package/content/templates/gotcha.template.md +31 -0
  165. package/content/templates/guardrail-runner.template.md +97 -0
  166. package/content/templates/guardrail.template.md +63 -0
  167. package/content/templates/learning.template.md +40 -0
  168. package/content/templates/procedure.template.md +47 -0
  169. package/content/templates/protocols/p1.md +29 -0
  170. package/content/templates/protocols/p2.md +28 -0
  171. package/content/templates/protocols/p3.md +47 -0
  172. package/content/templates/research.template.md +50 -0
  173. package/content/templates/session-handoff.template.md +58 -0
  174. package/content/templates/us.template.md +39 -0
  175. package/content/tools/gate/core-rules/PLUGIN-13-secrets.sh +36 -0
  176. package/content/tools/gate/core-rules/PLUGIN-21-code-language.sh +27 -0
  177. package/content/tools/gate/core-rules/PLUGIN-35-gate-complete.sh +19 -0
  178. package/content/tools/gate/core-rules/PLUGIN-36-absolute-path.sh +38 -0
  179. package/content/tools/gate/core-rules/PLUGIN-37-prose-language.sh +56 -0
  180. package/content/tools/gate/core-rules/PLUGIN-38-parser-marker.sh +82 -0
  181. package/content/tools/gate/core-rules/PLUGIN-39-frontmatter-ops.sh +325 -0
  182. package/content/tools/gate/core-rules/PLUGIN-40-dead-reference.sh +74 -0
  183. package/content/tools/gate/core-rules/README.md +55 -0
  184. package/content/tools/gate/guardrails.sh +740 -0
  185. package/content/tools/gate/lib/ratchet.sh +119 -0
  186. package/content/tools/gate/lib/scope.sh +85 -0
  187. package/content/tools/gate/ratchet.seed.tsv +1 -0
  188. package/content/tools/gate/rules/README.md +50 -0
  189. package/package.json +19 -5
  190. package/plugin/agy/hooks.json +29 -0
  191. package/plugin/agy/mcp_config.json +7 -0
  192. package/plugin/agy/plugin.json +6 -0
  193. package/plugin/agy/skills/afm-adr/SKILL.md +48 -0
  194. package/plugin/agy/skills/afm-analyze/SKILL.md +34 -0
  195. package/plugin/agy/skills/afm-clarify/SKILL.md +34 -0
  196. package/plugin/agy/skills/afm-deliver/SKILL.md +30 -0
  197. package/plugin/agy/skills/afm-diagnose/SKILL.md +46 -0
  198. package/plugin/agy/skills/afm-evolve/SKILL.md +49 -0
  199. package/plugin/agy/skills/afm-export-template/SKILL.md +43 -0
  200. package/plugin/agy/skills/afm-generalize/SKILL.md +37 -0
  201. package/plugin/agy/skills/afm-gotcha/SKILL.md +46 -0
  202. package/plugin/agy/skills/afm-init/SKILL.md +81 -0
  203. package/plugin/agy/skills/afm-plan/SKILL.md +34 -0
  204. package/plugin/agy/skills/afm-policy/SKILL.md +29 -0
  205. package/plugin/agy/skills/afm-promote/SKILL.md +43 -0
  206. package/plugin/agy/skills/afm-protocol/SKILL.md +45 -0
  207. package/plugin/agy/skills/afm-reconcile/SKILL.md +49 -0
  208. package/plugin/agy/skills/afm-refactor/SKILL.md +83 -0
  209. package/plugin/agy/skills/afm-reflect/SKILL.md +49 -0
  210. package/plugin/agy/skills/afm-remediate/SKILL.md +39 -0
  211. package/plugin/agy/skills/afm-restructure/SKILL.md +52 -0
  212. package/plugin/agy/skills/afm-rule/SKILL.md +49 -0
  213. package/plugin/agy/skills/afm-specify/SKILL.md +34 -0
  214. package/plugin/agy/skills/afm-tasks/SKILL.md +28 -0
  215. package/plugin/agy/skills/afm-update/SKILL.md +64 -0
  216. package/plugin/agy/skills/afm-verify/SKILL.md +35 -0
  217. package/plugin/claude-code/.claude-plugin/marketplace.json +15 -0
  218. package/plugin/claude-code/.claude-plugin/plugin.json +9 -0
  219. package/plugin/claude-code/.mcp.json +7 -0
  220. package/plugin/claude-code/agents/afm-executor.md +18 -0
  221. package/plugin/claude-code/agents/afm-explorer.md +20 -0
  222. package/plugin/claude-code/agents/afm-reviewer.md +21 -0
  223. package/plugin/claude-code/hooks/hooks.json +85 -0
  224. package/plugin/claude-code/skills/adr/SKILL.md +48 -0
  225. package/plugin/claude-code/skills/analyze/SKILL.md +34 -0
  226. package/plugin/claude-code/skills/clarify/SKILL.md +34 -0
  227. package/plugin/claude-code/skills/deliver/SKILL.md +30 -0
  228. package/plugin/claude-code/skills/diagnose/SKILL.md +46 -0
  229. package/plugin/claude-code/skills/evolve/SKILL.md +49 -0
  230. package/plugin/claude-code/skills/export-template/SKILL.md +43 -0
  231. package/plugin/claude-code/skills/generalize/SKILL.md +37 -0
  232. package/plugin/claude-code/skills/gotcha/SKILL.md +46 -0
  233. package/plugin/claude-code/skills/init/SKILL.md +81 -0
  234. package/plugin/claude-code/skills/plan/SKILL.md +34 -0
  235. package/plugin/claude-code/skills/policy/SKILL.md +29 -0
  236. package/plugin/claude-code/skills/promote/SKILL.md +43 -0
  237. package/plugin/claude-code/skills/protocol/SKILL.md +45 -0
  238. package/plugin/claude-code/skills/reconcile/SKILL.md +49 -0
  239. package/plugin/claude-code/skills/refactor/SKILL.md +83 -0
  240. package/plugin/claude-code/skills/reflect/SKILL.md +49 -0
  241. package/plugin/claude-code/skills/remediate/SKILL.md +39 -0
  242. package/plugin/claude-code/skills/restructure/SKILL.md +52 -0
  243. package/plugin/claude-code/skills/rule/SKILL.md +49 -0
  244. package/plugin/claude-code/skills/specify/SKILL.md +34 -0
  245. package/plugin/claude-code/skills/tasks/SKILL.md +28 -0
  246. package/plugin/claude-code/skills/update/SKILL.md +64 -0
  247. package/plugin/claude-code/skills/verify/SKILL.md +35 -0
  248. package/plugin/opencode/afm/index.js +245 -0
  249. package/plugin/opencode/afm/tui.js +21 -0
  250. package/plugin/opencode/agents/afm-executor.md +39 -0
  251. package/plugin/opencode/agents/afm-explorer.md +92 -0
  252. package/plugin/opencode/agents/afm-reviewer.md +93 -0
  253. package/plugin/opencode/skills/afm-adr/SKILL.md +48 -0
  254. package/plugin/opencode/skills/afm-analyze/SKILL.md +34 -0
  255. package/plugin/opencode/skills/afm-clarify/SKILL.md +34 -0
  256. package/plugin/opencode/skills/afm-deliver/SKILL.md +30 -0
  257. package/plugin/opencode/skills/afm-diagnose/SKILL.md +46 -0
  258. package/plugin/opencode/skills/afm-evolve/SKILL.md +49 -0
  259. package/plugin/opencode/skills/afm-export-template/SKILL.md +43 -0
  260. package/plugin/opencode/skills/afm-generalize/SKILL.md +37 -0
  261. package/plugin/opencode/skills/afm-gotcha/SKILL.md +46 -0
  262. package/plugin/opencode/skills/afm-init/SKILL.md +81 -0
  263. package/plugin/opencode/skills/afm-plan/SKILL.md +34 -0
  264. package/plugin/opencode/skills/afm-policy/SKILL.md +29 -0
  265. package/plugin/opencode/skills/afm-promote/SKILL.md +43 -0
  266. package/plugin/opencode/skills/afm-protocol/SKILL.md +45 -0
  267. package/plugin/opencode/skills/afm-reconcile/SKILL.md +49 -0
  268. package/plugin/opencode/skills/afm-refactor/SKILL.md +83 -0
  269. package/plugin/opencode/skills/afm-reflect/SKILL.md +49 -0
  270. package/plugin/opencode/skills/afm-remediate/SKILL.md +39 -0
  271. package/plugin/opencode/skills/afm-restructure/SKILL.md +52 -0
  272. package/plugin/opencode/skills/afm-rule/SKILL.md +49 -0
  273. package/plugin/opencode/skills/afm-specify/SKILL.md +34 -0
  274. package/plugin/opencode/skills/afm-tasks/SKILL.md +28 -0
  275. package/plugin/opencode/skills/afm-update/SKILL.md +64 -0
  276. package/plugin/opencode/skills/afm-verify/SKILL.md +35 -0
  277. package/README.md +0 -3
@@ -0,0 +1,35 @@
1
+ ---
2
+ type: module
3
+ description: 'Hard rules for App Router projects — Server Component by default, every "use client" carries a stated reason, and <html lang> renders in the localized segment. The route-handler-thinness rule this module used to state as a fixed 80-line trigger is an opt-in seed now.'
4
+ load: cold
5
+ stack: nextjs-app-router
6
+ patches: 'docs/rules/*.md'
7
+ detect: |
8
+ if command -v jq >/dev/null 2>&1; then jq -e '(.dependencies//{})+(.devDependencies//{})+(.peerDependencies//{})|has("next")' package.json >/dev/null 2>&1; else grep -q '"next"' package.json 2>/dev/null; fi
9
+ ---
10
+
11
+ # Patch — Hard rules for Next.js App Router projects
12
+
13
+ > Insert after the last existing hard rule in `/docs/afm.md` §3.
14
+
15
+ - **Server Component is the default.** Use `"use client"` only where the code needs real interactivity (state, DOM events, browser hooks). Each `"use client"` needs a clear reason.
16
+ *Verification:* `grep -rn '"use client"' src/app app 2>/dev/null` — each match has a clear reason in the diff.
17
+
18
+ > This module used to add a hard rule here that route handlers stay thin, verified by a fixed
19
+ > "all files under 80 lines" check — an arbitrary constant with no measurement behind it and no
20
+ > placeholder. The qualitative convention ("a route handler delegates to `server/`, it does not
21
+ > embed business logic") is not a Next.js fact either; it now lives as an opt-in seed at
22
+ > `code-style/nextjs-app-router/imports.md` (concern: imports). A project that wants a numeric
23
+ > line-count backstop should adopt `code-style/_any/file-size.md` instead, with a threshold it
24
+ > measured against its own files — not this module's unmeasured 80.
25
+
26
+ - **A page Server Component does not call raw `fetch()` when a typed client exists.** Use the stack's server-side client instead. For example, use a tRPC server caller to keep the types.
27
+ *Verification:* `grep -rn "fetch(" --include='page.tsx' src/app app 2>/dev/null`.
28
+ - **The system renders `<html lang>` in the `[lang]` segment, never with a fixed default in the root layout**. Localized routes (`app/[lang]/...`) need the correct locale in `<html lang>` at SSR/SSG time. Crawlers and screen readers read this value before hydration. A fixed root render (`<html lang="pt-BR">`) plus a client-side fix serves the wrong `lang` on `/en/*`. The client-side fix is a `useEffect` that sets `document.documentElement.lang`.
29
+
30
+ Next.js supports a root layout under a dynamic segment. **Delete `app/layout.tsx`.** Make `app/[lang]/layout.tsx` render `<html lang={...}>`. Take `lang` from `params.lang` (SSG-safe, no `headers()` call).
31
+
32
+ This has a required consequence: without a root layout, the `_not-found` route and non-locale paths become orphans. Add `app/global-not-found.tsx` (its own HTML document). Set `experimental.globalNotFound: true` in the Next.js config.
33
+
34
+ Move the styled `not-found.tsx` to `app/[lang]/not-found.tsx`.
35
+ *Verification:* `grep -rn 'html lang' src/app app 2>/dev/null` — the only dynamic-lang match is in `app/[lang]/layout.tsx` (the fixed default is allowed only in `app/global-not-found.tsx`). `grep -rn 'documentElement.lang' src/app app 2>/dev/null` returns empty (no client-side hack). `ls src/app/layout.tsx app/layout.tsx 2>/dev/null` shows the file does not exist.
@@ -0,0 +1,65 @@
1
+ ---
2
+ type: module
3
+ description: 'Adds the always-run-generate rule and the optimistic-locking pattern to the materialized ACH. Schema layout, generated-client path, and soft-delete are opt-in seeds — see the pointers below.'
4
+ load: cold
5
+ stack: prisma-postgres
6
+ patches: 'docs/ach.md'
7
+ detect: |
8
+ test -f prisma/schema.prisma
9
+ ---
10
+
11
+ # Patch — ACH for Prisma + Postgres projects
12
+
13
+ > Apply this to `/docs/ach.md`. It adds the generate-after-edit rule and the multi-actor locking
14
+ > pattern.
15
+
16
+ ## §2 — Add to the tree
17
+
18
+ ```
19
+ prisma/
20
+ schema.prisma # config + datasource
21
+ seed.ts # seed script (also wired as the Prisma seed cmd)
22
+ ```
23
+
24
+ > Prisma 5.15+ also supports splitting the schema across several files, merged by a
25
+ > `prisma.config.ts`, and the generated client's output path is configurable per project. Where
26
+ > those files live and where the client lands — and the three layer-direction import rules this
27
+ > patch used to state as fact alongside them — are house convention, not a Prisma requirement.
28
+ > They now live as an opt-in seed at `code-style/prisma-postgres/imports.md` (concern: imports).
29
+ > Materialize it only if the operator accepts it.
30
+
31
+ ## §6 — Add schema conventions
32
+
33
+ ```md
34
+ ### Schema conventions
35
+
36
+ - **Always run `npx prisma generate`** after you (a) clone the repo, (b) edit any schema file, or
37
+ (c) update the Prisma version. Without this step, the generated-client imports do not resolve.
38
+ ```
39
+
40
+ > The soft-delete convention this patch used to state as fact — `deletedAt`, a hard-delete SLA
41
+ > comment, the default query filter, and a scheduled cleanup job — is a design choice, not a
42
+ > Prisma fact: Prisma has no opinion on whether a row ever leaves the table. It now lives as an
43
+ > opt-in seed at `code-style/prisma-postgres/error-boundary.md` (concern: error-boundary).
44
+ > Materialize it only if the project actually needs soft delete.
45
+
46
+ ## §6 — Multi-actor concurrency
47
+
48
+ ```md
49
+ ### Optimistic locking for multi-actor entities
50
+
51
+ An entity that more than one actor can edit concurrently (example: a record with a status or
52
+ version field that a background job and a user action can both update) needs a **conditional
53
+ update**, not a plain `update`:
54
+
55
+ ```ts
56
+ const result = await db.record.updateMany({
57
+ where: { id, version: expectedVersion },
58
+ data: { version: nextVersion, ...changes },
59
+ });
60
+ if (result.count === 0) throw new DomainError({ code: "STALE_WRITE" });
61
+ ```
62
+
63
+ Without this pattern, the last write wins silently. For a side-effect-once endpoint (anything that
64
+ should not re-run if a stale client retries), use the same pattern and add a race-condition test.
65
+ ```
@@ -0,0 +1,28 @@
1
+ ---
2
+ type: module
3
+ description: 'Principle for Prisma and Postgres projects: migrations must stay forward-compatible for a rolling deploy. Ships no hard rule of its own — the two this module used to state are opt-in seeds now.'
4
+ load: cold
5
+ stack: prisma-postgres
6
+ patches: 'docs/rules/*.md'
7
+ detect: |
8
+ test -f prisma/schema.prisma
9
+ ---
10
+
11
+ # Patch — Hard rules for Prisma + Postgres projects
12
+
13
+ > Hard rules (with an executable trigger) go after the last hard rule in `/docs/afm.md` §3.
14
+ > Principles (verification by review only) go in `/docs/afm.md` §1.3.
15
+
16
+ ## Hard rules (executable trigger)
17
+
18
+ None. The two rules this module used to state as fact here — a domain function must receive `db`
19
+ through `ctx` instead of importing `PrismaClient` directly, and every query against a
20
+ soft-deletable model must filter `deletedAt: null` — are house conventions, not Prisma facts:
21
+ Prisma has no opinion on dependency injection or on whether a row is ever really deleted. Both are
22
+ now **opt-in seeds**, proposed rather than imposed, at `code-style/prisma-postgres/imports.md`
23
+ (concern: imports) and `code-style/prisma-postgres/error-boundary.md` (concern: error-boundary).
24
+ Materialize either only if the operator accepts it.
25
+
26
+ ## Principles (insert in §1.3 — review, not a trigger)
27
+
28
+ - **Migrations must stay forward-compatible**. A destructive column drop or rename needs a dual-read phase. Without it, a rolling deploy across N nodes creates an error window. *(No grep can prove rolling-deploy safety — this is a review checklist: "is this migration safe for a rolling deploy?")*
@@ -0,0 +1,42 @@
1
+ ---
2
+ type: module
3
+ description: 'Adds shadcn-style UI primitive conventions, the Tailwind layer, and the component boundary to the materialized ACH.'
4
+ load: cold
5
+ stack: shadcn-ui
6
+ patches: 'docs/ach.md'
7
+ detect: |
8
+ test -f components.json
9
+ ---
10
+
11
+ # Patch — ACH for projects with shadcn-style UI primitives
12
+
13
+ > Apply this to `/docs/ach.md`. It adds conventions for components, Tailwind, and primitives.
14
+
15
+ ## §3.2 — UI primitives
16
+
17
+ ```md
18
+ #### UI primitives
19
+
20
+ - **What it is:** base components in the shadcn style (Button, Input, Dialog, Sheet, ...). The CLI copies them in through `components.json`. You then edit them in place in the project.
21
+ - **Responsibility:** pure primitives. No domain logic, no fetch, no global state.
22
+ - **Stack:** Tailwind v4 (`@tailwindcss/postcss`), `tw-animate-css`, Radix UI underneath.
23
+ - **Modification:** primitives are copied, not a dependency. Edits stay permanent. Use `npx shadcn add` for new components, then treat them as your own code.
24
+ ```
25
+
26
+ > Where primitives and feature-composed components live (`src/components/ui/*` versus
27
+ > `src/components/<feature>/`) is house convention, not a shadcn fact — shadcn only prescribes
28
+ > where the CLI copies primitives *within* whatever `components.json` points at. That layout now
29
+ > lives as an **opt-in seed** at `code-style/shadcn-ui/naming.md`. Materialize it only if the
30
+ > operator accepts it. How a feature component fetches its data (tRPC, REST, GraphQL, a plain
31
+ > `fetch`) is a different stack's decision entirely — this module does not make it.
32
+
33
+ ## §6 — Conventions
34
+
35
+ ```md
36
+ ### shadcn / Tailwind
37
+
38
+ - `components.json` defines paths and config. Do not edit it by hand. Use the CLI.
39
+ - Use `class-variance-authority` (cva) for variants, typed for autocomplete.
40
+ - Use the `cn()` helper (`clsx` + `tailwind-merge`) as the standard way to compose classes.
41
+ - Components export `Component` plus a `ComponentProps` type. This makes extension easier.
42
+ ```
@@ -0,0 +1,46 @@
1
+ ---
2
+ type: module
3
+ description: 'Adds the canonical webhook and the Stripe Subscription Schedule conventions for billing to the materialized ACH.'
4
+ load: cold
5
+ stack: stripe-billing
6
+ patches: 'docs/ach.md'
7
+ detect: |
8
+ if command -v jq >/dev/null 2>&1; then jq -e '(.dependencies//{})+(.devDependencies//{})+(.peerDependencies//{})|has("stripe")' package.json >/dev/null 2>&1; else grep -q '"stripe"' package.json 2>/dev/null; fi
9
+ ---
10
+
11
+ # Patch — ACH for projects with Stripe billing
12
+
13
+ > Applies to `/docs/ach.md`. Adds conventions for the canonical webhook and Stripe Subscription
14
+ > Schedule handling. A file layout for the billing domain, an SDK-wrapper naming scheme, and a
15
+ > server-side gating accessor shape are proposed separately, as an opt-in code-style seed — see
16
+ > `code-style/stripe-billing/naming.md` — because none of the three is a fact about Stripe.
17
+
18
+ ## §6 — Add Billing conventions
19
+
20
+ ```md
21
+ ### Billing (webhook-driven state)
22
+
23
+ ### Hard rules for billing
24
+
25
+ - **The webhook is the canonical source** for subscription state, but a mutation procedure that
26
+ changes a subscription (a plan change, a cancellation, a checkout self-heal) also writes the
27
+ new state synchronously. That write is an idempotent upsert, so a webhook that arrives late
28
+ converges to the same state instead of creating a duplicate or a conflict.
29
+
30
+ ### Stripe Subscription Schedules
31
+
32
+ A multi-plan subscription flow uses four Stripe operations:
33
+
34
+ - An immediate Price swap with proration, for an upgrade that should take effect now.
35
+ - Creating or updating a `SubscriptionSchedule` for a Price change that should take effect at the
36
+ end of the current billing cycle, for a downgrade.
37
+ - Releasing an attached `SubscriptionSchedule`. The release call is a no-op, not an error, when
38
+ the subscription carries no schedule, so it is safe to call unconditionally before a mutation.
39
+ - Setting `cancel_at_period_end: true` on the subscription, for a cancellation that takes effect
40
+ at cycle end rather than immediately.
41
+
42
+ **Critical gotcha:** a subscription with an attached `SubscriptionSchedule` rejects any direct
43
+ mutation through the Subscriptions API (`subscription is managed by subscription schedule`).
44
+ Release the schedule before you mutate a subscription that may be carrying a pending downgrade.
45
+ See the gotcha for the exact error and remedy, and ADR-0002 (if one exists).
46
+ ```
@@ -0,0 +1,73 @@
1
+ ---
2
+ type: module
3
+ description: 'Seed gotchas for Stripe billing, starting with a subscription that carries a SubscriptionSchedule being immutable.'
4
+ load: cold
5
+ stack: stripe-billing
6
+ patches: 'docs/gotchas/*.md'
7
+ detect: |
8
+ if command -v jq >/dev/null 2>&1; then jq -e '(.dependencies//{})+(.devDependencies//{})+(.peerDependencies//{})|has("stripe")' package.json >/dev/null 2>&1; else grep -q '"stripe"' package.json 2>/dev/null; fi
9
+ ---
10
+
11
+ # Gotchas seed — Stripe billing
12
+
13
+ > Each entry below is a complete gotcha file. Copy one to `docs/gotchas/NNN-slug.md` (the first `---` of the entry as line 1) only when it bites in real use. Re-date `last_bite` on copy.
14
+
15
+ ---
16
+ type: gotcha
17
+ description: 'A subscription attached to a SubscriptionSchedule rejects every direct update, so release the schedule before you mutate it.'
18
+ load: warm
19
+ fires_on: event
20
+ fires_when: 'you edit a procedure that mutates a subscription (changePlan, cancelSubscription, createCheckout self-heal).'
21
+ bites: 1
22
+ last_bite: 2026-10
23
+ ---
24
+
25
+ ## Stripe — a sub with SubscriptionSchedule is immutable
26
+
27
+ ## Behavior
28
+
29
+ A sub attached to a `SubscriptionSchedule` returns `subscription is managed by subscription schedule` on any direct `subscription.update`. This blocks immediate upgrade and cancel when the user has a pending downgrade.
30
+
31
+ ## Solution
32
+
33
+ Call `stripe_releaseSubscriptionSchedule(subscriptionId)` before you mutate. The helper returns `null` when there is no schedule, so it is safe to call unconditionally. `changePlan({ planKey: currentPlan })` releases the schedule and clears `pendingPlan` (the "undo pending" pattern).
34
+
35
+ ---
36
+ type: gotcha
37
+ description: 'Changing a Price lookupKey in Stripe forces a new Product and unanchors the existing subscription base.'
38
+ load: warm
39
+ fires_on: event
40
+ fires_when: 'you rename a plan, change a price, or edit planConfig.ts.'
41
+ bites: 1
42
+ last_bite: 2026-10
43
+ ---
44
+
45
+ ## Stripe — a Price `lookupKey` is immutable after publish
46
+
47
+ ## Behavior
48
+
49
+ Changing a Price's `lookupKey` in Stripe forces creation of a **new Product**. Existing subs stay anchored to the old Product, so this unanchors the whole base. A `productName` rename in code is cosmetic only.
50
+
51
+ ## Solution
52
+
53
+ Stripe requires a manual rename in the dashboard. Or use a new lookupKey only for **new** subs. Keep the old lookupKey valid for the current base.
54
+
55
+ ---
56
+ type: gotcha
57
+ description: 'Stripe delivers webhook events at least once, so a handler with a direct INSERT creates duplicates.'
58
+ load: warm
59
+ fires_on: event
60
+ fires_when: 'you implement a webhook handler, for example for customer.subscription.updated.'
61
+ bites: 1
62
+ last_bite: 2026-10
63
+ ---
64
+
65
+ ## Stripe — `customer.subscription.updated` can arrive more than once
66
+
67
+ ## Behavior
68
+
69
+ Stripe can deliver the same event more than once (at-least-once delivery). A handler that runs a direct INSERT creates a duplicate.
70
+
71
+ ## Solution
72
+
73
+ Use an idempotent UPSERT keyed on the unique `stripeSubscriptionId`. Detect a plan transition (`existing.plan !== incoming.plan`) and act only on the real transition.
@@ -0,0 +1,30 @@
1
+ ---
2
+ type: module
3
+ description: 'Hard rules for Stripe billing, split between executable triggers and review-only principles.'
4
+ load: cold
5
+ stack: stripe-billing
6
+ patches: 'docs/rules/*.md'
7
+ detect: |
8
+ if command -v jq >/dev/null 2>&1; then jq -e '(.dependencies//{})+(.devDependencies//{})+(.peerDependencies//{})|has("stripe")' package.json >/dev/null 2>&1; else grep -q '"stripe"' package.json 2>/dev/null; fi
9
+ ---
10
+
11
+ # Patch — Hard rules for Stripe billing
12
+
13
+ > Hard rules (with an executable trigger) go in after the last hard rule in `/docs/afm.md` §3.
14
+ > Principles (verification by review only) go in `/docs/afm.md` §1.3.
15
+
16
+ ## Hard rules (executable trigger)
17
+
18
+ - **Card data NEVER touches the backend.** Checkout and Portal run hosted on Stripe.
19
+ *Verification:* `grep -rnE "card.*number|cvc|cvv" {{src-path}}/` returns 0.
20
+
21
+ > A server-side gate on every billing-restricted procedure is a genuine requirement — a
22
+ > frontend-only check is UX, not a boundary — but the exact accessor shape and its grep
23
+ > verification are one codebase's convention, not a Stripe fact. See
24
+ > `code-style/stripe-billing/naming.md` for that as an opt-in proposal.
25
+
26
+ ## Principles (insert in §1.3 — review, not a trigger)
27
+
28
+ - **A subscription-mutation procedure writes to the DB synchronously. It does not rely only on the webhook.** The webhook is canonical. The procedure is convergent. It uses upsert for idempotency. *(Review: each procedure that changes a subscription — a plan change, a cancellation — upserts the Subscription record after the Stripe call.)*
29
+ - **Before it mutates a subscription, the procedure checks whether a `SubscriptionSchedule` is attached and releases it first.** Stripe rejects a direct mutation on a subscription that has a schedule. *(Review: ask "can this procedure run on a subscription that carries a pending downgrade?".)*
30
+ - **The webhook verifies the signature BEFORE any lookup.** Without a valid signature, it does not touch the DB. *(Review: the Stripe SDK's signature-verification call is the first line of the route handler, before any DB read.)*
@@ -0,0 +1,64 @@
1
+ ---
2
+ type: module
3
+ description: 'Materializes the Task-like component type with Trigger.dev v4 vocabulary in the ACH.'
4
+ load: cold
5
+ stack: trigger-dev-v4
6
+ patches: 'docs/ach.md'
7
+ detect: |
8
+ if command -v jq >/dev/null 2>&1; then jq -e '(.dependencies//{})+(.devDependencies//{})+(.peerDependencies//{})|has("@trigger.dev/sdk")' package.json >/dev/null 2>&1; else grep -q '"@trigger.dev/sdk"' package.json 2>/dev/null; fi
9
+ ---
10
+
11
+ # Patch — ACH for Trigger.dev v4 projects
12
+
13
+ > Applies to `/docs/ach.md`. Materializes Task-like with Trigger.dev vocabulary.
14
+
15
+ ## §3.1 — Task-like → Trigger.dev v4 task
16
+
17
+ ```md
18
+ #### Task (Trigger.dev)
19
+
20
+ - **What it is:** an async, idempotent, retry-safe unit of work. The Trigger.dev v4 infrastructure runs it.
21
+ - **Single responsibility:** one workflow step, or a cron orchestrator. It reads canonical state from the DB. It returns early on terminal states. It updates the DB atomically. It triggers the next task when needed.
22
+ - **When to use it:**
23
+ - Cron (crawler's `schedules.task`).
24
+ - A long workflow that needs retry (build + publish).
25
+ - An external integration with variable latency or expected failure.
26
+ - **When NOT to use it:**
27
+ - The work fits in under 5 seconds with no retry. Use a procedure instead.
28
+ - The logic is pure with no I/O. Use domain instead.
29
+ - **Convention:** the task `id` matches the file name in kebab-case. Example: `start-deployment-workflow.ts` becomes `tasks.task("start-deployment-workflow", ...)`. This is Trigger.dev's own convention — the dashboard and CLI both key a run on it — so it stays here as fact, unlike the file-tree layout and import boundary below, which are one operator's choice.
30
+
31
+ ##### Pattern: a thin parent calls subtasks through `triggerAndWait`
32
+
33
+ A multi-stage workflow is a thin parent that coordinates, plus subtasks that do the work. Each subtask has its own `runId`, its own retry, its own log stream, and its own machine size. The parent only calls `triggerAndWait`.
34
+
35
+ Example topology:
36
+
37
+ ~~~
38
+ start-deployment-workflow (orchestrator — machine: small-1x)
39
+ ├── triggerAndWait("build-sdk", { buildId }) — machine: large-1x
40
+ └── triggerAndWait("publish-sdk", { publishId }) — machine: medium-1x
41
+ ~~~
42
+
43
+ Benefits: granular retry (publish alone, with no rebuild), isolated logs, a machine size tuned per stage, and composability (`retag-publish` reuses auth and the registry without a rebundle).
44
+ ```
45
+
46
+ > The task directory tree (`src/trigger/` with `cron-<X>.ts` / `<stage>-<entity>.ts` /
47
+ > `start-<entity>-workflow.ts`), the import boundary between tasks and the rest of the codebase,
48
+ > and the `createTriggerContext()` DB-access helper are house convention, not Trigger.dev facts —
49
+ > nothing about Trigger.dev requires this layout or this helper. They now live as an **opt-in
50
+ > seed**, proposed rather than imposed, at `code-style/trigger-dev-v4/naming.md` and
51
+ > `code-style/trigger-dev-v4/imports.md`. Materialize either only if the operator accepts it.
52
+
53
+ ## §6 — Trigger conventions
54
+
55
+ ```md
56
+ ### Trigger config (`trigger.config.ts`)
57
+
58
+ - `build.external` — deps that must NOT be bundled (for example, `tsdown` when used as a runtime subprocess).
59
+ - `machines` — sizing per task. The topology above sizes the orchestrator small, build large, and publish medium; size each stage against its own workload.
60
+ ```
61
+
62
+ > Where the project ID lives — pinned in `trigger.config.ts` versus read from env — is an
63
+ > operator call this module does not make either way; state it in the project's own docs if it
64
+ > matters there.
@@ -0,0 +1,53 @@
1
+ ---
2
+ type: module
3
+ description: 'Seed gotchas for Trigger.dev v4, starting with the run id changing between attempts of the same run.'
4
+ load: cold
5
+ stack: trigger-dev-v4
6
+ patches: 'docs/gotchas/*.md'
7
+ detect: |
8
+ if command -v jq >/dev/null 2>&1; then jq -e '(.dependencies//{})+(.devDependencies//{})+(.peerDependencies//{})|has("@trigger.dev/sdk")' package.json >/dev/null 2>&1; else grep -q '"@trigger.dev/sdk"' package.json 2>/dev/null; fi
9
+ ---
10
+
11
+ # Gotchas seed — Trigger.dev v4
12
+
13
+ > Each entry below is a complete gotcha file. Copy one to `docs/gotchas/NNN-slug.md` (the first `---` of the entry as line 1) only when it bites in real use. Re-date `last_bite` on copy.
14
+
15
+ ---
16
+ type: gotcha
17
+ description: 'ctx.run.id differs on each retry attempt, so a runId persisted to track one execution points at the last attempt instead.'
18
+ load: warm
19
+ fires_on: event
20
+ fires_when: 'you write a new Trigger.dev task, especially one that persists a runId in a row.'
21
+ bites: 1
22
+ last_bite: 2026-10
23
+ ---
24
+
25
+ ## Trigger.dev — `params.ctx.run.id` changes between attempts
26
+
27
+ ## Behavior
28
+
29
+ `ctx.run.id` differs on each retry attempt. A task that persists runId to track one execution loses the link on retry. It ends up pointing to the last run instead.
30
+
31
+ ## Solution
32
+
33
+ Make the runId that tracks a process **immutable after the first write**. Use `updateMany({ where: { id, runId: null }, data: { runId } })` for an atomic upsert. Keep canonical state in the DB, not in the run id.
34
+
35
+ ---
36
+ type: gotcha
37
+ description: 'Trigger.dev bundles everything a task imports, which breaks binaries the task runs as a subprocess unless they are listed in build.external.'
38
+ load: warm
39
+ fires_on: event
40
+ fires_when: 'you update Trigger.dev, edit trigger.config.ts, or remove a binary used as a subprocess at runtime.'
41
+ bites: 1
42
+ last_bite: 2026-10
43
+ ---
44
+
45
+ ## Trigger.dev — put `tsdown` (or similar) in `build.external`
46
+
47
+ ## Behavior
48
+
49
+ Trigger.dev tries to bundle everything a task imports. Binaries invoked as a subprocess (`tsdown`, `esbuild`, `rollup`) break when bundled. The task expects a file but receives a module instead.
50
+
51
+ ## Solution
52
+
53
+ Keep the binary listed in `trigger.config.ts` `build.external`. Document this in the config's comment.
@@ -0,0 +1,35 @@
1
+ ---
2
+ type: module
3
+ description: 'Hard rules for Trigger.dev v4 projects, split between executable triggers and review-only principles.'
4
+ load: cold
5
+ stack: trigger-dev-v4
6
+ patches: 'docs/rules/*.md'
7
+ detect: |
8
+ if command -v jq >/dev/null 2>&1; then jq -e '(.dependencies//{})+(.devDependencies//{})+(.peerDependencies//{})|has("@trigger.dev/sdk")' package.json >/dev/null 2>&1; else grep -q '"@trigger.dev/sdk"' package.json 2>/dev/null; fi
9
+ ---
10
+
11
+ # Patch — Hard rules for Trigger.dev v4 projects
12
+
13
+ > Hard rules (with an executable trigger) go in after the last hard rule in `/docs/afm.md` §3.
14
+ > Principles (verified by review only) go in `/docs/afm.md` §1.3.
15
+
16
+ ## Hard rules (executable trigger)
17
+
18
+ None. Both rules this module used to state as a hard, path-bound trigger — routing task DB
19
+ access through `createTriggerContext()` instead of `prismaClient` directly, and requiring an
20
+ idempotency test co-located with each task — are house convention, not Trigger.dev facts:
21
+ nothing about Trigger.dev requires either helper or that test layout. Worse, the first one gated
22
+ an invented helper at an invented path (`src/lib/trigger.ts`) as if it were framework law. Both
23
+ verifications also hardcoded one operator's `src/trigger/` directory, so they would fail
24
+ vacuously on any project laid out differently. They now live as an **opt-in seed**, proposed
25
+ rather than imposed, at `code-style/trigger-dev-v4/imports.md` (the context helper) and
26
+ `code-style/trigger-dev-v4/test-placement.md` (the idempotency test, plus the `runId`
27
+ atomic-upsert example that guards it). Materialize either only if the operator accepts it.
28
+
29
+ ## Principles (insert in §1.3 — review, not a trigger)
30
+
31
+ None currently. The `runId`-immutability principle that lived here was tied entirely to the
32
+ idempotency-test hard rule above and had no separate grep trigger of its own — it moved with
33
+ that rule to `code-style/trigger-dev-v4/test-placement.md`, as the worked example of the atomic
34
+ upsert. Add a principle here only for a genuine Trigger.dev fact that mechanical review, not a
35
+ grep, has to catch.
@@ -0,0 +1,56 @@
1
+ ---
2
+ type: module
3
+ description: 'Materializes the Procedure-like and Domain-like component types with tRPC v11 vocabulary in the ACH.'
4
+ load: cold
5
+ stack: trpc-v11
6
+ patches: 'docs/ach.md'
7
+ detect: |
8
+ if command -v jq >/dev/null 2>&1; then jq -e '(.dependencies//{})+(.devDependencies//{})+(.peerDependencies//{})|has("@trpc/server")' package.json >/dev/null 2>&1; else grep -q '"@trpc/server"' package.json 2>/dev/null; fi
9
+ ---
10
+
11
+ # Patch — ACH for tRPC v11 projects
12
+
13
+ > Apply this to `/docs/ach.md`. It materializes Procedure-like and Domain-like with tRPC vocabulary.
14
+
15
+ ## §3.1 — Procedure-like → tRPC procedure
16
+
17
+ ```md
18
+ #### Procedure (tRPC)
19
+
20
+ - **What it is:** a tRPC handler with Zod input, auth through middleware, and a typed return the frontend can consume.
21
+ - **Single responsibility:** orchestrate (< ~5s) — validate → authorize → call domain/lib → persist → respond.
22
+ - **When to use it:** every synchronous UI ↔ server interaction. REST `/api/v1/*` is derived through `trpc-to-openapi` when applicable.
23
+ ```
24
+
25
+ > Naming (export and file), the neighbor-test rule, and the subrouter's location are house
26
+ > convention, not a tRPC fact — a project can name and place these however it wants and the
27
+ > handler shape above still holds. They now live as an opt-in seed at
28
+ > `code-style/trpc-v11/naming.md` (naming) and `code-style/trpc-v11/test-placement.md`
29
+ > (test placement). Materialize either only if the operator accepts it.
30
+
31
+ ## §3.1 — Domain-like → function with `DomainInput<T>`
32
+
33
+ ```md
34
+ #### Domain (per module)
35
+
36
+ - **What it is:** pure business-rule functions. Signature: `({ ctx, input }: DomainInput<T>) => …`. Always use `{ ctx, input }`, even when `ctx` is unused, for consistency.
37
+ - **Domain does NOT import `TRPCError`.** `TRPCError` is a transport type — importing it in domain code couples business logic to the tRPC boundary. Use `DomainError({ code: "..." as const })` or `Result<T, E>` instead; the procedure maps the code to `TRPCError` at the boundary.
38
+ ```
39
+
40
+ > How many functions live in one file, and the file/export naming, are house convention — they
41
+ > now live as an opt-in seed at `code-style/trpc-v11/file-size.md` (one function per file) and
42
+ > `code-style/trpc-v11/naming.md` (export/file naming). A mechanical, path-bound check for the
43
+ > "no `TRPCError` in domain" principle above — which needs the project's actual domain-layer
44
+ > path — is also in the seed, at `code-style/trpc-v11/error-boundary.md`.
45
+
46
+ ## §6 — ISP through `Pick<TRPCContext, ...>`
47
+
48
+ ```md
49
+ - A domain function declares only the `ctx` fields it uses: `ctx: Pick<TRPCContext, "db" | "log">`. It does not receive the full `TRPCContext`. This keeps tests smaller and makes the dependency explicit.
50
+ ```
51
+
52
+ > The remaining house conventions this module used to state as fact — the subrouter's exact
53
+ > directory (`modules/<f>/router.ts`) and a specific `makeTestCtx` test helper naming one file
54
+ > path that exists in exactly one repo — are not a tRPC fact either. See
55
+ > `code-style/trpc-v11/naming.md` and `code-style/trpc-v11/test-placement.md` for the proposals
56
+ > that replace them.
@@ -0,0 +1,34 @@
1
+ ---
2
+ type: module
3
+ description: 'Hard rules for tRPC v11 projects, including domain code that never raises a transport error.'
4
+ load: cold
5
+ stack: trpc-v11
6
+ patches: 'docs/rules/*.md'
7
+ detect: |
8
+ if command -v jq >/dev/null 2>&1; then jq -e '(.dependencies//{})+(.devDependencies//{})+(.peerDependencies//{})|has("@trpc/server")' package.json >/dev/null 2>&1; else grep -q '"@trpc/server"' package.json 2>/dev/null; fi
9
+ ---
10
+
11
+ # Patch — Hard rules for tRPC v11 projects
12
+
13
+ > Hard rules (with an executable trigger) go after the last hard rule in `/docs/afm.md` §3.
14
+ > Principles (verified only by review) go in `/docs/afm.md` §1.3.
15
+
16
+ ## Hard rules (executable trigger)
17
+
18
+ None. Every rule this module used to state as a hard, path-bound trigger — the
19
+ `procedure_<action><Module>` / `domain_<action><Module>` export naming, and the ban on a
20
+ `__tests__/` folder — is house convention, not a tRPC fact: nothing about tRPC requires a
21
+ particular export prefix or test-file location. They now live as an **opt-in seed**, proposed
22
+ rather than imposed, at `code-style/trpc-v11/naming.md` and
23
+ `code-style/trpc-v11/test-placement.md`. Materialize either only if the operator accepts it.
24
+
25
+ ## Principles (insert into §1.3 — review, not a trigger)
26
+
27
+ - **A transport error type must not reach the domain layer.** `TRPCError` is tRPC-specific;
28
+ a domain function that imports it couples business logic to the transport boundary and makes
29
+ the domain layer harder to reuse outside tRPC (a queue worker, a CLI, a test). The procedure
30
+ maps a domain error code to `TRPCError` at the edge instead.
31
+ *(This is a genuine layering fact, but a mechanical check for it needs the project's actual
32
+ domain-layer path, which is house layout and varies by repo. A concrete, path-bound grep that
33
+ verifies it is proposed — not imposed — at `code-style/trpc-v11/error-boundary.md`.)*
34
+ - **A domain function uses `Pick<TRPCContext, ...>` instead of the full TRPCContext** when it needs only part of ctx. *(No grep can tell partial use from full use — this needs review. Flag a procedure that receives full ctx but uses only part of it.)*