<!-- Sema documentation — Reflection & Staged Code
     Sema is a new AI-native, neurosymbolic programming language most models
     have not been trained on. Full corpus: https://sema.49.12.246.95.sslip.io/llms-full.txt
     Install language support: https://sema.49.12.246.95.sslip.io/install-skill/ -->

# Reflection & Staged Code

> Read a program's own shape with reflect, generate typed Code[T] instead of eval, and turn any error into an LLM-ready repair packet with trace.

AI-native programs need to reason about *themselves*: hand a model the exact shape
and meaning of a type, generate a new function at runtime, and turn a failure into
context a model can fix. Python does this with `getattr`/`setattr`/`eval` and a
stringly stack trace — unbounded, invisible to types, ungoverned. Sema does it
with three governed constructs: **`reflect`** (read-only introspection),
**`Code[T]`** (staged code as typed data), and **`trace`** (a first-class,
model-ready error packet).

This guide covers all three. They are the substrate the documentation generator
and the debugger share (see
[Documents & Reports](/guides/documents/#documentation-as-a-reflected-artifact)).

## `reflect` — read the program's shape

`reflect(T)` and `reflect(f)` return `TypeInfo` / `CallableInfo` values: fields
with their types and `sem` descriptors, contracts, effect rows, and the wire
schema — a runtime API over the same artifacts the compiler already sealed into
the binary. Reflection is **read-only** and pure (`!{}`): it reads compile-time
constants, and there is **no mutating reflection** — no `setattr`, no dynamic
member addition, no monkey-patching. A program cannot observe a different shape of
itself than the compiler proved.

The point is that reflection is **prompt-ready by construction**. `TypeInfo` and
`CallableInfo` carry a canonical, build-stable rendering, so handing a model the
shape *and meaning* of anything is one splice into a prompt template:

```sema
template extraction_prompt(note: str) -> Prompt[Patient]:
    role system:
        text "Extract a structured record. The target schema, with field meanings:"
        text f"{reflect(Patient)}"          # name, field types, sem descriptors, ranges
    role user:
        text f"{note}"
```

`simulate def` already does this implicitly — the meaning IR *is* reflected
context — and `reflect` hands the same artifact to your own templates and
`context` slots. Contracts and policy summaries reflect the same way, which is how
an agentic program can explain its own constraints to a model mid-flight.

:::tip
Reflecting a type into a prompt is more robust than describing its fields by hand:
change the struct and the prompt updates itself, because the description *is* the
reflected shape. See [Reflection is prompt-ready](/neurosymbolic/schemas/) in the
schema docs.
:::

## `Code[T]` — staged code as typed data

Runtime-generated code in Sema is native, **typed data** — never ambient text fed
to an `eval`. The type parameter `T` is a *function type*, and function types
carry effect rows, so the row statically bounds everything the staged code could
ever do:

```sema
simulate def synthesize_scorer(spec: str) -> Code[(Candidate) -> f32 !{model.embed}]
        by coder:
    sem "Generate a Sema scoring function for the described ranking policy"

scorer = compile(synthesize_scorer(spec))?          # resident-compiler admission
ranked = parallel candidates map c => scorer.run(c) # !{code.exec("scoring-sandbox"), model.embed}
```

Three stages make this safe:

- **Admission.** `compile(c)` runs the resident incremental compiler over the
  candidate: parse, types, effects ⊆ `T`'s row, trust and policy well-formedness,
  contract attachment. Admission is pure analysis (`!{}`). A model-produced
  candidate enters `untrusted` and exits at most `validated` — never `trusted`.
- **Execution.** `c.run(args)` has row `{code.exec(<sandbox>)} ∪ row(T)` and
  demands an explicit policy grant naming the sandbox — an effect outside `row(T)`
  is a typed `EffectViolation` at the site, and `T`'s boundary contracts run at
  entry and exit exactly as at a bridge.
- **Honest guarantees.** A `run` site types at most `checked` for structure and
  `best_effort`/`statistical(α)` for behavior — no gauntlet ran over the staged
  body, so `assure` treats `run` like an FFI edge. Every `Code[T]` value carries
  provenance and is content-hash-addressed; staged execution journals like static
  code, so replay is exact.

:::caution[Prompt injection is contained by construction]
A prompt-injected candidate is born `untrusted`; admission lifts it to `validated`
at most, and with no sandbox grant it cannot execute. Injected text can be
*checked* but nothing it produces can *run*. Running a `Code[T]` value never
modifies the program — staged code is data, not self-mutation.
:::

## `trace` — errors as repair packets

Debugging is first-class too. When an error is caught with `except` or reaches the
top level, the runtime captures it with its **call frames**; the `trace` keyword
then assembles a self-describing packet by reflecting the functions involved —
their signatures, effect rows, and docstrings — so a human *and* a model have
everything needed to self-fix behind one word:

```sema
expect port = connect(raw):
    use(port)
except ContractViolation as e:
    t = trace(e)          # or bare `trace()` for the most recent error
    heal(t.markdown)      # hand the repair packet to a model
```

A `Trace` exposes `.kind`, `.message`, `.frames`, `.interfaces` (reflected
signatures), `.report` (human-readable), and `.markdown` (the LLM-ready repair
packet: the error and location, the call chain, the reflected interfaces with
their docstrings, any evidence values, and the repair task).

**An uncaught error prints the same packet automatically** — the stack trace a
user sees is already the context an agent needs, closing the self-repair loop.
This is the payoff of reflection: the doc reflector and the debugger share one
mechanism, so an error report carries the exact interfaces and intent, not just a
line number.

:::note
`trace` is deliberately a keyword, not a library call — as native as Python's
`traceback`, but reflected and model-ready by construction. The interactive
console surfaces it too: `sema repl` supports `:doc NAME` (a function's signature
+ docstring) and `:trace` (the last error's repair packet).
:::

## How they compose

The three constructs form a self-repair loop:

1. A function fails and raises a typed error.
2. `trace(e).markdown` reflects the involved interfaces into a repair packet.
3. A `simulate def … -> Code[T]` proposes a fix as staged, typed code.
4. `compile` admits it (untrusted → validated, effects ⊆ `T`), and it runs only
   inside a granted sandbox.

Persistent adaptation stays the business of the governed paths — `heal` under its
gauntlet, descriptor-space regeneration at `simulate` sites — so dynamic staging
composes with them rather than bypassing them.

## Run and verify

Reflection is `!{}` and staged execution journals like static code, so a project
using them checks and replays like any other:

```bash
sema check <your-project>
SEMA_STRICT=1 sema run <your-project>
sema assure <your-project> --grade silver
```

`assure` treats a `Code[T].run` site as an FFI edge — a `proved` region can never
contain one, and the guarantee ceiling is in the type, not in a run history.

## Variations

- **Schema-in-prompt.** Splice `reflect(T)` into any `simulate def` prompt so the
  model always sees the target shape and meaning — see
  [Schemas & Typed Decode](/neurosymbolic/schemas/).
- **Explain your constraints.** `reflect` a contract-bearing function to show a
  model the rules it must respect before it proposes a change.
- **Mid-flight self-heal.** Obtain `trace(e).markdown` *without* an uncaught error
  and feed it to a repair `simulate` — the program fixes itself before aborting.

## See also

- [Schemas & Typed Decode](/neurosymbolic/schemas/) · [simulate & Models](/neurosymbolic/simulate/)
- [Contracts](/neurosymbolic/contracts/) · [Supervise & Heal](/governance/supervise/)
- [Documents & Reports](/guides/documents/)
