Hand-build an app
Goal: without depending on “create from conversation”, using only this directory’s docs + a template walkthrough, hand-build a workspace app you can open, click, and optionally fet
Source docs/en/site/sdk-appsdk-build.md
Goal: without depending on “create from conversation”, using only this directory’s docs + a template walkthrough, hand-build a workspace app you can open, click, and optionally fetch data. Suggested reading order: README.
0. What you will build
Pick one of two paths:
| Path | Result | Must-read |
|---|---|---|
| A. Minimal notebook | New on top, list below, can delete | App UI description · App package layout |
| B. Do work + history (simplified profile style) | Form triggers a script; list shows results | Plus App actions · App data pipeline |
Complex visual operations console → read App views first; you should usually take pluginsdk.
1. Prep
- Local or test Cadau can open, and you are in a workspace.
- You know the runtime app directory (after create app):
{RUNTIME_DIR}/workspaces/{workspaceId}/apps/{appSlug}/
- If you will fetch: the workspace already has a data connection and usable predefined queries.
- After editing files, refresh the app page; custom scripts take effect on the next invoke (do not start a conflicting backend).
You can also use the “app-development assistant” to change a draft then publish; hand-editing the pack and the assistant change the same file conventions.
2. Path A: minimal notebook (30 minutes)
2.1 Create the shell
On the apps desktop, create a blank/notes-style app from a template, or copy an existing minimal pack. Confirm you have app.json, data/app.db, logic/handlers.py (may be a stub).
2.2 Write app.json
Requirements:
version: 1- ≥1
entities(fields havelabel/type) views: oneform(mode: "create") + onelist- No custom
actions(unless you are about to do path B)
Full field rules: App UI description §7 notes example.
2.3 Mental validation list
- Entity names, field names: lowercase + underscore
form/listentitypoints at a real entity- List
columnsuse existing field names
2.4 Verify
Open the app → submit one row → it appears in the list → delete (if allow_delete is not off). The whole path is built-in CRUD; handlers need not implement business.
3. Path B: add a “do work” action
On path A, add “input params → script → (optional) attachment/history”.
3.1 Change the UI description
- Add an
actionsitem:id,label,impl: "script",params(source of form controls). - Add an
action_formtoviews,action= that id. - If you want history: add fields such as
status,upload_idon the entity; keeplistcolumnsin sync.
Note: action-form controls = actions[].params, not views[].fields.
3.2 Change logic/handlers.py
Implement handle with a branch for that action:
- Validate params
- Need fetch →
ctx["platform"].query_run(or call yourgenerate_*.py) - Need a report →
save_upload, returnupload_id - Need history → write
ctx["app_db"]
Template-level notes: App actions.
3.3 Optional: pipeline fetch
- Prepare
query_ids on the data connection. - Add
references/pipeline.json. - Add
logic/generate_*.pyto run step by step. - When you need a layout, add
assets/*.html+scripts/assemble_*.py.
Details: App data pipeline. Walkthrough implementation: the workspace “employee profile” app pack, or the built-in template backend/internal/skillfromchat/bundled/employee-profile/.
3.4 Verify
- Submit the action: success hint / error copy matches expectations
- With
upload_id: can open or download - List status and notes update
- Intentionally wrong
query_id: you should see a clear error orwarnings, not a blank success
4. How “UI from a picture” lands in a hand-build
| Picture content | What to do |
|---|---|
| Result-report sample | Path B + HTML template (App views §4A) |
| Complex operations-console sample | Do not force the three views → pluginsdk |
| Only tweak an existing form/list | Change app.json; or give the picture to the app-development assistant and describe intent |
5. From-scratch checklist (complete app)
UI
- [ ] Only
list/form/action_form - [ ] Each
action_formid is found inactions - [ ] Action-form fields are on
params - [ ] User-visible copy is all in
label
Pack
- [ ]
app.jsonis valid (check whether platform save/publish errors) - [ ] With script actions,
handlers.handlecovers every id - [ ] Scripts only access external data via the platform bridge
- [ ] Templates/pipelines live in
assets/scripts/references/logic
Fetch (if needed)
- [ ] Predefined queries match the pipeline
- [ ] Main-step failure has a clear error; optional steps go into
warnings - [ ] Return value includes frontend-agreed fields
Experience
- [ ] Can open from the apps desktop
- [ ] Main path clicked through by hand
- [ ] (Optional) whether conversation skills need a matching register action such as
profile.register
6. Document map
| Question | Document |
|---|---|
| How to write fields/views | App UI description |
| Why no layout, UI from a picture | App views |
| What in the directory is required | App package layout |
| Code behind a button | App actions |
| Multi-step query and reports | App data pipeline |
| Mechanism and HTTP | Workspace apps |
7. Common stuck points
| Stuck | What to do |
|---|---|
Changed views[].fields and the action form did not change | Change actions[].params |
| Local py change seems not to take effect | Confirm you edited the current workspace runtime pack; submit the action again |
| Validation “need at least one entity” but you only want a button | Keep a placeholder entity + list/form, or reuse a history-table entity |
| Want a wizard overlay / multi-step UI | Not supported today → plugin, or split into several action forms |