Appearance
Actions
Actions are Atelier's execution plane: the bridge between what exists (your entities) and what is experienced (your screens). Where entities answer "what is true," actions answer "what can happen — to whom, under what conditions, and with what consequences."
You never write code to add a behavior. You declare an action and its parts, and Atelier compiles that declaration, at request time, into a uniform pipeline that validates inputs, checks preconditions, applies changes, records an audit trail, and triggers any follow-on effects. Adding a new behavior means adding a declaration — never editing an engine.
What an action is made of
An action is one declaration plus a small set of composable parts:
| Part | Purpose |
|---|---|
| Parameters | The inputs the action collects — the fields of its form, with types, constraints, and where their values may come from. |
| Preconditions | The criteria that must hold for the action to run: a field comparison, a computed check, or a reachability rule. If they fail, the action is refused with a clear message. |
| Edits | Declarative changes the action applies to the target entity once its preconditions pass. |
| Creates | Related records the action brings into being as part of the same operation. |
| Side effects | Events the action emits — most commonly a notification — when it succeeds. |
| Placements | Where the action appears: each placement projects the action onto a surface as a button or a form. |
Because placements are separate from the action itself, any action can appear on any compatible surface, and the catalog of actions grows simply by declaring more of them.
Inputs and value sources
Every input — and every value an action writes — can be a literal or a reference. References let an action pull from the submitted parameters, the target entity, the current user, the current time, a freshly created record, or a named function. This is what lets a single declaration express rich behavior without bespoke code: "set the resolver to the current user," "stamp the closed-at time to now," "copy the parent's organization onto the new record."
From a simple change to a durable workflow
Actions span a spectrum. A simple action — acknowledge, assign, close — runs synchronously and returns immediately. When an action is marked as a workflow, it is handed to Atelier's durable-execution backend instead: it can run for a long time, retry safely, pause for human approval, and call back to apply its results. The same declaration model covers both; you choose the execution mode. See Workflows & durable execution.
How it's declared
Two authoring surfaces, one model:
- The visual editor. A guided editor organizes the work into tabs — Purpose, Inputs, Rules, Effects, Placements, and Activity — so an operator can compose an action, prefill inputs from an entity's schema, add preconditions, wire up notifications, and place the action on a surface, all with a live preview that matches what ships.
- The declarative sheet. The same action is expressible in a Vertical Sheet under an
actions:block, with nestedparameters,creates,edits,submission_criteria,side_effects, andplacements. See Vertical Sheets.
There is no third, hidden source of truth: the declaration is the runtime contract.
How it's provisioned
Action types are part of the shared catalog. The complete, worked set of actions lives on the template and is copied — and rewired to stand alone — when you fork the template onto a new tenant. Afterward, each tenant edits its own actions with the same editor, with no special-case code. See Provisioning & tenancy.
Extension points
- A new action — declare it in a sheet's
actions:block or create it in the editor. - A new value source — extend the reference resolver and it becomes selectable in the editor.
- A new function — register a named function and reference it as a value or a precondition.
- A new precondition operator — add it to the operator set; it becomes available to every action.
- A new surface — register a surface and actions can be placed on it; no enums to edit.
- A new notification — declare the action's side effect and subscribe a rule to it. See Notifications.
What you can rely on
- The declaration is the contract. Actions are compiled from their declarations on demand — there is no stale cache to fall out of sync.
- The editor and the runtime agree. Whether an action is offered to a user is decided by the same preconditions the runtime enforces when it runs, so a visible action is a runnable one.
- Every run is audited. Each action records an activity entry — who did what, and what changed — which powers the Activity view.
- Effects never compromise the action. Notifications, events, and audit are best-effort and never cause an otherwise-valid action to fail.
Example: an action in a sheet
A staff-facing Validate action on a proposal. It collects no input, refuses unless the proposal is in received state, writes status: validated, emits a notification, and appears as a button on the admin detail page.
yaml
actions:
- key: validate_proposal
label: Validate
target_model: proposal
execution_mode: engine
status: published
is_active: true
permissions_policy:
view: ["admin", "staff"]
apply: ["admin"]
edit: ["admin"]
submission_criteria:
- key: must-be-received
criteria_type: field
config: { field: status, operator: eq, value: received }
message: Only received proposals can be validated.
order: 1
is_active: true
edits:
- { key: set-validated, field: status, value: validated }
side_effects:
- { key: proposal_validated, type: notification, event_type: proposal_validated, event: proposal_validated, order: 1, is_active: true }
placements:
- { key: validate-on-detail, surface: admin_detail, target_model: proposal }Values can be literals or references — $parameters.x, $entity.x, $user, $now, or $function.name — so an action expresses rich behavior without code.