SESL logo light
Login

SESL CLI User Guide

A complete guide to the SESL command line: online mode, batch mode, file execution, configuration, and troubleshooting.

1. Introduction

The SESL CLI is the quickest way to load, inspect, edit, validate, and run SESL models without writing any Python. It uses the same engine described in the Language User Guide and exposes everything you need for day-to-day work: online editing, batch automation, linting, tracing, and export-friendly output.

This guide focuses on how to drive the CLI effectively, how to configure it for your environment, and how to interpret its output. If you want the full language syntax, rule semantics, or engine internals, refer to the Language User Guide—they apply directly here.

2. Installation and setup

SESL is distributed as a standalone zip from sesl.ai. Download the zip for your platform, unzip it to a folder you control, and run the executable inside. No system-wide install or admin rights are required.

Quick steps:

  1. Download the SESL zip for your OS from https://www.sesl.ai.
  2. Extract it to a directory such as C:\tools\sesl (Windows) or ~/tools/sesl (macOS/Linux).
  3. Open a terminal in that directory.
  4. Run SESL to confirm it starts and shows the binary validity days:
./sesl --help     # macOS/Linux
sesl.exe --help   # Windows

If you prefer, add the SESL folder to your PATH so you can run it from anywhere. Upgrading is simple: download the new zip, unzip to a new folder (or replace the old one after backing up any models you keep there), and run again.

3. Operating modes

The CLI supports three primary modes. Choose interactive when you are exploring, batch when you are automating, and file-execution when you just want to run a model once.

3.1 Online (interactive) mode

Launches a guided shell with prompts. It will display the binary validity window (days remaining), then let you load a model and issue commands with completion.

./sesl --online        # or just: ./sesl
sesl.exe --online     # Windows

3.2 Batch mode

Runs one or more commands non-interactively—perfect for CI, scheduled jobs, or scripted runs. Pass a semicolon-separated command string.

./sesl --batch "LOAD model.sesl; RUN DESCRIBE"
sesl.exe --batch "LOAD model.sesl; RUN"

3.3 File-execution mode

Loads and runs a SESL file directly in one step. Useful for quick checks or when a pipeline feeds models to the CLI.

./sesl model.sesl
sesl.exe model.sesl

4. Starting the CLI

When you start SESL without arguments, it enters interactive online mode. The startup screen displays version information, licence expiry, and a comprehensive list of all available commands:

$ sesl

SESL Interactive Mode

Version 0.0.1, Licence expires on 2026-03-31 (in 113 days)
(c) Oblongix Ltd. all rights reserved.

Commands:
  NEW <template> / LOAD <file> / SAVE <file>
  ADD MODEL <name> (multiline END) / ADD META key=val / META DELETE key
  LIST / LIST ALL / LIST FACT ALL
  LIST RULE <name> / LIST SCENARIO <name> / LIST FACT <scenario>
  ADD FACT <scenario> <yaml> / ADD FACT <scenario> key.path=value
  ADD SCENARIO <name>     # empty scenario
  ADD FACT <scenario>     # multiline END supported
  ADD RULE <yaml>         # multiline END supported
  ADD SCENARIO <yaml>     # multiline END supported
  DELETE FACT <scenario> <key> / DELETE RULE <rule_name> / DELETE SCENARIO <name>
  COPY RULE <name> <new> / COPY SCENARIO <name> <new> / COPY FACT <scenario> <new>
  RUN / RUN REASON / RUN DRIVER / RUN DESCRIBE / RUN SCENARIO <name> [REASON|DRIVER|DESCRIBE]
  LINT / LINT ERRORS / LINT WARNINGS
  OPTIONS <key=value> / OPTIONS DELETE <key>
  COLOUR ON / COLOUR OFF
  HELP
  EXIT / CTRL^c / CTRL^z

SESL>

The startup screen shows how many days remain on your licence. After the command list, you're at the SESL> prompt, ready to load a model, create a new one, or execute any command. Type HELP at any time to redisplay the command list.

5. Configuration and environment

SESL provides a flexible configuration system that lets you control how the engine behaves, how errors are reported, and what safety checks are enforced. Configuration can be set at multiple levels: the engine ships with sensible defaults, you can override them with environment variables (applied system-wide or per terminal session), you can pass command-line flags when launching SESL (for one-off runs or scripts), and you can use the OPTIONS command inside the CLI to save persistent settings that apply to all future sessions. This layered approach means you can set strict policies globally for production environments while allowing developers to relax them locally for testing.

The configuration layers apply in a specific order: engine defaults are applied first, then environment variables override those defaults, then command-line flags override environment variables, and finally OPTIONS set during a CLI session override everything else. Later layers always win. This gives you fine-grained control: for example, you might set SESL_STRICT_PATHS=true in your shell profile to enforce path safety by default, but then run sesl --no-strict-paths for a quick test, or use OPTIONS strict_paths=false in the CLI to change the behavior mid-session. Understanding this precedence is key to debugging unexpected behavior—if a flag doesn't seem to take effect, check whether an OPTIONS setting or environment variable is overriding it.

5.1 Command-line flags

Control runtime behavior directly when launching SESL. These flags override environment variables and engine defaults but are overridden by OPTIONS set in the CLI session.

  • --online: Starts the CLI in interactive mode with a prompt, multiline input support, and command history. This is the default when you run sesl with no arguments. Use this for manual exploration, model development, and debugging.
  • --batch "COMMANDS": Runs one or more semicolon-separated commands non-interactively, then exits. Output is structured (typically JSON) for machine parsing. Use this for automation, CI/CD pipelines, or scripting. Example: sesl --batch "LOAD model.sesl; RUN".
  • --strict-operands / --no-strict-operands: When enabled, SESL requires explicit type consistency in comparisons (e.g., you cannot compare a string to a number). When disabled, SESL attempts implicit type coercion. Strict mode catches more errors early but may require more explicit type casting in your rules.
  • --strict-paths / --no-strict-paths: When enabled, SESL raises an error if a rule tries to write to a path that doesn't exist (e.g., writing person.address.city when person.address is undefined). When disabled, writes to missing paths are silently ignored. Strict mode prevents typos and logic errors; non-strict mode is useful during exploratory development.
  • --auto-create-paths / --no-auto-create-paths: When enabled, SESL automatically creates intermediate objects when writing to nested paths (e.g., if person exists but person.address doesn't, writing person.address.city="Portland" will create person.address as an empty object first). When disabled, such writes fail unless the parent already exists. Auto-creation is convenient but can hide structural issues; disabling it enforces explicit fact initialization.
  • --strict-actions / --legacy-actions: Controls how rule actions (the then block) are parsed. Strict mode enforces modern YAML syntax and rejects ambiguous or deprecated constructs. Legacy mode accepts older syntax for backward compatibility. Use strict mode for new models to ensure forward compatibility.
  • --error-style fancy|plain: Determines how errors are rendered. fancy uses ANSI colour codes, bold text, and formatted boxes for readability in modern terminals. plain outputs plain text suitable for log files or terminals without colour support. Default is fancy.
  • --plain-errors: Shortcut for --error-style plain. Useful for CI/CD environments or when redirecting output to files.
  • --conflict-policy warn|error: Determines what happens when two rules in the same iteration try to write conflicting values to the same fact path. warn logs a warning but allows the engine to continue (last write wins). error halts execution immediately. Use error to catch non-deterministic models; use warn when conflicts are expected and acceptable (e.g., override patterns).
  • --max-iter N: Sets the maximum number of forward-chaining iterations before the engine stops, even if convergence hasn't been reached. Default is 1000. Increase this if you have deeply nested or recursive logic; decrease it to fail fast during development. If the engine hits this limit, it indicates a potential infinite loop or a model that never stabilizes.
  • --graph: Enables dependency graph output in the results. The graph shows which facts depend on which rules, useful for visualizing rule interactions and debugging complex models. Output is typically in a graph format (adjacency list or DOT notation) depending on the engine version.
  • --trace: Includes a full execution trace in the output, showing every rule evaluation (whether it fired or not), every condition check, and every fact read/write. This generates verbose output but is invaluable for debugging why a rule didn't fire or where unexpected behavior occurred.
  • --no-tms: Disables the Truth Maintenance System (TMS), which tracks fact justifications and supports retraction when supporting facts are removed. Disabling TMS simplifies execution and improves performance but removes the ability to track why a fact was derived or to automatically retract derived facts. Use this for simpler models or when justification tracking isn't needed.
  • --store-snapshots: Saves a complete copy of the fact base after each forward-chaining iteration. This allows you to inspect the exact state of the world at each step, essential for deep debugging of complex execution flows. Warning: this generates large output for models with many iterations or large fact bases.

5.2 Environment variables

Set defaults system-wide (in your shell profile or system environment) or per-terminal session. Command-line flags override these, and OPTIONS override both. Environment variables are useful for team-wide policies or CI/CD standardization.

  • SESL_STRICT_OPERANDS=true|false: Sets the default for operand type strictness. When true, comparisons require matching types (e.g., 5 == "5" is an error). When false, SESL attempts coercion (e.g., 5 == "5" may be true depending on context). Recommended: true for production, false for rapid prototyping.
  • SESL_STRICT_PATHS=true|false: Sets the default for path strictness. When true, writing to a non-existent path raises an error. When false, such writes are ignored. Recommended: true to catch typos and logic errors early.
  • SESL_AUTO_CREATE_PATHS=true|false: Sets the default for automatic path creation. When true, SESL creates missing parent objects when writing to nested paths. When false, writes fail if parents don't exist. Recommended: false for strict models, true for exploratory development.
  • SESL_ERROR_STYLE=fancy|plain: Sets the default error rendering style. fancy uses colour and formatting; plain is suitable for logs. Recommended: fancy for interactive terminals, plain for CI/CD pipelines.
  • SESL_CONFLICT_POLICY=warn|error: Sets the default conflict handling policy. warn logs conflicts but continues execution; error halts on the first conflict. Recommended: error during development to catch non-determinism, warn in production if conflicts are intentional (e.g., override patterns).
  • SESL_MAX_ITERATIONS=N: Sets the default maximum iteration count. Higher values allow more complex logic but may hide infinite loops. Recommended: start with 1000 (default), increase only if you have verified deep recursion is intentional.
  • SESL_DEPENDENCY_SORT=true|false: When true, SESL sorts rules by dependency before each iteration, ensuring rules that write facts fire before rules that read those facts (within the same iteration). This can reduce iteration count for some models. When false, rules fire in definition order. Recommended: true for performance, false if you need explicit ordering control.
  • SESL_STORE_SNAPSHOTS=true|false: Sets the default for snapshot storage. When true, SESL saves fact snapshots after each iteration (large output). When false, snapshots are not stored. Recommended: false by default, enable only when deep debugging is needed.
  • SESL_LET_MISSING=true|false: When true, conditions that reference undefined paths evaluate to false instead of raising an error. When false, missing paths in conditions are errors. Recommended: false to catch typos, true for models that intentionally check for the presence/absence of optional fields.

5.3 OPTIONS persistence

Inside the CLI, OPTIONS key=value saves configuration to a local config file (typically ~/.sesl/config.yaml or alongside the executable). These persist across sessions. Use OPTIONS alone to list all saved settings, or OPTIONS DELETE key to remove one.

sesl> OPTIONS strict_paths=true
Option saved: strict_paths = true

sesl> OPTIONS max_iterations=500
Option saved: max_iterations = 500

sesl> OPTIONS
Saved options (from ~/.sesl/config.yaml):
  strict_paths = true
  error_style = fancy
  max_iterations = 500
  colour = on
  auto_create_paths = false

sesl> OPTIONS DELETE strict_paths
Option removed: strict_paths
Remaining options: error_style, max_iterations, colour, auto_create_paths

Common OPTIONS keys match environment variables and flags (without the SESL_ prefix or dashes):

  • strict_operands, strict_paths, auto_create_paths
  • error_style, conflict_policy, max_iterations
  • dependency_sort, store_snapshots, let_missing
  • colour: on/off (same as COLOUR ON command)

5.4 Licence and expiry behavior

The CLI displays binary validity days on startup. After expiry, most commands are disabled except for RUN and EXIT, and only on Tuesday–Friday. During the restricted window, you can still execute models but cannot edit, lint, or list. Contact support for licence renewal.

6. Command lifecycle

Understanding the command execution pipeline helps when debugging failures or unexpected behavior. Every CLI command—whether typed interactively in online mode, passed via --batch, or embedded in a template—flows through a consistent set of stages. Each stage has a specific responsibility, and knowing where a command fails (parsing, validation, execution, etc.) points you directly to the root cause. The pipeline also handles advanced features like token substitution, which lets you parameterize commands and templates with values from your configuration, making automation and reuse much easier.

6.1 Execution stages

  1. 1. Parse and normalize
    The command string is first broken into tokens (words, symbols, quoted strings). Keywords like LOAD, RUN, ADD RULE are recognized regardless of case (load, Load, LOAD are all equivalent). The parser checks that the command matches a known pattern—for example, LOAD filename requires exactly one argument, while RUN SCENARIO Name DESCRIBE requires a scenario name and optional modifiers. If the command doesn't match any known pattern (e.g., LOADD model.sesl with a typo, or RUN SCENARIO missing the scenario name), parsing fails immediately with a "Command not recognized" or "Invalid syntax" error. This stage is purely syntactic: it doesn't check whether the file exists, whether the scenario is in the model, or whether the operation is valid—it only checks that the command structure is correct.
  2. 2. Token substitution
    After parsing, the CLI scans the command for placeholders in the form %VARIABLE%. These placeholders are replaced with values from the OPTIONS store (set via OPTIONS key=value) or environment variables (set in your shell or system). This enables powerful automation and templating: you can define a path, model name, or configuration value once and reuse it across many commands. For example, if you've set OPTIONS model_path=/var/models, then the command LOAD %model_path%/loan.sesl becomes LOAD /var/models/loan.sesl after substitution. Similarly, if you've set the environment variable SESL_DEFAULT_SCENARIO=Demo, the command RUN SCENARIO %SESL_DEFAULT_SCENARIO% becomes RUN SCENARIO Demo. Substitution happens before validation, so the final command must be valid after replacement. If a placeholder refers to an undefined option or environment variable, substitution fails with "Undefined variable: %VARIABLE%". Here's a complete example showing token substitution in action:
    # Set configuration values
    sesl> OPTIONS project_dir=/workspace/models
    Option saved: project_dir = /workspace/models
    
    sesl> OPTIONS default_scenario=Regression_Test
    Option saved: default_scenario = Regression_Test
    
    # Use placeholders in commands
    sesl> LOAD %project_dir%/loan-approval.sesl
    # After substitution: LOAD /workspace/models/loan-approval.sesl
    Loaded model: loan-approval.sesl
    
    sesl> RUN SCENARIO %default_scenario% DESCRIBE
    # After substitution: RUN SCENARIO Regression_Test DESCRIBE
    Scenario: Regression_Test
    Result: { "decision": "approve" }
    ...
    Token substitution is especially useful in batch mode and templates. For instance, you can create a template file with placeholders for environment-specific values (paths, URLs, credentials) and instantiate it with NEW template.seslt, where the template engine replaces all %VARIABLE% tokens with current OPTIONS or environment values before creating the model.
  3. 3. Licence check
    Before dispatching the command, the CLI checks the binary's expiry date (shown on startup as "Licence expires on YYYY-MM-DD (in N days)"). If the licence has expired, most commands are blocked to prevent unauthorized use. However, SESL provides a grace period: on Tuesday, Wednesday, Thursday, and Friday of each week, the RUN and EXIT commands remain available even after expiry. This allows you to execute existing models and exit the CLI, but you cannot edit, lint, list, load, or save models. On Monday, Saturday, and Sunday, all commands are blocked post-expiry. If you try to run a blocked command, you'll see an error like "Licence expired. Command not available. Contact support for renewal." During the restricted window (Tue–Fri post-expiry), attempting to run LOAD, LINT, or ADD RULE will fail, but RUN (to execute already-loaded models) and EXIT work normally.
  4. 4. Dispatch
    After passing licence checks, the command is routed to the appropriate handler based on its type. File operations (LOAD, SAVE, DIR, CD) go to the file I/O subsystem. Model-editing commands (ADD RULE, DELETE SCENARIO, ADD META) are routed to the model editor. Execution commands (RUN, RUN DESCRIBE) are sent to the rule engine. Linting commands (LINT, LINT ERRORS) are sent to the static analysis module. Metadata and listing commands (LIST, LIST ALL) go to the introspection subsystem. This dispatch layer is where context-specific handlers take over: for example, LOAD opens a file, parses YAML, and validates the model structure, while RUN initializes the inference engine, loads scenarios, and begins forward chaining. Dispatch failures are rare but can occur if internal state is inconsistent (e.g., trying to RUN when no model is loaded).
  5. 5. Validation
    For commands that modify the model (ADD RULE, ADD FACT, DELETE, etc.), SESL performs deep validation before committing the change. This includes: (a) YAML syntax: ensuring the provided YAML is well-formed (correct indentation, no syntax errors, valid data types); (b) Schema compliance: checking that rules have required fields (rule, if, then), that facts are valid dictionaries, that scenarios are properly named; (c) Type consistency: verifying that values match expected types (e.g., priorities are numbers, conditions are boolean expressions, actions are key-value pairs); (d) Path strictness: if --strict-paths is enabled, checking that paths referenced in actions exist or can be created (depending on --auto-create-paths). If validation fails, the command is rejected with a detailed error message showing the problematic line, field, or value. The model is not modified. For example, if you try ADD RULE with invalid YAML (missing colon, bad indentation), you'll see "YAML parse error at line 3: expected key-value pair". If you try to write to applicant.address.city but applicant.address doesn't exist and --strict-paths is on, you'll see "Path not found: applicant.address. Enable --auto-create-paths or initialize the parent first."
  6. 6. Engine execution
    If the command triggers a model execution (RUN, RUN SCENARIO, RUN DESCRIBE, etc.), the SESL rule engine takes over. This is where the core expert system logic happens: (a) Scenario loading: The engine loads the initial facts from the specified scenario (or all scenarios if you ran RUN without a scenario name). (b) Rule evaluation: The engine enters forward-chaining mode, repeatedly evaluating all rules against the current fact base. For each rule, it checks whether the if condition is true given the current facts. If true, the rule fires: its then actions are executed, writing new facts or updating existing ones. (c) Iteration: After all rules have been checked, the engine starts a new iteration (pass). Newly written facts may now satisfy conditions of other rules that didn't fire before. This repeats until either (i) convergence: no rules fired in the latest pass (the model reached a stable state), or (ii) max iterations: the iteration limit was hit (default 1000, configurable via --max-iter), indicating a potential infinite loop or a model that never stabilizes. (d) Result collection: After convergence or max iterations, the engine packages the final fact base, metrics (iterations, rules fired, elapsed time), support/monitor/driver traces (if DESCRIBE was requested), and any snapshots or graphs (if enabled) into the result structure.
  7. 7. Output rendering
    Finally, the result is formatted and displayed. The format depends on the mode and command: (a) Online mode: Human-readable output with colour (if COLOUR ON), section headings, indented JSON, and visual separators. Errors are rendered with fancy formatting (boxes, bold text, colour-coded severity). (b) Batch mode: Structured JSON output suitable for parsing by scripts or CI/CD tools. Each command result is a JSON object with status (success/error), message, and data fields. No colour or interactive prompts. (c) DESCRIBE mode: Includes verbose traces: support (per-rule firing trace with iteration numbers), monitor (human-readable step-by-step execution log), driver (dependency tree showing how each conclusion was derived), and optionally snapshots (if --store-snapshots) or graph (if --graph). The renderer applies the --error-style setting (fancy or plain) to error messages, and respects the colour option (on/off) for syntax highlighting and semantic colour (e.g., green for success, red for errors, yellow for warnings). If output exceeds terminal width, it wraps intelligently or truncates with ellipses in online mode; in batch mode, output is never truncated (to preserve data integrity for downstream tools).

6.2 Error handling and debugging

When a command fails, the stage at which it fails tells you exactly where to look. Each stage produces distinct error messages that guide troubleshooting:

  • Parse errors: Occur in Stage 1 when the command syntax is unrecognized or malformed. Examples: LOADD model.sesl (typo in command name), RUN SCENARIO (missing required argument), ADD RULE extra-word (unexpected extra token). Error messages typically say "Command not recognized" or "Invalid syntax for command X". Fix: Check spelling, verify argument count, and consult HELP for the correct syntax. Parsing errors are purely syntactic—they have nothing to do with model content, just the command structure.
  • Substitution errors: Occur in Stage 2 when a %VARIABLE% placeholder references an undefined option or environment variable. Example: LOAD %model_dir%/model.sesl when model_dir hasn't been set via OPTIONS or as an environment variable. Error message: "Undefined variable: %model_dir%". Fix: Set the variable with OPTIONS model_dir=/path or define it in your environment before running SESL. Check spelling of variable names (case-sensitive in most shells).
  • Licence errors: Occur in Stage 3 when the binary has expired and you're trying to run a blocked command (anything other than RUN or EXIT on Tuesday–Friday). Error message: "Licence expired. Command not available outside restricted window. Contact support for renewal." Fix: Contact support to renew your licence. In the meantime, if it's Tuesday–Friday, you can still run RUN to execute loaded models, but you cannot edit, lint, or load new models.
  • Validation errors: Occur in Stage 5 when the model content violates schema, type, or path constraints. Examples: adding a rule with invalid YAML (bad indentation, missing colon), writing to a non-existent path with --strict-paths enabled, providing a non-numeric priority, or referencing a scenario that doesn't exist. Error messages are highly specific: "YAML parse error at line 5: mapping values are not allowed here" or "Path not found: person.address.city. Enable --auto-create-paths or initialize parent structure first." Fix: Review the error message carefully—it usually points to the exact line or field. For YAML errors, check indentation (use spaces, not tabs) and syntax. For path errors, either initialize the parent structure explicitly (e.g., ADD FACT Scenario person.address={}) or enable --auto-create-paths. For type errors, ensure values match expected types (strings in quotes, numbers unquoted, booleans as true/false).
  • Engine errors: Occur in Stage 6 during rule execution. Examples: hitting max iterations (infinite loop or non-converging model), conflicting rule writes (two rules trying to set the same fact to different values in one iteration with --conflict-policy error), or missing facts required by conditions when --let-missing is off. Error messages: "Max iterations (1000) reached without convergence" or "Conflict detected: rule A and rule B both wrote to 'result.decision' with different values." Fix: For max iterations, inspect the monitor output with RUN DESCRIBE to see which rules are firing repeatedly. Look for circular dependencies or missing termination conditions. For conflicts, review the rules firing in the same iteration—ensure priorities are set correctly or redesign logic to avoid simultaneous conflicting writes. For missing facts, use LINT to check for undefined paths, and ensure all required facts are initialized in your scenarios.

Debugging tips: Always start with LINT before running a model—it catches many errors (undefined paths, circular dependencies, unreachable rules) statically. Use RUN DESCRIBE to get full execution traces: the monitor shows exactly which rules fired in which order, what they read, what they wrote, and why. The driver tree shows dependency chains (which conclusions depend on which rules and inputs), essential for understanding complex logic. If output is overwhelming, use RUN SCENARIO Name DESCRIBE to focus on a single scenario. Enable --trace for even more detail (every condition check, every rule evaluation). If you suspect an infinite loop, reduce --max-iter to a small number (e.g., --max-iter 10) to fail fast, then inspect the monitor to see the repeating pattern.

6.3 Command modes

The CLI supports three execution modes, each optimized for different use cases. Commands behave differently in each mode, particularly around user interaction, output format, and error handling:

  • Online mode (default, or --online): This is the interactive mode you get when you run sesl with no arguments. It provides a SESL> prompt, command history (use up/down arrows to recall previous commands), and multiline input support. When you type a command like ADD RULE without inline YAML, the CLI enters multiline mode: you can type or paste YAML across many lines, and terminate with END on its own line. Output is human-readable with colour-coded results (green for success, red for errors, yellow for warnings, blue for informational messages), section headings, indented JSON, and visual separators (lines, boxes). Destructive operations (overwriting files with SAVE, deleting rules/scenarios/facts) prompt for confirmation: "File exists. Overwrite? (y/n)". You can type y or n to proceed or cancel. Online mode is ideal for model development, exploration, debugging, and learning SESL—you get immediate feedback, readable output, and safety prompts.
  • Batch mode (--batch "COMMANDS"): This is the non-interactive mode for automation, scripts, and CI/CD pipelines. Commands are provided as a string (one or more semicolon-separated commands), executed sequentially, and the CLI exits after completion. There are no prompts: destructive operations default to "yes" (files are overwritten, deletions proceed without confirmation). Output is structured JSON, one JSON object per command, with fields: status (success/error/warning), command (the executed command), message (human-readable summary), and data (results, metrics, traces). No colour, no interactive input, no multiline mode (all YAML must be inline or on one line). Batch mode is ideal for headless execution: testing models in CI, running models as part of a larger workflow, extracting results for downstream tools (parse the JSON output), or embedding SESL in scripts. Example: sesl --batch "LOAD model.sesl; RUN; SAVE results.json" loads a model, runs it, and saves output, all without user interaction. Exit code is 0 on success, non-zero on failure, so you can chain commands with && or check $? in shell scripts.
  • File-execution mode (sesl model.sesl): When you run sesl with a filename argument (and no --online or --batch flag), SESL enters file-execution mode. This is a shortcut equivalent to sesl --batch "LOAD model.sesl; RUN": it loads the specified model, runs all scenarios, prints results (JSON format), and exits. It's optimized for quick one-off execution—no editing, no CLI session, just load-and-run. This mode is useful for testing a model quickly, running models from cron jobs or schedulers, or integrating SESL into workflows where you just need to execute a model and capture output. Example: sesl loan-approval.sesl > results.json runs the model and redirects JSON output to a file for later analysis.

Choosing the right mode: Use online mode during development—when you're writing rules, testing scenarios, debugging logic, or learning SESL. Use batch mode for automation—CI/CD tests, scheduled runs, scripted workflows, or when you need structured JSON output. Use file-execution mode for quick one-off runs—testing a model without starting a full CLI session, running models as part of a larger script, or when you only need execution results (no editing or introspection).

7. Command reference

The CLI organizes commands by what they touch: files, metadata, rules, scenarios, facts, execution, linting, and options. Each group below explains the intent, how to use it, and a short sample of what you will see on screen.

7.1 File and model

Load, start, and save models; move around the filesystem.

LOAD model.sesl            # load and validate a model file
SAVE model.sesl            # write the current model to disk
NEW                        # create a blank in-memory model
NEW template.seslt         # create from a template, using %OPTION% substitutions
DIR                        # list SESL files in the current directory
CD path/to/dir             # change working directory used by the CLI
ADD MODEL model_name       # add a named model to the workspace

Example output (LOAD):

Loaded model: loan-approval.sesl
Scenarios found: Demo, Regression_HighIncome
Rules loaded: 12 | Facts: 2 | Meta keys: 3

Example output (SAVE with overwrite prompt):

File loan-approval.sesl already exists. Overwrite? (y/n): y
Model saved to loan-approval.sesl

7.2 Metadata

Add or remove top-level metadata entries that travel with the model. Common metadata fields include author, version, source, created date, and considerations.

ADD META author="Credit Risk Team"
ADD META version="2.1.0"
ADD META source="Loan approval policy document v3"
ADD META created="2025-12-08"
META DELETE author

Example output:

Metadata updated: version = "2.1.0"
Current meta keys: author, version, source, created

7.3 Listing

Inspect parts of the model without opening the file.

LIST                       # summary view (rules, scenarios, meta)
LIST ALL                   # full YAML of the loaded model
LIST RULE rule_name        # show a single rule
LIST SCENARIO Scenario     # show a scenario header
LIST FACT Scenario         # show facts for a scenario
LIST FACT ALL              # show facts for all scenarios

Example output (LIST):

Model: loan-approval
Rules: 12  | Scenarios: 2  | Meta keys: 3
Scenarios: Demo, Regression_HighIncome
Use LIST ALL for full YAML.

Example output (LIST RULE eligibility.check_age):

rule: eligibility.check_age
if: applicant.age >= 21
then:
  eligibility.age_ok: true
reason: "Applicant meets minimum age requirement"

7.4 Copy

Duplicate parts of the model to speed up authoring.

COPY MODEL new_name
COPY RULE old_rule [new_rule]
COPY SCENARIO OldScenario [NewScenario]
COPY FACT Scenario [NewScenario]

Example output (COPY RULE):

Copied rule "eligibility.age" -> "eligibility.age_copy"
Total rules: 13

7.5 Facts editing

Add or remove facts inside a scenario. Use dot-paths for targeted edits or multiline YAML for larger inserts.

ADD FACT Scenario applicant.income=55000
ADD FACT Scenario             # enter multiline YAML, finish with END
DELETE FACT Scenario applicant.debts

Example interactive add:

ADD FACT Demo
applicant:
  income: 72000
  debts: 12000
END
Facts merged into scenario "Demo".

Example DELETE FACT (with confirmation when deleting top-level keys):

DELETE FACT Demo applicant
Deleting top-level key 'applicant' will remove all nested data. Continue? (y/n): y
Deleted applicant from scenario Demo.

7.6 Rule editing

Create or remove rules directly from the shell. Multiline YAML is supported for full rule bodies.

ADD RULE                      # prompts for rule YAML until END
DELETE RULE rule_name

Example add (inline):

ADD RULE
rule: "flag.high_income"
if: "applicant.income >= 60000"
then:
  result.high_income: true
reason: "Income meets high threshold"
END
Rule added: flag.high_income

Example DELETE RULE output:

DELETE RULE flag.high_income
Rule deleted: flag.high_income
Total rules: 11

7.7 Scenario editing

Create or delete scenarios. When creating, you can optionally provide facts immediately. Deleting a scenario removes all associated facts.

ADD SCENARIO GoldCustomer     # optionally follow with YAML then END
DELETE SCENARIO BronzeCustomer

Example output (ADD SCENARIO):

Scenario created: GoldCustomer
Total scenarios: 3

Example output (DELETE SCENARIO with confirmation):

DELETE SCENARIO BronzeCustomer
Deleting scenario 'BronzeCustomer' will remove all its facts. Continue? (y/n): y
Scenario deleted: BronzeCustomer
Total scenarios: 2

7.8 Execution

Run the engine against all scenarios or a specific one. Add DESCRIBE to emit traces, monitor output, and driver trees for explainability.

RUN                           # run all scenarios
RUN DESCRIBE                  # all scenarios with traces/monitor
RUN SCENARIO Name             # single scenario
RUN SCENARIO Name DESCRIBE    # single scenario with explainability
RUN DRIVER                    # driver tree summary
RUN REASON                    # rule firing reasons

Basic RUN output

A simple RUN command executes all scenarios and shows minimal output: the final result facts, iteration count, rules fired, and elapsed time.

sesl> RUN
Scenario: Demo
Result: { "decision": "approve", "score": 0.82 }
Iterations: 3 | Rules fired: 7 | Elapsed: 124 ms

Scenario: Regression_HighIncome
Result: { "decision": "approve", "score": 0.91 }
Iterations: 2 | Rules fired: 6 | Elapsed: 98 ms

Fields explained:

  • Result: The final fact tree after all rules have fired and the engine converged. This is what your model produced.
  • Iterations: How many forward-chaining passes the engine made. Each pass evaluates all rules; rules that fire may write new facts, triggering another pass.
  • Rules fired: Total count of rules that triggered (their conditions were true and they wrote facts). A rule can fire once per iteration if conditions remain true.
  • Elapsed: Wall-clock time in milliseconds from start to convergence or max iterations.

RUN DESCRIBE output (full explainability)

Adding DESCRIBE includes detailed traces, metrics, driver trees, and monitor logs—essential for understanding why a decision was reached.

sesl> RUN SCENARIO Demo DESCRIBE
Scenario: Demo
Result: {
  "decision": "approve",
  "score": 0.82,
  "limits": { "max_amount": 25000 }
}

Metrics:
  Iterations: 3
  Rules evaluated: 42
  Rules fired: 7
  Elapsed: 124 ms
  Convergence: true
  TMS active: true

Driver (dependency tree):
  applicant.age: 25 (input fact)
  applicant.income: 55000 (input fact)
  applicant.debts: 10000 (input fact)
  ├─ eligibility.age_ok: true
  │    └─ rule: eligibility.check_age (fired iteration 1)
  │    └─ reason: "Applicant age >= 21"
  ├─ risk.score: 0.82
  │    └─ rule: risk.calculate (fired iteration 2)
  │    └─ reason: "Score = (income - debts) / income"
  └─ result.decision: approve
       └─ rule: decision.finalize (fired iteration 3)
       └─ depends on: [eligibility.age_ok, risk.score]
       └─ reason: "All eligibility checks passed and risk score >= 0.7"

Monitor (execution trace):
  [Iteration 1] Starting forward-chaining pass...
    Evaluating 12 rules...
    ✓ eligibility.check_age fired
      - Read: applicant.age (25)
      - Wrote: eligibility.age_ok = true
      - Reason: Applicant age >= 21
  
  [Iteration 2] Starting forward-chaining pass...
    Evaluating 12 rules...
    ✓ risk.calculate fired
      - Read: applicant.income (55000), applicant.debts (10000)
      - Wrote: risk.score = 0.82
      - Reason: Score = (income - debts) / income
  
  [Iteration 3] Starting forward-chaining pass...
    Evaluating 12 rules...
    ✓ decision.finalize fired
      - Read: eligibility.age_ok (true), risk.score (0.82)
      - Wrote: result.decision = approve, result.score = 0.82
      - Reason: All eligibility checks passed and risk score >= 0.7
  
  [Iteration 4] Starting forward-chaining pass...
    Evaluating 12 rules...
    No rules fired. Convergence reached.

Final state: 3 iterations, 7 rules fired, converged in 124 ms

DESCRIBE output sections explained:

  • Metrics: Includes convergence status (did the engine reach a stable state or hit max iterations?) and whether the Truth Maintenance System is active (tracking fact dependencies for retraction and justification).
  • Driver: A dependency tree showing how each conclusion was derived. Input facts appear at the top; derived facts show which rule wrote them and what they depend on. This is the audit trail for regulatory or business review.
  • Monitor: A chronological log of every iteration. For each pass, it shows which rules fired, what facts they read, what they wrote, and the reason text from the rule. Use this to debug unexpected behavior or trace a specific fact's origin.

Understanding convergence

The engine runs iterations until one of two conditions is met:

  1. Convergence: No rules fired in the latest pass (no new facts were written). The model has reached a stable state.
  2. Max iterations: The iteration limit (default 1000, configurable via --max-iter or OPTIONS max_iterations) was hit. This may indicate an infinite loop or a model that never stabilizes.

If you see "Max iterations reached" instead of "Convergence", inspect the monitor log to identify which rules are firing repeatedly and why. Common causes: circular dependencies, rules that always evaluate to true, or missing termination conditions.

7.9 Linting

Validate models before running to catch missing data, bad paths, or type issues. Use LINT ALL to validate all models in the workspace.

LINT                       # all findings for current model
LINT ERRORS                # only errors
LINT WARNINGS              # only warnings
LINT ALL                   # validate all models in workspace

Example output (LINT ERRORS):

LINT ERRORS
[error] rule eligibility.check_age: facts path applicant.age missing in scenario Demo
[error] rule decision.finalize: undefined reference to eligibility.credit_ok
Summary: 2 errors, 0 warnings

Example output (LINT ALL):

LINT ALL
Validating loan-approval.sesl...
  [warn] meta: version not set
  [warn] rule risk.calculate: potential division by zero

Validating insurance-policy.sesl...
  [error] rule premium.base: missing required field 'priority'

Summary: 1 error, 2 warnings across 2 models

7.10 Help

Show available commands and quick syntax help.

HELP                       # list all commands with brief descriptions
HELP command_name          # detailed help for a specific command
?                          # shortcut for HELP

Example output (HELP):

Available commands:
  LOAD file         - Load a SESL model
  SAVE file         - Save current model
  RUN [DESCRIBE]    - Execute scenarios
  LINT [ERRORS]     - Validate model
  LIST [ALL]        - Show model contents
  ADD ...           - Add rules, facts, scenarios, metadata
  DELETE ...        - Remove model elements
  OPTIONS [key=val] - Manage persistent settings
  HELP [cmd]        - Show this help or command details
  EXIT              - Quit the CLI

7.11 Options and colour

Persist session options and toggle colouring. Options follow the same names as the environment and flags.

OPTIONS                       # list saved options
OPTIONS strict_paths=true     # persist an option
OPTIONS DELETE strict_paths   # remove an option
COLOUR ON | COLOUR OFF        # toggle coloured output

Example output (OPTIONS):

Saved options (from ~/.sesl/config.yaml):
  strict_paths = true
  error_style = fancy
  max_iterations = 500
  colour = on

Example output (OPTIONS DELETE):

OPTIONS DELETE strict_paths
Option removed: strict_paths
Remaining options: error_style, max_iterations, colour

8. Dot-path editing

Use dot-paths to target nested structures without rewriting whole objects. Lists use numeric indices; missing indices are created as needed when auto-create is enabled.

ADD FACT Scenario1 person.address.street="High Road"
ADD FACT Scenario1 items.0.name="Apple"

9. YAML merging and multiline editing

9.1 Inline YAML

For quick single-line edits, you can provide YAML directly on the command line. This works for simple key-value pairs or compact objects:

ADD FACT ScenarioA { income: 50000, debts: 12000 }
ADD META author="John Smith"
ADD FACT Demo applicant.age=25

9.2 Multiline YAML mode

When you need to add complex nested structures, rules with multiple conditions, or larger blocks of data, omit the inline YAML and the CLI will enter multiline mode. You type or paste YAML across multiple lines, then signal completion with END on its own line:

sesl> ADD FACT Demo
applicant:
  name: Alice Johnson
  age: 28
  income: 72000
  address:
    street: 123 Main St
    city: Portland
    state: OR
  employment:
    employer: TechCorp
    years: 5
END
Facts merged into scenario "Demo".

9.3 The importance of END

Critical: The END statement is required to exit multiline mode and commit your changes. Without it, the CLI will continue waiting for more input, treating every subsequent line as part of the YAML block. If you forget END, your prompt will look like this:

sesl> ADD RULE
rule: eligibility.check_age
if: applicant.age >= 21
then:
  eligibility.age_ok: true
... (still waiting)
... (still waiting)
LIST    # This would be treated as YAML, not a command!
... (still waiting)

To recover from this, simply type END on its own line. The CLI will then process all the accumulated lines as YAML. If the YAML is invalid, you'll get an error and can retry.

9.4 YAML merging behavior

When you add facts or rules, the CLI merges the new YAML into the existing structure:

  • New keys are added to the existing dictionary.
  • Existing keys are overwritten with the new values.
  • Nested objects are merged recursively (deep merge), not replaced wholesale.
  • Lists are replaced entirely (no append; if you need to add to a list, read the current value, modify it, and write back).

Example merge:

# Existing facts in Demo:
applicant:
  income: 50000
  debts: 10000

# You run:
ADD FACT Demo
applicant:
  age: 25
END

# Result (merged):
applicant:
  income: 50000    # preserved
  debts: 10000     # preserved
  age: 25          # added

10. Output structure

A batch run such as ./sesl --batch "LOAD model.sesl; RUN DESCRIBE" prints structured output you can redirect to a file or parse programmatically. A representative snippet looks like this:

{
  "result": {
    "decision": "approve",
    "score": 0.82,
    "limits": { "max_amount": 25000 }
  },
  "metrics": {
    "iterations": 3,
    "rules_evaluated": 42,
    "rules_fired": 7,
    "elapsed_ms": 124
  },
  "_sesl": {
    "support": [
      {
        "rule": "eligibility.check_age",
        "fired": true,
        "facts_read": ["applicant.age"],
        "facts_written": ["eligibility.age_ok"],
        "reason": "age >= 21"
      },
      {
        "rule": "decision.finalize",
        "fired": true,
        "facts_read": ["eligibility.*", "risk.score"],
        "facts_written": ["result.decision", "result.score"],
        "reason": "risk.score >= 0.7 and eligibility.ok"
      }
    ],
    "monitor": [
      "RUN SCENARIO Demo",
      "TRACE: eligibility.check_age fired",
      "TRACE: decision.finalize fired"
    ],
    "driver": [
      { "node": "eligibility", "status": "ok" },
      { "node": "decision", "status": "approve", "score": 0.82 }
    ]
  }
}

Key sections to expect:

  1. result: your model’s output facts (often nested under result).
  2. metrics: iterations, rules evaluated/fired, and elapsed milliseconds.
  3. _sesl.support: per-rule trace showing what fired, what was read/written, and the reason.
  4. _sesl.monitor: ordered trace lines for step-by-step reasoning.
  5. _sesl.driver: a driver or decision-tree style summary.

11. CLI cheat sheet

Fast recall for the most common actions and all available commands:

Startup & modes

# Start interactive mode
./sesl --online
sesl.exe --online     # Windows

# Run a file directly
./sesl model.sesl

# Batch mode (non-interactive)
./sesl --batch "LOAD model.sesl; RUN DESCRIBE"

# With flags
./sesl --strict-paths --max-iter 500 model.sesl
sesl.exe --batch "LOAD model.sesl; RUN" --trace --graph

File operations

LOAD model.sesl           # Load model
SAVE model.sesl           # Save current model
NEW                       # Create blank model
NEW template.seslt        # Create from template
DIR                       # List .sesl files
CD path/to/dir            # Change directory
ADD MODEL model_name      # Add named model to workspace
EDIT RULE rule_name       # Edit existing rule (multiline)
EDIT FACT Scenario        # Edit scenario facts (multiline)

Metadata

ADD META key=value
META DELETE key
LIST                      # Show summary including meta

Listing & inspection

LIST                      # Model summary
LIST ALL                  # Full YAML
LIST RULE rule_name
LIST SCENARIO Scenario
LIST FACT Scenario
LIST FACT ALL             # All facts from all scenarios

Copying

COPY MODEL new_name
COPY RULE old [new]
COPY SCENARIO old [new]
COPY FACT scenario [newScenario]

Adding elements

ADD RULE                  # Multiline YAML, finish with END
ADD SCENARIO Name         # Optionally provide facts
ADD FACT Scenario key=val # Dot-path or multiline YAML
ADD META key=value
ADD MODEL model_name      # Add named model to workspace

Deleting elements

DELETE RULE rule_name
DELETE SCENARIO Name      # Removes all facts too
DELETE FACT Scenario key  # Dot-path or top-level key
META DELETE key

Execution

RUN                       # All scenarios
RUN DESCRIBE              # All scenarios with trace/monitor/driver
RUN SCENARIO Name         # Single scenario
RUN SCENARIO Name DESCRIBE
RUN DRIVER                # Driver tree only
RUN REASON                # Rule firing reasons

Linting

LINT                      # All findings
LINT ERRORS               # Errors only
LINT WARNINGS             # Warnings only
LINT ALL                  # Validate all models in workspace

Options & configuration

OPTIONS                   # List saved options
OPTIONS key=value         # Persist option
OPTIONS DELETE key        # Remove option
COLOUR ON                 # Enable coloured output
COLOUR OFF                # Disable colours

Help & exit

HELP                      # List commands
HELP command_name         # Command-specific help
?                         # Shortcut for HELP
EXIT                      # Quit CLI

Common workflows

# Quick validation
LINT ERRORS

# Trace a single scenario
RUN SCENARIO Demo DESCRIBE

# Persist configuration
OPTIONS strict_paths=true
OPTIONS max_iterations=500

# Batch run with full output
./sesl --batch "LOAD model.sesl; RUN DESCRIBE" > output.json

# Deep debugging
./sesl --store-snapshots --trace --graph model.sesl > debug.json

12. Call SESL from Python

You can launch the SESL executable in batch mode from Python with subprocess. Pass arguments as a list to avoid quoting issues. Update the paths to match your environment.

import subprocess
from pathlib import Path

SESL_EXE = Path(r"C:\\path\\to\\sesl.exe")   # or ./sesl on macOS/Linux
MODEL = Path(r"C:\\path\\to\\model.sesl")

cmd = [str(SESL_EXE), "--batch", str(MODEL)]

result = subprocess.run(
    cmd,
    capture_output=True,
    text=True,
    check=False  # set to True to raise on non-zero exit
)

print("returncode:", result.returncode)
print("stdout:\n", result.stdout)
print("stderr:\n", result.stderr)
          

If you need to capture output to a file or add more flags, extend the list:

cmd = [str(SESL_EXE), "--batch", str(MODEL), "--out", "results.json"]

Async variant:

import asyncio

async def run_sesl():
    cmd = ["./sesl", "--batch", "./model.sesl"]
    proc = await asyncio.create_subprocess_exec(
        *cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
    )
    stdout, stderr = await proc.communicate()
    print("returncode:", proc.returncode)
    print("stdout:\n", stdout.decode())
    print("stderr:\n", stderr.decode())

asyncio.run(run_sesl())

Tips:

  • Use absolute paths to avoid working-directory surprises.
  • Check returncode and stderr for failures.
  • Set cwd in subprocess.run if you want outputs in a specific directory.