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:
- Download the SESL zip for your OS from
https://www.sesl.ai. - Extract it to a directory such as
C:\tools\sesl(Windows) or~/tools/sesl(macOS/Linux). - Open a terminal in that directory.
- 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 runseslwith 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., writingperson.address.citywhenperson.addressis 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., ifpersonexists butperson.addressdoesn't, writingperson.address.city="Portland"will createperson.addressas 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 (thethenblock) 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.fancyuses ANSI colour codes, bold text, and formatted boxes for readability in modern terminals.plainoutputs plain text suitable for log files or terminals without colour support. Default isfancy.--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.warnlogs a warning but allows the engine to continue (last write wins).errorhalts execution immediately. Useerrorto catch non-deterministic models; usewarnwhen 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. Whentrue, comparisons require matching types (e.g.,5 == "5"is an error). Whenfalse, SESL attempts coercion (e.g.,5 == "5"may be true depending on context). Recommended:truefor production,falsefor rapid prototyping.SESL_STRICT_PATHS=true|false: Sets the default for path strictness. Whentrue, writing to a non-existent path raises an error. Whenfalse, such writes are ignored. Recommended:trueto catch typos and logic errors early.SESL_AUTO_CREATE_PATHS=true|false: Sets the default for automatic path creation. Whentrue, SESL creates missing parent objects when writing to nested paths. Whenfalse, writes fail if parents don't exist. Recommended:falsefor strict models,truefor exploratory development.SESL_ERROR_STYLE=fancy|plain: Sets the default error rendering style.fancyuses colour and formatting;plainis suitable for logs. Recommended:fancyfor interactive terminals,plainfor CI/CD pipelines.SESL_CONFLICT_POLICY=warn|error: Sets the default conflict handling policy.warnlogs conflicts but continues execution;errorhalts on the first conflict. Recommended:errorduring development to catch non-determinism,warnin 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: Whentrue, 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. Whenfalse, rules fire in definition order. Recommended:truefor performance,falseif you need explicit ordering control.SESL_STORE_SNAPSHOTS=true|false: Sets the default for snapshot storage. Whentrue, SESL saves fact snapshots after each iteration (large output). Whenfalse, snapshots are not stored. Recommended:falseby default, enable only when deep debugging is needed.SESL_LET_MISSING=true|false: Whentrue, conditions that reference undefined paths evaluate tofalseinstead of raising an error. Whenfalse, missing paths in conditions are errors. Recommended:falseto catch typos,truefor 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_pathserror_style,conflict_policy,max_iterationsdependency_sort,store_snapshots,let_missingcolour: on/off (same asCOLOUR ONcommand)
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. Parse and normalize
The command string is first broken into tokens (words, symbols, quoted strings). Keywords likeLOAD,RUN,ADD RULEare recognized regardless of case (load,Load,LOADare all equivalent). The parser checks that the command matches a known pattern—for example,LOAD filenamerequires exactly one argument, whileRUN SCENARIO Name DESCRIBErequires a scenario name and optional modifiers. If the command doesn't match any known pattern (e.g.,LOADD model.seslwith a typo, orRUN SCENARIOmissing 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. 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 viaOPTIONS 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 setOPTIONS model_path=/var/models, then the commandLOAD %model_path%/loan.seslbecomesLOAD /var/models/loan.seslafter substitution. Similarly, if you've set the environment variableSESL_DEFAULT_SCENARIO=Demo, the commandRUN SCENARIO %SESL_DEFAULT_SCENARIO%becomesRUN 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:
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# 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" } ...NEW template.seslt, where the template engine replaces all%VARIABLE%tokens with current OPTIONS or environment values before creating the model. -
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, theRUNandEXITcommands 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 runLOAD,LINT, orADD RULEwill fail, butRUN(to execute already-loaded models) andEXITwork normally. -
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,LOADopens a file, parses YAML, and validates the model structure, whileRUNinitializes the inference engine, loads scenarios, and begins forward chaining. Dispatch failures are rare but can occur if internal state is inconsistent (e.g., trying toRUNwhen no model is loaded). -
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-pathsis 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 tryADD RULEwith invalid YAML (missing colon, bad indentation), you'll see "YAML parse error at line 3: expected key-value pair". If you try to write toapplicant.address.citybutapplicant.addressdoesn't exist and--strict-pathsis on, you'll see "Path not found: applicant.address. Enable --auto-create-paths or initialize the parent first." -
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 ranRUNwithout 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 theifcondition is true given the current facts. If true, the rule fires: itsthenactions 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 (ifDESCRIBEwas requested), and any snapshots or graphs (if enabled) into the result structure. -
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 (ifCOLOUR 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 withstatus(success/error),message, anddatafields. 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 optionallysnapshots(if--store-snapshots) orgraph(if--graph). The renderer applies the--error-stylesetting (fancy or plain) to error messages, and respects thecolouroption (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 consultHELPfor 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.seslwhenmodel_dirhasn't been set viaOPTIONSor as an environment variable. Error message: "Undefined variable: %model_dir%". Fix: Set the variable withOPTIONS model_dir=/pathor 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
RUNorEXITon 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 runRUNto 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-pathsenabled, 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 astrue/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-missingis 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 themonitoroutput withRUN DESCRIBEto 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, useLINTto 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 runseslwith no arguments. It provides aSESL>prompt, command history (use up/down arrows to recall previous commands), and multiline input support. When you type a command likeADD RULEwithout inline YAML, the CLI enters multiline mode: you can type or paste YAML across many lines, and terminate withENDon 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 withSAVE, deleting rules/scenarios/facts) prompt for confirmation: "File exists. Overwrite? (y/n)". You can typeyornto 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), anddata(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 runseslwith a filename argument (and no--onlineor--batchflag), SESL enters file-execution mode. This is a shortcut equivalent tosesl --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.jsonruns 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:
- Convergence: No rules fired in the latest pass (no new facts were written). The model has reached a stable state.
- Max iterations: The iteration limit (default 1000, configurable via
--max-iterorOPTIONS 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:
- result: your model’s output facts (often nested under
result). - metrics: iterations, rules evaluated/fired, and elapsed milliseconds.
- _sesl.support: per-rule trace showing what fired, what was read/written, and the reason.
- _sesl.monitor: ordered trace lines for step-by-step reasoning.
- _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
returncodeandstderrfor failures. - Set
cwdinsubprocess.runif you want outputs in a specific directory.