Source on the left. A running model on the right.
A .grid file parses into an AST, builds into a dependency graph of typed cells, and executes on one of three runtimes — same language, same outputs, same error model.
# allocation weights = positions / SUM(positions) as percentage returns = HISTORICAL_RETURNS( positions, 252) cov = COV(returns) port_var = WEIGHTED_VAR(weights, cov) sharpe = (port_ret − rf) / SQRT(port_var)
Source becomes a running cell, in four stages.
The pipeline is rigid; the substrate is not. Parse, build, compile (or interpret), evaluate. Same path in production, in dev, in tests — only the evaluator at the end is different.
The boundary between "your spreadsheet" and "a deployed program"doesn't exist. The.grid file is the program.
01 Source
portfolio.gridgrid weights = positions / SUM(positions) cov = COV(returns) port_var = WEIGHTED_VAR(weights, cov)
A .grid file: cells, formulas, ranges, optional rules.
02 AST
astjson assign("weights", binop("/", ref("positions"), call("SUM", [ref(...)])))
A strict tree. Every literal typed. Every operator looked up. Every identifier resolved.
03 Model
A typed dependency graph. Each cell knows its inputs, its outputs, and its execution class.
04 Runtime
portfolioCellValueweights[0]0.240port_var0.0089sharpe1.84Lua compiled into Redis Functions, Lua interpreting JSON IR, or TypeScript evaluating in your process.
Recompute is local.
When positions changes, weights recomputes — and only the cells downstream of weights follow. In dependency order. Atomically. In Lua, in TypeScript, the same way.
Cycles fire #CIRC! at build time, with the cycle path in the error. Errors travel through the same channels as values, so every consumer sees them in context.
The model never moves. Only the evaluator does.
Same parser, same type system, same 496 functions, same error model. Byte-equivalent output for the portable subset across all three runtimes — selectable per-tenant, per-deployment, even per-file.
lua_generated
Each model becomes a self-contained Redis Functions library. Every cell write triggers an in-Redis recompute. Default at deploy time.
portablefastrule-awarelua_interpreter
One Lua runtime loads many models as JSON IR. Useful when many tenants share a single Redis. Same scheduler, same outputs, smaller cold start.
multi-tenantsharedJSON IRts
Pure TypeScript evaluator. Behaviorally equivalent for the portable language subset. Tests and local dev never need Redis to run.
localtestsno-redis