create-flowdular 0.4.3 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/README.md +9 -6
  2. package/agent-template/.agents/skills/auth-security-review/SKILL.md +1 -1
  3. package/agent-template/.agents/skills/bug-hunt/SKILL.md +1 -1
  4. package/agent-template/.agents/skills/spec-interview/SKILL.md +20 -20
  5. package/agent-template/.agents/skills/ux-design/SKILL.md +1 -1
  6. package/agent-template/.ai/README.md +5 -3
  7. package/agent-template/.ai/agents/README.md +1 -1
  8. package/agent-template/.ai/agents/sandbox/agentic-engineer.md +1 -0
  9. package/agent-template/.ai/agents/sandbox/backend-engineer.md +1 -0
  10. package/agent-template/.ai/agents/sandbox/frontend-engineer.md +1 -0
  11. package/agent-template/.ai/blueprints/add-migration/README.md +1 -1
  12. package/agent-template/.ai/examples/bad/client-imports-server/README.md +1 -1
  13. package/agent-template/.ai/examples/bad/missing-acl/README.md +1 -1
  14. package/agent-template/.ai/examples/bad/tenant-from-body/README.md +1 -1
  15. package/agent-template/.ai/guides/application-development.md +7 -5
  16. package/agent-template/.ai/platform-capabilities.md +2 -0
  17. package/agent-template/.ai/policies/task-budgets.yaml +1 -1
  18. package/agent-template/.ai/rules/flowdular.md +3 -2
  19. package/agent-template/.ai/skills/auth-security-review/SKILL.md +1 -1
  20. package/agent-template/.ai/skills/bug-hunt/SKILL.md +1 -1
  21. package/agent-template/.ai/skills/spec-interview/SKILL.md +20 -20
  22. package/agent-template/.ai/skills/ux-design/SKILL.md +1 -1
  23. package/agent-template/.ai/subagents/module-executor.md +25 -0
  24. package/agent-template/.ai/subagents/reviewer.md +23 -0
  25. package/agent-template/.ai/subagents/spec-author.md +23 -0
  26. package/agent-template/.claude/agents/module-executor.md +22 -0
  27. package/agent-template/.claude/agents/reviewer.md +24 -0
  28. package/agent-template/.claude/agents/spec-author.md +20 -0
  29. package/agent-template/.claude/skills/auth-security-review/SKILL.md +1 -1
  30. package/agent-template/.claude/skills/bug-hunt/SKILL.md +1 -1
  31. package/agent-template/.claude/skills/spec-interview/SKILL.md +20 -20
  32. package/agent-template/.claude/skills/ux-design/SKILL.md +1 -1
  33. package/agent-template/.codex/agents/module-executor.toml +17 -0
  34. package/agent-template/.codex/agents/reviewer.toml +14 -0
  35. package/agent-template/.codex/agents/spec-author.toml +15 -0
  36. package/agent-template/AGENTS.md +3 -2
  37. package/agent-template/CLAUDE.md +3 -2
  38. package/agent-template/docs/configuration.md +27 -0
  39. package/agent-template/docs/design-system.md +2 -2
  40. package/agent-template/docs/modules.md +6 -0
  41. package/agent-template/docs/sandbox.md +117 -6
  42. package/agent-template/rulesync.jsonc +1 -1
  43. package/dist/bin.js +9 -0
  44. package/package.json +2 -2
  45. package/template/default/.env.example +7 -0
  46. package/template/default/.prettierignore +2 -0
  47. package/template/default/README.md +6 -4
  48. package/template/default/modules/example/package.json +1 -1
  49. package/template/default/package.json +3 -2
  50. package/template/default/platform/index.html +7 -19
  51. package/template/default/platform/package.json +1 -1
  52. package/template/default/platform/public/favicon.svg +1 -1
  53. package/template/default/platform/src/App.tsrx +25 -1
  54. package/template/default/platform/src/generated/modules.server.ts +2 -0
@@ -23,26 +23,26 @@ For an edit, also read the module's current `spec/module.yaml` and write the sma
23
23
 
24
24
  One pass, in this order. For each row, write the default from the card into the spec and record it as a `decisions[]` entry with `decidedBy: default`. Ask only where the answer is a business fact that no default can supply.
25
25
 
26
- | Decision | Default to propose | Lands in |
27
- | ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------- |
28
- | Actors | Owner manages, member reads | `permissions`, `invariants` |
29
- | Entities and fields | One primary entity; `name` required, `maxLength` 120; no field the request did not name | `entities[]` |
30
- | Uniqueness | The human-facing code is `unique: tenant`; everything else `none` | `entities[].fields[].unique` |
31
- | States and transitions | `active` and `archived`, every transition behind the manage permission | `entities[].states` |
32
- | Who sees what | Both permissions in the same navigation entry; the manage action hidden without the scope | `permissions`, `screens[]`, `invariants` |
33
- | What is denied | Unauthenticated 401, missing permission 403, cross-tenant read returns nothing | `acceptanceScenarios` |
34
- | Failure behaviour | A duplicate returns a stable conflict and changes nothing; bounds return 400 | `invariants`, `acceptanceScenarios` |
35
- | Cross-module reads | None. A read of another module goes through its public capability and a declared dependency | `dependencies`, `dataOwnership` |
36
- | Screens | One `list` screen with the entity's identifying columns | `screens[]` |
37
- | Widgets | None. A count belongs on `dashboard.metrics` only when the request asks for it | `widgets[]` |
38
- | Settings | None. A number the business may change later is `scope: tenant` with a stated default | `settings[]` |
39
- | Feature flags | Ask for one whenever a change alters behaviour a workspace already relies on, or is hard to undo: `kind: flag`, boolean, `scope: tenant`, a stated default, and the behaviour named in an invariant | `settings[]` |
40
- | Agent tools | None. A tool is a later phase and `risk` may only be `read` or `workspace-write` | `agentTools[]` |
41
- | Outside sources | None. A named public source is `research`, with the entity its findings attach to | `research` |
42
- | Other systems | None. A named system is one `source` adapter per record kind, run on demand | `adapters[]` |
43
- | Documents | None. A named document is one `templates[]` entry on the record it describes | `templates[]` |
44
- | Reports | None. There is no export, no PDF and no search; a report is a screen or it is out of scope | `outOfScope[]` |
45
- | Out of scope | Every item from the card's gap list the request touched, each with its business decision | `outOfScope[]`, `decisions[]` |
26
+ | Decision | Default to propose | Lands in |
27
+ | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------- |
28
+ | Actors | Owner manages, member reads | `permissions`, `invariants` |
29
+ | Entities and fields | One primary entity; `name` required, `maxLength` 120; no field the request did not name | `entities[]` |
30
+ | Uniqueness | The human-facing code is `unique: tenant`; everything else `none` | `entities[].fields[].unique` |
31
+ | States and transitions | `active` and `archived`, every transition behind the manage permission | `entities[].states` |
32
+ | Who sees what | Both permissions in the same navigation entry; the manage action hidden without the scope | `permissions`, `screens[]`, `invariants` |
33
+ | What is denied | Unauthenticated 401, missing permission 403, cross-tenant read returns nothing | `acceptanceScenarios` |
34
+ | Failure behaviour | A duplicate returns a stable conflict and changes nothing; bounds return 400 | `invariants`, `acceptanceScenarios` |
35
+ | Cross-module reads | None. A read of another module goes through its public capability and a declared dependency | `dependencies`, `dataOwnership` |
36
+ | Screens | One `list` screen with the entity's identifying columns | `screens[]` |
37
+ | Widgets | None. A count belongs on `dashboard.metrics` only when the request asks for it | `widgets[]` |
38
+ | Settings | None. A number the business may change later is `scope: tenant` with a stated default | `settings[]` |
39
+ | Feature flags | Ask for one whenever a change alters behaviour a workspace already relies on, or is hard to undo: `kind: flag`, boolean, `scope: tenant`, a stated default, and the behaviour named in an invariant | `settings[]` |
40
+ | Agent tools | None. A tool is a later phase and `risk` may only be `read` or `workspace-write` | `agentTools[]` |
41
+ | Outside sources | None. A named public source is `research`, with the entity its findings attach to | `research` |
42
+ | Other systems | None. A named system is one `source` adapter per record kind, run on demand | `adapters[]` |
43
+ | Documents | None. A named document is one `templates[]` entry on the record it describes | `templates[]` |
44
+ | Reports | A named report is one provider on `reports.v1` returning tiles and series; a named list export is one `defineListExport`; named records are findable through `search.providers.v1`. None of the three has a specification key, so each needs a decision naming the screen or the `actions[]` entry that registers it | `actions[]`, `invariants` |
45
+ | Out of scope | Every item from the card's gap list the request touched, each with its business decision | `outOfScope[]`, `decisions[]` |
46
46
 
47
47
  A default you propose is still a decision: it goes into `decisions[]` so the operator can see and overturn it, and so the next agent never re-derives it.
48
48
 
@@ -81,7 +81,7 @@ Read-only master-detail (runs, playground) keeps `ui-two-col` (+ `--wide-aside`)
81
81
  - `Alert`: `tone` danger (default), warning, info.
82
82
  - `Avatar`: `name`, `square` (organizations), `large`.
83
83
  - `Icon`: `name`, `size` (18 default, 16 in controls, 14 in `Button size="sm"`), `strokeWidth`.
84
- - `BrandMark`: `size`, `signature`, `tone`; brand moments only.
84
+ - `BrandMark`: `size`, `tone`; three bars with a copper accent bar, brand moments only.
85
85
 
86
86
  Icon keys (`ICON_PATHS`, `packages/ui/src/icons/Icon.tsrx`): `dashboard`, `parties`, `catalog`, `user`, `users`, `shield`, `code`, `modules`, `file-text`, `play`, `bot`, `flask`, `activity`, `plug`, `search`, `chevron-down`, `chevron-left`, `chevron-right`, `chevrons-up-down`, `sort`, `calendar`, `plus`, `panel-left`, `check`, `filter`, `download`, `more`, `external`, `alert`, `x`, `sign-out`, `refresh`, `help`, `info`, `key`, `settings`, `braces`, `copy`. An unknown name renders `modules` silently, so check the list.
87
87
 
@@ -0,0 +1,17 @@
1
+ name = "module-executor"
2
+ description = "Implement one Flowdular blueprint end to end at the repository root: scaffold from an approved spec, write the module, run the gates and join it to the platform through the CLI. Use once a spec is approved and the change is ready to be built."
3
+ developer_instructions = '''
4
+ Your role prompt is `.ai/agents/module-executor.md`. Read it with the blueprint
5
+ under `.ai/blueprints/<id>/` and the one matching skill in
6
+ `.ai/skills/<name>/SKILL.md` before changing anything.
7
+
8
+ Implement only what the approved spec's acceptance scenarios describe, inside the
9
+ blueprint's `allowed-paths.yaml`. Join the platform through
10
+ `pnpm flowdular module enable <id> --apply` and `auth sync-scopes`; never edit
11
+ `flowdular.json`, `platform/package.json`, `platform/src/generated/**` or
12
+ `platform/octane.config.ts` by hand.
13
+
14
+ Run the module gates and `pnpm verify` yourself and report their exact commands
15
+ and results. Never waive a gate. End with `HANDOFF: reviewer - <what to review>`
16
+ or `HANDOFF: none - <blocker>`.
17
+ '''
@@ -0,0 +1,14 @@
1
+ name = "reviewer"
2
+ description = "Review a finished Flowdular change against its approved spec, the blueprint, the invariants and the executable evidence, and report findings by severity. Use as a separate phase before delivery or a pull request. Reports defects and fixes nothing."
3
+ developer_instructions = '''
4
+ Your role prompt is `.ai/agents/reviewer.md` and the one task skill for this
5
+ phase is `.ai/skills/auto-review/SKILL.md`. Read both before reviewing.
6
+
7
+ You write no production code. Review the complete requested change, its
8
+ requirements, callers and tests; report concrete findings by severity with file,
9
+ line and failure scenario. Never waive missing or failing verification, and never
10
+ approve a change whose evidence you have not seen run.
11
+
12
+ End with `HANDOFF: module-executor - <findings to fix>` or
13
+ `HANDOFF: none - <review result and remaining verification>`.
14
+ '''
@@ -0,0 +1,15 @@
1
+ name = "spec-author"
2
+ description = "Turn a business request into a schema-valid Flowdular module specification at the repository root. Use before any implementation, for a new module spec or a change to an existing one. Writes only modules/<dir>/spec/module.yaml and never approves it."
3
+ developer_instructions = '''
4
+ Your role prompt is `.ai/agents/spec-author.md` and the one task skill for this
5
+ phase is `.ai/skills/spec-interview/SKILL.md`. Read both before writing.
6
+
7
+ You write only `modules/<dir>/spec/module.yaml`. Propose a platform default for
8
+ every decision and ask the user for what cannot be inferred; never guess a
9
+ business fact. Never set `status: approved`: approval is the user's, recorded by
10
+ the `spec-approval` skill.
11
+
12
+ `pnpm flowdular spec validate --all --json` must report the file valid before you
13
+ report. End with `HANDOFF: reviewer - spec ready for owner approval` or
14
+ `HANDOFF: none - <open question>`.
15
+ '''
@@ -78,5 +78,6 @@ made in the Flowdular repository and released before this application uses it.
78
78
  The skills and examples use @flowdular/sdk subpath imports. For pnpm --filter,
79
79
  read the actual module package name from its package.json. Use pnpm verify and
80
80
  pnpm build for this application. Root .ai files are editable project guidance;
81
- run pnpm rules:generate after changing rules or skills, then pnpm rules:check.
82
- AGENTS.md, CLAUDE.md, .agents/skills and .claude/skills are generated copies.
81
+ run pnpm rules:generate after changing rules, skills or subagents, then pnpm
82
+ rules:check. AGENTS.md, CLAUDE.md, .agents/skills, .claude/skills, .claude/agents
83
+ and .codex/agents are generated copies.
@@ -78,5 +78,6 @@ made in the Flowdular repository and released before this application uses it.
78
78
  The skills and examples use @flowdular/sdk subpath imports. For pnpm --filter,
79
79
  read the actual module package name from its package.json. Use pnpm verify and
80
80
  pnpm build for this application. Root .ai files are editable project guidance;
81
- run pnpm rules:generate after changing rules or skills, then pnpm rules:check.
82
- AGENTS.md, CLAUDE.md, .agents/skills and .claude/skills are generated copies.
81
+ run pnpm rules:generate after changing rules, skills or subagents, then pnpm
82
+ rules:check. AGENTS.md, CLAUDE.md, .agents/skills, .claude/skills, .claude/agents
83
+ and .codex/agents are generated copies.
@@ -18,6 +18,33 @@ deployments must set the secret keys.
18
18
  | `FD_METRICS` | `false` | Expose `GET /api/metrics`; see [operations.md](operations.md) |
19
19
  | `FD_METRICS_TOKEN` | none | Bearer token a metrics scrape must present |
20
20
 
21
+ ## Branding
22
+
23
+ The name, the document title, the description, the link preview image, the
24
+ browser icon, the theme colour and the logo are not environment variables: they
25
+ are `system.core` settings an owner with `system.settings.manage` changes under
26
+ Administration, Branding, and every change is audited. One value serves the
27
+ whole deployment, so the sign-in screen and a shared link carry it too, and a
28
+ setting nobody changed renders the product's own.
29
+
30
+ An address is stored only as a path on this deployment (`/brand/logo.svg`) or an
31
+ https URL; `javascript:`, `data:` and protocol-relative values are refused when
32
+ they are written. Every https branding image origin is added to `img-src` of the
33
+ policy this deployment serves, including an `FD_CSP` of your own, so the browser
34
+ loads it; an `FD_CSP` without an `img-src` directive is left alone and then has
35
+ to name the origin itself. The label an authenticator lists an enrolled account
36
+ under is the same name, decided by the server, so a member who enrols after a
37
+ rename sees the new one.
38
+
39
+ An application scaffolded before this release owns its own `platform/src/App.tsrx`
40
+ and `platform/index.html`, so its head does not follow the settings until it
41
+ adopts two changes the template now carries: `configureBrandingFromPage(props)`
42
+ plus the `Seo`, `Link` and `Meta` block in the entry, and the removal of the
43
+ static `<link rel="icon">` and `<meta name="theme-color">` from the page, which
44
+ would otherwise compete with the rendered ones. `pnpm flowdular doctor` reports
45
+ both as the `platform.branding` check. The navigation, the mobile header and the
46
+ sign-in screen follow the settings without any change.
47
+
21
48
  ## Observability
22
49
 
23
50
  Spans are always recorded into a bounded in-process buffer and the logger always
@@ -25,7 +25,7 @@ shared primitives, tokens, and the rules for using them.
25
25
  refuses a class no stylesheet declares, there and in every `.tsrx`.
26
26
  - `platform/public`: `favicon.svg`, `og.png` (1200x630 Open Graph image).
27
27
  - Brand mark geometry is generated: `node packages/ui/scripts/gen-mark.mjs`
28
- rewrites `packages/ui/src/brand/mark.ts` from the weave parameters.
28
+ rewrites `packages/ui/src/brand/mark.ts` from the bar parameters.
29
29
 
30
30
  ## Rules
31
31
 
@@ -282,7 +282,7 @@ them; outside the shell the English defaults and the host locale apply.
282
282
  | `Switch` | Boolean setting that applies on its own (no form submit): `checked`, `label` as the accessible name, `disabled`, `onChange` |
283
283
  | `ConfirmDialog` | One question before an irreversible action: `open`, `title`, children, `confirmLabel`, `tone` danger (default) or primary, `busy`, `onConfirm`, `onCancel`; traps Tab and restores focus to the opener |
284
284
  | `Icon` | Stroke icon by `name` from `ICON_PATHS`; `size` 18 default, 16 in controls, 14 in `Button size="sm"`; `strokeWidth` 1.75 default |
285
- | `BrandMark` | The weave: `size`, `signature` (copper weft, large brand moments only), `tone` brand, current, inverse |
285
+ | `BrandMark` | Three bars, the short one copper: `size`, `tone` brand, current, inverse (`signature` is accepted and changes nothing) |
286
286
 
287
287
  `Drawer` takes one child, a `ui-drawer__form` (fields in `ui-drawer__body`, actions in `ui-drawer__foot`) or a plain `ui-drawer__body`; it closes on Escape and on the scrim. `SearchField` carries no visible label, so pass `label` as its accessible name. `FormField` renders `error` in place of `help` and marks it `role="alert"`. `SettingRow` is presentation only: the caller owns the draft value, the save call, and passes the result back as `status`. `ScopeSummary` is the read side of `CheckGrid`; both keep the first-seen module order, and `summarizeScopes` is exported for callers that need the grouping without the markup.
288
288
 
@@ -65,6 +65,12 @@ at least one entity (`SPEC_ACTION_PERMISSION_UNKNOWN`, `SPEC_ENTITY_UNKNOWN`,
65
65
  `SPEC_DUPLICATE_ID`). A client without a list screen, or a stored entity with no
66
66
  tenant-unique field, is a warning.
67
67
 
68
+ An action may not declare `risk: external`. A module never calls another system
69
+ directly: the harness and the CLI runner both refuse an external action, so the
70
+ specification catches it here instead of at delivery
71
+ (`SPEC_ACTION_RISK_UNSUPPORTED`). Model the effect as a connector definition plus
72
+ an adapter, or lower the risk.
73
+
68
74
  The three optional sections have checks of their own. `research.evidenceOwner`
69
75
  and `templates[].inputEntity` must name an entity (`SPEC_ENTITY_UNKNOWN`). An
70
76
  adapter id must start with the module id (`SPEC_ADAPTER_ID_NAMESPACE`); a source
@@ -12,12 +12,85 @@ Full documentation lives with the package:
12
12
 
13
13
  ```bash
14
14
  pnpm sandbox # from this repository
15
- npx @flowdular/sandbox # from any Flowdular workspace
15
+ npx @flowdular/sandbox # from any Flowdular workspace, or from an empty one
16
16
  ```
17
17
 
18
- The launcher walks up to `flowdular.json` to find the workspace and opens
19
- `http://127.0.0.1:4320`. `--port`, `--workspace`, `--host` and `--mode` override
20
- the defaults.
18
+ In an empty directory, the command creates a standalone Flowdular application,
19
+ installs its dependencies, starts it with a local embedded PostgreSQL database,
20
+ prepares the sandbox credential and serves the dashboard on
21
+ `http://127.0.0.1:4320`. You can then describe the module in the dashboard.
22
+
23
+ ## Start with nothing installed
24
+
25
+ There is no checkout step.
26
+
27
+ ```bash
28
+ mkdir acme-erp && cd acme-erp
29
+ npx @flowdular/sandbox
30
+ ```
31
+
32
+ The launcher uses `create-flowdular` at the sandbox package's exact version,
33
+ installs the generated application and makes its first local Git commit. It
34
+ reuses that workspace on later runs. The application lands in `./flowdular`
35
+ unless `--workspace <path>` names another directory. No remote is created until
36
+ you choose one.
37
+
38
+ The repository dialog can create a private GitHub repository or connect an
39
+ empty one after showing the initial push plan. If creation is interrupted, the
40
+ same dialog shows the pending attempt. You can resume it or discard its local
41
+ record after GitHub returns an authenticated 404. A private repository hidden
42
+ from the current account may also return 404, so check GitHub if creation may
43
+ have succeeded.
44
+
45
+ To work on an existing platform repository, run
46
+ `npx @flowdular/sandbox --connect <git-url>` and optionally `--branch <name>`.
47
+ The launcher clones into a new directory, checks `flowdular.json` and the
48
+ committed pnpm lockfile, then installs dependencies. Run from an existing
49
+ Flowdular checkout to reuse it without cloning. `--no-bootstrap` refuses to
50
+ create an application. The older `--repository` and pinned `--ref` options
51
+ remain available when you deliberately want a checkout of the Flowdular core
52
+ repository.
53
+
54
+ The launcher refuses to create or clone into a directory containing unrelated
55
+ files. For application generation, Git and pnpm are checked before files are
56
+ written.
57
+
58
+ An application already serving on the platform port is left alone, and no
59
+ credential is prepared for it. `--platform` and `--platform-port` say otherwise
60
+ explicitly; `--no-platform` never starts one.
61
+
62
+ ## The credential is prepared, not pasted
63
+
64
+ A business user used to sign in to the application, create an API token with
65
+ three scopes, paste it into the sandbox and grant that account sandbox access
66
+ before describing anything. None of those is a business decision, so the
67
+ application does them during its own boot when the launcher asks, and the
68
+ launcher collects the result.
69
+
70
+ - It runs only when the launcher started the application, so an application
71
+ someone else owns is never asked to create an account.
72
+ - It reuses an existing workspace and account rather than replacing them, and
73
+ mints exactly one token for the life of the deployment.
74
+ - The token carries the four sandbox scopes and nothing else.
75
+ - It is written to a `0600` file that the launcher seals into its own
76
+ configuration and deletes. It is never printed, so it reaches no terminal, log
77
+ or transcript.
78
+ - Token management over HTTP is unchanged: `POST /api/auth/api-tokens` still
79
+ refuses a machine credential, and no route was added for this.
80
+
81
+ The account is `sandbox-operator@example.com` in a `sandbox` workspace, with a
82
+ password nobody has. It exists to hold the grant.
83
+
84
+ **A failed provision never stops the application.** It is a convenience for a
85
+ local operator, and an application that will not serve because a sandbox account
86
+ could not be created is worse than one that serves and reports the problem.
87
+
88
+ For a remote deployment, or a sandbox started some other way, the dashboard says
89
+ what is missing. One command fixes it:
90
+
91
+ ```bash
92
+ pnpm flowdular sandbox provision --apply
93
+ ```
21
94
 
22
95
  ## Connect it to a running application
23
96
 
@@ -29,6 +102,8 @@ security a deployment has, and it is thrown away with the session.
29
102
  1. In the application, open Administration, API tokens, and issue a token with
30
103
  `sandbox.access.use` plus the read scopes the preview should see. Add
31
104
  `sandbox.preview.data` for live data and `sandbox.modules.eject` for eject.
105
+ Enable token writes so the sandbox can record sessions, eject modules and
106
+ publish the application repository when you request it.
32
107
  2. Grant sandbox access to the account, in the app under Development, Sandbox,
33
108
  or from the CLI:
34
109
 
@@ -38,9 +113,11 @@ pnpm flowdular sandbox access --tenant operations-demo
38
113
  ```
39
114
 
40
115
  3. Paste the token and the application address into the sandbox connect screen.
116
+ The same screen asks for a model provider and key, unless the workspace
117
+ already carries one, in which case it names the variable it adopted.
41
118
 
42
- The token is encrypted at rest under `.flowdular/sandbox/secret.key` and is never
43
- returned to the browser. The application may run anywhere: locally on
119
+ The token and the model key are encrypted at rest under
120
+ `.flowdular/sandbox/secret.key` and are never returned to the browser. The application may run anywhere: locally on
44
121
  `http://127.0.0.1:4310` or a deployment.
45
122
 
46
123
  ## Modes
@@ -54,6 +131,25 @@ A non-loopback `--host` forces `self-hosted`. A sandbox that cannot prove it is
54
131
  loopback never offers a local binary, because a local binary carries the
55
132
  operator's own login.
56
133
 
134
+ The bring-your-own-key driver takes its credential from the sandbox model
135
+ settings, or, when none is saved there, from `ANTHROPIC_API_KEY`,
136
+ `OPENAI_API_KEY`, `AZURE_API_KEY` or `AI_GATEWAY_API_KEY` in the environment or
137
+ the workspace `.env`. A scaffolded application ships the first of those as an
138
+ empty placeholder, so pasting a key is the whole setup.
139
+
140
+ ## Typed decisions
141
+
142
+ The brief classification the planner performs (which module, spans several,
143
+ which specialist starts) can be answered by a decision provider instead of a
144
+ coding-agent turn. It is off by default, turned on per sandbox with
145
+ `decisionsEnabled` through `POST /sandbox/api/config`, and takes its credential
146
+ from the sandbox configuration or `TYPESAFE_API_KEY` in the environment or the
147
+ workspace `.env`. Answers below their confidence thresholds, a brief that spans
148
+ modules, and any provider failure all fall back to the workspace rules and the
149
+ planner turn. Implementation writing stays with the coding agent: a decision
150
+ provider generates no text and drives no tools. See
151
+ [`packages/sandbox/README.md`](https://github.com/flowdular/flowdular/blob/main/packages/sandbox/README.md) for the settings.
152
+
57
153
  ## How a session works
58
154
 
59
155
  A planner names the modules the brief touches and the first specialist role.
@@ -147,6 +243,21 @@ commits the same change on a branch and opens a pull request; the operator's
147
243
  working tree and index stay untouched because the work happens in a detached
148
244
  worktree under `.flowdular/sandbox/worktrees/<session id>`, removed afterwards.
149
245
 
246
+ For an application generated by the sandbox, open **GitHub settings** and then
247
+ **Set up repository**. Choose an existing empty GitHub repository or
248
+ create a new private one. The sandbox shows the repository, local commit and
249
+ target branch for review before the operator confirms the first push. It then
250
+ adds a local `app` remote, pushes `main` without rewriting remote history and
251
+ configures GitHub delivery to use that remote. A repository that already has
252
+ commits should be opened with `--connect` when launching the sandbox. GitHub
253
+ CLI authentication or a token saved in GitHub settings is needed for creating
254
+ and pushing; the setup action also requires `sandbox.modules.eject`.
255
+
256
+ After the module specification is approved and the gates pass, choose the
257
+ `git-pr` delivery target to push a branch and open its review. The sandbox does
258
+ not approve a specification or push module code merely because a repository
259
+ was connected.
260
+
150
261
  ### Configuration
151
262
 
152
263
  Project settings live in `flowdular.json` under `sandbox.delivery`, read at
@@ -3,7 +3,7 @@
3
3
  "inputRoots": [".ai"],
4
4
  "outputRoots": ["."],
5
5
  "targets": ["codexcli", "claudecode"],
6
- "features": ["rules", "skills"],
6
+ "features": ["rules", "skills", "subagents"],
7
7
  "delete": true,
8
8
  "verbose": false,
9
9
  "silent": false,
package/dist/bin.js CHANGED
@@ -386,6 +386,15 @@ function renderEnvironmentFile(secrets) {
386
386
  "",
387
387
  "# 32 byte keys, generated once for this app; MFA uses base64url.",
388
388
  ...SECRET_KEYS.map((key) => `${key}=${secrets[key]}`),
389
+ "",
390
+ "# Model key for the chat-first sandbox (pnpm sandbox). Paste one here and",
391
+ "# the sandbox offers that model on the next start, with no further setup.",
392
+ "# OPENAI_API_KEY, AZURE_API_KEY and AI_GATEWAY_API_KEY are read the same",
393
+ "# way, with the model chosen in the sandbox model settings. A variable",
394
+ "# exported in the shell wins over this file. The application's own agents",
395
+ "# keep their credentials in the workspace vault instead: open",
396
+ "# Administration, AI providers.",
397
+ "ANTHROPIC_API_KEY=",
389
398
  ""
390
399
  ].join("\n");
391
400
  }
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "create-flowdular",
3
- "version": "0.4.3",
3
+ "version": "0.5.1",
4
4
  "type": "module",
5
5
  "description": "Scaffold a Flowdular application: the platform, one example module and the secrets a fresh install needs.",
6
6
  "license": "MIT",
7
7
  "repository": {
8
8
  "type": "git",
9
- "url": "git+https://github.com/flowdular/flowdular.git",
9
+ "url": "git+https://github.com/Flowdular/flowdular.git",
10
10
  "directory": "packages/create-flowdular"
11
11
  },
12
12
  "homepage": "https://flowdular.com",
@@ -91,6 +91,13 @@ FD_DATABASE_MIGRATOR_PASSWORD=
91
91
  FD_DATABASE_RUNTIME_PASSWORD=
92
92
  FD_DATABASE_BACKGROUND_PASSWORD=
93
93
 
94
+ # Model key for a self-hosted sandbox. The application itself never reads it:
95
+ # agents.core keeps every provider credential sealed in the workspace vault,
96
+ # configured in Administration, AI providers. The sandbox reads this name,
97
+ # OPENAI_API_KEY, AZURE_API_KEY and AI_GATEWAY_API_KEY from the environment or
98
+ # from the workspace .env, and an exported variable wins over the file.
99
+ ANTHROPIC_API_KEY=
100
+
94
101
  # Prometheus exposition on GET /api/metrics. Leave FD_METRICS unset to keep it off.
95
102
  FD_METRICS=false
96
103
  FD_METRICS_TOKEN=
@@ -6,4 +6,6 @@ AGENTS.md
6
6
  CLAUDE.md
7
7
  .agents/skills/
8
8
  .claude/skills/
9
+ .claude/agents/
10
+ .codex/agents/
9
11
  .ai/references/
@@ -33,11 +33,13 @@ install. Data lives under `.flowdular/data`.
33
33
  ## Work with coding agents
34
34
 
35
35
  `AGENTS.md` and `CLAUDE.md` introduce the application contract. `.ai` contains the
36
- editable rules, skills, role prompts, policies, blueprints and reference module.
37
- Codex and Claude Code discover generated skills in `.agents/skills` and
38
- `.claude/skills`. Supporting guides are in `docs`.
36
+ editable rules, skills, role prompts, subagents, policies, blueprints and
37
+ reference module. Codex and Claude Code discover generated skills in
38
+ `.agents/skills` and `.claude/skills`, and the spec author, module executor and
39
+ reviewer as subagents in `.codex/agents` and `.claude/agents`. Supporting guides
40
+ are in `docs`.
39
41
 
40
- After editing `.ai/rules` or `.ai/skills`, run `pnpm rules:generate`.
42
+ After editing `.ai/rules`, `.ai/skills` or `.ai/subagents`, run `pnpm rules:generate`.
41
43
  `pnpm rules:check` detects drift and also runs as part of `pnpm verify`.
42
44
  Extend local modules using the published `@flowdular/sdk` imports. Installed
43
45
  SDK source is reference material and must not be edited in `node_modules`.
@@ -16,7 +16,7 @@
16
16
  "dependencies": {
17
17
  "octane": "0.1.51",
18
18
  "segment-state": "0.2.1",
19
- "@flowdular/sdk": "0.4.3"
19
+ "@flowdular/sdk": "0.5.1"
20
20
  },
21
21
  "devDependencies": {
22
22
  "@tsrx/typescript-plugin": "0.3.120",
@@ -9,7 +9,7 @@
9
9
  },
10
10
  "scripts": {
11
11
  "dev": "node --env-file-if-exists=.env platform/scripts/dev.mjs",
12
- "sandbox": "npx @flowdular/sandbox",
12
+ "sandbox": "flowdular-sandbox",
13
13
  "typecheck": "pnpm -r --if-present typecheck",
14
14
  "test": "pnpm -r --if-present test",
15
15
  "format": "prettier --write .",
@@ -23,9 +23,10 @@
23
23
  "build": "flowdular module sync --apply && pnpm --filter @app/platform build"
24
24
  },
25
25
  "devDependencies": {
26
+ "@flowdular/sandbox": "0.5.1",
26
27
  "@tsrx/prettier-plugin": "0.3.120",
27
28
  "prettier": "3.6.2",
28
- "flowdular": "0.4.3",
29
+ "flowdular": "0.5.1",
29
30
  "rulesync": "16.21.0"
30
31
  }
31
32
  }
@@ -3,8 +3,6 @@
3
3
  <head>
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <meta name="theme-color" content="#141B2E" />
7
- <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
8
6
  <style id="flowdular-critical-splash">
9
7
  html,
10
8
  body,
@@ -34,6 +32,9 @@
34
32
  letter-spacing: 0.14em;
35
33
  text-transform: uppercase;
36
34
  }
35
+ .flowdular-splash__brand::after {
36
+ content: var(--flowdular-brand, 'Flowdular');
37
+ }
37
38
  .flowdular-splash__stage {
38
39
  min-width: 190px;
39
40
  font-size: 10px;
@@ -80,27 +81,14 @@
80
81
  aria-hidden="true"
81
82
  >
82
83
  <g fill="#ffffff">
83
- <circle cx="7.9" cy="3.4" r="1.7" />
84
- <rect x="6.2" y="3.4" width="3.4" height="1.6" />
85
- <rect x="6.2" y="10.8" width="3.4" height="9.8" />
86
- <circle cx="7.9" cy="20.6" r="1.7" />
87
- <circle cx="16.1" cy="3.4" r="1.7" />
88
- <rect x="14.4" y="3.4" width="3.4" height="9.8" />
89
- <rect x="14.4" y="19" width="3.4" height="1.6" />
90
- <circle cx="16.1" cy="20.6" r="1.7" />
84
+ <rect x="2" y="3" width="20" height="5" rx="2.5" />
85
+ <rect x="8" y="10" width="14" height="5" rx="2.5" />
91
86
  </g>
92
87
  <g fill="#e08a45">
93
- <circle cx="3.4" cy="7.9" r="1.7" />
94
- <rect x="3.4" y="6.2" width="9.8" height="3.4" />
95
- <rect x="19" y="6.2" width="1.6" height="3.4" />
96
- <circle cx="20.6" cy="7.9" r="1.7" />
97
- <circle cx="3.4" cy="16.1" r="1.7" />
98
- <rect x="3.4" y="14.4" width="1.6" height="3.4" />
99
- <rect x="10.8" y="14.4" width="9.8" height="3.4" />
100
- <circle cx="20.6" cy="16.1" r="1.7" />
88
+ <rect x="14" y="17" width="8" height="5" rx="2.5" />
101
89
  </g>
102
90
  </svg>
103
- <span>Flowdular</span>
91
+ <span class="flowdular-splash__brand"></span>
104
92
  <small id="flowdular-splash-stage" class="flowdular-splash__stage"
105
93
  >Starting workspace</small
106
94
  >
@@ -15,7 +15,7 @@
15
15
  "@octanejs/vite-plugin": "0.1.51",
16
16
  "octane": "0.1.51",
17
17
  "pg": "8.23.0",
18
- "@flowdular/sdk": "0.4.3"
18
+ "@flowdular/sdk": "0.5.1"
19
19
  },
20
20
  "devDependencies": {
21
21
  "@octanejs/app-core": "0.0.47",
@@ -1 +1 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><rect width="24" height="24" rx="5.4" fill="#2557D6"/><g transform="translate(12 12) scale(0.64) translate(-12 -12)"><circle cx="7.9" cy="3.4" r="1.7" fill="#fff"/><rect x="6.2" y="3.4" width="3.4" height="1.6" fill="#fff"/><rect x="6.2" y="10.8" width="3.4" height="9.8" fill="#fff"/><circle cx="7.9" cy="20.6" r="1.7" fill="#fff"/><circle cx="16.1" cy="3.4" r="1.7" fill="#fff"/><rect x="14.4" y="3.4" width="3.4" height="9.8" fill="#fff"/><rect x="14.4" y="19" width="3.4" height="1.6" fill="#fff"/><circle cx="16.1" cy="20.6" r="1.7" fill="#fff"/><circle cx="3.4" cy="7.9" r="1.7" fill="#fff"/><rect x="3.4" y="6.2" width="9.8" height="3.4" fill="#fff"/><rect x="19" y="6.2" width="1.6" height="3.4" fill="#fff"/><circle cx="20.6" cy="7.9" r="1.7" fill="#fff"/><circle cx="3.4" cy="16.1" r="1.7" fill="#fff"/><rect x="3.4" y="14.4" width="1.6" height="3.4" fill="#fff"/><rect x="10.8" y="14.4" width="9.8" height="3.4" fill="#fff"/><circle cx="20.6" cy="16.1" r="1.7" fill="#fff"/></g></svg>
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><rect width="24" height="24" rx="5.4" fill="#141b2e"/><g transform="translate(12 12) scale(0.72) translate(-12 -12)"><rect x="2" y="3" width="20" height="5" rx="2.5" fill="#ffffff"/><rect x="8" y="10" width="14" height="5" rx="2.5" fill="#ffffff"/><rect x="14" y="17" width="8" height="5" rx="2.5" fill="#e08a45"/></g></svg>