Docs: add execution-modes.md (RUNTIME vs WORKFLOW), update HEAR and script-execution-flow

This commit is contained in:
Rodrigo Rodriguez (Pragmatismo) 2026-03-16 23:28:12 -03:00
parent ae09de3dea
commit 94ca51a670
4 changed files with 130 additions and 0 deletions

View file

@ -0,0 +1,116 @@
# BASIC Execution Modes: RUNTIME vs WORKFLOW
General Bots BASIC scripts run in one of two execution modes. The mode is selected by a pragma at the top of the `.bas` file.
## Quick Comparison
| Feature | RUNTIME mode (default) | WORKFLOW mode ⚗️ |
|---------|----------------------|-----------------|
| **Pragma** | _(none)_ | `#workflow` |
| **Engine** | Rhai AST | Step engine + PostgreSQL |
| **HEAR behavior** | Blocks a thread | Suspends to DB, zero threads |
| **Server restart** | Loses position | Resumes exact step |
| **Side-effect re-run** | ❌ possible on crash | ✅ never |
| **Multi-day flows** | ❌ (1h timeout) | ✅ unlimited |
| **FOR EACH loops** | ✅ | ❌ |
| **FUNCTION / SUB** | ✅ | ❌ |
| **USE WEBSITE** | ✅ | ❌ |
| **Startup time** | ~1ms | ~2ms |
| **RAM per session** | 1 thread (~64KB) | 0 threads |
| **Observability** | Logs only | DB rows (queryable) |
| **Best for** | Tools, short dialogs | Multi-step dialogs, tickets, approvals |
---
## RUNTIME Mode (default)
Every `.bas` file without `#workflow` runs in RUNTIME mode. The script compiles to a Rhai AST and executes in a `spawn_blocking` thread. `HEAR` blocks the thread until the user replies (up to `hear-timeout-secs`, default 3600).
```basic
' ticket.bas — RUNTIME mode (no pragma)
TALK "Describe the issue"
HEAR description ' blocks thread, waits
SET ticket = CREATE(description)
TALK "Ticket #{ticket} created"
```
**When to use:** Tool scripts called by LLM, short dialogs (< 10 minutes), scripts using `FOR EACH`, `FUNCTION`, or `USE WEBSITE`.
---
## WORKFLOW Mode ⚗️
> **Status: Planned feature** — see `botserver/WORKFLOW_PLAN.md`
Add `#workflow` as the first line. The compiler produces a `Vec<Step>` instead of a Rhai AST. Each step is persisted to `workflow_executions` in PostgreSQL before execution. On `HEAR`, the engine saves state and returns — no thread held. On the next user message, execution resumes from the exact step.
```basic
#workflow
' ticket.bas — WORKFLOW mode
TALK "Describe the issue"
HEAR description ' saves state, returns, zero threads
SET ticket = CREATE(description)
TALK "Ticket #{ticket} created"
```
**When to use:** Multi-step dialogs, ticket creation, approval flows, anything that may span minutes or days.
### Keyword compatibility in WORKFLOW mode
| Category | Keywords | WORKFLOW support |
|----------|----------|-----------------|
| **Dialog** | `TALK`, `HEAR`, `WAIT` | ✅ |
| **Data** | `SET`, `GET`, `FIND`, `SAVE`, `INSERT`, `UPDATE`, `DELETE` | ✅ |
| **Communication** | `SEND MAIL`, `SEND TEMPLATE`, `SMS` | ✅ |
| **AI** | `USE KB`, `USE TOOL`, `REMEMBER`, `THINK KB` | ✅ |
| **HTTP** | `GET` (http), `POST`, `PUT`, `PATCH`, `DELETE` (http) | ✅ |
| **Scheduling** | `SCHEDULE`, `BOOK`, `CREATE TASK` | ✅ |
| **Expressions** | `FORMAT`, math, datetime, string functions | ✅ (via Rhai eval) |
| **Control flow** | `IF/ELSE/END IF` | ✅ |
| **Loops** | `FOR EACH / NEXT` | ❌ use RUNTIME |
| **Procedures** | `FUNCTION`, `SUB`, `CALL` | ❌ use RUNTIME |
| **Browser** | `USE WEBSITE` | ❌ use RUNTIME |
| **Events** | `ON EMAIL`, `ON CHANGE`, `WEBHOOK` | ❌ use RUNTIME |
### How WORKFLOW compiles
The compiler does **not** use Rhai for workflow mode. It is a line-by-line parser:
```
TALK "Hello ${name}" → Step::Talk { template: "Hello ${name}" }
HEAR description → Step::Hear { var: "description", type: "any" }
SET x = score + 1 → Step::Set { var: "x", expr: "score + 1" }
IF score > 10 THEN → Step::If { cond: "score > 10", then_steps, else_steps }
SEND MAIL to, s, body → Step::SendMail { to, subject, body }
```
Expressions (`score + 1`, `score > 10`) are stored as strings and evaluated at runtime using Rhai as a pure expression calculator — no custom syntax, no side effects.
### Observability
In WORKFLOW mode, every step is a DB row. You can query execution state directly:
```sql
SELECT script_path, current_step, state_json, status, updated_at
FROM workflow_executions
WHERE session_id = '<session-uuid>'
ORDER BY updated_at DESC;
```
---
## Choosing a Mode
```
Does the script use FOR EACH, FUNCTION, or USE WEBSITE?
YES → RUNTIME (no pragma)
Does the script have HEAR and may run for > 1 hour?
YES → WORKFLOW (#workflow)
Is it a tool script called by LLM (short, no HEAR)?
YES → RUNTIME (no pragma)
Is it a multi-step dialog (ticket, approval, enrollment)?
YES → WORKFLOW (#workflow) ⚗️ when available
```

View file

@ -2,6 +2,17 @@
The `HEAR` keyword pauses script execution and waits for user input. With optional type validation, it automatically verifies and normalizes input, retrying with helpful error messages when validation fails. The `HEAR` keyword pauses script execution and waits for user input. With optional type validation, it automatically verifies and normalizes input, retrying with helpful error messages when validation fails.
## Execution behavior by mode
| Mode | HEAR behavior | Thread held | Crash-safe |
|------|--------------|-------------|------------|
| **RUNTIME** (default) | Blocks `spawn_blocking` thread | Yes (up to `hear-timeout-secs`) | No |
| **WORKFLOW** ⚗️ | Saves state to DB, returns immediately | No | Yes |
In RUNTIME mode, the script thread is suspended — not re-run from the top. All code above `HEAR` does **not** execute again when the user replies. The timeout is configurable via `hear-timeout-secs` in `config.csv` (default: 3600 seconds).
In WORKFLOW mode (`#workflow` pragma), `HEAR` persists the current step and all variables to PostgreSQL and returns. The thread is released. When the user replies, execution resumes from the exact `HEAR` line. See [Execution Modes](./execution-modes.md).
<img src="../assets/gb-decorative-header.svg" alt="General Bots" style="max-height: 100px; width: 100%; object-fit: contain;"> <img src="../assets/gb-decorative-header.svg" alt="General Bots" style="max-height: 100px; width: 100%; object-fit: contain;">
## Basic Syntax ## Basic Syntax

View file

@ -2,6 +2,8 @@
Understanding how General Bots BASIC scripts are loaded, compiled, and executed is essential for building effective automation. This document covers the complete execution lifecycle. Understanding how General Bots BASIC scripts are loaded, compiled, and executed is essential for building effective automation. This document covers the complete execution lifecycle.
> **Two execution modes exist:** RUNTIME (default) and WORKFLOW. See [Execution Modes](./execution-modes.md) for the full comparison.
## Execution Entry Points ## Execution Entry Points
Scripts in General Bots can be triggered through several entry points: Scripts in General Bots can be triggered through several entry points:

View file

@ -73,6 +73,7 @@
- [Chapter 4: BASIC Scripting](./04-basic-scripting/README.md) - [Chapter 4: BASIC Scripting](./04-basic-scripting/README.md)
- [BASIC Basics](./04-basic-scripting/basics.md) - [BASIC Basics](./04-basic-scripting/basics.md)
- [Execution Modes: RUNTIME vs WORKFLOW](./04-basic-scripting/execution-modes.md)
- [API Possibilities](./04-basic-scripting/api-possibilities.md) - [API Possibilities](./04-basic-scripting/api-possibilities.md)
- [Universal Messaging](./04-basic-scripting/universal-messaging.md) - [Universal Messaging](./04-basic-scripting/universal-messaging.md)
- [BASIC vs n8n/Zapier/Make](./04-basic-scripting/basic-vs-automation-tools.md) - [BASIC vs n8n/Zapier/Make](./04-basic-scripting/basic-vs-automation-tools.md)