Skip to content

Decision Log

Sema’s design is recorded as a numbered decision log (the “D-log”) in the language specification. Each entry states a decision, the evidence behind it, and the alternatives it rejected. This page summarizes the load-bearing decisions, grouped by theme. For the full normative record — every entry with citations — see Decision record; for the motivation in prose, see Why Sema.

Why the language looks the way it does.

# Decision Why
D1 Pythonic indentation, not a Python superset; PEG grammar with soft keywords; editions from v0.1. LLMs already emit Python fluently, so a Python-shaped surface transfers that prior; a superset would inherit Python’s semantics (which Sema deliberately replaces). Mojo/Codon precedent.
D27 No classes. Methods live in struct/enum bodies; polymorphism is traits + ADTs with laws-as-contracts, header or impl conformance, and coherence rules. Trait laws make obligations (e.g. reducer associativity) assure-checkable instead of merely asserted; inheritance and structural duck typing are rejected.
D28 Immutable bindings by default, mut to opt in; value semantics for structs/collections. The trust lattice and contracts sit on the binding model; pervasive shared mutability would let trust labels launder through aliasing. Rust-grade borrow checking was judged too costly for the audience.
D43 Idiomatic ergonomics: lambda/=>, one *args + one **kwargs, ...expr spread, generic [T] params erased at runtime. Day-one LLM emission needs idiomatic variadics and lambdas; erased generics add expressiveness without a second guarantee regime (contracts + rows stay the guarantee story).
D26 Native dict/set/comprehensions/slicing, homogeneous, one Iterable/Iterator protocol; canonical sorted-order flattening. Guarantees that LLMs emit dict literals and comprehensions on day one; collections cannot cross an FFI bridge without losing trust labels and policy meet.

The capability discipline that makes the deterministic core provable.

# Decision Why
D7 Typed effects-and-handlers spine. policy = capability/effect restriction plus a Cedar-shaped decision layer with compile-verified examples; typed effects, never string matching. One mechanism covers policy, replay, mock, and batching; every string-based gate is respellable and therefore unsound. Rejected a Turing-complete (Rego-class) policy language.
D11 Trust lattice untrusted < validated < trusted on all values; generative outputs are born untrusted; endorsement only via contracts, verifiers, or human approval. Object-capability cleanliness from day one (CaMeL/FIDES lineage). Pure control-flow confinement without value labels was rejected.
D71 Effect-operation calls are checked. Calling an unrecognized op on a known namespace (fs.raed(...)) raises NameError at the call site; effect rows stay open (!{fs.raed} still parses). Closes the last silent-ignore: a typo’d effect call must fail like any builtin, while capability declarations stay extensible.
D72 Effect namespaces are fully real + configurable — real fs/net (HTTP+HTTPS)/db (embedded SQLite, swappable by DSN or a @provides("db") backend)/env/proc/ui. “No stubs”: every effect performs its real operation, and the HTTP knobs and SQL server swap via config + providers without touching Rust.

Making model calls first-class, legible, and honest.

# Decision Why
D5 simulate def … by <model> — a model implements the body, with sem descriptors, a public cached meaning-IR, contracts, budgets, and untrusted labeling. Adopts the published, user-studied Meaning-Typed-Programming result; rejects prompt-template DSLs (LMQL died) and LLM-as-VM designs.
D44 Semantic operations are first-party: the ~ sigil family (~=, ~<, ~in, ~+, ~and, ~[query], …) all derive model.invoke, plus a semantic namespace of primitive verbs and a coercion protocol (a type picks its own embed/sem_text). SymbolicAI proved semantic operators are ergonomic but hid failure behind fuzzy bools; marking the operation keeps model calls legible and forces a complete strict/bitwise/semantic logic story.
D2 ~= returns a graded Sim, not a bool. Branching on it requires calibration; regions are typed statistical(α). Fuzzy-bool casts are the SymbolicAI defect class; a calibrated conformal threshold gives an honest certificate instead of a silent coercion.
D3 semantics() pins a judge in the type(judge hash, calibration id, α) — evaluated by protocol, never one raw judge. Hallucination is inevitable and single-judge truth is ill-posed; the judge identity is part of the program’s semantics.
D13 Models are pinned first-class values with roles and calibrations; no floating string refs, ever. A "latest" string resolved at runtime makes the program’s semantics non-reproducible; the model artifact is pinned like any other dependency.

Why correctness is checked by default.

# Decision Why
D4 Contracts in the public signature. Failed values are typed-failed and cannot flow; check (graded, monitored) vs ensure (fatal); Findler–Felleisen blame everywhere. Enforcement, not advice — advisory contracts (comments/decorators outside the type) are rejected.
D6 Verification is default-on. The opt-in testable keyword is retired; assure bronze/silver/gold grades with a red/amber/green verdict and a mutation-adequacy gate. Weak suites launder wrong LLM code (EvalPlus); opt-out beats opt-in; property-based testing surfaces ~50× the mutant density of line coverage.
D34 Authored test declarations are native (test "name": block) with ensure/check as the assertion vocabulary and record-replay under pinned seeds; red-verdict counterexamples materialize back into test blocks. Every mainstream language ships native test entry points; reusing contract clauses keeps one semantics for all expectations. A separate matcher DSL was rejected.
D35 Semantic assertions reuse the contract forms — hard ensure semantics(...), soft check semantics(...) — with no new keyword; assert is a reserved, rejected token with a fix-it to ensure/check. One assertion semantics for deterministic and semantic predicates; Python’s assert strips under -O and would teach the wrong semantics.
D68 Real static checker + verification engine + constraint solver. sema check catches arity/field/type errors conservatively (zero false positives); sema assure fuzzes properties and mutation-tests at gold; solve: does finite-domain search. Backs the “statically typed + default-on verification + neurosymbolic” claim with real machinery, not lint.
D15 Monitor-or-decay. A statistical(α) obligation needs an active monitor on its input; without one it decays to best_effort at the type level. The compiler derives shared input monitors where none is declared. The α certificate is honest only while the deployment distribution matches calibration; the monitor is what guards that assumption.
# Decision Why
D30 No unwinding exceptions. Failures are Error-trait values in Result/sums, handled by expect/except, propagated by a trust- and blame-preserving ?; unwrap is a checked abort rejected under assure gold. Unwinding is invisible to effect rows and hostile to replay and blame; “a failed value cannot flow into non-handling code” needs a defined handling construct.
D31 No colored functions. async/await do not exist; concurrency is structural (scope/spawn/parallel over non-blocking effect handlers). The runtime is already non-blocking under structured concurrency; a vestigial async token would invite LLMs to emit an unspecified construct.
D32 One with <expr> as x: construct unifies scoped resources, policy scoping, and model rebinding, with deterministic journaled release and no user destructors. Nondeterministic finalization breaks replay; effect-handler scoping is the mechanism the runtime already uses.
D10 / D14 Self-healing is supervision-scoped and repair-only (supervise/heal, patch-scoped code.patch, a deterministic gauntlet) — not a program-wide mode, and not program growth. Sanctioned growth is descriptor-space regeneration at simulate sites plus human-approved patch-scope widening. Intrinsic self-modification without external grounded feedback degrades results; a general write capability would break healing’s escalation-proof property.
# Decision Why
D9 / D18 Split native (bind) vs ported (translate with a differential gate); foreign code uses typed bridge membranes by default (normal .py/.ts/.c files + expose def signatures), with inline begin/end blocks as small trusted glue. Bind ecosystems, translate only self-contained code; guarantees hold only at typed membranes, which keep native toolchains while adding Sema’s contracts, policy, and re-validation.
D57 / D58 / D59 / D60 Python bridge + native packages. python.call runs real Python behind a persistent warm worker (non-JSON results become object handles); sema add/remove/list wraps uv for PyPI and installs native Sema packages by path/git. Ecosystem access from day one is table stakes; a warm worker makes real classes/objects first-class Sema values.
D47 / D56 / D61 Real, feature-gated model backends. sema-model = pure-Rust local GGUF inference on candle (Metal/CPU); a real candle BERT embedder; both behind the real-model feature so default builds link no ML stack (the deterministic engine stays the explicit hermetic opt-in, never a silent fallback). The mock needs a credible real counterpart; candle keeps it single-binary and Python-free; feature-gating preserves fast portable builds.
D62 / D63 / D64 Documentation & debugging via reflection. sema doc reflects signatures + docstrings to Markdown; the trace keyword reflects error frames into a self-repair packet; sema repl + sema dap share the real evaluator. Docs must not drift (reflect them); a stack trace is already the agent’s self-repair context; a separate debug interpreter would drift from the real one.
D49 Opt-in bytecode VM. The tree-walker stays the reference/default; SEMA_VM=1 runs compilable functions on a stack VM with function-granularity fallback and delegated value ops. Beating CPython needs slot resolution + flat dispatch; partial-but-safe (fallback + delegated semantics) ships incrementally with no divergence.

Native constructs over library conventions

Section titled “Native constructs over library conventions”

Sema makes several concerns that are usually library conventions into typed, checked language constructs, so the compiler can see them.

Area Decisions The construct
Configuration, CLI args, DI D22 config/args/container/provide/inject with typed provenance and compile-checked graph resolution.
Tap collectors / metrics D23 collector channels + the reserved `
Parallelism D24 One contextual parallel construct, typed => lambdas, deterministic merge, optional worker profiles — not aliases or raw threads/futures.
Events D29 event/emit/subscriber with bounded queues and journal-integrated exactly-once-per-run delivery.
Templates / patterns / SQL D16 / D19 / D20 Native sem descriptors; typed interpolation, regex match, sql"…"; templatePrompt[T] and context state machines.
Streams D39 One Stream[T] with generator/parallel/service producers, affine scoped values, and pull-based backpressure — no async coloring.
Services (RPC) D38 service = a typed remote membrane with derived net.connect rows, wire-schema handshakes, and blame across the wire.
Math D42 equation blocks where math notation is the syntax, pure by construction (!{} row).