An AI generated, agnostic prompt language for describing software logic to AI systems.

Write what.
Let AI write how.

LogicScript sits between plain English (too vague) and production code (too specific). Describe your system's logic once — any AI turns it into TypeScript, Python, SQL, Java, Rust, C++ — or whatever!.

Sponsored by Lee Mellinger, Mellinger Consulting, LLC

logicscript prompt
Create a Python script from this LogicScript

SHAPE User
  id        : UUID      required auto
  email     : String    required unique
  role      : Enum[admin, user]  default=user

FUNC createUser(email, password, name)
  --- Creates a new user account. ---
  VALIDATE
    email    matches email pattern
    password length >= 8
    email    not exists in User

  DO
    hash = bcrypt(password)
    user = User.create(email, hash, name)
    EMIT UserCreated WITH user

  RETURN user
  ON FAIL THROW ValidationError

ON UserCreated
  TRIGGER EmailService.sendWelcome(user.email)
  LOG "New signup: {user.email}"
Language-agnostic

Write one LogicScript prompt. Generate TypeScript, Python, SQL, Java, Rust, or C++ from the same source. The AI handles the translation.

Intentionally incomplete

LogicScript describes intent, not implementation. Plain English is valid inside any block. The AI fills gaps with sensible defaults for your target stack.

Readable by anyone

Power users write prompts without programming experience. Developers use the same format to generate production-grade code with precise contracts.

Building blocks

SHAPE
Data structure with typed fields
FUNC
Action with validate / do / return
FLOW
Multi-step process, parallel support
VALIDATE
Pre-flight precondition checks
GUARD
Reusable access control block
POLICY
Cross-cutting system rule
ON / EMIT
Event subscriptions and emissions
STATE
State machine with transitions
QUERY
Named data retrieval operation
SCHEDULE
Recurring automated job
@annotations
Cache, retry, transaction hints
MODULE
Service boundary and public API

Four steps to working code

01

Define your data

Use SHAPE to describe records — fields, types, and constraints like required, unique, and default.

02

Describe your logic

Use FUNC with VALIDATE and DO blocks to describe actions, rules, and what happens when things go wrong.

03

Choose a target

Pick any language or framework. TypeScript, Python, SQL, Java, Rust, C++. The same prompt works for all of them.

04

Prompt an AI

Paste your LogicScript prompt with the instruction: "Implement this in [language]." Review and deploy.

prompt template
Implement the following LogicScript prompt in [TARGET LANGUAGE].

Rules:
- Honor every VALIDATE condition as a precondition check.
- Map ON FAIL clauses to the language's error/exception mechanism.
- Translate EMIT to the appropriate event or message-bus call.
- Do not add behavior not described in the prompt.

[LOGICSCRIPT PROMPT]

REFERENCE: https://github.com/logicscript/logicscript/blob/main/logicscript-spec.md

What is LogicScript?

LogicScript is a way of writing down the rules of a piece of software in plain, structured English — precise enough that an AI can turn it into working code, but readable enough that a non-programmer can write and understand it.

Think of it like writing a Standard Operating Procedure. You describe what information exists, what actions are allowed, what the rules are, and what happens automatically. You do not describe how the computer carries out those actions — that is the AI's job.

The key idea: Write what, not how.

How it works

  1. Write a prompt in LogicScript describing the system's rules and behaviours.
  2. Give it to an AI (Claude, GPT-4, etc.) with an instruction like "Implement this LogicScript prompt in Python."
  3. The AI produces working code that follows your prompt exactly.
  4. A developer reviews and deploys the code.
Note

LogicScript is a prompt language, not an execution runtime. It has no interpreter. Its output is always AI-generated code in a target language.

Your first prompt

A LogicScript prompt follows a few simple rules:

  • Keywords are ALL CAPSSHAPE, FUNC, VALIDATE, etc.
  • Indentation matters — two spaces per level, like a nested bullet list
  • Plain English is valid inside any block
  • Comments start with --
logicscript
SHAPE Product
  id    : UUID    required auto
  name  : String  required
  price : Float   required min=0
  stock : Int     required default=0

FUNC buyProduct(productId, quantity, userId)
  --- Lets a user purchase a product. ---

  VALIDATE
    quantity > 0
    product exists with id = productId
    product.stock >= quantity
    user has valid payment method

  DO
    charge user for quantity * product.price
    reduce product.stock by quantity
    create an Order record
    EMIT OrderPlaced WITH order

  RETURN order
  ON FAIL THROW PurchaseError

Core concepts

Indentation

LogicScript uses indentation (two spaces) to express nesting. No braces, brackets, or end keywords.

Plain English is always valid

If a condition or step is hard to express structurally, write it out in plain English. The AI infers the most idiomatic implementation.

Design philosophy

  • Intentionally incomplete — the AI fills sensible defaults
  • Indentation over syntax — no braces or semicolons
  • Declarative over imperative — describe outcomes, not steps

SHAPE — describing data

A SHAPE describes a type of record — its fields, types, and constraints.

Type Stores
String Text of any length
Int Whole number
Float Decimal number
Bool True or false
UUID Unique identifier (auto-generated)
Timestamp Date and time
Enum[a,b,c] One of a fixed list of values
List<Type> Ordered collection
Constraint Meaning
required Must always be present
optional May be blank or null
unique No two records share this value
auto System-generated automatically
default=X Use X when not supplied
min=N / max=N Value or length limits
immutable Cannot change after creation
indexed Optimise lookups on this field

FUNC — describing actions

A FUNC defines something the system can do. It has four optional blocks:

logicscript
FUNC submitExpenseReport(reportId, userId)
  --- Submits a draft expense report for approval. ---

  VALIDATE
    report exists with id = reportId
    report.status IS draft
    report.ownerId IS userId
    report.totalAmount > 0

  DO
    report.status = pending
    report.submittedAt = NOW
    EMIT ReportSubmitted WITH { reportId, userId }

  RETURN report
  ON FAIL LOG error, THROW SubmissionError

FLOW — multi-step processes

A FLOW is for processes with distinct named stages. Use PARALLEL to run steps concurrently.

logicscript
FLOW OnboardNewEmployee(employeeId)
  STEP createAccounts
    create email, Slack, and HR system accounts

  STEP scheduleOrientation
    find next available orientation session
    send calendar invite to employee and manager

  STEP notify
    EMIT EmployeeOnboarded WITH { employeeId }
    send welcome email to employee

VALIDATE — input checks

Each line in a VALIDATE block is one check. If any fails, execution stops before any side effects run.

logicscript
VALIDATE
  email not empty
  email matches email pattern
  password length >= 8
  age between 18 and 120
  productId exists in Product
  email not exists in User
  startDate is before endDate
  cart.items not empty  MESSAGE "Cart cannot be empty"

GUARD & ALLOW / DENY

Define reusable access rules once and apply them anywhere.

logicscript
GUARD AdminOnly
  REQUIRE user.role IS admin
  ON FAIL THROW ForbiddenError

FUNC deleteRecord(recordId)
  GUARD AdminOnly
  DO
    Record.delete(recordId)

ALLOW publish WHEN user.role IS admin OR user.role IS editor
DENY  delete  WHEN document.locked IS true

POLICY — system-wide rules

logicscript
POLICY RateLimit
  APPLIES TO all API endpoints
  ALLOW 100 requests PER user PER minute
  ON EXCEED return error "Too many requests"

POLICY DataRetention
  APPLIES TO CustomerActivityLog
  KEEP 2 years
  THEN archive to cold storage
  DELETE after 7 years

QUERY — finding records

logicscript
QUERY overdueInvoices
  FROM Invoice
  WHERE status IS sent
  AND   dueDate < TODAY
  ORDER BY dueDate ASC

QUERY recentOrdersForUser(userId)
  FROM Order
  WHERE userId IS userId
  AND   createdAt WITHIN 90 days
  INCLUDE items, shippingAddress
  ORDER BY createdAt DESC

ON & EMIT — events

Events decouple producers from consumers. EMIT fires an event; ON reacts to it.

logicscript
EMIT OrderPlaced WITH { orderId, userId, total }

ON OrderPlaced
  send order confirmation to user.email
  notify fulfillment team
  LOG "Order {orderId} placed"

ON PaymentFailed 3 times
  suspend user account
  ALERT billing-team

STATE — status workflows

logicscript
STATE Invoice
  STATES  draft, sent, paid, overdue, cancelled

  TRANSITION draft   -> sent    ON sendInvoice
  TRANSITION sent    -> paid    ON paymentReceived
  TRANSITION sent    -> overdue ON dueDatePassed
  TRANSITION ANY     -> cancelled ON cancelInvoice
    WHEN status NOT IN [paid]

  ON ENTER overdue
    send reminder to invoice.customerEmail
    ALERT accounts-receivable-team

SCHEDULE — recurring jobs

logicscript
SCHEDULE cleanExpiredSessions
  EVERY 1 hour
  DO
    deleted = Session.deleteExpired()
    LOG "Purged {deleted} sessions"

SCHEDULE monthlyBilling
  AT "first day of month 00:01 UTC"
  DO
    FOR EACH active subscription
      charge subscriber for their plan

Annotations — hints for the AI

Annotation Meaning
@transaction All steps succeed or all roll back
@retryable attempts=3 Retry on failure up to N times
@cached ttl=5m Cache the result for 5 minutes
@idempotent key="..." Only run once per unique input
@rateLimit 10/minute Limit calls per user per minute
@deprecated since="2.0" Mark as replaced by a newer function

Tips for better prompts

  • Mix plain English freely. If something is hard to express structurally, just write it out.
  • Keep each FUNC focused. One function, one purpose. If your DO block has more than 7 steps, consider splitting.
  • Name things clearly. submitExpenseReport is better than submit.
  • Be explicit about failures. Always include ON FAIL so the AI knows what error contract to implement.
  • Use @transaction for money. Any function that moves funds or adjusts inventory should be atomic.
  • Include the spec reference. Add REFERENCE: https://github.com/logicscript/logicscript/blob/main/logicscript-spec.md to your prompt so the AI gets up to speed quickly.

Quick reference card

structure
SHAPE   Name          — define a data record
FUNC    name(inputs)  — define an action
FLOW    Name(inputs)  — multi-step process
  STEP  stepName      — one stage of a flow
  PARALLEL … WAIT all — concurrent steps
MODULE  Name          — group related functions
  ENTRY functionName  — mark a function as public
logic
VALIDATE              — pre-flight checks (stop if any fail)
DO                    — sequential side-effect steps
RETURN value          — output value
ON FAIL THROW Error   — error handling
REQUIRE x ELSE THROW  — inline check inside DO
IF condition … ELSE   — branching
FOR EACH item         — iteration
access + events
GUARD  Name           — define a reusable access rule
ALLOW action WHEN x   — grant permission
DENY  action WHEN x   — block permission
EMIT  Event WITH data — fire an event
ON    EventName       — react to an event
  TRIGGER fn(args)    — call a function
  ALERT team-name     — notify a team
MODULEService or component boundary

Groups related functions and declares the public API surface. ENTRY marks a function as publicly callable.

syntax
MODULE ServiceName
  [IMPORT Symbol1, Symbol2 FROM source]
  [USE Symbol.method AS alias]
  ENTRY functionName(params)
SHAPEData structure definition

Defines a data structure with typed, constrained fields. Types and constraints are implementation hints — the AI maps them to the most appropriate constructs in the target language.

syntax
SHAPE ShapeName
  fieldName : Type  constraint [constraint ...]
FUNCFunction definition

Defines a unit of logic. All four blocks are optional, but at least DO or RETURN must be present.

syntax
FUNC functionName(param1, param2, ...)
  [--- doc comment ---]
  [VALIDATE
    condition ...]
  [DO
    step ...]
  [RETURN value]
  [ON FAIL strategy]

ON FAIL strategies

options
ON FAIL THROW ErrorName
ON FAIL LOG error
ON FAIL RETURN defaultValue
ON FAIL retry N times THEN THROW ErrorName
ON FAIL LOG error, THROW ErrorName
VALIDATEPrecondition block

A block of precondition checks inside a FUNC. Each line is one condition, evaluated top to bottom. The first failure stops execution and triggers the ON FAIL handler. Nothing in DO runs if any check fails.

common patterns
VALIDATE
  email not empty
  email matches email pattern
  password length >= 8
  age between 18 and 120
  price > 0
  productId exists in Product
  email not exists in User
  startDate is before endDate
  cart.items not empty  MESSAGE "Cart cannot be empty"

Plain English is valid for complex conditions the AI will infer the implementation for.

FLOWMulti-step process

A named, multi-step operation. Use when a process has distinct named stages. PARALLEL runs steps concurrently; WAIT all or WAIT any synchronises.

syntax
FLOW FlowName(params)
  STEP stepName
    logic ...
  PARALLEL
    STEP stepA ...
    STEP stepB ...
  WAIT all
GUARDReusable access control
syntax
GUARD GuardName
  REQUIRE condition [OR condition]
  ON FAIL THROW ErrorName
POLICYCross-cutting system rule
syntax
POLICY PolicyName
  APPLIES TO target [, target ...]
  ALLOW N requests PER unit PER period
  ON EXCEED action
QUERYNamed data retrieval
syntax
QUERY queryName(params)
  FROM     ShapeName
  [WHERE   condition [AND condition ...]]
  [INCLUDE relatedShape, ...]
  [ORDER BY field ASC|DESC]
  [LIMIT   N]
  [OFFSET  N]
ON / EMITEvent-driven reactions
syntax
EMIT EventName WITH { field1, field2 }

ON EventName
  TRIGGER functionName(args)
  SEND MessageType TO recipient
  LOG "message {variable}"
  ALERT team-name

ON EventName N times   -- fires after N occurrences
  ...
STATEFinite state machine
syntax
STATE ShapeName
  STATES  state1, state2, ...

  TRANSITION a -> b ON eventName
  TRANSITION ANY -> b ON eventName
    WHEN status NOT IN [excludedState]

  ON ENTER stateName
    steps ...
  ON EXIT stateName
    steps ...
SCHEDULERecurring job
syntax
SCHEDULE jobName
  EVERY N unit        -- minutes | hours | days
  DO
    steps ...

SCHEDULE jobName
  AT "schedule expression"
  DO
    steps ...

Examples: "9:00 AM daily", "Monday 8:00 AM", "first day of month 08:00 UTC"

@annotationsImplementation hints
Annotation Meaning Hint
@transaction Atomic block DB transaction + rollback on error
@retryable attempts=N backoff=X Retry on failure X: linear, exponential, or duration
@cached ttl=Ns key="..." Cache result In-memory, Redis, or equivalent
@idempotent key="..." Deduplicate calls Return stored result for same key
@deprecated since="X" use=fn Mark as replaced Add deprecation warning
@observable metrics=[m1,m2] Instrument with metrics Latency, error_rate, throughput
@rateLimit N/period per=field Per-caller rate limit Middleware or decorator

Primitive types

Type Description
String UTF-8 text of any length
Int 64-bit signed integer
Float 64-bit floating-point
Bool Boolean true or false
UUID RFC 4122 UUID
Timestamp ISO 8601 date-time with timezone
List<T> Ordered collection of T
Map<K,V> Key-value map
Enum[a,b,c] Closed set of named values
JSON Arbitrary JSON-serializable value

Constraints

Constraint Applies to Meaning
required All Must be present and non-null
optional All May be null or absent
unique All Value must be unique across all records
auto UUID, Timestamp System-generated automatically
default=X All Default when not supplied
min=N Int, Float, String, List Minimum value or length
max=N Int, Float, String, List Maximum value or length
pattern="regex" String Must match the regular expression
immutable All Cannot change after creation
indexed All Add a database index

Keyword index

Keyword Category Role
MODULE Structure Named service/component boundary
ENTRY Structure Public function within MODULE
SHAPE Structure Data structure definition
FUNC Structure Function definition
FLOW Structure Multi-step process
STEP Structure Named stage within a FLOW
PARALLEL / WAIT Structure Concurrent steps
VALIDATE Logic Precondition block
DO Logic Ordered steps
RETURN Logic Output value
ON FAIL Logic Error handling
REQUIRE … ELSE Logic Inline assertion in DO
IF / ELSE Logic Conditional branching
FOR EACH Logic Iteration
ALLOW / DENY Access Permission rules
GUARD Access Reusable access block
POLICY Access Cross-cutting rule
QUERY Data Named data retrieval
EMIT … WITH Events Publish a named event
ON [event] Events Subscribe to an event
TRIGGER Events Invoke from event handler
ALERT Events Notify a team or channel
STATE State State machine definition
TRANSITION State Valid state change
ON ENTER / ON EXIT State State lifecycle hooks
SCHEDULE Jobs Recurring job
EVERY / AT Jobs Job timing

Formal grammar (BNF)

bnf — approximate
program     ::= (declaration | comment)*

declaration ::= module | shape | func | flow | guard
              | policy | query | on_handler | state | schedule

IDENT       ::= [a-zA-Z][a-zA-Z0-9_]*
PASCAL      ::= [A-Z][a-zA-Z0-9]*
CAMEL       ::= [a-z][a-zA-Z0-9]*

module      ::= "MODULE" PASCAL NL
                (INDENT import_stmt)*
                (INDENT "ENTRY" CAMEL "(" params? ")")*

shape       ::= "SHAPE" PASCAL NL
                (INDENT field_decl)*

field_decl  ::= CAMEL ":" type constraint*

type        ::= "String" | "Int" | "Float" | "Bool"
              | "UUID" | "Timestamp" | "JSON"
              | "List<" type ">" | "Map<" type "," type ">"
              | "Enum[" CAMEL ("," CAMEL)* "]"

func        ::= annotation* "FUNC" CAMEL "(" params? ")" NL
                doc_comment?
                validate_block?
                do_block?
                return_stmt?
                on_fail?

NL          ::= newline
INDENT      ::= "  " (two spaces)

1. Hello world

The simplest possible LogicScript prompt — one function, one validation rule, one return value. A good starting point to see how each language handles the basics.

The prompt

prompt (paste this into any AI)
Generate Python from the following LogicScript.

FUNC greet(name)
  VALIDATE
    name not empty
  DO
    message = "Hello, " + name + "."
  RETURN message
  ON FAIL THROW ValidationError

Generated output

typescript
// Generated from LogicScript — greet

class ValidationError extends Error {
  constructor(message: string) {
    super(message);
    this.name = "ValidationError";
  }
}

export function greet(name: string): string {
  // VALIDATE: name not empty
  if (!name || name.trim() === "") {
    throw new ValidationError("name must not be empty");
  }

  // DO
  const message = `Hello, ${name}.`;

  // RETURN
  return message;
}
python
# Generated from LogicScript — greet

class ValidationError(Exception):
    pass

def greet(name: str) -> str:
    """Returns a personalised greeting.

    Raises:
        ValidationError: If name is empty.
    """
    # VALIDATE: name not empty
    if not name or not name.strip():
        raise ValidationError("name must not be empty")

    # DO
    message = f"Hello, {name}."

    # RETURN
    return message


if __name__ == "__main__":
    print(greet("world"))  # Hello, world.
sql
-- Generated from LogicScript — greet (PostgreSQL)

CREATE OR REPLACE FUNCTION greet(p_name TEXT)
RETURNS TEXT
LANGUAGE plpgsql AS $$
BEGIN
  -- VALIDATE: name not empty
  IF p_name IS NULL OR trim(p_name) = '' THEN
    RAISE EXCEPTION 'ValidationError: name must not be empty';
  END IF;

  -- DO + RETURN
  RETURN 'Hello, ' || p_name || '.';
END;
$$;

-- Usage:
-- SELECT greet('world');  -- Hello, world.
java
// Generated from LogicScript — greet

public class Greeter {

    public static class ValidationException extends RuntimeException {
        public ValidationException(String message) { super(message); }
    }

    /**
     * Returns a personalised greeting.
     * @throws ValidationException if name is null or blank
     */
    public static String greet(String name) {
        // VALIDATE: name not empty
        if (name == null || name.isBlank()) {
            throw new ValidationException("name must not be empty");
        }

        // DO + RETURN
        return "Hello, " + name + ".";
    }

    public static void main(String[] args) {
        System.out.println(greet("world")); // Hello, world.
    }
}
rust
// Generated from LogicScript — greet

#[derive(Debug, thiserror::Error)]
pub enum ValidationError {
    #[error("name must not be empty")]
    EmptyName,
}

/// Returns a personalised greeting.
///
/// # Errors
/// Returns `ValidationError::EmptyName` if `name` is empty or whitespace.
pub fn greet(name: &str) -> Result {
    // VALIDATE: name not empty
    if name.trim().is_empty() {
        return Err(ValidationError::EmptyName);
    }

    // DO + RETURN
    Ok(format!("Hello, {}.", name))
}

fn main() {
    println!("{}", greet("world").unwrap()); // Hello, world.
}
cpp
// Generated from LogicScript — greet
#include <stdexcept>
#include <string>
#include <iostream>

class ValidationError : public std::runtime_error {
public:
    explicit ValidationError(const std::string& msg)
        : std::runtime_error(msg) {}
};

/// Returns a personalised greeting.
/// @throws ValidationError if name is empty or whitespace.
std::string greet(const std::string& name) {
    // VALIDATE: name not empty
    bool blank = name.find_first_not_of("

") == std::string::npos;
    if (name.empty() || blank) {
        throw ValidationError("name must not be empty");
    }

    // DO + RETURN
    return "Hello, " + name + ".";
}

int main() {
    std::cout << greet("world") << std::endl; // Hello, world.
}

2. Create user

A realistic function with a data shape, multiple validation rules, password hashing, and an event emission. This is the pattern most apps need from day one.

The prompt

prompt (paste this into any AI)
Generate TypeScript (Node.js, Prisma, bcryptjs) from the following LogicScript.

SHAPE User
  id           : UUID      required auto
  email        : String    required unique
  name         : String    required min=2
  passwordHash : String    required
  role         : Enum[admin, user, guest]  default=user
  createdAt    : Timestamp auto

FUNC createUser(email, password, name)
  VALIDATE
    email    matches email pattern
    password length >= 8
    name     length >= 2
    email    not exists in User
  DO
    hash = bcrypt(password)
    user = User.create(email, hash, name)
    EMIT UserCreated WITH user
  RETURN user
  ON FAIL THROW ValidationError

ON UserCreated
  TRIGGER EmailService.sendWelcome(user.email, user.name)
  LOG "New signup: {user.email}"

Generated output

typescript
// Generated from LogicScript — SHAPE User + FUNC createUser
import bcrypt      from "bcryptjs";
import { v4 as uuidv4 } from "uuid";
import { db }      from "./db";        // Prisma client
import { eventBus } from "./events";

export interface User {
  id:           string;
  email:        string;
  name:         string;
  passwordHash: string;
  role:         "admin" | "user" | "guest";
  createdAt:    Date;
}

export class ValidationError extends Error {
  statusCode = 422;
  constructor(message: string) { super(message); this.name = "ValidationError"; }
}

const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;

/** @throws {ValidationError} */
export async function createUser(
  email: string, password: string, name: string
): Promise<User> {
  // VALIDATE
  if (!EMAIL_RE.test(email))       throw new ValidationError("Invalid email format");
  if (password.length < 8)         throw new ValidationError("Password must be at least 8 characters");
  if (name.length < 2)             throw new ValidationError("Name must be at least 2 characters");
  if (await db.user.findUnique({ where: { email } }))
                                   throw new ValidationError("Email already registered");

  // DO: hash + create
  const passwordHash = await bcrypt.hash(password, 12);
  const user = await db.user.create({
    data: { id: uuidv4(), email, name, passwordHash, role: "user", createdAt: new Date() },
  });

  // EMIT UserCreated
  eventBus.emit("UserCreated", user);
  return user;
}

// ON UserCreated
eventBus.on("UserCreated", async (user: User) => {
  await EmailService.sendWelcome(user.email, user.name); // TRIGGER
  console.log(`New signup: ${user.email}`);              // LOG
});
python
# Generated from LogicScript — SHAPE User + FUNC createUser
import re, uuid, bcrypt, logging
from dataclasses import dataclass, field
from datetime   import datetime, timezone
from enum       import Enum

log = logging.getLogger(__name__)

class Role(Enum):
    ADMIN = "admin"; USER = "user"; GUEST = "guest"

@dataclass
class User:
    id:            str      = field(default_factory=lambda: str(uuid.uuid4()))
    email:         str      = ""
    name:          str      = ""
    password_hash: str      = ""
    role:          Role     = Role.USER
    created_at:    datetime = field(default_factory=lambda: datetime.now(timezone.utc))

class ValidationError(Exception):
    status_code = 422

EMAIL_RE = re.compile(r"^[^\s@]+@[^\s@]+\.[^\s@]+$")

def create_user(email: str, password: str, name: str, db, event_bus) -> User:
    """Creates a new user. Raises ValidationError if any check fails."""
    # VALIDATE
    if not EMAIL_RE.match(email):
        raise ValidationError("Invalid email format")
    if len(password) < 8:
        raise ValidationError("Password must be at least 8 characters")
    if len(name) < 2:
        raise ValidationError("Name must be at least 2 characters")
    if db.user_exists(email):
        raise ValidationError("Email already registered")

    # DO: hash + create
    hashed = bcrypt.hashpw(password.encode(), bcrypt.gensalt()).decode()
    user   = User(email=email, password_hash=hashed, name=name)
    db.save(user)

    # EMIT UserCreated
    event_bus.emit("UserCreated", user)
    return user

# ON UserCreated
def on_user_created(user: User):
    email_service.send_welcome(user.email, user.name)   # TRIGGER
    log.info(f"New signup: {user.email}")               # LOG
sql
-- Generated from LogicScript — SHAPE User + FUNC createUser (PostgreSQL)
CREATE EXTENSION IF NOT EXISTS pgcrypto;
CREATE TYPE user_role AS ENUM ('admin', 'user', 'guest');

-- SHAPE User → CREATE TABLE
CREATE TABLE users (
  id            UUID         PRIMARY KEY DEFAULT gen_random_uuid(),
  email         TEXT         NOT NULL UNIQUE,
  name          VARCHAR(200) NOT NULL CHECK (length(name) >= 2),
  password_hash TEXT         NOT NULL,
  role          user_role    NOT NULL DEFAULT 'user',
  created_at    TIMESTAMPTZ  NOT NULL DEFAULT now()
);
CREATE INDEX idx_users_email ON users (email);

-- FUNC createUser → plpgsql function
CREATE OR REPLACE FUNCTION create_user(
  p_email TEXT, p_password TEXT, p_name TEXT
) RETURNS users LANGUAGE plpgsql AS $$
DECLARE v_hash TEXT; v_user users;
BEGIN
  -- VALIDATE
  IF p_email !~ '^[^\s@]+@[^\s@]+\.[^\s@]+$' THEN
    RAISE EXCEPTION 'ValidationError: invalid email format'; END IF;
  IF length(p_password) < 8 THEN
    RAISE EXCEPTION 'ValidationError: password too short'; END IF;
  IF length(p_name) < 2 THEN
    RAISE EXCEPTION 'ValidationError: name too short'; END IF;
  IF EXISTS(SELECT 1 FROM users WHERE email = p_email) THEN
    RAISE EXCEPTION 'ValidationError: email already registered'; END IF;

  -- DO: hash + insert
  v_hash := crypt(p_password, gen_salt('bf', 12));
  INSERT INTO users(email, name, password_hash)
    VALUES(p_email, p_name, v_hash) RETURNING * INTO v_user;

  -- EMIT UserCreated via pg_notify
  PERFORM pg_notify('user_created', row_to_json(v_user)::text);
  RETURN v_user;
END; $$;
java
// Generated from LogicScript — SHAPE User + FUNC createUser
import java.time.Instant;
import java.util.UUID;
import java.util.regex.Pattern;

public record User(UUID id, String email, String name,
                   String passwordHash, Role role, Instant createdAt) {
    public enum Role { ADMIN, USER, GUEST }
}

public class ValidationException extends RuntimeException {
    public final int statusCode = 422;
    public ValidationException(String msg) { super(msg); }
}

@Service
public class UserService {
    private static final Pattern EMAIL =
        Pattern.compile("^[^\s@]+@[^\s@]+\.[^\s@]+$");

    private final UserRepository     repo;
    private final PasswordEncoder    encoder;
    private final ApplicationEventPublisher publisher;
    private static final Logger log = LoggerFactory.getLogger(UserService.class);

    public User createUser(String email, String password, String name) {
        // VALIDATE
        if (!EMAIL.matcher(email).matches())
            throw new ValidationException("Invalid email format");
        if (password.length() < 8)
            throw new ValidationException("Password must be at least 8 characters");
        if (name.length() < 2)
            throw new ValidationException("Name must be at least 2 characters");
        if (repo.existsByEmail(email))
            throw new ValidationException("Email already registered");

        // DO: hash + create
        String hash = encoder.encode(password);
        User user = new User(UUID.randomUUID(), email, name, hash, User.Role.USER, Instant.now());
        user = repo.save(user);

        // EMIT UserCreated
        publisher.publishEvent(new UserCreatedEvent(this, user));
        return user;
    }
}

// ON UserCreated
@EventListener
public void onUserCreated(UserCreatedEvent event) {
    emailService.sendWelcome(event.user().email(), event.user().name()); // TRIGGER
    log.info("New signup: {}", event.user().email());                    // LOG
}
rust
// Generated from LogicScript — SHAPE User + FUNC createUser
use uuid::Uuid;
use chrono::{DateTime, Utc};
use regex::Regex;
use std::sync::OnceLock;

#[derive(Debug, Clone, serde::Serialize)]
pub struct User {
    pub id:            Uuid,
    pub email:         String,
    pub name:          String,
    pub password_hash: String,
    pub role:          Role,
    pub created_at:    DateTime<Utc>,
}

#[derive(Debug, Clone, serde::Serialize, sqlx::Type)]
#[sqlx(type_name = "user_role", rename_all = "lowercase")]
pub enum Role { Admin, User, Guest }

#[derive(Debug, thiserror::Error)]
pub enum ValidationError {
    #[error("Invalid email format")]        InvalidEmail,
    #[error("Password must be >= 8 chars")] PasswordTooShort,
    #[error("Name must be >= 2 chars")]     NameTooShort,
    #[error("Email already registered")]    EmailTaken,
    #[error(transparent)] Db(#[from] sqlx::Error),
}

static EMAIL_RE: OnceLock<Regex> = OnceLock::new();

pub async fn create_user(
    pool: &sqlx::PgPool, bus: &impl EventBus,
    email: String, password: String, name: String,
) -> Result<User, ValidationError> {
    let re = EMAIL_RE.get_or_init(|| Regex::new(r"^[^\s@]+@[^\s@]+\.[^\s@]+$").unwrap());

    // VALIDATE
    if !re.is_match(&email)  { return Err(ValidationError::InvalidEmail); }
    if password.len() < 8   { return Err(ValidationError::PasswordTooShort); }
    if name.len() < 2        { return Err(ValidationError::NameTooShort); }
    let exists: bool = sqlx::query_scalar("SELECT EXISTS(SELECT 1 FROM users WHERE email=$1)")
        .bind(&email).fetch_one(pool).await?;
    if exists { return Err(ValidationError::EmailTaken); }

    // DO: hash + insert
    let hash = bcrypt::hash(&password, bcrypt::DEFAULT_COST).unwrap();
    let user: User = sqlx::query_as(
        "INSERT INTO users(id,email,name,password_hash,role,created_at)
         VALUES($1,$2,$3,$4,'user',NOW()) RETURNING *")
        .bind(Uuid::new_v4()).bind(&email).bind(&name).bind(&hash)
        .fetch_one(pool).await?;

    // EMIT UserCreated
    bus.emit("UserCreated", &user).await;
    tracing::info!("New signup: {}", user.email); // LOG
    Ok(user)
}
cpp
// Generated from LogicScript — SHAPE User + FUNC createUser
#pragma once
#include <string>, <stdexcept>, <regex>, <chrono>
#include "uuid.hpp", "db.hpp", "event_bus.hpp", "bcrypt.hpp"

enum class Role { Admin, User, Guest };

struct User {
    std::string id, email, name, password_hash;
    Role        role{Role::User};
    std::chrono::system_clock::time_point created_at;
};

class ValidationError : public std::runtime_error {
public:
    int status_code = 422;
    explicit ValidationError(const std::string& msg) : std::runtime_error(msg) {}
};

class UserService {
public:
    UserService(Database& db, EventBus& bus)
        : db_(db), bus_(bus)
        , email_re_(R"(^[^\s@]+@[^\s@]+\.[^\s@]+$)", std::regex::ECMAScript) {}

    User createUser(std::string_view email, std::string_view password, std::string_view name) {
        // VALIDATE
        if (!std::regex_match(email.begin(), email.end(), email_re_))
            throw ValidationError("Invalid email format");
        if (password.size() < 8)
            throw ValidationError("Password must be at least 8 characters");
        if (name.size() < 2)
            throw ValidationError("Name must be at least 2 characters");
        if (db_.user_exists_by_email(email))
            throw ValidationError("Email already registered");

        // DO: hash + insert
        auto hash = bcrypt::hash(password, 12);
        User user{ uuid::v4(), std::string(email), std::string(name),
                   std::move(hash), Role::User, std::chrono::system_clock::now() };
        user = db_.insert_user(user);

        // EMIT UserCreated
        bus_.emit("UserCreated", user);
        return user;
    }
private:
    Database& db_; EventBus& bus_; std::regex email_re_;
};

3. Transfer funds

Shows @transaction — all steps must succeed together or none of them happen. The AI maps this to the target language's transaction mechanism with automatic rollback.

The prompt

prompt (paste this into any AI)
Generate Java (Spring Boot, @Transactional) from the following LogicScript.

@transaction
FUNC transferFunds(fromId, toId, amount)
  VALIDATE
    amount > 0
    fromId not equals toId
    Account.find(fromId).balance >= amount
  DO
    Account.debit(fromId, amount)
    Account.credit(toId, amount)
    EMIT FundsTransferred WITH { fromId, toId, amount }
  RETURN { success: true, timestamp: NOW }
  ON FAIL THROW TransferError, ROLLBACK

Generated output

typescript
// Generated from LogicScript — @transaction FUNC transferFunds
import { db }       from "./db";
import { eventBus } from "./events";

export class TransferError extends Error {
  constructor(msg: string) { super(msg); this.name = "TransferError"; }
}

interface TransferResult { success: boolean; timestamp: string; }

/** @throws {TransferError} */
export async function transferFunds(
  fromId: string, toId: string, amount: number
): Promise<TransferResult> {
  // VALIDATE
  if (amount <= 0)    throw new TransferError("Amount must be positive");
  if (fromId === toId) throw new TransferError("Cannot transfer to the same account");

  const from = await db.account.findUniqueOrThrow({ where: { id: fromId } });
  if (from.balance < amount) throw new TransferError("Insufficient funds");

  // @transaction — db.$transaction rolls back on any thrown error
  try {
    await db.$transaction(async (tx) => {
      await tx.account.update({ where: { id: fromId }, data: { balance: { decrement: amount } } });
      await tx.account.update({ where: { id: toId },   data: { balance: { increment: amount } } });
    });
  } catch (err) {
    throw new TransferError(`Transaction failed: ${(err as Error).message}`);
  }

  // EMIT FundsTransferred
  eventBus.emit("FundsTransferred", { fromId, toId, amount });
  return { success: true, timestamp: new Date().toISOString() };
}
python
# Generated from LogicScript — @transaction FUNC transferFunds
from datetime import datetime, timezone

class TransferError(Exception): pass

def transfer_funds(from_id: str, to_id: str, amount: float, db, event_bus) -> dict:
    """Moves funds atomically. Raises TransferError on any failure."""
    # VALIDATE
    if amount <= 0:
        raise TransferError("Amount must be positive")
    if from_id == to_id:
        raise TransferError("Cannot transfer to the same account")

    from_acct = db.query(Account).filter_by(id=from_id).one()
    if from_acct.balance < amount:
        raise TransferError("Insufficient funds")

    # @transaction → SQLAlchemy context manager; rolls back on any exception
    try:
        with db.begin():
            from_acct.balance -= amount
            to_acct = db.query(Account).filter_by(id=to_id).one()
            to_acct.balance   += amount
    except Exception as exc:
        raise TransferError(f"Transaction failed: {exc}") from exc

    # EMIT FundsTransferred
    event_bus.emit("FundsTransferred", {"from": from_id, "to": to_id, "amount": amount})
    return {"success": True, "timestamp": datetime.now(timezone.utc).isoformat()}
sql
-- Generated from LogicScript — @transaction FUNC transferFunds (PostgreSQL)
-- Procedures run inside an implicit transaction; RAISE causes automatic rollback.

CREATE OR REPLACE PROCEDURE transfer_funds(
  p_from_id UUID, p_to_id UUID, p_amount NUMERIC(18,2)
) LANGUAGE plpgsql AS $$
DECLARE v_balance NUMERIC(18,2);
BEGIN
  -- VALIDATE
  IF p_amount <= 0 THEN
    RAISE EXCEPTION 'TransferError: amount must be positive'; END IF;
  IF p_from_id = p_to_id THEN
    RAISE EXCEPTION 'TransferError: cannot transfer to the same account'; END IF;

  -- Lock the source row to prevent race conditions
  SELECT balance INTO v_balance
    FROM accounts WHERE id = p_from_id FOR UPDATE;
  IF v_balance < p_amount THEN
    RAISE EXCEPTION 'TransferError: insufficient funds'; END IF;

  -- DO: atomic debit/credit — any error here triggers automatic ROLLBACK
  UPDATE accounts SET balance = balance - p_amount WHERE id = p_from_id;
  UPDATE accounts SET balance = balance + p_amount WHERE id = p_to_id;

  -- EMIT FundsTransferred via pg_notify
  PERFORM pg_notify('funds_transferred',
    json_build_object('from', p_from_id, 'to', p_to_id, 'amount', p_amount)::text);
END; $$;

-- Usage (caller controls outer transaction):
-- CALL transfer_funds('uuid-a', 'uuid-b', 100.00);
java
// Generated from LogicScript — @transaction FUNC transferFunds
import java.math.BigDecimal;
import java.time.Instant;
import java.util.Map;

public class TransferException extends RuntimeException {
    public TransferException(String msg) { super(msg); }
}

record TransferResult(boolean success, Instant timestamp) {}

@Service
public class TransferService {

    private final AccountRepository accountRepository;
    private final ApplicationEventPublisher publisher;

    // @transaction → Spring @Transactional rolls back on any RuntimeException
    @Transactional
    public TransferResult transferFunds(String fromId, String toId, BigDecimal amount) {
        // VALIDATE
        if (amount.compareTo(BigDecimal.ZERO) <= 0)
            throw new TransferException("Amount must be positive");
        if (fromId.equals(toId))
            throw new TransferException("Cannot transfer to the same account");

        Account from = accountRepository.findByIdWithLock(fromId) // SELECT FOR UPDATE
            .orElseThrow(() -> new TransferException("Account not found"));
        if (from.getBalance().compareTo(amount) < 0)
            throw new TransferException("Insufficient funds");

        // DO: debit + credit
        accountRepository.debit(fromId, amount);
        accountRepository.credit(toId,  amount);

        // EMIT FundsTransferred
        publisher.publishEvent(new FundsTransferredEvent(this, fromId, toId, amount));

        return new TransferResult(true, Instant.now());
    }
}
rust
// Generated from LogicScript — @transaction FUNC transferFunds
use rust_decimal::Decimal;
use uuid::Uuid;
use serde_json::json;

#[derive(Debug, thiserror::Error)]
pub enum TransferError {
    #[error("Amount must be positive")]               InvalidAmount,
    #[error("Cannot transfer to the same account")]   SameAccount,
    #[error("Insufficient funds")]                    InsufficientFunds,
    #[error(transparent)]                             Db(#[from] sqlx::Error),
}

pub async fn transfer_funds(
    pool: &sqlx::PgPool, bus: &impl EventBus,
    from_id: Uuid, to_id: Uuid, amount: Decimal,
) -> Result<(), TransferError> {
    // VALIDATE
    if amount <= Decimal::ZERO { return Err(TransferError::InvalidAmount); }
    if from_id == to_id         { return Err(TransferError::SameAccount); }

    // @transaction — explicit sqlx transaction; rollback on any ? error
    let mut tx = pool.begin().await?;

    let balance: Decimal = sqlx::query_scalar(
        "SELECT balance FROM accounts WHERE id = $1 FOR UPDATE")
        .bind(from_id).fetch_one(&mut *tx).await?;

    if balance < amount {
        tx.rollback().await?;
        return Err(TransferError::InsufficientFunds);
    }

    // DO: debit + credit
    sqlx::query("UPDATE accounts SET balance = balance - $1 WHERE id = $2")
        .bind(amount).bind(from_id).execute(&mut *tx).await?;
    sqlx::query("UPDATE accounts SET balance = balance + $1 WHERE id = $2")
        .bind(amount).bind(to_id).execute(&mut *tx).await?;

    tx.commit().await?; // rolls back automatically if any step above failed

    // EMIT FundsTransferred
    bus.emit("FundsTransferred", &json!({"from": from_id, "to": to_id, "amount": amount})).await;
    Ok(())
}
cpp
// Generated from LogicScript — @transaction FUNC transferFunds
#include <stdexcept>, <string>, <chrono>
#include "db.hpp", "event_bus.hpp"

class TransferError : public std::runtime_error {
public:
    explicit TransferError(const std::string& msg) : std::runtime_error(msg) {}
};

struct TransferResult { bool success; std::chrono::system_clock::time_point timestamp; };

class PaymentService {
public:
    TransferResult transferFunds(
        const std::string& from_id, const std::string& to_id, double amount)
    {
        // VALIDATE
        if (amount <= 0.0)    throw TransferError("Amount must be positive");
        if (from_id == to_id) throw TransferError("Cannot transfer to the same account");

        auto from = db_.get_account_for_update(from_id);  // locks the row
        if (from.balance < amount) throw TransferError("Insufficient funds");

        // @transaction — RAII: rolls back in destructor if not committed
        auto txn = db_.begin_transaction();
        try {
            db_.debit(from_id, amount, txn);
            db_.credit(to_id,  amount, txn);
            txn.commit();
        } catch (...) {
            txn.rollback();   // ROLLBACK
            throw;
        }

        // EMIT FundsTransferred
        bus_.emit("FundsTransferred", {from_id, to_id, amount});
        return { true, std::chrono::system_clock::now() };
    }
private:
    Database& db_;
    EventBus&  bus_;
};

4. Order state machine

Shows STATE — a finite state machine with valid transitions and ON ENTER hooks. The AI generates a transition table and enforces that only valid status changes are allowed.

The prompt

prompt (paste this into any AI)
Generate Python from the following LogicScript.

STATE Order
  STATES  draft, pending, paid, shipped, delivered, cancelled

  TRANSITION draft   -> pending   ON submitOrder
  TRANSITION pending -> paid      ON paymentReceived
  TRANSITION paid    -> shipped   ON fulfillOrder
  TRANSITION shipped -> delivered ON deliveryConfirmed
  TRANSITION ANY     -> cancelled ON cancelOrder
    WHEN status NOT IN [delivered]

  ON ENTER shipped
    EMIT OrderShipped WITH order
    NOTIFY user via email WITH trackingInfo

  ON ENTER cancelled
    TRIGGER refundIfPaid(order)
    LOG "Order {order.id} cancelled"

Generated output

typescript
// Generated from LogicScript — STATE Order
type OrderStatus = "draft"|"pending"|"paid"|"shipped"|"delivered"|"cancelled";

interface Transition { from: OrderStatus[]; to: OrderStatus; }

const TRANSITIONS: Record<string, Transition> = {
  submitOrder:       { from: ["draft"],                               to: "pending"   },
  paymentReceived:   { from: ["pending"],                            to: "paid"      },
  fulfillOrder:      { from: ["paid"],                               to: "shipped"   },
  deliveryConfirmed: { from: ["shipped"],                            to: "delivered" },
  cancelOrder:       { from: ["draft","pending","paid","shipped"],   to: "cancelled" },
};

export async function transitionOrder(order: Order, event: string) {
  const t = TRANSITIONS[event];
  if (!t)                           throw new Error(`Unknown event: ${event}`);
  if (!t.from.includes(order.status)) throw new Error(`Invalid transition from ${order.status}`);

  order.status = t.to;
  await db.order.update({ where: { id: order.id }, data: { status: t.to } });

  // ON ENTER hooks
  if (t.to === "shipped") {
    eventBus.emit("OrderShipped", order);                   // EMIT
    await notifyUser(order.userId, { trackingInfo: order.trackingInfo }); // NOTIFY
  }
  if (t.to === "cancelled") {
    await refundIfPaid(order);                              // TRIGGER
    console.log(`Order ${order.id} cancelled`);            // LOG
  }
  return order;
}
python
# Generated from LogicScript — STATE Order
import logging
from enum import Enum

log = logging.getLogger(__name__)

class OrderStatus(Enum):
    DRAFT     = "draft"
    PENDING   = "pending"
    PAID      = "paid"
    SHIPPED   = "shipped"
    DELIVERED = "delivered"
    CANCELLED = "cancelled"

TRANSITIONS = {
    "submitOrder":       ([OrderStatus.DRAFT],                                   OrderStatus.PENDING),
    "paymentReceived":   ([OrderStatus.PENDING],                                 OrderStatus.PAID),
    "fulfillOrder":      ([OrderStatus.PAID],                                    OrderStatus.SHIPPED),
    "deliveryConfirmed": ([OrderStatus.SHIPPED],                                 OrderStatus.DELIVERED),
    "cancelOrder":       ([OrderStatus.DRAFT, OrderStatus.PENDING,
                           OrderStatus.PAID,  OrderStatus.SHIPPED],              OrderStatus.CANCELLED),
}

def transition_order(order, event: str, db, event_bus, notification_service):
    if event not in TRANSITIONS:
        raise ValueError(f"Unknown event: {event}")

    from_states, to_status = TRANSITIONS[event]
    if order.status not in from_states:
        raise ValueError(f"Invalid transition from {order.status.value} via {event}")

    order.status = to_status
    db.save(order)

    # ON ENTER hooks
    if to_status == OrderStatus.SHIPPED:
        event_bus.emit("OrderShipped", order)                           # EMIT
        notification_service.notify_user(order.user_id, order.tracking_info)  # NOTIFY

    if to_status == OrderStatus.CANCELLED:
        refund_if_paid(order)                                           # TRIGGER
        log.info(f"Order {order.id} cancelled")                        # LOG

    return order
sql
-- Generated from LogicScript — STATE Order (PostgreSQL)
CREATE TYPE order_status AS ENUM
  ('draft','pending','paid','shipped','delivered','cancelled');

CREATE OR REPLACE PROCEDURE transition_order(p_order_id UUID, p_event TEXT)
LANGUAGE plpgsql AS $$
DECLARE
  v_current order_status;
  v_next    order_status;
BEGIN
  SELECT status INTO v_current FROM orders WHERE id = p_order_id FOR UPDATE;

  -- TRANSITION table
  v_next := CASE
    WHEN p_event = 'submitOrder'       AND v_current = 'draft'    THEN 'pending'
    WHEN p_event = 'paymentReceived'   AND v_current = 'pending'  THEN 'paid'
    WHEN p_event = 'fulfillOrder'      AND v_current = 'paid'     THEN 'shipped'
    WHEN p_event = 'deliveryConfirmed' AND v_current = 'shipped'  THEN 'delivered'
    WHEN p_event = 'cancelOrder'
         AND v_current IN ('draft','pending','paid','shipped')     THEN 'cancelled'
    ELSE NULL
  END;

  IF v_next IS NULL THEN
    RAISE EXCEPTION 'Invalid transition: % via %', v_current, p_event;
  END IF;

  UPDATE orders SET status = v_next WHERE id = p_order_id;

  -- ON ENTER hooks via pg_notify
  IF v_next = 'shipped' THEN
    PERFORM pg_notify('order_shipped',   p_order_id::text);
  END IF;
  IF v_next = 'cancelled' THEN
    PERFORM pg_notify('order_cancelled', p_order_id::text);
    PERFORM refund_if_paid(p_order_id);
  END IF;
END; $$;
java
// Generated from LogicScript — STATE Order
import java.util.*;

public enum OrderStatus { DRAFT, PENDING, PAID, SHIPPED, DELIVERED, CANCELLED }

@Service
public class OrderStateMachine {
    private record Transition(Set<OrderStatus> from, OrderStatus to) {}
    private static final Logger log = LoggerFactory.getLogger(OrderStateMachine.class);

    private static final Map<String, Transition> TRANSITIONS = Map.of(
        "submitOrder",       new Transition(Set.of(DRAFT),                          PENDING),
        "paymentReceived",   new Transition(Set.of(PENDING),                        PAID),
        "fulfillOrder",      new Transition(Set.of(PAID),                           SHIPPED),
        "deliveryConfirmed", new Transition(Set.of(SHIPPED),                        DELIVERED),
        "cancelOrder",       new Transition(Set.of(DRAFT,PENDING,PAID,SHIPPED),     CANCELLED)
    );

    public Order transition(Order order, String event) {
        Transition t = TRANSITIONS.get(event);
        if (t == null)                        throw new IllegalArgumentException("Unknown event: " + event);
        if (!t.from().contains(order.getStatus()))
            throw new IllegalStateException("Invalid transition from " + order.getStatus());

        order.setStatus(t.to());
        order = orderRepository.save(order);

        // ON ENTER hooks
        if (t.to() == SHIPPED) {
            publisher.publishEvent(new OrderShippedEvent(this, order));     // EMIT
            notifService.notifyUser(order.getUserId(), order.getTracking()); // NOTIFY
        }
        if (t.to() == CANCELLED) {
            refundService.refundIfPaid(order);                              // TRIGGER
            log.info("Order {} cancelled", order.getId());                  // LOG
        }
        return order;
    }
}
rust
// Generated from LogicScript — STATE Order
#[derive(Debug, Clone, PartialEq, sqlx::Type, serde::Serialize)]
#[sqlx(type_name = "order_status", rename_all = "lowercase")]
pub enum OrderStatus { Draft, Pending, Paid, Shipped, Delivered, Cancelled }

#[derive(Debug, thiserror::Error)]
pub enum TransitionError {
    #[error("Unknown event: {0}")]                  UnknownEvent(String),
    #[error("Invalid transition from {0:?} via {1}")] Invalid(OrderStatus, String),
    #[error(transparent)]                           Db(#[from] sqlx::Error),
}

pub async fn transition_order(
    pool: &sqlx::PgPool, bus: &impl EventBus,
    order: &mut Order, event: &str,
) -> Result<(), TransitionError> {
    use OrderStatus::*;
    let next = match (event, &order.status) {
        ("submitOrder",       Draft)   => Pending,
        ("paymentReceived",   Pending) => Paid,
        ("fulfillOrder",      Paid)    => Shipped,
        ("deliveryConfirmed", Shipped) => Delivered,
        ("cancelOrder", Draft | Pending | Paid | Shipped) => Cancelled,
        ("cancelOrder", _) => return Err(TransitionError::Invalid(order.status.clone(), event.into())),
        _                  => return Err(TransitionError::UnknownEvent(event.into())),
    };

    order.status = next.clone();
    sqlx::query("UPDATE orders SET status=$1 WHERE id=$2")
        .bind(&next).bind(order.id).execute(pool).await?;

    // ON ENTER hooks
    if next == Shipped {
        bus.emit("OrderShipped", order).await;                    // EMIT
        notify_user(pool, order.user_id, &order.tracking_info).await?; // NOTIFY
    }
    if next == Cancelled {
        refund_if_paid(pool, order).await?;                       // TRIGGER
        tracing::info!("Order {} cancelled", order.id);           // LOG
    }
    Ok(())
}
cpp
// Generated from LogicScript — STATE Order
#include <unordered_map>, <unordered_set>, <stdexcept>, <string>
#include "db.hpp", "event_bus.hpp", "notification_service.hpp"

enum class OrderStatus { Draft, Pending, Paid, Shipped, Delivered, Cancelled };

class OrderStateMachine {
    using StatusSet = std::unordered_set<OrderStatus>;
    struct Transition { StatusSet from; OrderStatus to; };

    const std::unordered_map<std::string, Transition> transitions_ = {
        {"submitOrder",       {{OrderStatus::Draft},                                       OrderStatus::Pending}},
        {"paymentReceived",   {{OrderStatus::Pending},                                     OrderStatus::Paid}},
        {"fulfillOrder",      {{OrderStatus::Paid},                                        OrderStatus::Shipped}},
        {"deliveryConfirmed", {{OrderStatus::Shipped},                                     OrderStatus::Delivered}},
        {"cancelOrder",       {{OrderStatus::Draft, OrderStatus::Pending,
                                OrderStatus::Paid,  OrderStatus::Shipped},                 OrderStatus::Cancelled}},
    };

public:
    Order& transition(Order& order, const std::string& event) {
        auto it = transitions_.find(event);
        if (it == transitions_.end())
            throw std::invalid_argument("Unknown event: " + event);
        if (!it->second.from.count(order.status))
            throw std::logic_error("Invalid transition via: " + event);

        order.status = it->second.to;
        db_.save(order);

        // ON ENTER hooks
        if (it->second.to == OrderStatus::Shipped) {
            bus_.emit("OrderShipped", order);                    // EMIT
            notif_.notify_user(order.user_id, order.tracking);  // NOTIFY
        }
        if (it->second.to == OrderStatus::Cancelled) {
            refund_if_paid(order);                               // TRIGGER
            std::clog << "Order " << order.id << " cancelled
"; // LOG
        }
        return order;
    }
private:
    Database&            db_;
    EventBus&            bus_;
    NotificationService& notif_;
};
Playground
Write LogicScript on the left — click Run to generate code on the right.
LogicScript
Load example
Output
Output appears here after you click Run.

Load an example below or write your own LogicScript prompt. Start with the instruction — "Generate Python from the following LogicScript." — then paste your spec below it.
Ready logicscript-server.onrender.com