The shape of the system in one page. Read this first — everything else assumes it.
Deeper: the process engine for how a run advances, storage for what is persisted, expressions for how templates make decisions.
A template is a recipe. A process is one cooking of it.
You write a template once. Every time someone starts it you get a new process — a live instance carrying its own answers.
Template "allocation-risk-assessment-app" written once │ ├── Process a3f9… Acme deal running ├── Process 7b21… Beta deal completed └── Process c40e… Gamma deal running
A template holds steps[], but array order means nothing. Each step names the next
one:
firstStepKey ──▶ input_description
│ nextStepKey
▼
review_description
│ nextStepKey
▼
review_condition ◀── a condition step
│ │
thenStepKey ──┘ └── elseStepKey
│ │
▼ ▼
input_risk_model input_description ← loops back
│
▼
agent_recommendation
│
▼
nextStepKey: null ──▶ run completes
A condition step uses thenStepKey / elseStepKey instead of nextStepKey. Pointing
elseStepKey at an earlier step is how you build a loop. nextStepKey: null ends the
run.
Every value a run collects lands in process.context, keyed by step:
context.input_description = { description: "we need a new halo because…" }
context.review = { approved: true }
context.run = { dealName: "Acme" } // reserved, from runInputs
A later step reads an earlier answer as ${input_description.description}.
Every write appends to stepContextAudit[] — { at, userId, stepKey, updates } —
append-only, never overwritten. Automation writes use the actor id "system". That
trail is what audit and export reads.
This is the distinction everything else follows from.
input | The other 11 | |
|---|---|---|
| Waits for | a person | nothing |
| Advanced by | an API request | the worker, every second |
| Fails how | rarely | three different ways |
Without a worker running, only input steps advance. npm run dev starts the web
app alone; npm run dev:all starts both. A run that reaches a condition or a
slack_notify step with no worker sits there indefinitely.
See the process engine for how each stage works.
Storage — MongoDB or JSON files, decided once at startup by whether
MONGO_URL is set.
Permissions — Auth0 with Organizations, checked at process/start access, step contracts and field permissions. Source-only metadata is not an authorization guarantee.
Expressions — one evaluator, six slots. How a template branches, gates completion, and shows or hides fields without code changes.
UI — the read-only template viewer and process runner.
Skins — the same app deployed several times with different branding and navigation. Never an access boundary.
App source / Domain + Integration builders → author-time expansion and compilation → registration: immutable execution + versioned presentation → start: pin execution, initialize memory, enter named activity → human interaction OR generic effect suspension → authorized command / journaled effect outcome → continue, finish, fail, or hold for reconciliation
The executor reduces closed syntax, not source strings or provider handler names. Current presentation may overlay a public read without changing pinned execution. The machine distinguishes failure; the public compatibility status remains completed plus error. See engine and language.