From simulator
Creates and edits Smart Forms (CDU/Script/Application) on the Simulator.Company platform. Handles page layout, locale, viewModel, styles, definitions, widgets, and release management.
How this skill is triggered — by the user, by Claude, or both
Slash command
/simulator:simulator-smart-formsThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
You are a specialist in creating and editing **Smart Forms** (also called CDU,
You are a specialist in creating and editing Smart Forms (also called CDU,
Script, or Application) on the Simulator.Company platform, using the simulator
MCP server plus the pullSmartForm / pushSmartForm engine tools.
A Smart Form is three things in one:
| Layer | What it is |
|---|---|
| Actor | An actor in the scripts system form — has id, ref, title, data.sharedWith |
| Versioned project | A folder/file tree per environment: pages, locale, viewModel, styles, definitions, widgets |
| Backend binding | Corezoid credentials per env — dynamic data and control flow come from Corezoid at runtime |
A Smart Form is not a data-schema form. Compare:
Regular Form (template) Smart Form (actor + app)
────────────────────── ───────────────────────
defines field schema is itself an actor in "scripts" form
actors are instances of it carries pages/styles/i18n as versioned files
account definitions Corezoid process supplies runtime data
Every Smart Form always has exactly two environments:
| Env | readonly | Purpose |
|---|---|---|
develop | false | Active editing target |
production | true | Live, served to end users |
Rule: never write directly to production. Edit develop, then deploy
develop → production via a release. The server rejects writes to readonly envs.
After pullSmartForm the local directory looks like:
<actorId>/
develop/
.manifest.json ← file IDs + SHA-256 hashes (used by pushSmartForm)
pages/
index/
config ← Page layout (JSON): { grid, forms[] }
locale ← Page-scoped i18n (JSON): { key: { en: "…", uk: "…" } }
locale ← App-wide i18n (JSON)
viewModel ← Default view-model values (JSON)
widgets ← Widget/control settings (JSON, ctrlSettings)
definitions/
button ← Reusable component fragment (JSON), used via "$ref": "#/button"
styles/
index ← Less stylesheet (text/css); compiled → scoped CSS at serve time
production/
.manifest.json
… (same tree, read-only)
| File | MIME | Purpose |
|---|---|---|
pages/<id>/config | application/json | Page layout: grid + forms + sections + items |
pages/<id>/locale | application/json | Page-level i18n strings |
locale | application/json | App-wide i18n strings; merged with page locale at serve time |
viewModel | application/json | Default values; merged with Corezoid-supplied viewModel at serve time |
definitions/<name> | application/json | Reusable component fragments; inlined by $ref at serve time |
styles/index | text/css | Less source; compiled to scoped CSS (.cdu-page scope); @import "pages/<id>/style" works |
widgets | application/json | ctrlSettings for embedded third-party widgets |
createSmartForm(title="My App", ref="my-app")
→ { actorId: "...", envs: [{id: 1, title: "develop"}, {id: 2, title: "production"}] }
corezoidCredentials is optional — omit it for static/design-only forms and configure the Corezoid binding later.
Then immediately pull the default skeleton:
pullSmartForm(actorId="<uuid>")
→ downloads all envs to <actorId>/develop/ and <actorId>/production/
1. pullSmartForm(actorId="<uuid>")
→ downloads all envs to <actorId>/develop/ and <actorId>/production/
→ writes .manifest.json (file IDs + hashes) in each env dir
2. Edit files under <actorId>/develop/
(page config, locale, viewModel, styles, definitions)
3. pushSmartForm(actorId="<uuid>")
→ walks <actorId>/develop/, diffs every file/folder against .manifest.json
→ validates new + changed files against the CDU page protocol schema
→ if errors: aborts with { validationErrors: [...] } — fix and retry
→ POSTs new folders (parents first) and new files, then PUTs modified files
→ updates .manifest.json with returned ids + content hashes
→ returns { created: { folders, files }, updated, unchanged, orphanFiles }
4. Deploy (when ready to publish):
deploySmartForm(actorId="<uuid>")
→ deploys develop → production; returns { releaseId, releaseNumber, status }
pages/<id>/config)The page config is the layout template. Structure: Page → Grid → Form → Section → Item.
{
"grid": {
"type": "one_column",
"components": {
"center": ["main"]
}
},
"forms": [
{
"id": "main",
"title": "My Form",
"sections": [
{
"id": "body",
"type": "body",
"content": [
{
"id": "greeting",
"class": "label",
"value": "[[hello]]"
},
{
"id": "name",
"class": "edit",
"value": "{{defaultName}}",
"type": "text",
"placeholder": "Enter your name",
"required": true
},
{
"id": "submit",
"class": "button",
"title": "Submit",
"type": "default"
}
]
}
]
}
]
}
{
"type": "one_column" | "two_column",
"header": {
"class": "default" | "steps",
"extra": { "steps": ["Step 1", "Step 2"], "active": 1 }
},
"components": {
"header": ["<formId>"],
"left": ["<formId>"],
"center": ["<formId>"],
"right": ["<formId>"],
"footer": ["<formId>"],
"sidebar": ["<formId>"]
},
"styleClass": "custom-grid"
}
{
"id": "info",
"title": "Details",
"styleClass": "card",
"visibility": "visible", // "visible" | "disabled" | "hidden"
"sections": [ /* Section[] */ ]
}
{
"id": "s1",
"type": "body", // "body" | "block" | "modal" | "float"
"visibility": "visible",
"header": [ /* Item[] — Label | Button only */ ],
"content": [ /* Item[] — the main items */ ]
// modal-only: "modalHeader": [ /* Item[] */ ],
// "modalSize": "small"|"medium"|"large"|"xlarge",
// "modalCloseConfirmText": "…"
}
block renders as a grouped card; modal/float are overlays.
⚠️ A section has no
footer— onlyheaderandcontent(plusmodalHeaderfor modals). Put bottom-of-form actions as the last items incontent, or in a separate form bound to the grid'sfooterregion. (The grid'scomponents.footerabove holds form ids, not items.)
Every item has class + base fields (id, value, visibility, required, error,
errorMsg, styleClass, row, w). Below are the most common components:
Layout/rendering gotchas (verified live — full list in
docs/user-flows/cdu-page-protocol.md§12):rowrenders as a CSS table so its items do not wrap (usedisplay:inline-blockitems directly incontentfor a wrapping grid);styleClasson arowis dropped from the DOM (style leaf items, not therow); aradio/selectoption dot is a JSS-hashed<i class="i-icon-…">(not the<input>) — restylable via[class*="icon"]/[class*="content"](match by substring; thei-prefix isn't stable across builds) but that couples to renderer internals, sobuttons are the version-proof alternative for card pickers; and a field withvisibility:"visible"but hidden via CSS still submits (the idiomatic hidden value carrier).
class | value type | Key extra / options |
|---|---|---|
edit | string | type: text email int float phone multiline date password colorPicker; placeholder, regexp, mask, submitOnEnter |
select | string | options: [{title, value, visibility, icon, tooltip, avatar, badge, styleClass}]; type: default autocomplete; submitOnChange |
multiselect | string[] | options: [{title, value, visibility, tooltip}]; extra.length (max) |
radio | string | options: [{title, value, visibility}]; extra.direction: row|column |
check | boolean | checkbox |
toggle | boolean | title label on the switch |
slider | number | extra: { min, max, step } |
phone | {countryCode, number} | options (country codes), regexp |
otp | object otp-0…otp-N | extra.length (2–20); type: text|int |
class | Notes |
|---|---|
label | Static text; supports [[locale]] and {{viewModel}} tokens; BBCode rendered; align: left|center|right |
divider | Visual separator; no value |
image | value = src URL; extra: {alt, height, width} |
carousel | items[] (slides); extra: {autoplay, interval} |
timer | value = remaining ms; extra.duration |
comments | Comment thread widget; title |
class | Notes |
|---|---|
button | title (bbcode), type: default secondary tertiary text error; submits its form. extra.url opens the URL instead of submitting (extra.target _self|_blank — newer renderers only; older open same-tab); extra.action:'logout'; extra.request (bare fetch first, submits only if it resolves); extra.autoSubmit {interval,maxCount} (interval clamped 5–60s); extra.options[] (menu — bypasses url/request/action/submit); extra.icon, rounded, mobileVisible |
copy | value = text to copy; title = button label |
class | Notes |
|---|---|
table | head: [{id,title}], body: [{<id>: value}]; type: default radio check; submitOnChange, submitOnScroll |
tab | options: [{id,title}]; value = selected id; submitOnChange |
stepper | options: [{id,title}]; value = step; extra.direction |
mainMenu | Nested navigation; options tree |
class | Notes |
|---|---|
file | Preview/download; value: FileProps; extra: {downloadUrl, auth} |
upload | File upload; type: default|webcam; extra: {accept, minSize, maxSize} |
attachment | Multi-file viewer; value: FileProps[]; extra.downloadUrl |
signature | Canvas signature → base64; extra: {strokeStyle, saveButtonTitle} |
Do not hand-author a row component. Lay items out with the base row / w fields that
every component carries: give sibling items the same row string to place them on one line, and
set w (relative weight — rendered width is w / Σw of the row) on each. The renderer
synthesizes a row component from them. For drag-reorder use a section with sortable: true +
contentLoop (there is also a standalone draggable component, but the section route is the usual
one). row and draggable are absent from the Scripts swagger, so prefer these field-based paths.
// two items on one line, 50% each (equal weights → each gets half the row):
{ "id": "first", "class": "edit", "type": "text", "row": "1", "w": "50" }
{ "id": "last", "class": "edit", "type": "text", "row": "1", "w": "50" }
class: "widget" — type: iframe onfido twilio amazonConnect webComments. Each type has its own extra schema.
All substitution is server-side — the renderer receives concrete values.
| Syntax | Source | Example |
|---|---|---|
[[key]] | locale (app + page merged) | "[[hello]]" → "Hello" in en |
{{key}} | viewModel (default + Corezoid-supplied merged) | "{{userName}}" → "Alice" |
"$ref": "#/button" | definitions/button file | inlined at serve time |
contentLoop | section array expansion | one template → N rows |
| BBCode | label/button/edit/check titles | [b] [i] [u] [color=#f00] [size=N] [br], and [url=https://…]text[/url] → clickable <a target="_blank"> (inline link; [iurl=…] opens same-tab; renderer supports more, e.g. [bg]). Raw <a> HTML is escaped — use [url]. |
{
"hello": { "en": "Hello", "uk": "Привіт" },
"submit": { "en": "Submit", "uk": "Надіслати" }
}
{
"defaultName": "Anonymous",
"maxItems": 10
}
{
"class": "button",
"title": "[[submit]]",
"type": "default"
}
Used in config as { "$ref": "#/button" } — the whole object is replaced.
When Corezoid returns code: 200, the server sends changes[] — surgical patches
to the live page without a full re-render:
[
{
"id": "name", // item / section / form id
"class": "edit", // component class (for item changes)
"value": "Alice",
"visibility": "visible",
"error": false,
"required": true
},
{
"id": "status",
"options": [{"id":"a","title":"Active"},{"id":"i","title":"Inactive"}],
"changeRules": {
"options": { "action": "replace" } // concat | unshift | delete | replace | merge
}
}
]
code: 205 → re-render a whole page (can switch to a different pageId).
code: 302 → redirect to nextPage.
Use the createSmartForm MCP tool — it calls POST /papi/1.0/applications/<accId> internally
and returns the actor ID plus both env IDs ready for use.
createSmartForm(
title="My App",
ref="my-app",
description="Optional", // optional
sharedWith="userList", // optional, default userList
apiLogin="...", // optional — set Corezoid binding later if omitted
apiSecret="...",
procId="...",
companyId="..."
)
→ { actorId: "...", ref: "my-app", title: "My App",
envs: [{ id: 12, title: "develop", readonly: false },
{ id: 13, title: "production", readonly: true }],
next: "run pullSmartForm(actorId=...) to download the initial file tree" }
sharedWith values: userList | allWorkspaceUsers | allRegisteredUsers | anyone.
Corezoid credentials are optional at creation time and can be configured later.
After creation always run pullSmartForm to download the default file skeleton before editing.
deploySmartForm(actorId="<uuid>")
→ { actorId, sourceEnv: "develop", targetEnv: "production",
releaseId, releaseNumber, status: "active" }
sourceEnv and targetEnv default to develop and production; pass them explicitly
to deploy between non-standard envs.
listReleases(actorId="<uuid>") // production releases (default)
listReleases(actorId="<uuid>", env="develop")
→ { actorId, env, releases: [{ id, release_number, status, created_at, … }] }
diffReleases(actorId="<uuid>", releaseId="5", vsReleaseId="3")
→ { added[], removed[], modified[] }
Compared by source_hash — no file bytes transferred. Use before a rollback to preview
what will change.
rollbackRelease(actorId="<uuid>", releaseId="3")
→ { actorId, rolledBackTo: "3", newReleaseId, releaseNumber, status: "active" }
Rollback is forward-only: a new active release is created whose content equals
the target release. History is never rewritten.
Retention: 5 releases per env (objects). Older release manifests remain for audit; only objects are GC'd. A release outside the 5-release window cannot be rolled back.
// List versions of a file (fileId from .manifest.json)
getFileHistory(actorId="<uuid>", fileId=12345)
getFileHistory(actorId="<uuid>", fileId=12345, limit=20, offset=0)
→ list of { versionId, operation, createdAt, … }
// Fetch source of a specific version
getFileVersion(actorId="<uuid>", fileId=12345, versionId="<id>")
→ { source: "…full file content…" }
// Restore file to a prior version (creates a new version; run pullSmartForm to refresh local)
rollbackFile(actorId="<uuid>", fileId=12345, versionId="<id>")
// List soft-deleted objects in an env
listTrash(actorId="<uuid>") // develop (default)
listTrash(actorId="<uuid>", env="production")
→ list of { objectId, title, objType, deletedAt, … }
// Restore a deleted object
restoreFromTrash(actorId="<uuid>", objectId="<id>")
All writes create a before-state history row (operation: create|update|move|rename|delete).
Retention: 50 versions per file.
develop — the server rejects writes to production (readonly env).pullSmartForm before editing — establishes .manifest.json needed by pushSmartForm.source opaque — no structural validation of config, viewModel, or locale JSON at save time. Validate page JSON against the CDU protocol before pushing.$ref resolves to {} — a definitions/button reference to a non-existent fragment silently produces an empty object at serve time. Always verify definition names.is_system files (styles/index, pages/index/config, etc.) cannot be renamed or deleted. Edit their content freely; rename/delete will fail.styles/index is compiled with Less at serve time, wrapped in .cdu-page {}. Use Less syntax; @import "pages/<id>/style" imports page-level stylesheets.viewModel values and control flow (code 200/205/302).// 1. Pull all files to develop/
pullSmartForm(actorId="69bbd03e-0d4c-4122-9234-e06ffe9ca1eb")
→ { envs: [{ env: "develop", dir: "…/develop", files: 11 }, { env: "production", … }] }
// 2. Edit pages/index/config — add a new label item to the body section
// 3. Push changes (also creates new pages/folders if you added them locally)
pushSmartForm(actorId="69bbd03e-0d4c-4122-9234-e06ffe9ca1eb")
→ { created: { folders: 0, files: 0 }, updated: 1, unchanged: 10, orphanFiles: [] }
// Adding a new page? Just create the files locally and push:
// develop/pages/survey/config (page layout JSON)
// develop/pages/survey/locale (page i18n JSON)
// → pushSmartForm POSTs the "survey" folder, then both files inside it,
// then writes their ids back into .manifest.json. No PUT/PATCH needed.
// 4. Deploy to production
deploySmartForm(actorId="69bbd03e-0d4c-4122-9234-e06ffe9ca1eb")
→ { releaseId: 5, releaseNumber: 3, status: "active" }
A Smart Form is an actor — but you can also embed and run it inside another actor's card or inside a reaction, so the smart form shows up where the user is working:
appId = the Smart Form actor id on createActor /
updateActor; the actor's card then renders/runs that smart form. appSettings tunes it:
{ autorun:boolean, expired:int (unix s), users:int[], groups:int[], fullWidth:boolean } (or null).
updateActor(formId=<f>, actorId="<actorId>",
appId="<smartFormActorId>", appSettings={ "autorun": true })
appId / appSettings on createReaction / updateReaction — the
reaction renders the smart form (e.g. drop an interactive form into a discussion thread).
createReaction(type="comment", actorId="<actor>", appId="<smartFormActorId>",
appSettings={ "fullWidth": true })
Also see
simulator-reactions(theappId+extra.linkedActorId+[application=…]chip) anddocs/entities/reactions.md→ "Embedding".
All Smart Form operations use the public API (/papi/1.0/...) with an OAuth2 bearer token.
| Operations | Required scope |
|---|---|
| Read (get app, envs, releases, file history, env struct, folder content) | actors.readonly |
| Write (create app, edit content, deploy, rollback, restore trash) | actors.management |
| Page serving (render/submit pages) | actors.readonly + sharedWith rules |
pullSmartForm and pushSmartForm both require actors.management scope.
This skill covers page structure — pages, locale, viewModel defaults, and the layout JSON
(grid/forms/sections/items) plus the styleClass hooks you attach to them. It does not
author the CSS/Less itself. When the user wants to style / restyle / theme a Smart Form —
write the style / styles/ layer, re-skin a component, build a design system, fix
spacing/colors/fonts — switch to the simulator-styles skill. It reuses the same
pull/push/deploy cycle and consumes the styleClass hooks defined here.
For the backend that produces dynamic viewModel values, handles /send submits, and
mutates the page via changes[], switch to the simulator-smart-forms-logic
skill. It is a brief generator: it translates the user's intent into prompts for
the Corezoid plugin's corezoid-create / corezoid-edit skills, and binds the
resulting bound process to the Smart Form env via corezoidCredentials / procId.
| Path | When to read |
|---|---|
$CLAUDE_PLUGIN_ROOT/docs/user-flows/smart-forms.md | Full lifecycle, data model, deploy/release internals, access model, API reference table |
$CLAUDE_PLUGIN_ROOT/docs/user-flows/cdu-page-protocol.md | Complete component catalogue, templating, change protocol, server-side save validation |
$CLAUDE_PLUGIN_ROOT/skills/simulator-styles/SKILL.md | Style/restyle the form: the style / styles/ (Less) layer, theming, component re-skinning |
$CLAUDE_PLUGIN_ROOT/skills/simulator-smart-forms-logic/SKILL.md | Author + bind the Corezoid backend processes for this Smart Form |
npx claudepluginhub corezoid/simulator-ai-plugin --plugin simulatorAuthors and evolves the Corezoid backend behind Simulator.Company Smart Forms — dynamic viewModel, submit handling, page transitions, notifications, and actor write-backs. Acts as an orchestrator that prepares briefs for Corezoid create/edit skills.
Reference for 1C managed form layout patterns including document, data processor, list, catalog element, and wizard forms. Use before designing forms to apply archetypes, naming conventions, and advanced techniques.
Provides step-by-step guidance and generates configurations for form builder helper tasks in business automation workflows.