Skip to content

Modules & Imports

A Sema module is one .sema file; a package is the tree rooted at a sema.toml manifest, whose [package] name is the import root. Declarations are private by default; pub marks the public surface. Imports resolve at compile time against the lockfile — there are no runtime sys.path surprises, and no wildcard imports.

Two forms, both resolved at compile time:

from finops.domain import LedgerEntry, Money # bring specific names in
import finops.policies as policies # bind a whole module (with alias)
  • import a.b.c [as x] binds the module; you call through it (policies.decide(...)).
  • from a.b.c import N1, N2 binds specific names directly.
  • Wildcard imports do not exist — every imported name is explicit (this is what keeps code reviewable and constrained decoding tractable).
  • Cyclic imports are compile errors, reported with the package-graph path.

A project splits across files under src/, each addressed as <pkg>.<file>. The graphrag example is built this way (types / embed / similarity / store / api / main) to demonstrate a non-monolithic layout:

from graphrag.embed import embed, project
from graphrag.similarity import cosine

See /start/project-layout/ for the directory structure.

Declarations are module-private by default. pub (a soft keyword) marks a declaration as part of the public surface — a struct, function, trait, or method other modules may import:

pub struct ReconciliationDecision:
entry_id: int
matched: bool
pub def reconcile(lines: list[BankLine]) -> list[ReconciliationDecision] !{model.embed}:
...
def candidate_score(bank: BankLine, entry: LedgerEntry) -> f32 !{model.embed}:
... # no `pub` → module-private, invisible to other modules

Accessing a private declaration across modules is a compile error naming the missing pub. This matters beyond encapsulation: the public signature — the signature of a pub declaration including its contract clauses and effect row — is the cache key of Sema’s incremental verification economy, and the module is the attachment unit for assure grades, module-level policies, and monitor budgets.

Re-export is explicit — there is no implicit passthrough:

pub from finops.domain import Money # re-export Money from this module

Sema draws a clean line between two orthogonal axes:

  • Effect capabilities (fs, net, code, proc, observe, clock, …) are authorized by the !{...} effect row on a function. That row is already the explicit, governed declaration of what a function may do, so these stay ambient — no import needed.
  • Standard-library modules (math, io, http, and the std.* libraries) are APIs you call. They must be brought in with an explicit import.
import math # numeric functions + constants
import io # files + stdio
from std.belief import Belief
from std.cache import memoize
from std.document import Report, render
from std.agent_loop import loop_until
x = math.sqrt(2.0) # NameError without `import math`

The std.* modules are the standard library written in Sema, embedded in the compiler and importable everywhere.

Rationale: a program’s library dependencies are legible at the top of the file (as in Python), and because the name is a bound module handle rather than a magic global, an optimized implementation can be swapped in behind it later without touching call sites. Using a library module without importing it is a NameError with an actionable hint (“module ‘math’ used without import — add import math), never a silent fallback.

Module initialization is deterministic and effect-checked: top-level statements run once, in dependency order, under the module’s policy attachment. A module whose initializer needs effects beyond !{} must declare them in the manifest ([package] init_effects), which sema doctor reports.

Assurance grades attach at three levels, with this precedence:

manifest [assurance] default < module `assure` declaration < per-function @assure

See /neurosymbolic/verification/ for what the grades (bronze/silver/gold) require.

  • Unresolvable or cyclic import → compile error with the package-graph path.
  • Private access across modules → compile error naming the missing pub.
  • Two packages exporting the same root name → manifest aliasing required, never silent shadowing.
  • Library module used without importNameError with an actionable hint.
  • Python’s runtime sys.path/importlib semantics — undermines the lockfile, replay, and constrained decoding.
  • Wildcard imports.
  • Implicit re-export.
  • File-scope pub granularity — visibility is per-declaration, which is what the verification cache keys need.