create-flowdular 0.2.4 → 0.2.6
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.
- package/README.md +11 -0
- package/agent-template/.agents/skills/agent-tool-design/SKILL.md +203 -0
- package/agent-template/.agents/skills/auth-security-review/SKILL.md +90 -0
- package/agent-template/.agents/skills/auto-review/SKILL.md +103 -0
- package/agent-template/.agents/skills/bug-hunt/SKILL.md +104 -0
- package/agent-template/.agents/skills/business-agent-design/SKILL.md +182 -0
- package/agent-template/.agents/skills/cli-extension/SKILL.md +108 -0
- package/agent-template/.agents/skills/core-extend/SKILL.md +99 -0
- package/agent-template/.agents/skills/database-adapter/SKILL.md +198 -0
- package/agent-template/.agents/skills/database-adapter/references/first-run-and-matrix.md +105 -0
- package/agent-template/.agents/skills/migration-authoring/SKILL.md +161 -0
- package/agent-template/.agents/skills/module-new/SKILL.md +171 -0
- package/agent-template/.agents/skills/module-update/SKILL.md +91 -0
- package/agent-template/.agents/skills/perf-audit/SKILL.md +98 -0
- package/agent-template/.agents/skills/release-eject-pr/SKILL.md +107 -0
- package/agent-template/.agents/skills/spec-approval/SKILL.md +106 -0
- package/agent-template/.agents/skills/test-hardening/SKILL.md +79 -0
- package/agent-template/.agents/skills/translations-i18n/SKILL.md +78 -0
- package/agent-template/.agents/skills/ux-design/SKILL.md +92 -0
- package/agent-template/.agents/skills/variables/SKILL.md +156 -0
- package/agent-template/.agents/skills/workflow-development/SKILL.md +192 -0
- package/agent-template/.ai/README.md +62 -0
- package/agent-template/.ai/agents/README.md +27 -0
- package/agent-template/.ai/agents/module-executor.md +36 -0
- package/agent-template/.ai/agents/reviewer.md +23 -0
- package/agent-template/.ai/agents/sandbox/agentic-engineer.md +31 -0
- package/agent-template/.ai/agents/sandbox/backend-engineer.md +36 -0
- package/agent-template/.ai/agents/sandbox/business-manager.md +23 -0
- package/agent-template/.ai/agents/sandbox/frontend-engineer.md +27 -0
- package/agent-template/.ai/agents/sandbox/ux-designer.md +23 -0
- package/agent-template/.ai/agents/spec-author.md +29 -0
- package/agent-template/.ai/blueprints/add-migration/README.md +5 -0
- package/agent-template/.ai/blueprints/add-migration/allowed-paths.yaml +23 -0
- package/agent-template/.ai/blueprints/add-migration/blueprint.json +14 -0
- package/agent-template/.ai/blueprints/add-migration/examples/invalid/input-destructive.json +6 -0
- package/agent-template/.ai/blueprints/add-migration/examples/invalid/plan-unnumbered-file.json +9 -0
- package/agent-template/.ai/blueprints/add-migration/examples/valid/input.json +6 -0
- package/agent-template/.ai/blueprints/add-migration/examples/valid/plan.json +9 -0
- package/agent-template/.ai/blueprints/add-migration/gates.yaml +30 -0
- package/agent-template/.ai/blueprints/add-migration/input.schema.json +23 -0
- package/agent-template/.ai/blueprints/add-migration/plan.schema.json +54 -0
- package/agent-template/.ai/blueprints/add-migration/required-files.yaml +18 -0
- package/agent-template/.ai/blueprints/add-migration/spec-requirements.yaml +13 -0
- package/agent-template/.ai/blueprints/add-migration/steps.yaml +62 -0
- package/agent-template/.ai/blueprints/author-spec/README.md +5 -0
- package/agent-template/.ai/blueprints/author-spec/allowed-paths.yaml +7 -0
- package/agent-template/.ai/blueprints/author-spec/blueprint.json +14 -0
- package/agent-template/.ai/blueprints/author-spec/examples/invalid/input-missing-outcome.json +5 -0
- package/agent-template/.ai/blueprints/author-spec/examples/valid/input.json +6 -0
- package/agent-template/.ai/blueprints/author-spec/gates.yaml +13 -0
- package/agent-template/.ai/blueprints/author-spec/input.schema.json +20 -0
- package/agent-template/.ai/blueprints/author-spec/plan.schema.json +14 -0
- package/agent-template/.ai/blueprints/author-spec/required-files.yaml +6 -0
- package/agent-template/.ai/blueprints/author-spec/spec-requirements.yaml +35 -0
- package/agent-template/.ai/blueprints/author-spec/steps.yaml +28 -0
- package/agent-template/.ai/blueprints/author-spec/templates/module.yaml +45 -0
- package/agent-template/.ai/blueprints/bug-fix/README.md +5 -0
- package/agent-template/.ai/blueprints/bug-fix/allowed-paths.yaml +29 -0
- package/agent-template/.ai/blueprints/bug-fix/blueprint.json +14 -0
- package/agent-template/.ai/blueprints/bug-fix/examples/invalid/input-no-symptom.json +4 -0
- package/agent-template/.ai/blueprints/bug-fix/examples/invalid/plan-no-test.json +8 -0
- package/agent-template/.ai/blueprints/bug-fix/examples/valid/input.json +6 -0
- package/agent-template/.ai/blueprints/bug-fix/examples/valid/plan.json +8 -0
- package/agent-template/.ai/blueprints/bug-fix/gates.yaml +30 -0
- package/agent-template/.ai/blueprints/bug-fix/input.schema.json +25 -0
- package/agent-template/.ai/blueprints/bug-fix/plan.schema.json +53 -0
- package/agent-template/.ai/blueprints/bug-fix/required-files.yaml +7 -0
- package/agent-template/.ai/blueprints/bug-fix/spec-requirements.yaml +7 -0
- package/agent-template/.ai/blueprints/bug-fix/steps.yaml +51 -0
- package/agent-template/.ai/blueprints/core-extend/README.md +5 -0
- package/agent-template/.ai/blueprints/core-extend/allowed-paths.yaml +49 -0
- package/agent-template/.ai/blueprints/core-extend/blueprint.json +14 -0
- package/agent-template/.ai/blueprints/core-extend/examples/invalid/input-unknown-package.json +5 -0
- package/agent-template/.ai/blueprints/core-extend/examples/invalid/plan-missing-gates.json +7 -0
- package/agent-template/.ai/blueprints/core-extend/examples/valid/input.json +6 -0
- package/agent-template/.ai/blueprints/core-extend/examples/valid/plan.json +10 -0
- package/agent-template/.ai/blueprints/core-extend/gates.yaml +16 -0
- package/agent-template/.ai/blueprints/core-extend/input.schema.json +54 -0
- package/agent-template/.ai/blueprints/core-extend/plan.schema.json +39 -0
- package/agent-template/.ai/blueprints/core-extend/required-files.yaml +36 -0
- package/agent-template/.ai/blueprints/core-extend/spec-requirements.yaml +17 -0
- package/agent-template/.ai/blueprints/core-extend/steps.yaml +54 -0
- package/agent-template/.ai/blueprints/edit-module/README.md +9 -0
- package/agent-template/.ai/blueprints/edit-module/allowed-paths.yaml +27 -0
- package/agent-template/.ai/blueprints/edit-module/blueprint.json +20 -0
- package/agent-template/.ai/blueprints/edit-module/examples/invalid/input-unknown-change.json +5 -0
- package/agent-template/.ai/blueprints/edit-module/examples/invalid/plan-touches-platform.json +15 -0
- package/agent-template/.ai/blueprints/edit-module/examples/valid/input.json +5 -0
- package/agent-template/.ai/blueprints/edit-module/examples/valid/plan.json +25 -0
- package/agent-template/.ai/blueprints/edit-module/gates.yaml +30 -0
- package/agent-template/.ai/blueprints/edit-module/input.schema.json +31 -0
- package/agent-template/.ai/blueprints/edit-module/plan.schema.json +65 -0
- package/agent-template/.ai/blueprints/edit-module/required-files.yaml +80 -0
- package/agent-template/.ai/blueprints/edit-module/spec-requirements.yaml +15 -0
- package/agent-template/.ai/blueprints/edit-module/steps.yaml +115 -0
- package/agent-template/.ai/blueprints/new-module/README.md +7 -0
- package/agent-template/.ai/blueprints/new-module/allowed-paths.yaml +27 -0
- package/agent-template/.ai/blueprints/new-module/blueprint.json +20 -0
- package/agent-template/.ai/blueprints/new-module/examples/invalid/input-spec-outside-modules.json +4 -0
- package/agent-template/.ai/blueprints/new-module/examples/invalid/plan-unknown-gate.json +8 -0
- package/agent-template/.ai/blueprints/new-module/examples/valid/input.json +5 -0
- package/agent-template/.ai/blueprints/new-module/examples/valid/plan.json +22 -0
- package/agent-template/.ai/blueprints/new-module/gates.yaml +30 -0
- package/agent-template/.ai/blueprints/new-module/input.schema.json +21 -0
- package/agent-template/.ai/blueprints/new-module/plan.schema.json +58 -0
- package/agent-template/.ai/blueprints/new-module/required-files.yaml +73 -0
- package/agent-template/.ai/blueprints/new-module/spec-requirements.yaml +30 -0
- package/agent-template/.ai/blueprints/new-module/steps.yaml +138 -0
- package/agent-template/.ai/blueprints/release/README.md +5 -0
- package/agent-template/.ai/blueprints/release/allowed-paths.yaml +19 -0
- package/agent-template/.ai/blueprints/release/blueprint.json +14 -0
- package/agent-template/.ai/blueprints/release/examples/invalid/input-bad-version.json +4 -0
- package/agent-template/.ai/blueprints/release/examples/invalid/plan-bad-branch.json +7 -0
- package/agent-template/.ai/blueprints/release/examples/valid/input.json +5 -0
- package/agent-template/.ai/blueprints/release/examples/valid/plan.json +20 -0
- package/agent-template/.ai/blueprints/release/gates.yaml +20 -0
- package/agent-template/.ai/blueprints/release/input.schema.json +24 -0
- package/agent-template/.ai/blueprints/release/plan.schema.json +46 -0
- package/agent-template/.ai/blueprints/release/required-files.yaml +19 -0
- package/agent-template/.ai/blueprints/release/spec-requirements.yaml +8 -0
- package/agent-template/.ai/blueprints/release/steps.yaml +47 -0
- package/agent-template/.ai/blueprints/security-review/README.md +5 -0
- package/agent-template/.ai/blueprints/security-review/allowed-paths.yaml +6 -0
- package/agent-template/.ai/blueprints/security-review/blueprint.json +14 -0
- package/agent-template/.ai/blueprints/security-review/examples/invalid/input-unknown-kind.json +4 -0
- package/agent-template/.ai/blueprints/security-review/examples/invalid/plan-finding-without-scenario.json +14 -0
- package/agent-template/.ai/blueprints/security-review/examples/valid/input.json +4 -0
- package/agent-template/.ai/blueprints/security-review/examples/valid/plan.json +19 -0
- package/agent-template/.ai/blueprints/security-review/gates.yaml +22 -0
- package/agent-template/.ai/blueprints/security-review/input.schema.json +20 -0
- package/agent-template/.ai/blueprints/security-review/plan.schema.json +65 -0
- package/agent-template/.ai/blueprints/security-review/required-files.yaml +6 -0
- package/agent-template/.ai/blueprints/security-review/spec-requirements.yaml +9 -0
- package/agent-template/.ai/blueprints/security-review/steps.yaml +38 -0
- package/agent-template/.ai/examples/README.md +8 -0
- package/agent-template/.ai/examples/bad/client-imports-server/README.md +20 -0
- package/agent-template/.ai/examples/bad/client-imports-server/api.ts +12 -0
- package/agent-template/.ai/examples/bad/missing-acl/README.md +23 -0
- package/agent-template/.ai/examples/bad/missing-acl/endpoints.ts +12 -0
- package/agent-template/.ai/examples/bad/tenant-from-body/README.md +19 -0
- package/agent-template/.ai/examples/bad/tenant-from-body/endpoints.ts +33 -0
- package/agent-template/.ai/examples/client-contribution/CustomerListView.tsrx +34 -0
- package/agent-template/.ai/examples/client-contribution/README.md +11 -0
- package/agent-template/.ai/examples/client-contribution/contribution.tsrx +48 -0
- package/agent-template/.ai/examples/client-contribution/index.ts +20 -0
- package/agent-template/.ai/examples/client-contribution/permissions.ts +8 -0
- package/agent-template/.ai/examples/customer-cli-extension/README.md +14 -0
- package/agent-template/.ai/examples/customer-cli-extension/commands.json +17 -0
- package/agent-template/.ai/examples/customer-cli-extension/index.ts +36 -0
- package/agent-template/.ai/examples/module-create/task-packet.json +11 -0
- package/agent-template/.ai/guides/application-development.md +97 -0
- package/agent-template/.ai/policies/capabilities.yaml +164 -0
- package/agent-template/.ai/policies/model-routing.yaml +72 -0
- package/agent-template/.ai/policies/path-ownership.yaml +65 -0
- package/agent-template/.ai/policies/task-budgets.yaml +37 -0
- package/agent-template/.ai/references/catalog/LICENSE +21 -0
- package/agent-template/.ai/references/catalog/migrations/0001_catalog_core.down.sql +2 -0
- package/agent-template/.ai/references/catalog/migrations/0001_catalog_core.up.sql +21 -0
- package/agent-template/.ai/references/catalog/migrations/0002_catalog_history.down.sql +3 -0
- package/agent-template/.ai/references/catalog/migrations/0002_catalog_history.up.sql +20 -0
- package/agent-template/.ai/references/catalog/migrations/0003_catalog_history_service_actors.down.sql +3 -0
- package/agent-template/.ai/references/catalog/migrations/0003_catalog_history_service_actors.up.sql +36 -0
- package/agent-template/.ai/references/catalog/migrations/0004_catalog_idempotency_ledger.down.sql +3 -0
- package/agent-template/.ai/references/catalog/migrations/0004_catalog_idempotency_ledger.up.sql +19 -0
- package/agent-template/.ai/references/catalog/migrations/README.md +3 -0
- package/agent-template/.ai/references/catalog/module.json +27 -0
- package/agent-template/.ai/references/catalog/package.json +49 -0
- package/agent-template/.ai/references/catalog/spec/module.yaml +86 -0
- package/agent-template/.ai/references/catalog/src/acl/permissions.ts +6 -0
- package/agent-template/.ai/references/catalog/src/agent/tools.ts +164 -0
- package/agent-template/.ai/references/catalog/src/api/endpoints.ts +243 -0
- package/agent-template/.ai/references/catalog/src/client/CatalogHistoryDrawer.tsrx +123 -0
- package/agent-template/.ai/references/catalog/src/client/CatalogItemForm.tsrx +190 -0
- package/agent-template/.ai/references/catalog/src/client/CatalogView.tsrx +473 -0
- package/agent-template/.ai/references/catalog/src/client/api.ts +111 -0
- package/agent-template/.ai/references/catalog/src/client/contribution.tsrx +61 -0
- package/agent-template/.ai/references/catalog/src/client/index.ts +18 -0
- package/agent-template/.ai/references/catalog/src/client/navigation-copy.ts +9 -0
- package/agent-template/.ai/references/catalog/src/client/state.ts +24 -0
- package/agent-template/.ai/references/catalog/src/domain/types.ts +32 -0
- package/agent-template/.ai/references/catalog/src/domain/variables.ts +111 -0
- package/agent-template/.ai/references/catalog/src/index.ts +31 -0
- package/agent-template/.ai/references/catalog/src/platform.ts +35 -0
- package/agent-template/.ai/references/catalog/src/server/index.ts +4 -0
- package/agent-template/.ai/references/catalog/src/server/runtime.ts +86 -0
- package/agent-template/.ai/references/catalog/src/services/catalog-service.ts +306 -0
- package/agent-template/.ai/references/catalog/src/services/database-repository.ts +440 -0
- package/agent-template/.ai/references/catalog/src/services/index.ts +4 -0
- package/agent-template/.ai/references/catalog/src/services/migration.ts +171 -0
- package/agent-template/.ai/references/catalog/src/services/repository.ts +36 -0
- package/agent-template/.ai/references/catalog/src/services/target-idempotency.ts +59 -0
- package/agent-template/.ai/references/catalog/tests/agent-tools.test.ts +277 -0
- package/agent-template/.ai/references/catalog/tests/endpoints.test.ts +320 -0
- package/agent-template/.ai/references/catalog/tests/idempotency.test.ts +297 -0
- package/agent-template/.ai/references/catalog/tests/migrations.test.ts +149 -0
- package/agent-template/.ai/references/catalog/tests/module.test.ts +271 -0
- package/agent-template/.ai/references/catalog/tests/support/database.ts +76 -0
- package/agent-template/.ai/references/catalog/translations/en.json +101 -0
- package/agent-template/.ai/references/catalog/translations/pl.json +101 -0
- package/agent-template/.ai/references/catalog/tsconfig.json +15 -0
- package/agent-template/.ai/references/catalog/vitest.config.ts +16 -0
- package/agent-template/.ai/references/catalog.provenance.json +55 -0
- package/agent-template/.ai/rules/flowdular.md +86 -0
- package/agent-template/.ai/skills/README.md +36 -0
- package/agent-template/.ai/skills/agent-tool-design/SKILL.md +209 -0
- package/agent-template/.ai/skills/auth-security-review/SKILL.md +96 -0
- package/agent-template/.ai/skills/auto-review/SKILL.md +112 -0
- package/agent-template/.ai/skills/bug-hunt/SKILL.md +110 -0
- package/agent-template/.ai/skills/business-agent-design/SKILL.md +188 -0
- package/agent-template/.ai/skills/cli-extension/SKILL.md +114 -0
- package/agent-template/.ai/skills/core-extend/SKILL.md +104 -0
- package/agent-template/.ai/skills/database-adapter/SKILL.md +204 -0
- package/agent-template/.ai/skills/database-adapter/references/first-run-and-matrix.md +105 -0
- package/agent-template/.ai/skills/migration-authoring/SKILL.md +167 -0
- package/agent-template/.ai/skills/module-new/SKILL.md +180 -0
- package/agent-template/.ai/skills/module-update/SKILL.md +100 -0
- package/agent-template/.ai/skills/perf-audit/SKILL.md +105 -0
- package/agent-template/.ai/skills/release-eject-pr/SKILL.md +113 -0
- package/agent-template/.ai/skills/spec-approval/SKILL.md +112 -0
- package/agent-template/.ai/skills/test-hardening/SKILL.md +86 -0
- package/agent-template/.ai/skills/translations-i18n/SKILL.md +85 -0
- package/agent-template/.ai/skills/ux-design/SKILL.md +97 -0
- package/agent-template/.ai/skills/variables/SKILL.md +164 -0
- package/agent-template/.ai/skills/workflow-development/SKILL.md +199 -0
- package/agent-template/.claude/skills/agent-tool-design/SKILL.md +203 -0
- package/agent-template/.claude/skills/auth-security-review/SKILL.md +90 -0
- package/agent-template/.claude/skills/auto-review/SKILL.md +103 -0
- package/agent-template/.claude/skills/bug-hunt/SKILL.md +104 -0
- package/agent-template/.claude/skills/business-agent-design/SKILL.md +182 -0
- package/agent-template/.claude/skills/cli-extension/SKILL.md +108 -0
- package/agent-template/.claude/skills/core-extend/SKILL.md +99 -0
- package/agent-template/.claude/skills/database-adapter/SKILL.md +198 -0
- package/agent-template/.claude/skills/database-adapter/references/first-run-and-matrix.md +105 -0
- package/agent-template/.claude/skills/migration-authoring/SKILL.md +161 -0
- package/agent-template/.claude/skills/module-new/SKILL.md +171 -0
- package/agent-template/.claude/skills/module-update/SKILL.md +91 -0
- package/agent-template/.claude/skills/perf-audit/SKILL.md +98 -0
- package/agent-template/.claude/skills/release-eject-pr/SKILL.md +107 -0
- package/agent-template/.claude/skills/spec-approval/SKILL.md +106 -0
- package/agent-template/.claude/skills/test-hardening/SKILL.md +79 -0
- package/agent-template/.claude/skills/translations-i18n/SKILL.md +78 -0
- package/agent-template/.claude/skills/ux-design/SKILL.md +92 -0
- package/agent-template/.claude/skills/variables/SKILL.md +156 -0
- package/agent-template/.claude/skills/workflow-development/SKILL.md +192 -0
- package/agent-template/AGENTS.md +77 -0
- package/agent-template/CLAUDE.md +77 -0
- package/agent-template/docs/adr/0001-development-reload.md +16 -0
- package/agent-template/docs/adr/0002-durable-agent-execution.md +21 -0
- package/agent-template/docs/adr/0003-module-settings.md +22 -0
- package/agent-template/docs/adr/0004-enterprise-access-and-audit.md +36 -0
- package/agent-template/docs/adr/0005-sandbox-runtime-and-coding-agents.md +81 -0
- package/agent-template/docs/adr/0006-agentic-workflows.md +1702 -0
- package/agent-template/docs/adr/0007-module-owned-agents.md +429 -0
- package/agent-template/docs/adr/0008-database-adapter-contract.md +90 -0
- package/agent-template/docs/agent-contract.md +45 -0
- package/agent-template/docs/configuration.md +122 -0
- package/agent-template/docs/database-adapters.md +346 -0
- package/agent-template/docs/design-system.md +217 -0
- package/agent-template/docs/modules.md +146 -0
- package/agent-template/platform/scripts/build.mjs +38 -0
- package/agent-template/rulesync.jsonc +11 -0
- package/dist/bin.js +40 -2
- package/package.json +3 -2
- package/template/default/.prettierignore +9 -0
- package/template/default/README.md +12 -0
- package/template/default/flowdular.json +3 -3
- package/template/default/modules/example/package.json +2 -2
- package/template/default/package.json +6 -2
- package/template/default/platform/octane.config.ts +17 -6
- package/template/default/platform/package.json +3 -2
- package/template/default/pnpm-workspace.yaml +1 -0
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
# ADR 0007: Module-owned business agents
|
|
2
|
+
|
|
3
|
+
- Status: accepted
|
|
4
|
+
- Date: 2026-09-02
|
|
5
|
+
- Approval gate: `modules/agents/spec/module.yaml` 0.7.0
|
|
6
|
+
|
|
7
|
+
## Problem
|
|
8
|
+
|
|
9
|
+
Modules can register tools, but they cannot ship the business-agent behavior
|
|
10
|
+
that uses those tools. An operator must recreate the same instructions and tool
|
|
11
|
+
allowlist as tenant data. That copy drifts when the module changes and prevents
|
|
12
|
+
an enabled module from being agentic by itself.
|
|
13
|
+
|
|
14
|
+
Every agent managed by `agents.core` is a business agent. "Module-owned" and
|
|
15
|
+
"tenant-created" describe who owns its definition, not two different kinds of
|
|
16
|
+
runtime. Sandbox coding specialists are a separate development system and are
|
|
17
|
+
not registered through this contract.
|
|
18
|
+
|
|
19
|
+
The concrete consumers are:
|
|
20
|
+
|
|
21
|
+
1. A business module such as `catalog.core`, which ships a catalog curation
|
|
22
|
+
agent next to its tools.
|
|
23
|
+
2. `agents.core`, which lists, configures and executes module-owned agents next
|
|
24
|
+
to tenant-created agents.
|
|
25
|
+
3. `workflows.core`, which publishes a graph against an exact executable agent
|
|
26
|
+
revision and must keep that revision after the module publishes a newer one.
|
|
27
|
+
4. Any module that invokes an agent through the public run capability using a
|
|
28
|
+
trusted tenant, actor and permission snapshot.
|
|
29
|
+
|
|
30
|
+
## Decision
|
|
31
|
+
|
|
32
|
+
Build module-owned business agents, but keep module-owned behavior separate
|
|
33
|
+
from tenant-owned execution configuration.
|
|
34
|
+
|
|
35
|
+
- A module registers a frozen definition with `defineAgent()` during server
|
|
36
|
+
composition.
|
|
37
|
+
- The platform carries a generic agent definition registry in the composition
|
|
38
|
+
context. It is created and sealed by the platform, not by `agents.core`.
|
|
39
|
+
- `defineAgent()` and the semantic agent types are exported by
|
|
40
|
+
`@flowdular/sdk/modules/agents/server`. A module using them declares `agents.core`
|
|
41
|
+
as a module dependency and `@flowdular/sdk/modules/agents` as a package dependency.
|
|
42
|
+
- Module source owns identity, display copy, instructions, maximum tool
|
|
43
|
+
allowlist and execution limits.
|
|
44
|
+
- Each tenant owns a binding that selects an available provider and model,
|
|
45
|
+
chooses active or paused state, and may reduce the code tool allowlist.
|
|
46
|
+
- Module-owned behavior is read-only in the Agents UI. Tenant binding fields
|
|
47
|
+
remain editable under `agents.definitions.manage`.
|
|
48
|
+
- Tenant-created definitions keep their current lifecycle and appear separately
|
|
49
|
+
from module-owned agents.
|
|
50
|
+
|
|
51
|
+
The 0.7.0 specification is approved. Runtime, migrations, UI, and module
|
|
52
|
+
integrations implement this contract together.
|
|
53
|
+
|
|
54
|
+
## Consumer call sites
|
|
55
|
+
|
|
56
|
+
### A module defines and registers an agent
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
// modules/catalog/src/agent/agents.ts
|
|
60
|
+
import { defineAgent } from '@flowdular/sdk/modules/agents/server';
|
|
61
|
+
|
|
62
|
+
export const catalogCurator = defineAgent({
|
|
63
|
+
moduleId: 'catalog.core',
|
|
64
|
+
key: 'catalog-curator',
|
|
65
|
+
definitionRevision: 1,
|
|
66
|
+
name: 'Catalog curator',
|
|
67
|
+
description: 'Normalizes and enriches catalog items.',
|
|
68
|
+
instructions:
|
|
69
|
+
'Review the requested catalog records. Use only the tools available to you.',
|
|
70
|
+
allowedTools: ['catalog.item.create', 'catalog.item.list'],
|
|
71
|
+
limits: {
|
|
72
|
+
maxSteps: 8,
|
|
73
|
+
timeoutMs: 120_000,
|
|
74
|
+
temperature: 0.2,
|
|
75
|
+
maxOutputTokens: 4_096,
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
// modules/catalog/src/platform.ts
|
|
82
|
+
context.agentDefinitions.register([catalogCurator]);
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
The module does not choose credentials, a provider connection or a model. Those
|
|
86
|
+
are tenant deployment choices and never belong in a distributable business
|
|
87
|
+
agent definition.
|
|
88
|
+
|
|
89
|
+
### agents.core consumes the sealed registry
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
const runtime = createAgentRuntime({
|
|
93
|
+
moduleAgents: () => context.agentDefinitions.list(),
|
|
94
|
+
tools: () => context.agentTools.list(),
|
|
95
|
+
// existing options
|
|
96
|
+
});
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
The registry is read when `start()` runs, after every module has composed and
|
|
100
|
+
the platform has sealed both registries.
|
|
101
|
+
|
|
102
|
+
### A module invokes an agent
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
await runQueue.enqueue(
|
|
106
|
+
{
|
|
107
|
+
agentId,
|
|
108
|
+
trigger: 'service',
|
|
109
|
+
input,
|
|
110
|
+
toolGrants: ['catalog.item.list'],
|
|
111
|
+
idempotencyKey,
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
tenantId: principal.tenantId,
|
|
115
|
+
actor,
|
|
116
|
+
permissionSnapshot: principal.scopes,
|
|
117
|
+
},
|
|
118
|
+
);
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
The tenant and authority are trusted context, never input fields. An omitted
|
|
122
|
+
`toolGrants` list means no tools, not every tool.
|
|
123
|
+
|
|
124
|
+
## Public contract
|
|
125
|
+
|
|
126
|
+
The semantic surface belongs to `@flowdular/sdk/modules/agents/server`:
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
export interface ModuleAgentDefinitionInput {
|
|
130
|
+
readonly moduleId: string;
|
|
131
|
+
readonly key: string;
|
|
132
|
+
readonly definitionRevision: number;
|
|
133
|
+
readonly name: string;
|
|
134
|
+
readonly description: string;
|
|
135
|
+
readonly instructions: string;
|
|
136
|
+
readonly allowedTools: readonly string[];
|
|
137
|
+
readonly limits: {
|
|
138
|
+
readonly maxSteps: number;
|
|
139
|
+
readonly timeoutMs: number;
|
|
140
|
+
readonly temperature: number;
|
|
141
|
+
readonly maxOutputTokens: number;
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export interface ModuleAgentDefinition
|
|
146
|
+
extends Readonly<ModuleAgentDefinitionInput> {
|
|
147
|
+
readonly id: string;
|
|
148
|
+
readonly ownership: {
|
|
149
|
+
readonly kind: 'module';
|
|
150
|
+
readonly moduleId: string;
|
|
151
|
+
readonly definitionRevision: number;
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export function defineAgent(
|
|
156
|
+
input: ModuleAgentDefinitionInput,
|
|
157
|
+
): ModuleAgentDefinition;
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
`id` is derived as `module-agent:<moduleId>:<key>` and is limited to 128
|
|
161
|
+
characters. The prefix is reserved and tenant-created agents cannot use it.
|
|
162
|
+
Callers never supply or parse this id. They treat it as opaque after
|
|
163
|
+
registration.
|
|
164
|
+
|
|
165
|
+
The generic registry belongs to `@flowdular/sdk/kernel` so auth and the composition
|
|
166
|
+
contract do not import `agents.core`:
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
export interface PlatformAgentRegistry<T = unknown> {
|
|
170
|
+
register(definitions: readonly T[]): void;
|
|
171
|
+
list(): readonly T[];
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export interface MutablePlatformAgentRegistry<T>
|
|
175
|
+
extends PlatformAgentRegistry<T> {
|
|
176
|
+
seal(): void;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export function createPlatformAgentRegistry<
|
|
180
|
+
T extends { readonly id: string },
|
|
181
|
+
>(): MutablePlatformAgentRegistry<T>;
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
`PlatformServerContext` gains:
|
|
185
|
+
|
|
186
|
+
```ts
|
|
187
|
+
readonly agentDefinitions: PlatformAgentRegistry;
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
The platform creates the registry before module composition and calls `seal()`
|
|
191
|
+
after every module has composed but before any module `start()` hook. Duplicate
|
|
192
|
+
ids fail registration. Registration after sealing fails. A sealed `list()` is
|
|
193
|
+
sorted by id and returns an immutable snapshot.
|
|
194
|
+
|
|
195
|
+
## Ownership and served state
|
|
196
|
+
|
|
197
|
+
The API exposes ownership as a discriminated union:
|
|
198
|
+
|
|
199
|
+
```ts
|
|
200
|
+
export type AgentOwnership =
|
|
201
|
+
| { readonly kind: 'tenant' }
|
|
202
|
+
| {
|
|
203
|
+
readonly kind: 'module';
|
|
204
|
+
readonly moduleId: string;
|
|
205
|
+
readonly definitionRevision: number;
|
|
206
|
+
};
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
A tenant-created agent keeps its positive `revision`. A module-owned agent also
|
|
210
|
+
has a tenant-local positive executable `revision` after binding. Before binding,
|
|
211
|
+
it is served with `status: 'unconfigured'` and no executable revision. Workflow
|
|
212
|
+
catalogs exclude unconfigured agents.
|
|
213
|
+
|
|
214
|
+
The Agents screen presents two explicit groups:
|
|
215
|
+
|
|
216
|
+
- Module agents: module, definition revision, provider binding, model, status,
|
|
217
|
+
enabled tools and unavailable reasons. Behavior fields are read-only.
|
|
218
|
+
- Custom agents: the existing tenant-created agent table and full create,
|
|
219
|
+
update, archive and eligible-delete lifecycle.
|
|
220
|
+
|
|
221
|
+
Calling the current tenant-agent update, archive or delete operation with a
|
|
222
|
+
module-owned id returns `MODULE_AGENT_READ_ONLY`. Binding changes use a separate
|
|
223
|
+
operation so a careless client cannot replace module behavior while appearing
|
|
224
|
+
to edit deployment configuration.
|
|
225
|
+
|
|
226
|
+
## Tenant binding and executable revisions
|
|
227
|
+
|
|
228
|
+
The tenant binding contains:
|
|
229
|
+
|
|
230
|
+
```ts
|
|
231
|
+
export interface ModuleAgentBinding {
|
|
232
|
+
readonly tenantId: string;
|
|
233
|
+
readonly agentId: string;
|
|
234
|
+
readonly provider: string;
|
|
235
|
+
readonly model: string;
|
|
236
|
+
readonly enabledTools: readonly string[];
|
|
237
|
+
readonly status: 'active' | 'paused';
|
|
238
|
+
readonly moduleDefinitionRevision: number;
|
|
239
|
+
readonly executableRevision: number;
|
|
240
|
+
readonly revision: number;
|
|
241
|
+
readonly updatedBy: string;
|
|
242
|
+
readonly updatedAt: number;
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
`revision` is the optimistic concurrency revision of the mutable binding.
|
|
247
|
+
`executableRevision` is the monotonic revision referenced by runs and workflows.
|
|
248
|
+
Creating a binding, changing provider or model, changing enabled tools, or
|
|
249
|
+
receiving a higher module definition revision creates a new immutable executable
|
|
250
|
+
snapshot. Pausing alone changes availability and audit state without rewriting
|
|
251
|
+
an existing snapshot.
|
|
252
|
+
|
|
253
|
+
The immutable snapshot extends the existing retained revision with ownership and
|
|
254
|
+
module definition revision. It contains the resolved provider, model,
|
|
255
|
+
instructions, exact enabled tools and limits. It is the only source for an
|
|
256
|
+
exact-revision run.
|
|
257
|
+
|
|
258
|
+
At runtime start, `agents.core` reconciles registered definitions against
|
|
259
|
+
existing bindings in one transaction:
|
|
260
|
+
|
|
261
|
+
- same definition revision and same content hash is unchanged;
|
|
262
|
+
- higher definition revision creates the next executable revision for each
|
|
263
|
+
existing binding;
|
|
264
|
+
- same definition revision with a different hash fails boot;
|
|
265
|
+
- a lower definition revision fails boot;
|
|
266
|
+
- a definition absent from the sealed registry is unavailable for new work;
|
|
267
|
+
retained revisions and historical runs remain untouched.
|
|
268
|
+
|
|
269
|
+
An old retained revision remains executable after a newer module definition is
|
|
270
|
+
registered only while the owning module is still present, the selected provider
|
|
271
|
+
is usable, and every required registered tool still exists. Module removal is
|
|
272
|
+
not promised to keep new workflow executions alive.
|
|
273
|
+
|
|
274
|
+
## Tool authority
|
|
275
|
+
|
|
276
|
+
Agent access is a capability intersection, not an instruction convention.
|
|
277
|
+
|
|
278
|
+
For a tenant-created agent:
|
|
279
|
+
|
|
280
|
+
```text
|
|
281
|
+
effective tools = definition allowedTools
|
|
282
|
+
intersect invocation toolGrants
|
|
283
|
+
intersect registered tools allowed by actor permissions
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
For a module-owned business agent:
|
|
287
|
+
|
|
288
|
+
```text
|
|
289
|
+
effective tools = code allowedTools
|
|
290
|
+
intersect tenant binding enabledTools
|
|
291
|
+
intersect invocation toolGrants
|
|
292
|
+
intersect registered tools allowed by actor permissions
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
The permission term means every `requiredPermissions` entry declared by the
|
|
296
|
+
tool exists in the trusted permission snapshot captured at enqueue. Tool ids are
|
|
297
|
+
exact identifiers. `*`, prefix patterns and implicit all-tools defaults are
|
|
298
|
+
invalid. A grant outside the agent maximum is refused. A missing permission
|
|
299
|
+
removes the tool before provider execution and a later invocation is denied
|
|
300
|
+
before the tool body runs.
|
|
301
|
+
|
|
302
|
+
The model never receives `PlatformCapabilityRegistry`. Platform capabilities
|
|
303
|
+
are typed module-to-module services. Model-visible authority is only the
|
|
304
|
+
registered tool surface enforced by the harness.
|
|
305
|
+
|
|
306
|
+
Every run persists the effective tool ids and permission digest used for that
|
|
307
|
+
decision. The existing short-lived run grant remains bound to both digests.
|
|
308
|
+
|
|
309
|
+
## Workflow compatibility
|
|
310
|
+
|
|
311
|
+
`agents.run-execution.v2` continues to use `{ agentId, revision }`. For a
|
|
312
|
+
module-owned agent, `revision` means the tenant executable revision, not the code
|
|
313
|
+
definition revision or mutable binding revision.
|
|
314
|
+
|
|
315
|
+
Workflow publication receives only configured active executable revisions. A
|
|
316
|
+
published workflow keeps its exact retained snapshot after a module publishes a
|
|
317
|
+
higher code revision or a tenant changes the current provider binding. Newly
|
|
318
|
+
published workflows select the new current executable revision.
|
|
319
|
+
|
|
320
|
+
Changing the ownership discriminator is additive for list consumers. Existing
|
|
321
|
+
tenant revisions are adopted as `{ kind: 'tenant' }`. Existing workflow rows do
|
|
322
|
+
not change shape.
|
|
323
|
+
|
|
324
|
+
## Guarantees
|
|
325
|
+
|
|
326
|
+
- Module agent identity is stable for one module id and key.
|
|
327
|
+
- Business-agent definition content is immutable within one definition
|
|
328
|
+
revision.
|
|
329
|
+
- Duplicate ids, late registration, revision downgrade and same-revision content
|
|
330
|
+
drift fail platform boot before workers start.
|
|
331
|
+
- Module behavior cannot be edited or deleted through tenant APIs.
|
|
332
|
+
- Tenant bindings cannot enable a tool outside the code allowlist.
|
|
333
|
+
- Every execution is tenant-scoped and uses the initiating Actor and trusted
|
|
334
|
+
permission snapshot.
|
|
335
|
+
- Workflow execution uses the exact retained executable revision it published.
|
|
336
|
+
- Completed runs, revisions and audit evidence survive module removal.
|
|
337
|
+
- No credentials or provider secrets enter a code definition, binding response,
|
|
338
|
+
run event or audit payload.
|
|
339
|
+
|
|
340
|
+
## Deliberately unspecified
|
|
341
|
+
|
|
342
|
+
- The database table layout and whether reconciliation uses one or several
|
|
343
|
+
internal transactions.
|
|
344
|
+
- The visual grouping control used by the Agents screen.
|
|
345
|
+
- Internal hashes and serialization used to detect definition content drift.
|
|
346
|
+
- Whether a future release offers bulk binding APIs or module installation
|
|
347
|
+
defaults.
|
|
348
|
+
- Ordering before the registry is sealed. Consumers may rely only on the sealed
|
|
349
|
+
id order.
|
|
350
|
+
- Automatic migration of a binding when its selected provider or a required
|
|
351
|
+
tool disappears. Version one reports the agent unavailable and requires an
|
|
352
|
+
operator decision.
|
|
353
|
+
|
|
354
|
+
## Lifecycle and errors
|
|
355
|
+
|
|
356
|
+
- `defineAgent()` validates and freezes one value. Calling it has no I/O.
|
|
357
|
+
- Registration is valid only during module composition.
|
|
358
|
+
- Double registration and registration after seal are programmer errors and
|
|
359
|
+
throw synchronously.
|
|
360
|
+
- `list()` after seal is read-only and repeatable.
|
|
361
|
+
- Binding mutations use optimistic concurrency and audit the user actor.
|
|
362
|
+
- Runs already durably queued continue from their snapshot when a binding is
|
|
363
|
+
paused or the module reloads.
|
|
364
|
+
- New runs refuse `unconfigured`, `paused`, missing-provider, stale-readiness,
|
|
365
|
+
missing-tool and missing-permission states with stable codes.
|
|
366
|
+
- Shutdown disposes workers as it does today. The registry owns no timers,
|
|
367
|
+
connections or callbacks.
|
|
368
|
+
|
|
369
|
+
## Stability and evolution
|
|
370
|
+
|
|
371
|
+
This surface is experimental with `agents.core` 0.7.0. Identity and revision
|
|
372
|
+
semantics are one-way serialized contracts because workflows and run history
|
|
373
|
+
store them. They require compatibility tests before the surface can become
|
|
374
|
+
stable.
|
|
375
|
+
|
|
376
|
+
Additive fields belong in the options object. New binding controls must default
|
|
377
|
+
to further restriction, never broader authority. Wildcard grants are excluded
|
|
378
|
+
from future compatibility.
|
|
379
|
+
|
|
380
|
+
## Challenge result
|
|
381
|
+
|
|
382
|
+
### Strongest case for the feature
|
|
383
|
+
|
|
384
|
+
The feature removes a real manual copy between a module and tenant data. It lets
|
|
385
|
+
a module ship a ready business agent and its tools together while keeping
|
|
386
|
+
credentials and tenant authority outside the distributable definition. Catalog
|
|
387
|
+
and workflows are current, concrete consumers.
|
|
388
|
+
|
|
389
|
+
### Alternatives rejected
|
|
390
|
+
|
|
391
|
+
1. Module migrations insert rows into `agents.core`. This crosses a module
|
|
392
|
+
database boundary, needs tenant enumeration, creates unclear uninstall
|
|
393
|
+
behavior and lets code mutate another module's storage.
|
|
394
|
+
2. `module.json` embeds agent definitions. This turns long instructions into
|
|
395
|
+
manifest data, duplicates runtime validation, and still needs a registry and
|
|
396
|
+
tenant provider binding.
|
|
397
|
+
3. A module ships a tenant-agent template with a Copy button. This is simpler,
|
|
398
|
+
but the copy immediately loses module ownership, safe upgrades and exact
|
|
399
|
+
revision lineage. It does not solve the stated problem.
|
|
400
|
+
4. Code pins provider and model. This avoids a binding table but makes modules
|
|
401
|
+
non-portable and can name a connection that does not exist in another tenant.
|
|
402
|
+
|
|
403
|
+
### Cost and failure modes
|
|
404
|
+
|
|
405
|
+
Boot registration is O(d) time and O(d) memory for d current module definitions.
|
|
406
|
+
Tenant listing is O(d + a) for d module definitions and a tenant agents. Storage
|
|
407
|
+
grows O(b \* r) for bindings b and retained executable revisions r, which is the
|
|
408
|
+
same deliberate append-only cost already accepted for exact workflow revisions.
|
|
409
|
+
|
|
410
|
+
The largest blast radius is a bad reconciliation that advances many tenant
|
|
411
|
+
bindings during boot. The mitigation is content-addressed comparison, a
|
|
412
|
+
transactional repository operation, same-revision drift refusal, fault-injection
|
|
413
|
+
rollback tests and no deletion of retained rows.
|
|
414
|
+
|
|
415
|
+
The strongest surviving objection is operational setup: a module-owned agent is
|
|
416
|
+
not runnable until each tenant binds a provider and model. That friction is
|
|
417
|
+
intentional. Silent provider selection would make behavior non-portable and
|
|
418
|
+
would change published workflows without a new executable revision.
|
|
419
|
+
|
|
420
|
+
### Verdict
|
|
421
|
+
|
|
422
|
+
Build the split definition and binding design. Do not build direct database
|
|
423
|
+
seeding or a code-pinned provider shortcut.
|
|
424
|
+
|
|
425
|
+
The falsifying experiment is a thin implementation with one catalog agent and
|
|
426
|
+
two tenants using different provider bindings. It must prove distinct tool
|
|
427
|
+
reductions, immutable workflow revisions across a code bump, and boot refusal
|
|
428
|
+
for same-revision content drift. If that cannot be done without module database
|
|
429
|
+
access or mutable workflow behavior, revise this ADR before expanding adoption.
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# ADR 0008: asynchronous database adapter contract
|
|
2
|
+
|
|
3
|
+
Status: accepted; the SQLite half is superseded (2026-09)
|
|
4
|
+
|
|
5
|
+
> Superseded in part (2026-09): the contract, the leased provider and the v2
|
|
6
|
+
> ledger stand, but SQLite is no longer an adapter and the synchronous
|
|
7
|
+
> compatibility path is gone. Flowdular runs on PostgreSQL everywhere, embedded
|
|
8
|
+
> through PGlite outside production. See `docs/database-adapters.md`.
|
|
9
|
+
|
|
10
|
+
## Decision
|
|
11
|
+
|
|
12
|
+
Flowdular uses the asynchronous, dialect-explicit contract in
|
|
13
|
+
`@flowdular/sdk/database` for new database adapter work. SQLite is the local adapter.
|
|
14
|
+
PostgreSQL is the first production adapter target through a structural driver
|
|
15
|
+
port owned by platform composition.
|
|
16
|
+
|
|
17
|
+
Transactions receive a callback-scoped session pinned to one connection.
|
|
18
|
+
Adapters own placeholder style, schema introspection, migration locking,
|
|
19
|
+
cancellation capability, pooling, and disposal. Modules own repository ports,
|
|
20
|
+
queries, and SQL for every dialect they support. The platform owns credentials
|
|
21
|
+
and gives modules leased handles through `DatabaseProvider`.
|
|
22
|
+
|
|
23
|
+
The existing synchronous SQLite repositories and kernel migration runner remain
|
|
24
|
+
supported during conversion. They are a compatibility path, not the contract
|
|
25
|
+
for PostgreSQL.
|
|
26
|
+
|
|
27
|
+
The package depends on `@flowdular/sdk/kernel` only for the existing checksum
|
|
28
|
+
algorithm, so v1 and v2 ledgers calculate identical hashes for identical SQL.
|
|
29
|
+
The kernel does not import `@flowdular/sdk/database`. A future
|
|
30
|
+
`PlatformServerContext` type may import the provider from this package at the
|
|
31
|
+
module composition layer without creating a package cycle.
|
|
32
|
+
|
|
33
|
+
## Consumer call sites
|
|
34
|
+
|
|
35
|
+
The immediate consumers are existing SQLite module repositories. They can move
|
|
36
|
+
to `SqliteDatabaseAdapter` one module at a time without changing storage. The
|
|
37
|
+
named next consumer is a PostgreSQL deployment, which supplies a pool bridge and
|
|
38
|
+
the same `DatabaseHandle` to an asynchronous repository.
|
|
39
|
+
|
|
40
|
+
Migration authors provide an explicit SQL script per supported dialect and an
|
|
41
|
+
optional adapter-backed adoption inspection. The runner owns the namespaced
|
|
42
|
+
ledger, checksums, transaction, and migration lock.
|
|
43
|
+
|
|
44
|
+
## Guarantees
|
|
45
|
+
|
|
46
|
+
- Query values are separate from SQL text.
|
|
47
|
+
- SQL is never translated between placeholder styles.
|
|
48
|
+
- A transaction uses one connection and its session cannot escape the callback.
|
|
49
|
+
- Migration checksum drift is checked before new SQL runs.
|
|
50
|
+
- Migration DDL and ledger rows commit or roll back together.
|
|
51
|
+
- Disposal is idempotent and drains accepted work.
|
|
52
|
+
- Capabilities describe cancellation, isolation, DDL, lock, and returning
|
|
53
|
+
behavior without version sniffing.
|
|
54
|
+
|
|
55
|
+
## Deliberately unspecified
|
|
56
|
+
|
|
57
|
+
The contract does not standardize SQL syntax, database error codes, generated
|
|
58
|
+
identifier values, query planning, physical pool behavior, or row type parsing.
|
|
59
|
+
Repositories translate database-specific errors into domain errors at their own
|
|
60
|
+
boundary.
|
|
61
|
+
|
|
62
|
+
## Challenge record
|
|
63
|
+
|
|
64
|
+
The strongest argument against this addition is conversion cost. Current
|
|
65
|
+
services and repositories are synchronous, so the adapter does not make the
|
|
66
|
+
existing application PostgreSQL-ready by itself. A narrow seam still earns its
|
|
67
|
+
cost because the production target is already PostgreSQL and retaining a
|
|
68
|
+
synchronous public port would make that target impossible.
|
|
69
|
+
|
|
70
|
+
Rejected alternatives:
|
|
71
|
+
|
|
72
|
+
1. Extend the current structural `MigrationDatabase`. Its implementation relies
|
|
73
|
+
on `sqlite_master`, `PRAGMA`, `STRICT`, and `BEGIN IMMEDIATE`, and every method
|
|
74
|
+
is synchronous. Calling it neutral would preserve a false contract.
|
|
75
|
+
2. Rewrite `?` placeholders to `$1`. SQL strings contain comments, literals,
|
|
76
|
+
operators, DDL types, and dialect features. Text replacement would corrupt
|
|
77
|
+
valid queries while hiding the real compatibility work.
|
|
78
|
+
3. Put `pg` pools and DSNs in modules. That duplicates pools, exposes secrets,
|
|
79
|
+
and makes teardown and production policy impossible to enforce centrally.
|
|
80
|
+
4. Adopt an ORM now. It would add a second query and migration system before one
|
|
81
|
+
reference module proves that abstraction is needed.
|
|
82
|
+
|
|
83
|
+
## Follow-up required for production PostgreSQL
|
|
84
|
+
|
|
85
|
+
Add `pg` to the deployable platform, create the secret-backed provider, inject
|
|
86
|
+
it into server composition, convert a reference module to asynchronous
|
|
87
|
+
repositories, add PostgreSQL integration tests, and run its migrations against
|
|
88
|
+
an empty database plus an upgraded snapshot. Until that work lands, PostgreSQL
|
|
89
|
+
is an implemented adapter seam rather than the application's selected production
|
|
90
|
+
database.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Agent implementation reference
|
|
2
|
+
|
|
3
|
+
Lookup only. The always-active contract lives in `.ai/rules/flowdular.md`.
|
|
4
|
+
Use one task skill; consult only the relevant section here, not the whole file.
|
|
5
|
+
|
|
6
|
+
Flowdular is an agentic foundation framework. It ships the foundation platform (accounts, workspaces, permissions, modules, agents runtime, CLI, sandbox) and people build their own platform on top of it: in the sandbox with AI specialists, or with the skills in `.ai/skills` inside their own coding tool. This contract and the skills are the shared RuleSync sources for every supported coding agent. Spec-first is a working rule here: a module is created only from an approved `spec/module.yaml`.
|
|
7
|
+
|
|
8
|
+
Use the already selected task skill. Consult `.ai/references/catalog` for implementation examples and `docs/design-system.md` for visual work only when the current task needs them.
|
|
9
|
+
|
|
10
|
+
## Rules
|
|
11
|
+
|
|
12
|
+
1. Read the owning code end to end before changing it. Copy the shape of `.ai/references/catalog`; do not invent architecture, permissions, entities, routes, or dependencies. Stop when required input is missing or contradictory and say exactly what you need.
|
|
13
|
+
2. Write only inside the paths your role or blueprint names. In the sandbox these paths are advisory; a reviewer treats a write elsewhere as a defect.
|
|
14
|
+
3. Identifiers: module, permission and capability ids match `^[a-z][a-z0-9-]*(\.[a-z][a-z0-9-]*)+$`. `inventory.core` lives in `modules/inventory` as `@flowdular/module-inventory`; permissions are `<module>.<entity>.read` and `<module>.<entity>.manage`.
|
|
15
|
+
4. A module is created only from a spec with `status: approved`. An agent never initiates, infers, or grants approval from its own judgment. After a current, explicit user instruction naming the spec, a host coding agent may record that decision mechanically by following `spec-approval`; a sandbox specialist can only request approval, and the operator route records the exact approved hash. Spec `permissions[].id` equal the constants in `src/acl/permissions.ts`: the spec is what `auth sync-scopes` grants.
|
|
16
|
+
5. Every endpoint is `defineEndpoint` from `@flowdular/sdk/server` with `access: { kind: 'permission', permission }` and `resolveIdentity: endpointIdentityFromContext`. Deny by default; a public endpoint needs a written reason. No raw `new ServerRoute` outside `modules/auth`.
|
|
17
|
+
6. The tenant id comes only from `principalFromContext(octane)!.tenantId`, never from the body, query or headers. Every query on a tenant-owned table filters by `tenant_id`; unique constraints and indexes start with `tenant_id`; SQL uses bound parameters. PostgreSQL tenant tables also enable and force row-level security with `USING` and `WITH CHECK` policies bound to transaction-local `coreloom.tenant_id`. Every repository operation runs through `database.transaction(..., { tenantId, access })`; the runtime role is never a superuser and never has `BYPASSRLS`, while a separate migration lease may own DDL. `WHERE tenant_id = ...` remains defense in depth.
|
|
18
|
+
7. Every mutation calls `sessionMutationDenial(octane, auth)` first and reads its body with `readJsonObject` plus `requiredString`, `optionalString`, `requiredInteger`. Clients send `content-type: application/json`, `x-csrf-token`, and `credentials: 'same-origin'`.
|
|
19
|
+
8. Routes mount only through `src/platform.ts` exporting `createServerComposition(context)` with `platform.server: true` in `module.json` and a `./platform` export in `package.json`; the context carries `auth`, `settings`, `agentTools`, `agentDefinitions`, `capabilities` and `databases`. A database module passes `context.databases` into one runtime, which acquires and releases one provider lease lazily; `prepare` stays read-only and never opens a database. A composition may return `settings`, read-only `prepare`, `start`, background-work `stop`, and final `dispose`. Client contributions mount only through `createClientContribution(context)` in `src/client/index.ts` with `platform.client: true`. `pnpm flowdular module validate` fails on a missing entry (`PLATFORM_*`).
|
|
20
|
+
9. Never edit the composition by hand: `platform/octane.config.ts`, `platform/src/App.tsrx`, `platform/src/generated/**`, `platform/package.json` dependencies and `modules.enabled` in `flowdular.json` are written by `pnpm flowdular module enable <id> --apply` and `pnpm flowdular module sync --apply`.
|
|
21
|
+
10. `pnpm flowdular module enable <id> --apply` grants the spec permissions to every tenant owner as its last step; `pnpm flowdular auth sync-scopes --module <id> --apply` re-grants later (new permission, another database). Members receive scopes only through `MEMBER_SCOPES` in `modules/auth/src/acl/scopes.ts`, a core change.
|
|
22
|
+
11. Declare every imported package in the module `package.json`; the sandbox `dependencies` gate and the eject fail otherwise. Every relative import carries its `.ts` or `.tsrx` extension.
|
|
23
|
+
12. Use the CLI for discovery, validation and scaffolding: `doctor`, `spec validate`, `module validate`, `module new`, `module enable`, `auth sync-scopes`. Run destructive, external or production capabilities only with what the runner demands (`--apply`, `--confirm`, `--spec`) and never work around a refusal.
|
|
24
|
+
13. Gates are `spec-schema`, `module-schema`, `dependencies`, `typecheck`, `tests`, `format`. The sandbox runs `dependencies` plus the ones your role lists after every turn, per draft module, and feeds a failure back to you; with a shell you may run the module's own gate commands yourself, never installs, network or git. From a checkout run them yourself and `pnpm verify` before any pull request.
|
|
25
|
+
14. Build UI only from `@flowdular/sdk/ui` components, `ui-*` classes and tokens (`docs/design-system.md`). No hardcoded colors, fonts or sizes; never restyle a `ui-*` class; `glyph` and `Icon name` are `ICON_PATHS` keys. Modules use the shared `Table` and `TableCard`, which are backed by the official Octane TanStack adapter; they never import `@octanejs/tanstack-table` directly. A missing primitive becomes a module-local component on tokens, flagged as a promotion candidate. Guard UX: no visual artifacts. A card head is one line (title left; search and the `Filters` dropdown right), filters live inside `Filters` not loose in the head, form rows top-align so a `help` line never drops its neighbour, a field is labelled once, no decorative tags, long values use `ui-mono`/`ui-table-wrap`, adjacent top-level nodes go in a fragment. Look at the rendered screen before handing off and fix any alignment, wrapping, padding or duplicated-label glitch.
|
|
26
|
+
15. One screen, form, table or stateful region per named component. Records own the page; create and edit happen in a `Drawer`. Every screen shows loading, empty, error, populated and denied.
|
|
27
|
+
16. Tests live in `tests/*.test.ts`: identity, tenant isolation, uniqueness, one 401 and one 403 per endpoint, each validation bound. Repository behavior uses `createTestDatabaseProvider()` from `@flowdular/sdk/database-testing`: PGlite locally and isolated server PostgreSQL in CI. Open one provider per test file, migrate once, and truncate module tables between cases (`modules/profile/tests/support/database.ts`). Tenancy tests use two tenants, prove `TENANT_CONTEXT_REQUIRED` without a transaction tenant id, prove RLS prevents cross-tenant reads and writes under the non-bypass runtime role, and cover `WITH CHECK`. Tenant fixture work also supplies tenant context on a migration connection. The sandbox `tests` gate passes with zero tests, so an empty suite is a defect.
|
|
28
|
+
17. Translations are live. Every module contribution registers all declared `translations/*.json` bundles, user-facing copy uses fully qualified `t('<module>.<key>')` keys, navigation labels are lazy getters, locale-aware formatting uses `activeLocale()`, and every locale has the same key set. `module validate` rejects missing files, key drift, and missing static translation keys.
|
|
29
|
+
18. Numbered migration SQL is immutable source. `migrations/000N_<module>_<name>.up.sql` and `.down.sql` are PostgreSQL and the only schema source; there is no dialect subdirectory. `src/services/migration.ts` mirrors every `.up.sql` byte for byte as `databaseMigrations: readonly DatabaseMigration[]` with `sql: { postgresql: ... }`, and the runtime calls `runDatabaseMigrations` through its provider lease. The namespaced `_coreloom_migrations_v2` ledger records the checksum, adopts only an explicit complete `inspectExisting` result (`postgresTenantTableState` is the standard check), and refuses drift, duplicates, or partial schema. Add a new numbered, additive migration instead of changing existing bytes. A tenant table's migration includes enabled and forced RLS plus a tenant policy, with the policy behavior covered by tests.
|
|
30
|
+
19. Module CLI commands live in `src/cli/commands.json` and `src/cli/index.ts`, metadata-identical, inside the module namespace.
|
|
31
|
+
20. Passwords, session tokens and provider credentials never leave `auth.core` (or the `agents.core` vault) and never appear in logs, audit metadata or responses.
|
|
32
|
+
21. `setup quick` and `auth greenfield` are destructive local resets. Preview first, stop the app, never point them at a custom or deployed database.
|
|
33
|
+
22. Agent workers reach ERP data only through registered API or CLI tools. A module defines them with `defineApiAgentTool` or `defineCliAgentTool` from `@flowdular/sdk/harness/tool-adapters` and registers them inside `createServerComposition` with `context.agentTools.register([...])`; nothing else registers a tool. A module may also ship business agents with `defineAgent()` from `@flowdular/sdk/modules/agents/server` and `context.agentDefinitions.register([...])` after declaring an `agents.core` dependency. Their code allowlist is only a maximum: effective tools are the exact intersection of that allowlist, the tenant binding reduction, invocation grants, registered tool permissions, the actor's saved permission ceiling, and live authorization. Sandbox coding specialists are not business agents.
|
|
34
|
+
23. A background run is persisted before enqueue returns; idempotency, leases, recovery, tenant scoping and append-only audit evidence for every fire-and-forget run.
|
|
35
|
+
24. Module settings are declared with `defineModuleSettings` from `@flowdular/sdk/kernel`, returned as `settings` from the composition, read live with `context.settings.get(tenantId, '<module>.core', key)`, and rendered in that module's drawer under Administration, Modules. User-facing setting copy declares fully qualified `labelKey` and `descriptionKey` present in every module locale, with English `label` and `description` literals as compatibility fallbacks. Values, identifiers and secrets are never translated. Administration, Settings is only for workspace and organization settings. A cross-module setting read needs a declared dependency and a `shared`, non-secret setting. Cross-module operations use a typed public service registered in `context.capabilities` by the owning module and resolved by a declared dependent, never the other module's database.
|
|
36
|
+
25. `Development` navigation is owner-only in the client; server permissions remain authoritative.
|
|
37
|
+
26. Generated files are production code: no placeholders, silent fallbacks or skipped tests. `module.json` `version`, spec `specVersion` and `package.json` `version` move together.
|
|
38
|
+
27. Commits and pull requests: short body, one line on verification, no AI attribution footers, generated files only through the CLI. Never use em or en dashes in anything you write.
|
|
39
|
+
28. End a sandbox turn with one line: `HANDOFF: <role-id> - <why>` or `HANDOFF: none - <why>`. The role must be in your role's `handoff` list and never yourself; otherwise the sandbox routing decides.
|
|
40
|
+
|
|
41
|
+
## Sequence
|
|
42
|
+
|
|
43
|
+
Sandbox: brief, planner picks all affected modules and the first role, specification or spec delta per module, operator approval of each exact spec hash, specialist turns with gates per changed module, scaffold when needed, eject (fresh spec-hash check, gates, copy and removals, `pnpm install`, `module enable` with scope grant for new modules, `auth sync-scopes`, platform typecheck, restart note), pull request. A host agent may invoke the operator approval action only after the user explicitly delegates that exact approval through `spec-approval`.
|
|
44
|
+
|
|
45
|
+
Checkout: read the skill, approved spec, `module new` dry run then `--apply`, implement, gates, `module enable --apply` (grants scopes), `pnpm verify`, pull request.
|