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