> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sensecode.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Roadmap

> What's built, what's deliberately deferred and why, phase by phase. This page is kept current as the language evolves.

<Info>
  This is the reader-facing version of `docs/ROADMAP.md` in the repository,
  which is the actual working design log (denser, more cross-referenced to
  `goals/`). Both are updated together — this page exists so the same
  information reads well outside the codebase.
</Info>

Sense follows an explicit 8-phase plan, and a rule that shapes every entry
below: a phase ships the smallest slice with **no unmet dependencies**,
never the full feature list a design doc describes at once — and whatever's
left out is written down with a reason, not silently skipped. A keyword
that exists but doesn't mean anything real yet is treated as a worse
outcome than a keyword that doesn't exist.

<Steps>
  <Step title="Phase 0 — Language Research" icon="circle-check">
    **Done.** The problem statement, computational model, and the central
    safety law all predate any code — see [Philosophy](/philosophy/thesis).
  </Step>

  <Step title="Phase 1 — Minimal Language" icon="circle-check">
    **Done.** Lexer, parser, AST, a shallow type-annotation checker, functions,
    variables, control flow, arrays, modules — the deterministic core covered
    in the [Language Tour](/language/values-and-types). Not built: real static
    type inference (today's checking is a runtime check, not a type system in
    the PL-theory sense), user-defined structs/product types, a bytecode VM
    (the reference implementation is a tree-walking interpreter, which is fine
    for design iteration and revisited once semantics stabilize).
  </Step>

  <Step title="Phase 2 — AI Primitives" icon="circle-check">
    **Done.** `model`, `set delegation`, `ask`, `Answer` — see
    [Model, Ask & Answer](/ai-native/model-ask-answer) — plus, once the
    policy/capability layer they depended on existed (Phase 4), the three
    primitives originally deferred here: [`tool`](/ai-native/tool) (a
    capability-checked function with no prepare/verify/commit lifecycle),
    [`memory`](/ai-native/memory) (a session-scoped key/value store, distinct
    from a variable — plus a [`persistent`](/ai-native/memory#persistence-and-versioning-persistent-memory)
    form backed by a durable, versioned SQLite log, scoped down from "Delta
    Lake" to the mechanism worth keeping: append-only writes give versioning
    and auditing as the same log, for free). `tool` also comes in an
    [`async`](/ai-native/tool#concurrency-async-tool) form — see Phase 3. A
    real model provider now exists too — two, in fact, behind one function,
    [`inference(provider, ...)`](/ai-native/model-ask-answer#real-providers-inference-provider-model-id)
    — the identical `ModelProvider` seam kept `ask()`'s semantics from
    ever needing to change, and each is capability-gated (`policy: deny
        model.anthropic`) since a real call costs money and leaves the process,
    closing a gap this page used to name as "tabled for Phase 4." A real
    model can now also
    [choose which tool to invoke](/ai-native/tool-calling)
    via `ask_with_tools(prompt, tools)` — the first time a model's own
    decision, not human-written Sense control flow, reaches the same
    `_call_tool` enforcement path a hand-written call goes through: a
    policy-denied tool the model asks for is never executed, and the denial
    is fed back to the model rather than crashing the call. `tool` gained an
    optional description for this, required only once a tool is actually
    handed to `ask_with_tools`.
    [Memory is now wired into reasoning](/ai-native/memory#reasoning-with-memory-ask_with_memory)
    too, and it split cleanly on investigation: write-back needed **no new
    interpreter feature** — a `tool` wrapping `<memory>.remember(...)` handed
    to `ask_with_tools` already lets a model manage its own memory, gated
    and audited like anything else; only retrieval
    (`ask_with_memory(prompt, memory)`, injecting a memory's contents
    ahead of the prompt) was genuinely new, and it works with
    `inference("mock", ...)` too.
    [`skill(name, description, tools)`](/ai-native/skills)
    rounds out four of the original five pieces — no dedicated keyword,
    same reasoning as `model`: a plain builtin returning a `Skill` value that
    `ask_with_tools` flattens into its component tools, prepending the
    skill's description to the prompt (real effect, not just documentation).
    Not built: `async tool` support inside the tool-calling loop, a
    provider-agnostic tool-calling abstraction, other providers (OpenAI, a
    local model), automatic write-back from free text (deliberate — see the
    page above), nested skills, and a per-skill capability.
    [`connect_mcp(name, command, args, capability?)`](/ai-native/mcp)
    closes the fifth and last piece — and the one that's genuinely different
    in kind from the other four: an MCP tool has no Sense body at all, so
    invoking one is a JSON-RPC call to an external process over the official
    `mcp` SDK, bridged into the synchronous interpreter through a background
    thread running one persistent asyncio event loop for the connection's
    whole lifetime (necessary because Sense has no closures to make an
    async-native callback shape work, and because an MCP session has to be
    reused across many calls, not re-opened per call). Tested against a real
    subprocess MCP server, not a mock — which is exactly how a real bug in
    the bridge design got caught and fixed (`anyio`'s cancel scopes needing
    `__aenter__`/`__aexit__` in the same asyncio task). All five pieces from
    the original "real LLM integration" ask are done, each scoped to the
    smallest honest slice that still does the real thing. Not built: MCP
    HTTP/SSE transport (stdio only), MCP resources/prompts (tools only), and
    reconnection on a dead session.
  </Step>

  <Step title="Phase 3 — Agent Runtime" icon="circle-half-stroke">
    **In progress — session, agent, pause/resume, and opt-in async scheduling
    done.** [Sessions](/ai-native/sessions) and [Agents](/ai-native/agents) are
    real: an agent is a first-class runtime entity with identity, persistent
    state, and a genuine [pause/resume](/ai-native/pause-resume) lifecycle, not
    sugar over a function call. [`async agent`](/ai-native/agents#concurrency-async-agent)
    (and its `async tool` counterpart) makes `start()`/`resume()` skip their
    blocking wait, so an agent's body and its caller — or two `async` agents —
    can genuinely execute at the same time; a plain (sync) `agent`/`tool` is
    completely unaffected. This was chosen deliberately over making the whole
    language async-by-default (every call non-blocking unless marked `sync`),
    which forces function coloring through every plain call — see
    [Design Order](/philosophy/design-order) for why that conflicts with
    Sense's simplicity-first goal. Not built: inter-agent delegation/
    communication/spawning (an agent can't `start` or message another agent
    yet), a true scheduler (each `async` call is just a fresh daemon thread,
    not fairness/priority-managed), any synchronization primitive (lock,
    channel, atomic — two `async` bodies sharing state can race),
    serialization/persistence across process restarts (a paused agent's state
    lives in a live thread's call stack), budget, evaluation, and `Process` as
    a concept distinct from `Agent` (not needed yet — nothing requires "has a
    lifecycle but doesn't pursue a goal" separately from Agent).
  </Step>

  <Step title="Phase 4 — Safety and Control" icon="circle-half-stroke">
    **Core done, including human-in-the-loop, declared approval, and audit
    logs.** `reversible`/`irreversible action` with a real
    prepare → verify → commit law, plus capability-gated `policy: allow/deny`
    — see [Actions](/safety/actions) and [Policy](/safety/policy). This closes
    what was, for a while, the single biggest gap between what was built and
    what actually makes Sense *Sense*: every function call used to execute
    immediately, with no notion of a staged, verifiable, revocable effect.
    `ask_human(question)` — see [Pause & Resume](/ai-native/pause-resume) —
    is the human-intervention primitive, built directly on
    `pause()`/`resume()` once those were generalized to carry a value across
    the suspend boundary. An action can now also **mandate** approval
    (`requires x.y, approval`) rather than relying on calling code to add it by
    hand — `<action>.approve()` is a separate method, and `.commit()` checks it
    the same independent way it re-checks capability, raising the new
    `SenseApprovalError` if skipped. Every prepare/verify/commit/deny/fail
    event is recorded in `Interpreter.audit_log`, with a simplified
    `audit_log()` view for Sense programs themselves.
    A `reversible action` must now also declare a `rollback` block, run by
    `<action>.rollback()` after commit — see [Rollback](/safety/actions#rollback-undoing-a-committed-action)
    — and wrapping what an action's body calls in a [`tool`](/ai-native/tool)
    closes what used to be a gap ("only the action itself is gated, not what it
    calls"). [`--dry-run`](/safety/actions#dry-run-preview-what-would-commit-without-letting-it-happen)
    (on both `sense run` and `sense inspect`) reuses the prepare → verify →
    commit law directly: every capability/approval check still runs for real,
    only the body performing the actual effect is skipped, with the
    simulation marked on the existing audit log rather than a new state or a
    second log. Not built: resource budgets, capability dimensions beyond a
    name (cost, risk, rate limits, for both `action` and `tool`), default-deny
    sandboxing (today's default is allow — see [Policy](/safety/policy)),
    durable/persisted audit logging (today's is in-memory only, per
    `Interpreter` instance), and a recovery story for a rollback block that
    itself fails mid-rollback.
  </Step>

  <Step title="Python Interoperability" icon="plug">
    **Core done.** Not one of the original 8 phases — its own plan, sequenced
    right after capability enforcement and before broader ecosystem work.
    [`import python "module"`](/language/python-interop) reaches directly
    into any installed Python package, and it's simpler than a typical
    cross-language bridge for one specific reason: Sense's own reference
    interpreter is itself written in Python, running in the same process — no
    FFI, no serialization boundary. Most Sense values already *are* Python
    values (`Int` is `int`, `Array` is `list`), so a Python function returning
    a list is immediately a usable Sense `Array`; anything else (a `dict`, a
    class instance) comes back as an opaque, still-usable wrapper. The safety
    model **extends** to cover this rather than being bypassed by it: a bare
    import and call is exactly as unrestricted as a plain Sense `fn` always
    was, and wrapping a Python call in an `action` gets full
    prepare → verify → commit enforcement regardless of what the body's
    implementation happens to be.
    Not built: automatic safety classification of foreign functions (declaring
    `requires` is still something you do by hand), sandboxing or conformance
    testing of imported code, interop with any language other than Python,
    importing a specific name rather than a whole module, and pre-built
    integration packages (`sense-pandas`, `sense-numpy`, etc.).
  </Step>

  <Step title="Phase 5 — Developer Experience" icon="circle-half-stroke">
    **Testing framework, formatter, and inspector console core done.**
    [`test "description": ...`](/language/testing) — an isolated,
    individually-reported assertion block — plus `sense test <path>`, which
    discovers `test_*.sns`/`*_test.sns` files under a file or directory path
    and reports a PASS/FAIL summary; a failure inside one `test` block
    doesn't stop its siblings, the rest of the file, or the rest of a run.
    [`sense fmt`](/reference/formatter) is a canonical pretty-printer over the
    existing AST, with two guarantees checked against every real example:
    formatting never changes program behavior, and it's idempotent
    (`fmt(fmt(x)) == fmt(x)`) — comments are re-attached rather than dropped,
    and blank lines are preserved (never imposed).
    [`sense inspect <file>`](/reference/cli) serves
    a live, local, browser-based console over a program's declared surface —
    every tool/action/agent/memory/skill/MCP server, plus policy state
    and a live audit log — closer to Swagger UI's "try it out" than to static
    docs, except every panel calls straight into an existing `Interpreter`
    method rather than a new enforcement path: a policy denial clicked from
    the browser is the exact same `SensePolicyError` a hand-written call
    would raise. Watching a real agent hit `pause()` and resuming it from the
    browser is the one panel with no REST/Swagger analog at all. Stdlib-only,
    no CDN — the same offline-by-design principle `inference("mock", ...)`/
    `ask()` established, extended to tooling.
    Not built: package manager, formatter line-width wrapping/alignment/
    configuration, language server, debugger, test fixtures/mocking/coverage,
    agent simulation environment, observability tools, "try it out" for MCP
    tools (needs something only Sense code can honestly supply), WebSocket
    push for the console (short polling instead).
  </Step>

  <Step title="Phase 6 — Real Applications" icon="circle" iconType="regular">
    **Not started.** Reference apps (a coding agent, a research agent, an
    enterprise analyst) meant to prove Sense earns its keep over
    `Python + SDK + framework` — see [Non-Goals](/philosophy/non-goals) for the
    exact test this phase is meant to pass.
  </Step>

  <Step title="Phase 7 — Ecosystem" icon="circle" iconType="regular">
    **Not started.** Package/capability registries, model/tool providers, a
    hosted runtime.
  </Step>
</Steps>

## The pattern, if you're skimming

Every phase above follows the same shape: ship the piece with no unmet
dependencies, write down what's missing and why, and treat "not built yet"
as different from "forgotten." If you're deciding whether to build on
Sense today, the "not built" lines in each phase are the actual scope of
what you'd be signing up to work around — take them as seriously as the
"done" lines.

## Continue

<CardGroup cols={2}>
  <Card title="Design order" icon="list-ol" href="/philosophy/design-order">
    Why phases are sequenced this way instead of built in parallel.
  </Card>

  <Card title="FAQ" icon="circle-question" href="/faq">
    Common questions this roadmap raises.
  </Card>
</CardGroup>
