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,182 @@
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
+ ---
8
+ # Design a module-owned business agent
9
+
10
+ `defineAgent()` describes a business agent shipped by a module. It is the same
11
+ kind of business agent that appears in `agents.core`, but its behavior is owned
12
+ by module source. It is not a sandbox specialist, coding role, `.ai` skill, or
13
+ permission grant. Read `docs/adr/0007-module-owned-agents.md` and the approved
14
+ module spec before editing.
15
+
16
+ If a required tool is missing, pause this phase and hand it off as a separate
17
+ `agent-tool-design` task. A business agent can use only registered tools.
18
+
19
+ ## Ownership split
20
+
21
+ The module owns:
22
+
23
+ - the stable module id and agent key;
24
+ - name, description, instructions, and positive definition revision;
25
+ - the maximum exact tool allowlist;
26
+ - maximum steps, timeout, temperature, and output-token limits.
27
+
28
+ The tenant owns a separate binding in `agents.core`:
29
+
30
+ - provider connection and model;
31
+ - active or paused state;
32
+ - an enabled-tool subset that can narrow the module allowlist;
33
+ - optimistic binding revision and the resulting executable revision.
34
+
35
+ Never put provider ids, model ids, credentials, tenant ids, or tenant-specific
36
+ instructions in module source. The Agents UI presents module behavior as
37
+ read-only and lets an authorized tenant manager configure only the binding.
38
+
39
+ ## Define and register
40
+
41
+ Declare `agents.core` in both `spec/module.yaml` and `module.json` dependencies,
42
+ and add `"@flowdular/sdk/modules/agents": "workspace:*"` to `package.json`. Keep the
43
+ import server-only.
44
+
45
+ ```ts
46
+ // src/agent/agents.ts
47
+ import { defineAgent } from '@flowdular/sdk/modules/agents/server';
48
+
49
+ export const catalogCurator = defineAgent({
50
+ moduleId: 'catalog.core',
51
+ key: 'catalog-curator',
52
+ definitionRevision: 1,
53
+ name: 'Catalog curator',
54
+ description: 'Reviews and normalizes catalog records.',
55
+ instructions:
56
+ 'Review the requested catalog records. Use only the tools available to you.',
57
+ allowedTools: ['catalog.item.create', 'catalog.item.list'],
58
+ limits: {
59
+ maxSteps: 8,
60
+ timeoutMs: 120_000,
61
+ temperature: 0.2,
62
+ maxOutputTokens: 4_096,
63
+ },
64
+ });
65
+
66
+ export const catalogBusinessAgents = [catalogCurator] as const;
67
+ ```
68
+
69
+ ```ts
70
+ // src/platform.ts, during createServerComposition
71
+ context.agentDefinitions.register(catalogBusinessAgents);
72
+ ```
73
+
74
+ Registration happens during composition. The platform seals
75
+ `agentDefinitions` before any module `start()` hook. A malformed definition,
76
+ duplicate id, wildcard tool, registration after sealing, code downgrade, or
77
+ same-revision content drift fails boot. Do not catch and hide these errors.
78
+
79
+ `defineAgent()` derives the opaque id
80
+ `module-agent:<moduleId>:<key>`, validates the fields, sorts the exact tool ids,
81
+ and freezes the result. Callers do not construct or parse the derived id.
82
+
83
+ ## Authority is an intersection
84
+
85
+ For a module-owned agent, the tools visible to a run are exactly:
86
+
87
+ ```text
88
+ code allowedTools
89
+ intersect tenant binding enabledTools
90
+ intersect invocation toolGrants
91
+ intersect registered tools allowed by the actor's saved ceiling
92
+ intersect registered tools allowed by the actor's live permissions
93
+ ```
94
+
95
+ Every id is exact. An omitted grant means no tools. `*`, prefixes, and implicit
96
+ all-tools behavior are invalid. Every tool independently declares its
97
+ `requiredPermissions`; instructions and skills never grant authority. A scope
98
+ revoked after enqueue is rechecked before the tool body runs. The model never
99
+ receives `PlatformCapabilityRegistry` or direct service, repository, database,
100
+ shell, filesystem, or credential access.
101
+
102
+ The module allowlist is a permanent ceiling for that definition revision. The
103
+ tenant may reduce it, and each caller may reduce it again. A caller cannot
104
+ broaden it. Keep the list to the smallest surface needed for the stated job.
105
+
106
+ Mutating tools also follow the durable idempotency contract in
107
+ `agent-tool-design`. Do not add a write tool to a business agent until the tool
108
+ has its target-side ledger, transaction, replay test, and
109
+ `idempotencyProtection: 'target-ledger'` declaration.
110
+
111
+ ## Revisions and workflows
112
+
113
+ Increase `definitionRevision` whenever any executable module-owned content
114
+ changes: instructions, display copy, allowed tools, or limits. Never reuse a
115
+ revision with different content and never decrement it.
116
+
117
+ Binding a provider or model, changing enabled tools, or reconciling a higher
118
+ module definition creates a new immutable tenant executable revision. Runs and
119
+ published workflows pin that executable revision, not the code definition or
120
+ mutable binding revision. Old retained revisions and audit evidence survive an
121
+ upgrade or module removal. A removed module-owned agent becomes unavailable for
122
+ new work.
123
+
124
+ An unconfigured module agent remains visible but cannot run or be published in
125
+ a workflow. Do not choose a provider or model automatically to make setup look
126
+ complete.
127
+
128
+ ## Spec and files
129
+
130
+ The approved spec states:
131
+
132
+ - the business outcome and refusal conditions;
133
+ - each exact tool and required permission;
134
+ - that tenant binding cannot broaden the module ceiling;
135
+ - unavailable, unconfigured, revision, and module-removal behavior where
136
+ relevant.
137
+
138
+ Typical files are:
139
+
140
+ - `src/agent/agents.ts` for definitions;
141
+ - `src/agent/tools.ts` for module tools;
142
+ - `src/platform.ts` for both registry calls;
143
+ - `tests/business-agents.test.ts` and `tests/agent-tools.test.ts`;
144
+ - `spec/module.yaml`, `module.json`, and `package.json` for dependencies and
145
+ the coordinated version bump.
146
+
147
+ ## Tests
148
+
149
+ The business module proves:
150
+
151
+ 1. The definition has the expected derived id, ownership, revision, limits,
152
+ and exact sorted tool ceiling, and is frozen.
153
+ 2. Every allowed tool is registered by the module or a declared dependency.
154
+
155
+ The shared `agents.core` integration suite proves:
156
+
157
+ 1. A tenant binding can reduce tools but cannot add one outside the code
158
+ ceiling.
159
+ 2. A run without an invocation grant or required live scope never calls the
160
+ tool body and records a denial.
161
+ 3. Two tenants can bind different providers, models, and tool subsets without
162
+ seeing each other's binding or runs.
163
+ 4. A higher definition revision retains the old executable revision; a
164
+ downgrade and same-revision drift refuse startup.
165
+ 5. Module absence blocks new runs while retained run and workflow evidence
166
+ remains readable.
167
+
168
+ Use the module's isolated test provider for persistence tests. The end-to-end access intersection
169
+ belongs in `modules/agents/tests`, while a business module proves its own
170
+ definition and tool behavior locally. Do not edit `agents.core` merely to
171
+ duplicate its platform contract tests. Run the module tests, typecheck, spec
172
+ and module validation, then `pnpm verify` before delivery.
173
+
174
+ ## Refuse
175
+
176
+ - Treating a sandbox coding specialist as a business agent definition.
177
+ - Letting a tenant edit module-owned instructions or the code tool ceiling.
178
+ - Wildcard tools, tenant ids in model input, or authority derived from prompts.
179
+ - Code-pinned provider connections, models, credentials, or secrets.
180
+ - Registration outside `createServerComposition` or after registry sealing.
181
+ - A write tool without target-side durable idempotency.
182
+ - Reusing a definition revision after changing executable content.
@@ -0,0 +1,108 @@
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
+ ---
7
+ # Add a module CLI command
8
+
9
+ 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`.
10
+
11
+ ## 1. Declare the capability
12
+
13
+ `module.json`: add `"cli"` to `capabilities` and
14
+
15
+ ```json
16
+ "cli": { "catalog": "src/cli/commands.json", "entry": "src/cli/index.ts" }
17
+ ```
18
+
19
+ `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.
20
+
21
+ ## 2. Catalog: `src/cli/commands.json`
22
+
23
+ ```json
24
+ {
25
+ "protocolVersion": 1,
26
+ "moduleId": "inventory.core",
27
+ "commands": [
28
+ {
29
+ "path": ["inventory", "export"],
30
+ "capability": {
31
+ "id": "inventory.export",
32
+ "version": 1,
33
+ "summary": "Export tenant-scoped stock locations to a workspace path.",
34
+ "risk": "workspace-write",
35
+ "requiresApprovedSpec": true,
36
+ "supportsDryRun": true
37
+ }
38
+ }
39
+ ]
40
+ }
41
+ ```
42
+
43
+ Rules enforced by `packages/cli/src/extensions.ts` (`validateCliCatalog`, `loadCliExtensions`):
44
+
45
+ - `moduleId` equals `module.json` `id`; the module must be enabled in `flowdular.json`, otherwise its commands do not load.
46
+ - `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`).
47
+ - `capability.id` starts with `<namespace>.`, is unique across all enabled modules, `version` is an integer >= 1, `summary` 1 to 240 characters.
48
+ - `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`).
49
+
50
+ ## 3. Implementation: `src/cli/index.ts`
51
+
52
+ ```ts
53
+ import { defineCliExtension } from '@flowdular/sdk/cli-protocol';
54
+
55
+ export const cliExtension = defineCliExtension({
56
+ protocolVersion: 1,
57
+ moduleId: 'inventory.core',
58
+ commands: [
59
+ {
60
+ path: ['inventory', 'export'],
61
+ capability: {
62
+ id: 'inventory.export',
63
+ version: 1,
64
+ summary: 'Export tenant-scoped stock locations to a workspace path.',
65
+ risk: 'workspace-write' as const,
66
+ requiresApprovedSpec: true,
67
+ supportsDryRun: true,
68
+ },
69
+ execute: async (context) => ({
70
+ data: {
71
+ applied: context.apply,
72
+ target: context.arguments[0] ?? 'json',
73
+ },
74
+ evidence: ['modules/inventory/spec/module.yaml'],
75
+ warnings: context.apply ? [] : ['Dry run only.'],
76
+ }),
77
+ },
78
+ ],
79
+ });
80
+
81
+ export default cliExtension;
82
+ ```
83
+
84
+ `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.
85
+
86
+ ## 4. What the runner does with the descriptor (`packages/cli/src/runner.ts`, `runExtensionCommand`)
87
+
88
+ - `risk: 'external'`: refused with `APPROVAL_VERIFIER_REQUIRED`. `risk: 'destructive'` without `localOnly`: the same.
89
+ - `localOnly: true`: refused with `LOCAL_ONLY_CAPABILITY` unless `FD_ENV` or `NODE_ENV` is `development` or `test` (unset counts as development).
90
+ - `requiresApprovedSpec: true`: needs `--spec <path>` to a schema-valid spec with `status: approved`, otherwise `APPROVED_SPEC_REQUIRED`, `SPEC_VALIDATION_FAILED` or `SPEC_NOT_APPROVED`.
91
+ - `destructive` with `--apply`: needs `--confirm <confirmation>` (`CONFIRMATION_REQUIRED`).
92
+ - 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.`
93
+ - 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.
94
+
95
+ ## 5. Tests
96
+
97
+ `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).
98
+
99
+ ## 6. Landing
100
+
101
+ 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.
102
+
103
+ ## Pitfalls
104
+
105
+ - A `summary` edited in one file only: `CLI implementation for "inventory export" does not match its catalog.`
106
+ - Two enabled modules claiming the same `path` or capability id: `CLI command collision`.
107
+ - `risk: 'read'` commands run without `--apply`; anything that writes must not be `read`.
108
+ - Commands run in the developer's process with the full environment; never print secrets in `data`.
@@ -0,0 +1,99 @@
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
+ ---
8
+ # Extend the platform core
9
+
10
+ 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.
11
+
12
+ ## 1. Dependency direction
13
+
14
+ `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`.
15
+
16
+ ## 2. Surfaces every module and every agent sees
17
+
18
+ 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:
19
+
20
+ - `packages/server/src/index.ts`: `defineEndpoint`, `HttpProblem`, `jsonResponse`, `problemResponse`, `readJsonObject`, `requiredString`, `optionalString`, `requiredInteger`, types `EndpointIdentity`, `EndpointExecutionContext`.
21
+ - `packages/client/src/contributions.ts`: `ModuleClientContext`, `ModuleClientContribution`, `WORKSPACE_SLOTS`, `NavigationGroup`, `createClientContributionRegistry`. `packages/client/src/state.ts`.
22
+ - `packages/contracts/src/index.ts` and `packages/contracts/schemas/*.json`.
23
+ - `packages/ui/src/index.ts`, `packages/ui/src/components/*.tsrx`, `packages/ui/src/styles/components.css`.
24
+ - `modules/auth/src/{index.ts,acl/scopes.ts,domain/types.ts,server/index.ts,services/auth-service.ts}`.
25
+ - `AGENTS.md`, `docs/design-system.md`, and `.ai/skills/**` (copied to `reference/skills/`).
26
+
27
+ ## 3. Checklists by change type
28
+
29
+ Schema change (`packages/contracts/schemas/*.schema.json`):
30
+
31
+ 1. Edit the schema and the matching type in `packages/contracts/src/index.ts`.
32
+ 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`.
33
+ 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.
34
+ 4. `pnpm validate` (spec, blueprint, module validation) and `pnpm test`.
35
+
36
+ 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.
37
+
38
+ 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.
39
+
40
+ 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`.
41
+
42
+ 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`.
43
+
44
+ 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.
45
+
46
+ ## 3b. Worked example: how the optional composition members landed
47
+
48
+ 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`):
49
+
50
+ ```ts
51
+ export interface PlatformServerContext {
52
+ readonly environment: NodeJS.ProcessEnv;
53
+ readonly workspaceRoot: string;
54
+ readonly auth: AuthRuntime;
55
+ readonly settings: ModuleSettingsRuntime; // live, tenant-scoped reads
56
+ readonly agentTools: PlatformToolRegistry; // register(tools), list()
57
+ readonly agentDefinitions: PlatformAgentRegistry; // register(definitions), list()
58
+ readonly capabilities: PlatformCapabilityRegistry; // register(id, service), get(id), has(id)
59
+ }
60
+
61
+ export interface PlatformServerComposition {
62
+ readonly routes: readonly ServerRoute[];
63
+ readonly settings?: ModuleSettingsDeclaration; // declared by the platform after composing
64
+ readonly prepare?: () => void | Promise<void>; // read-only checks before HMR activation
65
+ readonly start?: () => void; // called after every module composed
66
+ readonly stop?: () => void | Promise<void>; // drains background work before disposal
67
+ readonly dispose?: () => void | Promise<void>; // releases owned resources
68
+ }
69
+ ```
70
+
71
+ 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`.
72
+
73
+ ## 4. Generated and composed files
74
+
75
+ `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`.
76
+
77
+ ## 5. Verification
78
+
79
+ ```bash
80
+ pnpm verify # typecheck, test, validate, format:check
81
+ pnpm build # cli build and smoke, module sync --apply, platform build
82
+ ```
83
+
84
+ 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`.
85
+
86
+ After implementation and these checks, switch to `auto-review` as a separate
87
+ read-only phase before declaring completion. Fix findings in an implementation
88
+ phase, rerun affected checks, and repeat the review.
89
+
90
+ ## 6. Do not build on dead code
91
+
92
+ `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.
93
+
94
+ ## Pitfalls
95
+
96
+ - A new required key in `module.schema.json` breaks every module manifest and the scaffold at once; ship it optional first.
97
+ - Changing an error code string (`UNAUTHENTICATED`, `FORBIDDEN`, `CSRF_REJECTED`) breaks module tests that assert it.
98
+ - `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.
99
+ - Prettier uses tabs and `@tsrx/prettier-plugin`; run `pnpm format` before `format:check`.
@@ -0,0 +1,198 @@
1
+ ---
2
+ name: database-adapter
3
+ description: >-
4
+ Build a Flowdular module repository on the asynchronous
5
+ @flowdular/sdk/database contract: PostgreSQL everywhere, embedded PGlite for
6
+ local and test runs, provider leases, forced row-level security, migrations,
7
+ and tenant isolation tests.
8
+ ---
9
+ # Use the database adapter contract
10
+
11
+ Read `docs/database-adapters.md`, `packages/database/src/contracts.ts`, and the
12
+ converted `modules/profile` repository before editing. This skill is for coding
13
+ agents. It is unrelated to tenant-defined Procedures in `agents.core`.
14
+ For a driver adapter, first-run setup, adapter switch, or delivery matrix, also
15
+ read [references/first-run-and-matrix.md](references/first-run-and-matrix.md).
16
+
17
+ ## 1. Keep three layers separate
18
+
19
+ 1. The business repository port is database-agnostic. Domain types, services,
20
+ errors, and callers never import a driver or branch on a dialect.
21
+ 2. The module owns persistence for every dialect it declares: explicit queries,
22
+ row mapping, error normalization, migrations, and contract tests.
23
+ 3. Platform composition owns driver adapters and `DatabaseProvider`: paths,
24
+ credentials, pools, TLS, timeouts, and disposal. A module never receives a
25
+ DSN and never creates a production pool.
26
+
27
+ `DatabaseProvider.acquire({ namespace, purpose })` returns a lease. The module
28
+ uses `lease.database` and releases only that lease. `DatabaseHandle` exposes
29
+ async operations, open `adapterId` and `dialectId`, capabilities, transactions,
30
+ and schema introspection. A callback-scoped transaction expires on return.
31
+
32
+ The platform runs on PostgreSQL. A deployment points at a server; a
33
+ workstation, a preview and a test suite get the same PostgreSQL embedded in the
34
+ process through PGlite, so there is nothing to install and no second dialect to
35
+ keep in step. Modules write PostgreSQL and only PostgreSQL. The contract still
36
+ carries an open `dialectId` and capability negotiation so a future driver can
37
+ join, but never add a core exhaustive switch that must be edited for each one.
38
+
39
+ ## 2. Make the whole repository chain asynchronous
40
+
41
+ Changing only the driver is not a conversion. Update every method in the chain:
42
+
43
+ 1. `src/services/repository.ts` returns `Promise<T>` or `Promise<void>`.
44
+ 2. The database repository awaits every `query`, `execute`, transaction, and
45
+ migration call.
46
+ 3. Services await repository methods. Preserve validation and domain error
47
+ codes at this boundary.
48
+ 4. Endpoints, agent tools, capabilities, background jobs, and tests await the
49
+ service.
50
+ 5. Remove synchronous assumptions such as returning a write input before the
51
+ database confirms it.
52
+
53
+ Search every caller of the repository interface before changing it. A missed
54
+ caller can compile through an inferred promise and then serialize the wrong
55
+ value into an API response.
56
+
57
+ ## 3. Acquire one provider lease per module runtime
58
+
59
+ `createServerComposition` remains synchronous. Pass `context.databases` into
60
+ the module runtime. The runtime owns one shared initialization promise that
61
+ acquires the lease and runs migrations lazily before the first repository
62
+ operation. Concurrent first requests await that same promise.
63
+
64
+ Use the module id as the namespace. Acquire `purpose: 'migration'`, run
65
+ `runDatabaseMigrations`, and release that lease before acquiring
66
+ `purpose: 'runtime'`; the application role never owns DDL. Preview uses
67
+ `purpose: 'preview'` and isolated tests use `purpose: 'test'`. A read that must
68
+ cross tenants takes `purpose: 'background'`, a read-only role with no blanket
69
+ table grant. Follow `modules/profile/src/server/runtime.ts` for the exact
70
+ sequence. `prepare()` stays read-only and never acquires a lease. `dispose()`
71
+ awaits started initialization and releases every lease once.
72
+
73
+ ## 4. Write explicit SQL
74
+
75
+ Repositories own their SQL. There is no translation layer and no placeholder
76
+ rewriting.
77
+
78
+ - Parameters are `$1`, `$2`, and so on, in the order the statement binds them.
79
+ - Values always go in `DatabaseStatement.parameters`. Never concatenate request
80
+ data, tenant ids, identifiers, sort directions, or filter values into SQL.
81
+ - Dynamic identifiers and ordering come from a closed code-owned allowlist.
82
+ - `executeScript()` is only for trusted, checked-in migration DDL. Runtime
83
+ writes use `execute()`.
84
+ - Keep tenant predicates and tenant-first uniqueness in both dialects. Every
85
+ tenant-owned read and write includes `tenant_id` from the trusted principal.
86
+
87
+ PostgreSQL tenant-owned tables also use database-enforced isolation:
88
+
89
+ - enable and force row-level security on the table;
90
+ - define a policy whose `USING` and `WITH CHECK` clauses compare `tenant_id`
91
+ with `current_setting('coreloom.tenant_id', true)`;
92
+ - run application traffic under a role that is neither a superuser nor granted
93
+ `BYPASSRLS`;
94
+ - use a separate migration role for DDL or policy ownership when required.
95
+
96
+ The adapter sets `coreloom.tenant_id` with parameterized `set_config(..., true)`
97
+ after `BEGIN` on the pinned connection. Never use an unpinned root query.
98
+ Explicit tenant predicates remain required as defense in depth.
99
+
100
+ PostgreSQL returns `BIGINT` as a string. Normalize every integer column on the
101
+ way out of a row through a local `integer()` helper. A comparison such as
102
+ `enabled === 1` silently fails without it, and a count read raw compares against
103
+ a string. Only widen timestamps and sequences to `BIGINT`; leave flags, counters
104
+ and version columns `INTEGER`.
105
+
106
+ Normalize driver-specific unique, foreign-key, serialization, and timeout
107
+ failures into the module's stable service error codes. `DatabaseError` covers
108
+ contract misuse and lifecycle errors; raw driver error classes are deliberately
109
+ not a public module contract.
110
+
111
+ ## 5. Transactions and concurrency
112
+
113
+ Use the transaction argument for every operation inside a transaction callback.
114
+ Calling the root handle from that callback is rejected, and retaining the
115
+ transaction after the callback is `TRANSACTION_CONTEXT_MISUSE`.
116
+
117
+ PostgreSQL may run root operations concurrently, while a transaction is pinned
118
+ to one pooled client. Do not depend on physical connection identity or pool
119
+ order. State transitions that must be atomic belong in one transaction with an
120
+ affected-row or version check. A public repository method called from inside
121
+ another method's transaction opens a second transaction and is rejected; give it
122
+ a private in-transaction variant that takes the transaction instead.
123
+
124
+ Pass `AbortSignal` and a bounded `timeoutMs` from long-running jobs. Read
125
+ `database.capabilities` before depending on isolation or cancellation. A
126
+ `before-start` cancellation capability does not stop a driver call already in
127
+ progress. Portable tenant-owned repository methods use
128
+ `database.transaction(operation, { tenantId, access, isolation })`, including
129
+ reads. PostgreSQL root query, execute, and schema calls, and PostgreSQL
130
+ transactions without `tenantId`, fail with `TENANT_CONTEXT_REQUIRED`.
131
+
132
+ ## 6. Migrations v2 and schema inspection
133
+
134
+ Use `DatabaseMigration` and `runDatabaseMigrations` from `@flowdular/sdk/database`.
135
+ Each migration has one immutable id and its PostgreSQL SQL. The ledger is
136
+ `_coreloom_migrations_v2`, keyed by module namespace and migration id; its
137
+ checksum covers that exact SQL.
138
+
139
+ `inspectExisting(database)` is the only pre-ledger adoption proof. Use
140
+ `database.schema.hasTable`, `hasColumn`, and `hasIndex` with fixed identifiers
141
+ and return:
142
+
143
+ - `complete` only when every effect of the migration exists;
144
+ - `absent` only when none exists;
145
+ - `partial` for every mixed state, which the runner refuses.
146
+
147
+ The runner acquires the adapter's migration lock and applies outstanding DDL
148
+ plus ledger rows in one serializable transaction. It refuses a missing dialect,
149
+ checksum drift, duplicate ids, partial adoption, and adapters without
150
+ transactional DDL. Never edit applied migration bytes or bypass a refusal.
151
+
152
+ `inspectExisting` must pass thunks, not eager promises. Adoption runs inside a
153
+ single-connection transaction, and overlapping queries on that connection break
154
+ it. Check in numbered `.up.sql` source and mirror its bytes in the migration
155
+ constant. A migration-only task uses `migration-authoring` in a separate phase.
156
+
157
+ ## 7. Tests run on a real PostgreSQL
158
+
159
+ `createTestDatabaseProvider()` from `@flowdular/sdk/database-testing` gives a suite its
160
+ own PostgreSQL in process by default, with the same `coreloom_runtime` and
161
+ `coreloom_background` roles and the same forced row-level security a deployment
162
+ enforces. There is no server to start and no second dialect to keep green, so
163
+ the isolation assertions run on every turn rather than behind an environment
164
+ flag. CI selects server PostgreSQL with `FD_TEST_DATABASE_ADAPTER=postgresql`
165
+ and the three test role URLs; missing credentials fail instead of falling back.
166
+
167
+ Starting the engine costs roughly half a second. Open one per test file and
168
+ truncate between cases instead of paying it per test.
169
+
170
+ Cover CRUD, commit and rollback, tenant isolation and uniqueness, stable error
171
+ normalization, and concurrent version conflicts. Migration tests cover empty
172
+ apply, safe adoption, refusals, and a clean second start.
173
+
174
+ With two tenants, prove that each can see and mutate only its own rows, that a
175
+ read or write without tenant context fails with `TENANT_CONTEXT_REQUIRED`, and
176
+ that `WITH CHECK` blocks inserting another tenant id. Where a module polls
177
+ across tenants, prove that the background role reads exactly the routing columns
178
+ and is refused everything else, including writes.
179
+
180
+ ## Refuse
181
+
182
+ - A DSN, password, pool, or `pg` dependency inside a module.
183
+ - A module that opens its own database file or connection.
184
+ - A closed core switch over known adapter ids.
185
+ - Root-handle work inside a transaction callback or a transaction that escapes.
186
+ - Cross-module database access. Use the owner's typed capability or API.
187
+ - A tenant table without enabled and forced row-level security, a tenant policy,
188
+ or tests under a role that cannot bypass it.
189
+ - A cross-tenant read on the runtime role, or a background role granted whole
190
+ rows instead of the columns its poll needs.
191
+ - Tests that mock away SQL, migration, concurrency, or tenant predicates.
192
+
193
+ ## Verification
194
+
195
+ Run the module typecheck and tests, the `@flowdular/sdk/database` contract tests when
196
+ the adapter changes, `pnpm flowdular module validate --json`, and `pnpm verify`
197
+ before landing. An adapter change also needs a shutdown test proving that active
198
+ work drains before pool disposal.