create-flowdular 0.2.4 → 0.2.5

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 (269) hide show
  1. package/README.md +11 -0
  2. package/agent-template/.agents/skills/agent-tool-design/SKILL.md +203 -0
  3. package/agent-template/.agents/skills/auth-security-review/SKILL.md +90 -0
  4. package/agent-template/.agents/skills/auto-review/SKILL.md +103 -0
  5. package/agent-template/.agents/skills/bug-hunt/SKILL.md +104 -0
  6. package/agent-template/.agents/skills/business-agent-design/SKILL.md +182 -0
  7. package/agent-template/.agents/skills/cli-extension/SKILL.md +108 -0
  8. package/agent-template/.agents/skills/core-extend/SKILL.md +99 -0
  9. package/agent-template/.agents/skills/database-adapter/SKILL.md +198 -0
  10. package/agent-template/.agents/skills/database-adapter/references/first-run-and-matrix.md +105 -0
  11. package/agent-template/.agents/skills/migration-authoring/SKILL.md +161 -0
  12. package/agent-template/.agents/skills/module-new/SKILL.md +171 -0
  13. package/agent-template/.agents/skills/module-update/SKILL.md +91 -0
  14. package/agent-template/.agents/skills/perf-audit/SKILL.md +98 -0
  15. package/agent-template/.agents/skills/release-eject-pr/SKILL.md +107 -0
  16. package/agent-template/.agents/skills/spec-approval/SKILL.md +106 -0
  17. package/agent-template/.agents/skills/test-hardening/SKILL.md +79 -0
  18. package/agent-template/.agents/skills/translations-i18n/SKILL.md +78 -0
  19. package/agent-template/.agents/skills/ux-design/SKILL.md +92 -0
  20. package/agent-template/.agents/skills/variables/SKILL.md +156 -0
  21. package/agent-template/.agents/skills/workflow-development/SKILL.md +192 -0
  22. package/agent-template/.ai/README.md +62 -0
  23. package/agent-template/.ai/agents/README.md +27 -0
  24. package/agent-template/.ai/agents/module-executor.md +36 -0
  25. package/agent-template/.ai/agents/reviewer.md +23 -0
  26. package/agent-template/.ai/agents/sandbox/agentic-engineer.md +31 -0
  27. package/agent-template/.ai/agents/sandbox/backend-engineer.md +36 -0
  28. package/agent-template/.ai/agents/sandbox/business-manager.md +23 -0
  29. package/agent-template/.ai/agents/sandbox/frontend-engineer.md +27 -0
  30. package/agent-template/.ai/agents/sandbox/ux-designer.md +23 -0
  31. package/agent-template/.ai/agents/spec-author.md +29 -0
  32. package/agent-template/.ai/blueprints/add-migration/README.md +5 -0
  33. package/agent-template/.ai/blueprints/add-migration/allowed-paths.yaml +23 -0
  34. package/agent-template/.ai/blueprints/add-migration/blueprint.json +14 -0
  35. package/agent-template/.ai/blueprints/add-migration/examples/invalid/input-destructive.json +6 -0
  36. package/agent-template/.ai/blueprints/add-migration/examples/invalid/plan-unnumbered-file.json +9 -0
  37. package/agent-template/.ai/blueprints/add-migration/examples/valid/input.json +6 -0
  38. package/agent-template/.ai/blueprints/add-migration/examples/valid/plan.json +9 -0
  39. package/agent-template/.ai/blueprints/add-migration/gates.yaml +30 -0
  40. package/agent-template/.ai/blueprints/add-migration/input.schema.json +23 -0
  41. package/agent-template/.ai/blueprints/add-migration/plan.schema.json +54 -0
  42. package/agent-template/.ai/blueprints/add-migration/required-files.yaml +18 -0
  43. package/agent-template/.ai/blueprints/add-migration/spec-requirements.yaml +13 -0
  44. package/agent-template/.ai/blueprints/add-migration/steps.yaml +62 -0
  45. package/agent-template/.ai/blueprints/author-spec/README.md +5 -0
  46. package/agent-template/.ai/blueprints/author-spec/allowed-paths.yaml +7 -0
  47. package/agent-template/.ai/blueprints/author-spec/blueprint.json +14 -0
  48. package/agent-template/.ai/blueprints/author-spec/examples/invalid/input-missing-outcome.json +5 -0
  49. package/agent-template/.ai/blueprints/author-spec/examples/valid/input.json +6 -0
  50. package/agent-template/.ai/blueprints/author-spec/gates.yaml +13 -0
  51. package/agent-template/.ai/blueprints/author-spec/input.schema.json +20 -0
  52. package/agent-template/.ai/blueprints/author-spec/plan.schema.json +14 -0
  53. package/agent-template/.ai/blueprints/author-spec/required-files.yaml +6 -0
  54. package/agent-template/.ai/blueprints/author-spec/spec-requirements.yaml +35 -0
  55. package/agent-template/.ai/blueprints/author-spec/steps.yaml +28 -0
  56. package/agent-template/.ai/blueprints/author-spec/templates/module.yaml +45 -0
  57. package/agent-template/.ai/blueprints/bug-fix/README.md +5 -0
  58. package/agent-template/.ai/blueprints/bug-fix/allowed-paths.yaml +29 -0
  59. package/agent-template/.ai/blueprints/bug-fix/blueprint.json +14 -0
  60. package/agent-template/.ai/blueprints/bug-fix/examples/invalid/input-no-symptom.json +4 -0
  61. package/agent-template/.ai/blueprints/bug-fix/examples/invalid/plan-no-test.json +8 -0
  62. package/agent-template/.ai/blueprints/bug-fix/examples/valid/input.json +6 -0
  63. package/agent-template/.ai/blueprints/bug-fix/examples/valid/plan.json +8 -0
  64. package/agent-template/.ai/blueprints/bug-fix/gates.yaml +30 -0
  65. package/agent-template/.ai/blueprints/bug-fix/input.schema.json +25 -0
  66. package/agent-template/.ai/blueprints/bug-fix/plan.schema.json +53 -0
  67. package/agent-template/.ai/blueprints/bug-fix/required-files.yaml +7 -0
  68. package/agent-template/.ai/blueprints/bug-fix/spec-requirements.yaml +7 -0
  69. package/agent-template/.ai/blueprints/bug-fix/steps.yaml +51 -0
  70. package/agent-template/.ai/blueprints/core-extend/README.md +5 -0
  71. package/agent-template/.ai/blueprints/core-extend/allowed-paths.yaml +49 -0
  72. package/agent-template/.ai/blueprints/core-extend/blueprint.json +14 -0
  73. package/agent-template/.ai/blueprints/core-extend/examples/invalid/input-unknown-package.json +5 -0
  74. package/agent-template/.ai/blueprints/core-extend/examples/invalid/plan-missing-gates.json +7 -0
  75. package/agent-template/.ai/blueprints/core-extend/examples/valid/input.json +6 -0
  76. package/agent-template/.ai/blueprints/core-extend/examples/valid/plan.json +10 -0
  77. package/agent-template/.ai/blueprints/core-extend/gates.yaml +16 -0
  78. package/agent-template/.ai/blueprints/core-extend/input.schema.json +54 -0
  79. package/agent-template/.ai/blueprints/core-extend/plan.schema.json +39 -0
  80. package/agent-template/.ai/blueprints/core-extend/required-files.yaml +36 -0
  81. package/agent-template/.ai/blueprints/core-extend/spec-requirements.yaml +17 -0
  82. package/agent-template/.ai/blueprints/core-extend/steps.yaml +54 -0
  83. package/agent-template/.ai/blueprints/edit-module/README.md +9 -0
  84. package/agent-template/.ai/blueprints/edit-module/allowed-paths.yaml +27 -0
  85. package/agent-template/.ai/blueprints/edit-module/blueprint.json +20 -0
  86. package/agent-template/.ai/blueprints/edit-module/examples/invalid/input-unknown-change.json +5 -0
  87. package/agent-template/.ai/blueprints/edit-module/examples/invalid/plan-touches-platform.json +15 -0
  88. package/agent-template/.ai/blueprints/edit-module/examples/valid/input.json +5 -0
  89. package/agent-template/.ai/blueprints/edit-module/examples/valid/plan.json +25 -0
  90. package/agent-template/.ai/blueprints/edit-module/gates.yaml +30 -0
  91. package/agent-template/.ai/blueprints/edit-module/input.schema.json +31 -0
  92. package/agent-template/.ai/blueprints/edit-module/plan.schema.json +65 -0
  93. package/agent-template/.ai/blueprints/edit-module/required-files.yaml +80 -0
  94. package/agent-template/.ai/blueprints/edit-module/spec-requirements.yaml +15 -0
  95. package/agent-template/.ai/blueprints/edit-module/steps.yaml +115 -0
  96. package/agent-template/.ai/blueprints/new-module/README.md +7 -0
  97. package/agent-template/.ai/blueprints/new-module/allowed-paths.yaml +27 -0
  98. package/agent-template/.ai/blueprints/new-module/blueprint.json +20 -0
  99. package/agent-template/.ai/blueprints/new-module/examples/invalid/input-spec-outside-modules.json +4 -0
  100. package/agent-template/.ai/blueprints/new-module/examples/invalid/plan-unknown-gate.json +8 -0
  101. package/agent-template/.ai/blueprints/new-module/examples/valid/input.json +5 -0
  102. package/agent-template/.ai/blueprints/new-module/examples/valid/plan.json +22 -0
  103. package/agent-template/.ai/blueprints/new-module/gates.yaml +30 -0
  104. package/agent-template/.ai/blueprints/new-module/input.schema.json +21 -0
  105. package/agent-template/.ai/blueprints/new-module/plan.schema.json +58 -0
  106. package/agent-template/.ai/blueprints/new-module/required-files.yaml +73 -0
  107. package/agent-template/.ai/blueprints/new-module/spec-requirements.yaml +30 -0
  108. package/agent-template/.ai/blueprints/new-module/steps.yaml +138 -0
  109. package/agent-template/.ai/blueprints/release/README.md +5 -0
  110. package/agent-template/.ai/blueprints/release/allowed-paths.yaml +19 -0
  111. package/agent-template/.ai/blueprints/release/blueprint.json +14 -0
  112. package/agent-template/.ai/blueprints/release/examples/invalid/input-bad-version.json +4 -0
  113. package/agent-template/.ai/blueprints/release/examples/invalid/plan-bad-branch.json +7 -0
  114. package/agent-template/.ai/blueprints/release/examples/valid/input.json +5 -0
  115. package/agent-template/.ai/blueprints/release/examples/valid/plan.json +20 -0
  116. package/agent-template/.ai/blueprints/release/gates.yaml +20 -0
  117. package/agent-template/.ai/blueprints/release/input.schema.json +24 -0
  118. package/agent-template/.ai/blueprints/release/plan.schema.json +46 -0
  119. package/agent-template/.ai/blueprints/release/required-files.yaml +19 -0
  120. package/agent-template/.ai/blueprints/release/spec-requirements.yaml +8 -0
  121. package/agent-template/.ai/blueprints/release/steps.yaml +47 -0
  122. package/agent-template/.ai/blueprints/security-review/README.md +5 -0
  123. package/agent-template/.ai/blueprints/security-review/allowed-paths.yaml +6 -0
  124. package/agent-template/.ai/blueprints/security-review/blueprint.json +14 -0
  125. package/agent-template/.ai/blueprints/security-review/examples/invalid/input-unknown-kind.json +4 -0
  126. package/agent-template/.ai/blueprints/security-review/examples/invalid/plan-finding-without-scenario.json +14 -0
  127. package/agent-template/.ai/blueprints/security-review/examples/valid/input.json +4 -0
  128. package/agent-template/.ai/blueprints/security-review/examples/valid/plan.json +19 -0
  129. package/agent-template/.ai/blueprints/security-review/gates.yaml +22 -0
  130. package/agent-template/.ai/blueprints/security-review/input.schema.json +20 -0
  131. package/agent-template/.ai/blueprints/security-review/plan.schema.json +65 -0
  132. package/agent-template/.ai/blueprints/security-review/required-files.yaml +6 -0
  133. package/agent-template/.ai/blueprints/security-review/spec-requirements.yaml +9 -0
  134. package/agent-template/.ai/blueprints/security-review/steps.yaml +38 -0
  135. package/agent-template/.ai/examples/README.md +8 -0
  136. package/agent-template/.ai/examples/bad/client-imports-server/README.md +20 -0
  137. package/agent-template/.ai/examples/bad/client-imports-server/api.ts +12 -0
  138. package/agent-template/.ai/examples/bad/missing-acl/README.md +23 -0
  139. package/agent-template/.ai/examples/bad/missing-acl/endpoints.ts +12 -0
  140. package/agent-template/.ai/examples/bad/tenant-from-body/README.md +19 -0
  141. package/agent-template/.ai/examples/bad/tenant-from-body/endpoints.ts +33 -0
  142. package/agent-template/.ai/examples/client-contribution/CustomerListView.tsrx +34 -0
  143. package/agent-template/.ai/examples/client-contribution/README.md +11 -0
  144. package/agent-template/.ai/examples/client-contribution/contribution.tsrx +48 -0
  145. package/agent-template/.ai/examples/client-contribution/index.ts +20 -0
  146. package/agent-template/.ai/examples/client-contribution/permissions.ts +8 -0
  147. package/agent-template/.ai/examples/customer-cli-extension/README.md +14 -0
  148. package/agent-template/.ai/examples/customer-cli-extension/commands.json +17 -0
  149. package/agent-template/.ai/examples/customer-cli-extension/index.ts +36 -0
  150. package/agent-template/.ai/examples/module-create/task-packet.json +11 -0
  151. package/agent-template/.ai/guides/application-development.md +97 -0
  152. package/agent-template/.ai/policies/capabilities.yaml +164 -0
  153. package/agent-template/.ai/policies/model-routing.yaml +72 -0
  154. package/agent-template/.ai/policies/path-ownership.yaml +65 -0
  155. package/agent-template/.ai/policies/task-budgets.yaml +37 -0
  156. package/agent-template/.ai/references/catalog/LICENSE +21 -0
  157. package/agent-template/.ai/references/catalog/migrations/0001_catalog_core.down.sql +2 -0
  158. package/agent-template/.ai/references/catalog/migrations/0001_catalog_core.up.sql +21 -0
  159. package/agent-template/.ai/references/catalog/migrations/0002_catalog_history.down.sql +3 -0
  160. package/agent-template/.ai/references/catalog/migrations/0002_catalog_history.up.sql +20 -0
  161. package/agent-template/.ai/references/catalog/migrations/0003_catalog_history_service_actors.down.sql +3 -0
  162. package/agent-template/.ai/references/catalog/migrations/0003_catalog_history_service_actors.up.sql +36 -0
  163. package/agent-template/.ai/references/catalog/migrations/0004_catalog_idempotency_ledger.down.sql +3 -0
  164. package/agent-template/.ai/references/catalog/migrations/0004_catalog_idempotency_ledger.up.sql +19 -0
  165. package/agent-template/.ai/references/catalog/migrations/README.md +3 -0
  166. package/agent-template/.ai/references/catalog/module.json +27 -0
  167. package/agent-template/.ai/references/catalog/package.json +49 -0
  168. package/agent-template/.ai/references/catalog/spec/module.yaml +86 -0
  169. package/agent-template/.ai/references/catalog/src/acl/permissions.ts +6 -0
  170. package/agent-template/.ai/references/catalog/src/agent/tools.ts +164 -0
  171. package/agent-template/.ai/references/catalog/src/api/endpoints.ts +243 -0
  172. package/agent-template/.ai/references/catalog/src/client/CatalogHistoryDrawer.tsrx +123 -0
  173. package/agent-template/.ai/references/catalog/src/client/CatalogItemForm.tsrx +190 -0
  174. package/agent-template/.ai/references/catalog/src/client/CatalogView.tsrx +473 -0
  175. package/agent-template/.ai/references/catalog/src/client/api.ts +111 -0
  176. package/agent-template/.ai/references/catalog/src/client/contribution.tsrx +61 -0
  177. package/agent-template/.ai/references/catalog/src/client/index.ts +18 -0
  178. package/agent-template/.ai/references/catalog/src/client/navigation-copy.ts +9 -0
  179. package/agent-template/.ai/references/catalog/src/client/state.ts +24 -0
  180. package/agent-template/.ai/references/catalog/src/domain/types.ts +32 -0
  181. package/agent-template/.ai/references/catalog/src/domain/variables.ts +111 -0
  182. package/agent-template/.ai/references/catalog/src/index.ts +31 -0
  183. package/agent-template/.ai/references/catalog/src/platform.ts +35 -0
  184. package/agent-template/.ai/references/catalog/src/server/index.ts +4 -0
  185. package/agent-template/.ai/references/catalog/src/server/runtime.ts +86 -0
  186. package/agent-template/.ai/references/catalog/src/services/catalog-service.ts +306 -0
  187. package/agent-template/.ai/references/catalog/src/services/database-repository.ts +440 -0
  188. package/agent-template/.ai/references/catalog/src/services/index.ts +4 -0
  189. package/agent-template/.ai/references/catalog/src/services/migration.ts +171 -0
  190. package/agent-template/.ai/references/catalog/src/services/repository.ts +36 -0
  191. package/agent-template/.ai/references/catalog/src/services/target-idempotency.ts +59 -0
  192. package/agent-template/.ai/references/catalog/tests/agent-tools.test.ts +277 -0
  193. package/agent-template/.ai/references/catalog/tests/endpoints.test.ts +320 -0
  194. package/agent-template/.ai/references/catalog/tests/idempotency.test.ts +297 -0
  195. package/agent-template/.ai/references/catalog/tests/migrations.test.ts +149 -0
  196. package/agent-template/.ai/references/catalog/tests/module.test.ts +271 -0
  197. package/agent-template/.ai/references/catalog/tests/support/database.ts +76 -0
  198. package/agent-template/.ai/references/catalog/translations/en.json +101 -0
  199. package/agent-template/.ai/references/catalog/translations/pl.json +101 -0
  200. package/agent-template/.ai/references/catalog/tsconfig.json +15 -0
  201. package/agent-template/.ai/references/catalog/vitest.config.ts +16 -0
  202. package/agent-template/.ai/references/catalog.provenance.json +55 -0
  203. package/agent-template/.ai/rules/flowdular.md +86 -0
  204. package/agent-template/.ai/skills/README.md +36 -0
  205. package/agent-template/.ai/skills/agent-tool-design/SKILL.md +209 -0
  206. package/agent-template/.ai/skills/auth-security-review/SKILL.md +96 -0
  207. package/agent-template/.ai/skills/auto-review/SKILL.md +112 -0
  208. package/agent-template/.ai/skills/bug-hunt/SKILL.md +110 -0
  209. package/agent-template/.ai/skills/business-agent-design/SKILL.md +188 -0
  210. package/agent-template/.ai/skills/cli-extension/SKILL.md +114 -0
  211. package/agent-template/.ai/skills/core-extend/SKILL.md +104 -0
  212. package/agent-template/.ai/skills/database-adapter/SKILL.md +204 -0
  213. package/agent-template/.ai/skills/database-adapter/references/first-run-and-matrix.md +105 -0
  214. package/agent-template/.ai/skills/migration-authoring/SKILL.md +167 -0
  215. package/agent-template/.ai/skills/module-new/SKILL.md +180 -0
  216. package/agent-template/.ai/skills/module-update/SKILL.md +100 -0
  217. package/agent-template/.ai/skills/perf-audit/SKILL.md +105 -0
  218. package/agent-template/.ai/skills/release-eject-pr/SKILL.md +113 -0
  219. package/agent-template/.ai/skills/spec-approval/SKILL.md +112 -0
  220. package/agent-template/.ai/skills/test-hardening/SKILL.md +86 -0
  221. package/agent-template/.ai/skills/translations-i18n/SKILL.md +85 -0
  222. package/agent-template/.ai/skills/ux-design/SKILL.md +97 -0
  223. package/agent-template/.ai/skills/variables/SKILL.md +164 -0
  224. package/agent-template/.ai/skills/workflow-development/SKILL.md +199 -0
  225. package/agent-template/.claude/skills/agent-tool-design/SKILL.md +203 -0
  226. package/agent-template/.claude/skills/auth-security-review/SKILL.md +90 -0
  227. package/agent-template/.claude/skills/auto-review/SKILL.md +103 -0
  228. package/agent-template/.claude/skills/bug-hunt/SKILL.md +104 -0
  229. package/agent-template/.claude/skills/business-agent-design/SKILL.md +182 -0
  230. package/agent-template/.claude/skills/cli-extension/SKILL.md +108 -0
  231. package/agent-template/.claude/skills/core-extend/SKILL.md +99 -0
  232. package/agent-template/.claude/skills/database-adapter/SKILL.md +198 -0
  233. package/agent-template/.claude/skills/database-adapter/references/first-run-and-matrix.md +105 -0
  234. package/agent-template/.claude/skills/migration-authoring/SKILL.md +161 -0
  235. package/agent-template/.claude/skills/module-new/SKILL.md +171 -0
  236. package/agent-template/.claude/skills/module-update/SKILL.md +91 -0
  237. package/agent-template/.claude/skills/perf-audit/SKILL.md +98 -0
  238. package/agent-template/.claude/skills/release-eject-pr/SKILL.md +107 -0
  239. package/agent-template/.claude/skills/spec-approval/SKILL.md +106 -0
  240. package/agent-template/.claude/skills/test-hardening/SKILL.md +79 -0
  241. package/agent-template/.claude/skills/translations-i18n/SKILL.md +78 -0
  242. package/agent-template/.claude/skills/ux-design/SKILL.md +92 -0
  243. package/agent-template/.claude/skills/variables/SKILL.md +156 -0
  244. package/agent-template/.claude/skills/workflow-development/SKILL.md +192 -0
  245. package/agent-template/AGENTS.md +77 -0
  246. package/agent-template/CLAUDE.md +77 -0
  247. package/agent-template/docs/adr/0001-development-reload.md +16 -0
  248. package/agent-template/docs/adr/0002-durable-agent-execution.md +21 -0
  249. package/agent-template/docs/adr/0003-module-settings.md +22 -0
  250. package/agent-template/docs/adr/0004-enterprise-access-and-audit.md +36 -0
  251. package/agent-template/docs/adr/0005-sandbox-runtime-and-coding-agents.md +81 -0
  252. package/agent-template/docs/adr/0006-agentic-workflows.md +1702 -0
  253. package/agent-template/docs/adr/0007-module-owned-agents.md +429 -0
  254. package/agent-template/docs/adr/0008-database-adapter-contract.md +90 -0
  255. package/agent-template/docs/agent-contract.md +45 -0
  256. package/agent-template/docs/configuration.md +122 -0
  257. package/agent-template/docs/database-adapters.md +346 -0
  258. package/agent-template/docs/design-system.md +217 -0
  259. package/agent-template/docs/modules.md +146 -0
  260. package/agent-template/platform/scripts/build.mjs +38 -0
  261. package/agent-template/rulesync.jsonc +11 -0
  262. package/dist/bin.js +3 -1
  263. package/package.json +3 -2
  264. package/template/default/.prettierignore +9 -0
  265. package/template/default/README.md +12 -0
  266. package/template/default/flowdular.json +3 -3
  267. package/template/default/package.json +6 -2
  268. package/template/default/platform/octane.config.ts +17 -6
  269. package/template/default/platform/package.json +2 -1
@@ -0,0 +1,188 @@
1
+ ---
2
+ name: business-agent-design
3
+ description: >-
4
+ Ship a module-owned business agent with defineAgent, an exact tool ceiling,
5
+ tenant provider binding, retained revisions, and tests. Use for business
6
+ automation delivered by a module, not for sandbox coding specialists.
7
+ roles:
8
+ - agentic-engineer
9
+ - backend-engineer
10
+ - module-executor
11
+ when: A module should provide a ready business agent that tenants can configure and run.
12
+ ---
13
+
14
+ # Design a module-owned business agent
15
+
16
+ `defineAgent()` describes a business agent shipped by a module. It is the same
17
+ kind of business agent that appears in `agents.core`, but its behavior is owned
18
+ by module source. It is not a sandbox specialist, coding role, `.ai` skill, or
19
+ permission grant. Read `docs/adr/0007-module-owned-agents.md` and the approved
20
+ module spec before editing.
21
+
22
+ If a required tool is missing, pause this phase and hand it off as a separate
23
+ `agent-tool-design` task. A business agent can use only registered tools.
24
+
25
+ ## Ownership split
26
+
27
+ The module owns:
28
+
29
+ - the stable module id and agent key;
30
+ - name, description, instructions, and positive definition revision;
31
+ - the maximum exact tool allowlist;
32
+ - maximum steps, timeout, temperature, and output-token limits.
33
+
34
+ The tenant owns a separate binding in `agents.core`:
35
+
36
+ - provider connection and model;
37
+ - active or paused state;
38
+ - an enabled-tool subset that can narrow the module allowlist;
39
+ - optimistic binding revision and the resulting executable revision.
40
+
41
+ Never put provider ids, model ids, credentials, tenant ids, or tenant-specific
42
+ instructions in module source. The Agents UI presents module behavior as
43
+ read-only and lets an authorized tenant manager configure only the binding.
44
+
45
+ ## Define and register
46
+
47
+ Declare `agents.core` in both `spec/module.yaml` and `module.json` dependencies,
48
+ and add `"@flowdular/sdk/modules/agents": "workspace:*"` to `package.json`. Keep the
49
+ import server-only.
50
+
51
+ ```ts
52
+ // src/agent/agents.ts
53
+ import { defineAgent } from '@flowdular/sdk/modules/agents/server';
54
+
55
+ export const catalogCurator = defineAgent({
56
+ moduleId: 'catalog.core',
57
+ key: 'catalog-curator',
58
+ definitionRevision: 1,
59
+ name: 'Catalog curator',
60
+ description: 'Reviews and normalizes catalog records.',
61
+ instructions:
62
+ 'Review the requested catalog records. Use only the tools available to you.',
63
+ allowedTools: ['catalog.item.create', 'catalog.item.list'],
64
+ limits: {
65
+ maxSteps: 8,
66
+ timeoutMs: 120_000,
67
+ temperature: 0.2,
68
+ maxOutputTokens: 4_096,
69
+ },
70
+ });
71
+
72
+ export const catalogBusinessAgents = [catalogCurator] as const;
73
+ ```
74
+
75
+ ```ts
76
+ // src/platform.ts, during createServerComposition
77
+ context.agentDefinitions.register(catalogBusinessAgents);
78
+ ```
79
+
80
+ Registration happens during composition. The platform seals
81
+ `agentDefinitions` before any module `start()` hook. A malformed definition,
82
+ duplicate id, wildcard tool, registration after sealing, code downgrade, or
83
+ same-revision content drift fails boot. Do not catch and hide these errors.
84
+
85
+ `defineAgent()` derives the opaque id
86
+ `module-agent:<moduleId>:<key>`, validates the fields, sorts the exact tool ids,
87
+ and freezes the result. Callers do not construct or parse the derived id.
88
+
89
+ ## Authority is an intersection
90
+
91
+ For a module-owned agent, the tools visible to a run are exactly:
92
+
93
+ ```text
94
+ code allowedTools
95
+ intersect tenant binding enabledTools
96
+ intersect invocation toolGrants
97
+ intersect registered tools allowed by the actor's saved ceiling
98
+ intersect registered tools allowed by the actor's live permissions
99
+ ```
100
+
101
+ Every id is exact. An omitted grant means no tools. `*`, prefixes, and implicit
102
+ all-tools behavior are invalid. Every tool independently declares its
103
+ `requiredPermissions`; instructions and skills never grant authority. A scope
104
+ revoked after enqueue is rechecked before the tool body runs. The model never
105
+ receives `PlatformCapabilityRegistry` or direct service, repository, database,
106
+ shell, filesystem, or credential access.
107
+
108
+ The module allowlist is a permanent ceiling for that definition revision. The
109
+ tenant may reduce it, and each caller may reduce it again. A caller cannot
110
+ broaden it. Keep the list to the smallest surface needed for the stated job.
111
+
112
+ Mutating tools also follow the durable idempotency contract in
113
+ `agent-tool-design`. Do not add a write tool to a business agent until the tool
114
+ has its target-side ledger, transaction, replay test, and
115
+ `idempotencyProtection: 'target-ledger'` declaration.
116
+
117
+ ## Revisions and workflows
118
+
119
+ Increase `definitionRevision` whenever any executable module-owned content
120
+ changes: instructions, display copy, allowed tools, or limits. Never reuse a
121
+ revision with different content and never decrement it.
122
+
123
+ Binding a provider or model, changing enabled tools, or reconciling a higher
124
+ module definition creates a new immutable tenant executable revision. Runs and
125
+ published workflows pin that executable revision, not the code definition or
126
+ mutable binding revision. Old retained revisions and audit evidence survive an
127
+ upgrade or module removal. A removed module-owned agent becomes unavailable for
128
+ new work.
129
+
130
+ An unconfigured module agent remains visible but cannot run or be published in
131
+ a workflow. Do not choose a provider or model automatically to make setup look
132
+ complete.
133
+
134
+ ## Spec and files
135
+
136
+ The approved spec states:
137
+
138
+ - the business outcome and refusal conditions;
139
+ - each exact tool and required permission;
140
+ - that tenant binding cannot broaden the module ceiling;
141
+ - unavailable, unconfigured, revision, and module-removal behavior where
142
+ relevant.
143
+
144
+ Typical files are:
145
+
146
+ - `src/agent/agents.ts` for definitions;
147
+ - `src/agent/tools.ts` for module tools;
148
+ - `src/platform.ts` for both registry calls;
149
+ - `tests/business-agents.test.ts` and `tests/agent-tools.test.ts`;
150
+ - `spec/module.yaml`, `module.json`, and `package.json` for dependencies and
151
+ the coordinated version bump.
152
+
153
+ ## Tests
154
+
155
+ The business module proves:
156
+
157
+ 1. The definition has the expected derived id, ownership, revision, limits,
158
+ and exact sorted tool ceiling, and is frozen.
159
+ 2. Every allowed tool is registered by the module or a declared dependency.
160
+
161
+ The shared `agents.core` integration suite proves:
162
+
163
+ 1. A tenant binding can reduce tools but cannot add one outside the code
164
+ ceiling.
165
+ 2. A run without an invocation grant or required live scope never calls the
166
+ tool body and records a denial.
167
+ 3. Two tenants can bind different providers, models, and tool subsets without
168
+ seeing each other's binding or runs.
169
+ 4. A higher definition revision retains the old executable revision; a
170
+ downgrade and same-revision drift refuse startup.
171
+ 5. Module absence blocks new runs while retained run and workflow evidence
172
+ remains readable.
173
+
174
+ Use the module's isolated test provider for persistence tests. The end-to-end access intersection
175
+ belongs in `modules/agents/tests`, while a business module proves its own
176
+ definition and tool behavior locally. Do not edit `agents.core` merely to
177
+ duplicate its platform contract tests. Run the module tests, typecheck, spec
178
+ and module validation, then `pnpm verify` before delivery.
179
+
180
+ ## Refuse
181
+
182
+ - Treating a sandbox coding specialist as a business agent definition.
183
+ - Letting a tenant edit module-owned instructions or the code tool ceiling.
184
+ - Wildcard tools, tenant ids in model input, or authority derived from prompts.
185
+ - Code-pinned provider connections, models, credentials, or secrets.
186
+ - Registration outside `createServerComposition` or after registry sealing.
187
+ - A write tool without target-side durable idempotency.
188
+ - Reusing a definition revision after changing executable content.
@@ -0,0 +1,114 @@
1
+ ---
2
+ name: cli-extension
3
+ description: >-
4
+ Add a module-owned CLI command through commands.json and defineCliExtension,
5
+ with the namespace, risk, approval, and dry-run rules the runner enforces.
6
+ roles:
7
+ - backend-engineer
8
+ - module-executor
9
+ - reviewer
10
+ when: A module needs an operator command (status, export, grant, verify) reachable as pnpm flowdular <namespace> <action>.
11
+ ---
12
+
13
+ # Add a module CLI command
14
+
15
+ Examples to copy: `modules/agents/src/cli/{commands.json,index.ts}` (read plus a `localOnly` verifier) and `modules/auth/src/cli/{commands.json,index.ts}` (`process` with dry run, `destructive` with confirmation). Small template: `.ai/examples/customer-cli-extension`.
16
+
17
+ ## 1. Declare the capability
18
+
19
+ `module.json`: add `"cli"` to `capabilities` and
20
+
21
+ ```json
22
+ "cli": { "catalog": "src/cli/commands.json", "entry": "src/cli/index.ts" }
23
+ ```
24
+
25
+ `packages/contracts/schemas/module.schema.json` requires the `cli` block when the capability is present and the capability when the block is present. The spec `capabilities` list gets `cli` too.
26
+
27
+ ## 2. Catalog: `src/cli/commands.json`
28
+
29
+ ```json
30
+ {
31
+ "protocolVersion": 1,
32
+ "moduleId": "inventory.core",
33
+ "commands": [
34
+ {
35
+ "path": ["inventory", "export"],
36
+ "capability": {
37
+ "id": "inventory.export",
38
+ "version": 1,
39
+ "summary": "Export tenant-scoped stock locations to a workspace path.",
40
+ "risk": "workspace-write",
41
+ "requiresApprovedSpec": true,
42
+ "supportsDryRun": true
43
+ }
44
+ }
45
+ ]
46
+ }
47
+ ```
48
+
49
+ Rules enforced by `packages/cli/src/extensions.ts` (`validateCliCatalog`, `loadCliExtensions`):
50
+
51
+ - `moduleId` equals `module.json` `id`; the module must be enabled in `flowdular.json`, otherwise its commands do not load.
52
+ - `path` has at least two segments matching `^[a-z][a-z0-9-]*$`; the first segment equals the first segment of the module id (`inventory` for `inventory.core`) and is not a reserved group (`help`, `doctor`, `capability`, `spec`, `blueprint`, `module`, `setup`).
53
+ - `capability.id` starts with `<namespace>.`, is unique across all enabled modules, `version` is an integer >= 1, `summary` 1 to 240 characters.
54
+ - `risk` is one of `read`, `workspace-write`, `process`, `external`, `destructive`. A `destructive` capability with `localOnly: true` must also set `confirmation` (`^[a-z][a-z0-9-]{2,63}$`) and `supportsDryRun: true` (schema `allOf` in `cli-extension.schema.json`).
55
+
56
+ ## 3. Implementation: `src/cli/index.ts`
57
+
58
+ ```ts
59
+ import { defineCliExtension } from '@flowdular/sdk/cli-protocol';
60
+
61
+ export const cliExtension = defineCliExtension({
62
+ protocolVersion: 1,
63
+ moduleId: 'inventory.core',
64
+ commands: [
65
+ {
66
+ path: ['inventory', 'export'],
67
+ capability: {
68
+ id: 'inventory.export',
69
+ version: 1,
70
+ summary: 'Export tenant-scoped stock locations to a workspace path.',
71
+ risk: 'workspace-write' as const,
72
+ requiresApprovedSpec: true,
73
+ supportsDryRun: true,
74
+ },
75
+ execute: async (context) => ({
76
+ data: {
77
+ applied: context.apply,
78
+ target: context.arguments[0] ?? 'json',
79
+ },
80
+ evidence: ['modules/inventory/spec/module.yaml'],
81
+ warnings: context.apply ? [] : ['Dry run only.'],
82
+ }),
83
+ },
84
+ ],
85
+ });
86
+
87
+ export default cliExtension;
88
+ ```
89
+
90
+ `execute(context: { workspaceRoot, moduleRoot, apply, flags: ReadonlyMap<string, string | boolean>, arguments: readonly string[] })` returns `{ data, evidence?, warnings? }` (`packages/cli-protocol/src/index.ts`). The loader (`loadCliCommand`) imports the entry only when the command runs, accepts a `default` or `cliExtension` export, and refuses when `path` or the whole `capability` object differs from the catalog (`commandKey` compares the JSON). Keep both files metadata-identical, down to the summary text. Declare `@flowdular/sdk/cli-protocol` in `package.json`. Read module data through the module's own runtime (`xRuntimeOptionsFromEnvironment(process.env, context.workspaceRoot)` then the service), as `modules/agents/src/cli/index.ts` does; never through another module's database.
91
+
92
+ ## 4. What the runner does with the descriptor (`packages/cli/src/runner.ts`, `runExtensionCommand`)
93
+
94
+ - `risk: 'external'`: refused with `APPROVAL_VERIFIER_REQUIRED`. `risk: 'destructive'` without `localOnly`: the same.
95
+ - `localOnly: true`: refused with `LOCAL_ONLY_CAPABILITY` unless `FD_ENV` or `NODE_ENV` is `development` or `test` (unset counts as development).
96
+ - `requiresApprovedSpec: true`: needs `--spec <path>` to a schema-valid spec with `status: approved`, otherwise `APPROVED_SPEC_REQUIRED`, `SPEC_VALIDATION_FAILED` or `SPEC_NOT_APPROVED`.
97
+ - `destructive` with `--apply`: needs `--confirm <confirmation>` (`CONFIRMATION_REQUIRED`).
98
+ - Non-read without `supportsDryRun` and without `--apply`: `EXPLICIT_APPLY_REQUIRED`. Non-read with dry run support and no `--apply` runs with `apply: false` and appends the warning `Dry run only. No writes were authorized.`
99
+ - Invocation: `pnpm flowdular inventory export --apply` or `pnpm flowdular capability run inventory.export --apply`; `arguments` are the positionals after the path (or after `capability run <id>`); flags are `--name value`, `--name=value`, or `--flag` (`packages/cli/src/arguments.ts`). `pnpm flowdular capability list` and `describe <id>` show the descriptor; `pnpm flowdular help` lists module paths.
100
+
101
+ ## 5. Tests
102
+
103
+ `packages/cli/tests/extensions.test.ts` shows the style: call `validateCliCatalog(catalog, manifest)` with a good catalog and with a path that claims a reserved group, assert the error text. In the module, test `execute` directly with a hand-built context (`apply: false` returns the dry-run shape, `apply: true` writes inside `context.workspaceRoot` only). Then `pnpm flowdular module validate --json` and `pnpm flowdular help` (the new path appears once the module is enabled).
104
+
105
+ ## 6. Landing
106
+
107
+ Sandbox sessions strip `cli` from other modules' manifests and do not run module commands; the CLI parts of a module are exercised after eject or at the repository root. Both paths end with `pnpm verify` and a PR; `.ai/policies/capabilities.yaml` lists module capabilities, add yours.
108
+
109
+ ## Pitfalls
110
+
111
+ - A `summary` edited in one file only: `CLI implementation for "inventory export" does not match its catalog.`
112
+ - Two enabled modules claiming the same `path` or capability id: `CLI command collision`.
113
+ - `risk: 'read'` commands run without `--apply`; anything that writes must not be `read`.
114
+ - Commands run in the developer's process with the full environment; never print secrets in `data`.
@@ -0,0 +1,104 @@
1
+ ---
2
+ name: core-extend
3
+ description: >-
4
+ Change a platform package (contracts, kernel, server, client, ui, cli,
5
+ sandbox, coding-agent) without breaking the modules and generated files that
6
+ depend on it.
7
+ roles:
8
+ - module-executor
9
+ - reviewer
10
+ when: A change is needed under packages/**, platform/**, or the schemas in packages/contracts, and no module-level change can deliver it.
11
+ ---
12
+
13
+ # Extend the platform core
14
+
15
+ This work runs at the repository root; a sandbox session cannot do it (the session workspace holds one module plus read-only `reference/` copies). When a sandbox role needs a core change, it stops with `HANDOFF: none - <the exact core change>` and this skill picks it up.
16
+
17
+ ## 1. Dependency direction
18
+
19
+ `packages/contracts` (types and JSON schemas, no runtime) -> `packages/kernel` (registry, ACL, settings) -> `packages/server`, `packages/client`, `packages/ui` -> modules -> `platform` (composition) and `packages/cli`, `packages/sandbox`, `packages/coding-agent`, `packages/harness`, `packages/ai-provider`. A lower layer never imports a higher one. `platform/octane.config.ts` imports `@flowdular/sdk/modules/auth/server` and the generated `modules.server.ts`; nothing else in `packages/` may import a module. `@flowdular/sdk/modules/auth/server` is effectively part of the server contract: `PlatformServerContext` and `PlatformServerComposition` live in `modules/auth/src/server/composition.ts`.
20
+
21
+ ## 2. Surfaces every module and every agent sees
22
+
23
+ Keep these stable or migrate every consumer in the same change. `packages/sandbox/src/server/reference.ts` copies them into every session, so agents code against them:
24
+
25
+ - `packages/server/src/index.ts`: `defineEndpoint`, `HttpProblem`, `jsonResponse`, `problemResponse`, `readJsonObject`, `requiredString`, `optionalString`, `requiredInteger`, types `EndpointIdentity`, `EndpointExecutionContext`.
26
+ - `packages/client/src/contributions.ts`: `ModuleClientContext`, `ModuleClientContribution`, `WORKSPACE_SLOTS`, `NavigationGroup`, `createClientContributionRegistry`. `packages/client/src/state.ts`.
27
+ - `packages/contracts/src/index.ts` and `packages/contracts/schemas/*.json`.
28
+ - `packages/ui/src/index.ts`, `packages/ui/src/components/*.tsrx`, `packages/ui/src/styles/components.css`.
29
+ - `modules/auth/src/{index.ts,acl/scopes.ts,domain/types.ts,server/index.ts,services/auth-service.ts}`.
30
+ - `AGENTS.md`, `docs/design-system.md`, and `.ai/skills/**` (copied to `reference/skills/`).
31
+
32
+ ## 3. Checklists by change type
33
+
34
+ Schema change (`packages/contracts/schemas/*.schema.json`):
35
+
36
+ 1. Edit the schema and the matching type in `packages/contracts/src/index.ts`.
37
+ 2. Update `packages/cli/src/module-scaffold.ts` so a fresh module satisfies the schema, and its test `packages/cli/tests/module-scaffold.test.ts`.
38
+ 3. Update every `modules/*/module.json` or `modules/*/spec/module.yaml` the change affects, and the `.ai/blueprints/*/required-files.yaml`, `spec-requirements.yaml` and the business manager prompt when keys change.
39
+ 4. `pnpm validate` (spec, blueprint, module validation) and `pnpm test`.
40
+
41
+ Server or auth contract (`packages/server`, `modules/auth/src/server/composition.ts`): change the type, then every `src/platform.ts` and `src/api/endpoints.ts` under `modules/`, then `packages/cli/src/module-sync.ts` if the generated composition shape changes, then `platform/octane.config.ts`. Adding a hook to `PlatformServerComposition` (for example an optional `agentTools`) must stay optional so existing modules compile.
42
+
43
+ Client or UI primitive: add the component to `packages/ui/src/components/`, export it from `packages/ui/src/index.ts`, add its classes to `packages/ui/src/styles/components.css` with tokens only, document props and classes in `docs/design-system.md`, and delete the module-local promotion candidate it replaces. An icon is one path in `ICON_PATHS` (`packages/ui/src/icons/Icon.tsrx`), 24x24 stroke geometry.
44
+
45
+ CLI: commands are dispatched in `packages/cli/src/runner.ts`; a new core capability is a descriptor in `packages/cli/src/capabilities.ts` (`id`, `version`, `summary`, `risk`, `requiresApprovedSpec`, `supportsDryRun`); flags are parsed by `packages/cli/src/arguments.ts` (`--name value` or `--name=value`, `--flag`); `reservedGroups` in `packages/cli/src/extensions.ts` protects core groups from module namespaces; `pnpm flowdular help` output lists the commands. Tests in `packages/cli/tests`. Update `.ai/policies/capabilities.yaml` and `README.md`.
46
+
47
+ Sandbox and coding agent: gate ids live in `packages/sandbox/src/server/gates.ts` (`GateId`, `GATE_DEFINITIONS`) and must match the `gates:` front matter of `.ai/agents/sandbox/*.md`; role defaults in `packages/coding-agent/src/roles/defaults.ts` are regenerated from those files (sync script in `.ai/README.md`), never hand-edited; the instruction contract is `packages/coding-agent/src/roles/contract.ts`. Reference copies for sessions: `packages/sandbox/src/server/reference.ts` `REFERENCE_SOURCES`.
48
+
49
+ Module and blueprint discovery: `packages/cli/src/validation.ts` `findNamedFiles` walks the workspace for `module.json`, `module.yaml` and `blueprint.json`, skipping `node_modules`, `dist` and tool state directories; check its skip list before adding a new discoverable file type.
50
+
51
+ ## 3b. Worked example: how the optional composition members landed
52
+
53
+ The agent-tool hook, capability registry and module settings show the shape of a safe contract change (`modules/auth/src/server/composition.ts`, `packages/kernel/src/{capability-registry,tool-registry,module-settings}.ts`, `platform/octane.config.ts`):
54
+
55
+ ```ts
56
+ export interface PlatformServerContext {
57
+ readonly environment: NodeJS.ProcessEnv;
58
+ readonly workspaceRoot: string;
59
+ readonly auth: AuthRuntime;
60
+ readonly settings: ModuleSettingsRuntime; // live, tenant-scoped reads
61
+ readonly agentTools: PlatformToolRegistry; // register(tools), list()
62
+ readonly agentDefinitions: PlatformAgentRegistry; // register(definitions), list()
63
+ readonly capabilities: PlatformCapabilityRegistry; // register(id, service), get(id), has(id)
64
+ }
65
+
66
+ export interface PlatformServerComposition {
67
+ readonly routes: readonly ServerRoute[];
68
+ readonly settings?: ModuleSettingsDeclaration; // declared by the platform after composing
69
+ readonly prepare?: () => void | Promise<void>; // read-only checks before HMR activation
70
+ readonly start?: () => void; // called after every module composed
71
+ readonly stop?: () => void | Promise<void>; // drains background work before disposal
72
+ readonly dispose?: () => void | Promise<void>; // releases owned resources
73
+ }
74
+ ```
75
+
76
+ New context members are required (every module receives them; nobody has to read them), new composition members are optional (existing modules compile unchanged). The platform composes modules in dependency order, binds each `agentDefinitions` registrar to that module id, declares every `settings`, seals the definitions, runs every `prepare`, retires the old generation, and then calls every `start`. Retirement completes every `stop` before any `dispose`, so background work cannot outlive a repository it uses. The generic registries live in `@flowdular/sdk/kernel` so `modules/auth` does not import the harness or a provider module. `agentTools` carries model-visible tool identities; `agentDefinitions` carries immutable module-owned business agent behavior; `capabilities` carries typed public services between modules, with the provider owning the service type and the consumer declaring the module dependency and handling absence from `get`.
77
+
78
+ ## 4. Generated and composed files
79
+
80
+ `platform/src/generated/modules.server.ts` and `modules.client.ts` are written by `pnpm flowdular module sync --apply` (also run by `pnpm dev` and `pnpm build`). `flowdular.json` `modules.enabled` and `platform/package.json` dependencies are written by `module enable --apply`. Never edit them by hand; change the generator and regenerate. `packages/ui/src/brand/mark.ts` is generated by `node packages/ui/scripts/gen-mark.mjs`.
81
+
82
+ ## 5. Verification
83
+
84
+ ```bash
85
+ pnpm verify # typecheck, test, validate, format:check
86
+ pnpm build # cli build and smoke, module sync --apply, platform build
87
+ ```
88
+
89
+ Run the affected package alone while iterating: `pnpm --filter @flowdular/<pkg> test`. A change to `packages/ui` or `packages/client` also needs `pnpm --filter @flowdular/platform typecheck` and a look at the shell in `pnpm dev`.
90
+
91
+ After implementation and these checks, switch to `auto-review` as a separate
92
+ read-only phase before declaring completion. Fix findings in an implementation
93
+ phase, rerun affected checks, and repeat the review.
94
+
95
+ ## 6. Do not build on dead code
96
+
97
+ `RegisteredModule.navigation` in `packages/contracts` is declared by modules but never read at run time (the shell reads `ModuleClientContribution.navigation`). `validateTaskPacket` and `packages/harness/schemas/task-packet.schema.json` have no runtime caller. Module translations load through `ModuleClientContribution.translations` and the shared client i18n registry; do not introduce a second loader. Extend the live path or remove the dead one in its own change; do not add a third variant.
98
+
99
+ ## Pitfalls
100
+
101
+ - A new required key in `module.schema.json` breaks every module manifest and the scaffold at once; ship it optional first.
102
+ - Changing an error code string (`UNAUTHENTICATED`, `FORBIDDEN`, `CSRF_REJECTED`) breaks module tests that assert it.
103
+ - `packages/ui/src/index.ts` imports fonts and `styles/index.css` at module top; a test that imports `@flowdular/sdk/ui` needs a DOM environment.
104
+ - Prettier uses tabs and `@tsrx/prettier-plugin`; run `pnpm format` before `format:check`.
@@ -0,0 +1,204 @@
1
+ ---
2
+ name: database-adapter
3
+ description: >-
4
+ Build a Flowdular module repository on the asynchronous @flowdular/sdk/database
5
+ contract: PostgreSQL everywhere, embedded PGlite for local and test runs,
6
+ provider leases, forced row-level security, migrations, and tenant isolation
7
+ tests.
8
+ roles:
9
+ - backend-engineer
10
+ - module-executor
11
+ - reviewer
12
+ when: A module needs a repository on the shared database provider, a new table, a cross-tenant read, or a migration.
13
+ ---
14
+
15
+ # Use the database adapter contract
16
+
17
+ Read `docs/database-adapters.md`, `packages/database/src/contracts.ts`, and the
18
+ converted `modules/profile` repository before editing. This skill is for coding
19
+ agents. It is unrelated to tenant-defined Procedures in `agents.core`.
20
+ For a driver adapter, first-run setup, adapter switch, or delivery matrix, also
21
+ read [references/first-run-and-matrix.md](references/first-run-and-matrix.md).
22
+
23
+ ## 1. Keep three layers separate
24
+
25
+ 1. The business repository port is database-agnostic. Domain types, services,
26
+ errors, and callers never import a driver or branch on a dialect.
27
+ 2. The module owns persistence for every dialect it declares: explicit queries,
28
+ row mapping, error normalization, migrations, and contract tests.
29
+ 3. Platform composition owns driver adapters and `DatabaseProvider`: paths,
30
+ credentials, pools, TLS, timeouts, and disposal. A module never receives a
31
+ DSN and never creates a production pool.
32
+
33
+ `DatabaseProvider.acquire({ namespace, purpose })` returns a lease. The module
34
+ uses `lease.database` and releases only that lease. `DatabaseHandle` exposes
35
+ async operations, open `adapterId` and `dialectId`, capabilities, transactions,
36
+ and schema introspection. A callback-scoped transaction expires on return.
37
+
38
+ The platform runs on PostgreSQL. A deployment points at a server; a
39
+ workstation, a preview and a test suite get the same PostgreSQL embedded in the
40
+ process through PGlite, so there is nothing to install and no second dialect to
41
+ keep in step. Modules write PostgreSQL and only PostgreSQL. The contract still
42
+ carries an open `dialectId` and capability negotiation so a future driver can
43
+ join, but never add a core exhaustive switch that must be edited for each one.
44
+
45
+ ## 2. Make the whole repository chain asynchronous
46
+
47
+ Changing only the driver is not a conversion. Update every method in the chain:
48
+
49
+ 1. `src/services/repository.ts` returns `Promise<T>` or `Promise<void>`.
50
+ 2. The database repository awaits every `query`, `execute`, transaction, and
51
+ migration call.
52
+ 3. Services await repository methods. Preserve validation and domain error
53
+ codes at this boundary.
54
+ 4. Endpoints, agent tools, capabilities, background jobs, and tests await the
55
+ service.
56
+ 5. Remove synchronous assumptions such as returning a write input before the
57
+ database confirms it.
58
+
59
+ Search every caller of the repository interface before changing it. A missed
60
+ caller can compile through an inferred promise and then serialize the wrong
61
+ value into an API response.
62
+
63
+ ## 3. Acquire one provider lease per module runtime
64
+
65
+ `createServerComposition` remains synchronous. Pass `context.databases` into
66
+ the module runtime. The runtime owns one shared initialization promise that
67
+ acquires the lease and runs migrations lazily before the first repository
68
+ operation. Concurrent first requests await that same promise.
69
+
70
+ Use the module id as the namespace. Acquire `purpose: 'migration'`, run
71
+ `runDatabaseMigrations`, and release that lease before acquiring
72
+ `purpose: 'runtime'`; the application role never owns DDL. Preview uses
73
+ `purpose: 'preview'` and isolated tests use `purpose: 'test'`. A read that must
74
+ cross tenants takes `purpose: 'background'`, a read-only role with no blanket
75
+ table grant. Follow `modules/profile/src/server/runtime.ts` for the exact
76
+ sequence. `prepare()` stays read-only and never acquires a lease. `dispose()`
77
+ awaits started initialization and releases every lease once.
78
+
79
+ ## 4. Write explicit SQL
80
+
81
+ Repositories own their SQL. There is no translation layer and no placeholder
82
+ rewriting.
83
+
84
+ - Parameters are `$1`, `$2`, and so on, in the order the statement binds them.
85
+ - Values always go in `DatabaseStatement.parameters`. Never concatenate request
86
+ data, tenant ids, identifiers, sort directions, or filter values into SQL.
87
+ - Dynamic identifiers and ordering come from a closed code-owned allowlist.
88
+ - `executeScript()` is only for trusted, checked-in migration DDL. Runtime
89
+ writes use `execute()`.
90
+ - Keep tenant predicates and tenant-first uniqueness in both dialects. Every
91
+ tenant-owned read and write includes `tenant_id` from the trusted principal.
92
+
93
+ PostgreSQL tenant-owned tables also use database-enforced isolation:
94
+
95
+ - enable and force row-level security on the table;
96
+ - define a policy whose `USING` and `WITH CHECK` clauses compare `tenant_id`
97
+ with `current_setting('coreloom.tenant_id', true)`;
98
+ - run application traffic under a role that is neither a superuser nor granted
99
+ `BYPASSRLS`;
100
+ - use a separate migration role for DDL or policy ownership when required.
101
+
102
+ The adapter sets `coreloom.tenant_id` with parameterized `set_config(..., true)`
103
+ after `BEGIN` on the pinned connection. Never use an unpinned root query.
104
+ Explicit tenant predicates remain required as defense in depth.
105
+
106
+ PostgreSQL returns `BIGINT` as a string. Normalize every integer column on the
107
+ way out of a row through a local `integer()` helper. A comparison such as
108
+ `enabled === 1` silently fails without it, and a count read raw compares against
109
+ a string. Only widen timestamps and sequences to `BIGINT`; leave flags, counters
110
+ and version columns `INTEGER`.
111
+
112
+ Normalize driver-specific unique, foreign-key, serialization, and timeout
113
+ failures into the module's stable service error codes. `DatabaseError` covers
114
+ contract misuse and lifecycle errors; raw driver error classes are deliberately
115
+ not a public module contract.
116
+
117
+ ## 5. Transactions and concurrency
118
+
119
+ Use the transaction argument for every operation inside a transaction callback.
120
+ Calling the root handle from that callback is rejected, and retaining the
121
+ transaction after the callback is `TRANSACTION_CONTEXT_MISUSE`.
122
+
123
+ PostgreSQL may run root operations concurrently, while a transaction is pinned
124
+ to one pooled client. Do not depend on physical connection identity or pool
125
+ order. State transitions that must be atomic belong in one transaction with an
126
+ affected-row or version check. A public repository method called from inside
127
+ another method's transaction opens a second transaction and is rejected; give it
128
+ a private in-transaction variant that takes the transaction instead.
129
+
130
+ Pass `AbortSignal` and a bounded `timeoutMs` from long-running jobs. Read
131
+ `database.capabilities` before depending on isolation or cancellation. A
132
+ `before-start` cancellation capability does not stop a driver call already in
133
+ progress. Portable tenant-owned repository methods use
134
+ `database.transaction(operation, { tenantId, access, isolation })`, including
135
+ reads. PostgreSQL root query, execute, and schema calls, and PostgreSQL
136
+ transactions without `tenantId`, fail with `TENANT_CONTEXT_REQUIRED`.
137
+
138
+ ## 6. Migrations v2 and schema inspection
139
+
140
+ Use `DatabaseMigration` and `runDatabaseMigrations` from `@flowdular/sdk/database`.
141
+ Each migration has one immutable id and its PostgreSQL SQL. The ledger is
142
+ `_coreloom_migrations_v2`, keyed by module namespace and migration id; its
143
+ checksum covers that exact SQL.
144
+
145
+ `inspectExisting(database)` is the only pre-ledger adoption proof. Use
146
+ `database.schema.hasTable`, `hasColumn`, and `hasIndex` with fixed identifiers
147
+ and return:
148
+
149
+ - `complete` only when every effect of the migration exists;
150
+ - `absent` only when none exists;
151
+ - `partial` for every mixed state, which the runner refuses.
152
+
153
+ The runner acquires the adapter's migration lock and applies outstanding DDL
154
+ plus ledger rows in one serializable transaction. It refuses a missing dialect,
155
+ checksum drift, duplicate ids, partial adoption, and adapters without
156
+ transactional DDL. Never edit applied migration bytes or bypass a refusal.
157
+
158
+ `inspectExisting` must pass thunks, not eager promises. Adoption runs inside a
159
+ single-connection transaction, and overlapping queries on that connection break
160
+ it. Check in numbered `.up.sql` source and mirror its bytes in the migration
161
+ constant. A migration-only task uses `migration-authoring` in a separate phase.
162
+
163
+ ## 7. Tests run on a real PostgreSQL
164
+
165
+ `createTestDatabaseProvider()` from `@flowdular/sdk/database-testing` gives a suite its
166
+ own PostgreSQL in process by default, with the same `coreloom_runtime` and
167
+ `coreloom_background` roles and the same forced row-level security a deployment
168
+ enforces. There is no server to start and no second dialect to keep green, so
169
+ the isolation assertions run on every turn rather than behind an environment
170
+ flag. CI selects server PostgreSQL with `FD_TEST_DATABASE_ADAPTER=postgresql`
171
+ and the three test role URLs; missing credentials fail instead of falling back.
172
+
173
+ Starting the engine costs roughly half a second. Open one per test file and
174
+ truncate between cases instead of paying it per test.
175
+
176
+ Cover CRUD, commit and rollback, tenant isolation and uniqueness, stable error
177
+ normalization, and concurrent version conflicts. Migration tests cover empty
178
+ apply, safe adoption, refusals, and a clean second start.
179
+
180
+ With two tenants, prove that each can see and mutate only its own rows, that a
181
+ read or write without tenant context fails with `TENANT_CONTEXT_REQUIRED`, and
182
+ that `WITH CHECK` blocks inserting another tenant id. Where a module polls
183
+ across tenants, prove that the background role reads exactly the routing columns
184
+ and is refused everything else, including writes.
185
+
186
+ ## Refuse
187
+
188
+ - A DSN, password, pool, or `pg` dependency inside a module.
189
+ - A module that opens its own database file or connection.
190
+ - A closed core switch over known adapter ids.
191
+ - Root-handle work inside a transaction callback or a transaction that escapes.
192
+ - Cross-module database access. Use the owner's typed capability or API.
193
+ - A tenant table without enabled and forced row-level security, a tenant policy,
194
+ or tests under a role that cannot bypass it.
195
+ - A cross-tenant read on the runtime role, or a background role granted whole
196
+ rows instead of the columns its poll needs.
197
+ - Tests that mock away SQL, migration, concurrency, or tenant predicates.
198
+
199
+ ## Verification
200
+
201
+ Run the module typecheck and tests, the `@flowdular/sdk/database` contract tests when
202
+ the adapter changes, `pnpm flowdular module validate --json`, and `pnpm verify`
203
+ before landing. An adapter change also needs a shutdown test proving that active
204
+ work drains before pool disposal.