Skip to main content

emergent

Write meaning, not code.

PyPI Python 3.13+ License: MIT Types: pyright strict

A compilation platform where frozen data compiles itself through fold. You build compilation languages — not applications. HTTP, CLI, Telegram, OpenAPI, Pydantic, SQL are libraries on the platform. Your custom compilers sit next to them.

@schema_meta(http_crud("/users", Users))
@dataclass
class User:
    id: Annotated[int, Identity]
    name: str
    email: Annotated[str, Unique, MaxLen(255)]

5 REST endpoints. Pydantic models. OpenAPI spec. You write fields, fold writes everything else.

# distribution is "emergent-py"; you import it as "emergent"
pip install emergent-py

# add extras for the targets you compile to:
pip install "emergent-py[fastapi]"   # HTTP + OpenAPI
pip install "emergent-py[sa]"        # SQLAlchemy
pip install "emergent-py[all]"       # everything (fastapi, sa, tg, cli)
import emergent

The idea

Frameworks today scatter meaning across files. A User lives in a model, a serializer, a view, a URL config, a migration, a test fixture — and none of those files know about each other. An LLM (or a human) has to hold the whole graph in their head to make a correct change.

emergent inverts this. Everything about a thing is on the thing:

@schema_meta(
    http_crud("/bounties", Board, ops=(LIST, GET, CREATE)),
    Methods(),
)
@dataclass
class Bounty:
    id: Annotated[int, Identity]
    title: str
    reward: Annotated[int, Min(0)]
    status: Annotated[str, MaxLen(20), Doc("Bounty status"),
                       cli.Help("Current status"), openapi.Description("Status field"),
                       sql.Index("idx_status")]

    @classmethod
    @post("/bounties/{bounty_id}/claim")
    async def claim(cls, db: ..., bounty_id: int, hunter: str) -> Result[Bounty, DomainError]: ...

One place. Every concern — validation, CLI help text, OpenAPI description, SQL index, HTTP routes — lives as an annotation on the field it belongs to. The fold compiler reads these annotations and produces correct output for each target. No sync bugs. No scattered state. No framework magic to reverse-engineer.

This is locality by construction, and it's what makes emergent work for humans and machines alike.

The entire platform is one function. Compilation, verification, explanation, query execution, derivation — all are fold applied to different data. Everything else is consequences.


Platform, not framework

Frameworks prescribe structure. Rails prescribes MVC. Django prescribes models-views-templates. You write code inside the framework.

emergent provides primitives. You build on the platform:

Primitive What it does
fold(items, ctx, protocol, method) Iterate capabilities, dispatch by protocol, accumulate context. 8 lines.
CompilationPhase(ctx_type, protocol, initial) Reified fold configuration. One phase = one compilation language.
SchemaCompiler(phases) Composable set of phases. Algebra: +, |, -, &.
TargetCompiler(trigger, codecs, pipeline, assemble) Surface-level compilation: Application → framework artifact.

The 4 axes (Schema, Surface, Storage, Query) are libraries built on these primitives. wire.derive is a library. Your custom compilers sit next to them — not inside a framework, but on the same platform.

Create your own compiler

to_pydantic(User, axes) reads capabilities, produces a Pydantic model. to_json_schema produces OpenAPI. You build to_search_index the same way. This runs:

from dataclasses import dataclass, replace
from typing import Annotated, Protocol, runtime_checkable

from emergent.wire.compile._core import Axes
from emergent.wire.compile._phase import CompilationPhase
from emergent.wire.compile import to_pydantic, to_json_schema
from emergent.wire.axis.schema import Identity, MaxLen, Min
from emergent.wire.axis.schema._universal import SchemaAxisCapability
from emergent.wire.axis._capability import OpenAPIContext, openapi_schema


# ── Your language: search index ──────────────────────────────

@dataclass(frozen=True, slots=True)
class IndexFieldCtx:
    field_name: str = ""
    field_type: type = object
    searchable: bool = False
    boost: float = 1.0
    facet: bool = False


@runtime_checkable
class IndexCompilable(Protocol):
    def compile_index(self, ctx: IndexFieldCtx) -> IndexFieldCtx: ...


INDEX_PHASE = CompilationPhase(
    IndexFieldCtx,
    IndexCompilable,
    lambda n, t: IndexFieldCtx(field_name=n, field_type=t),
)


# ── Capabilities: one atom, two languages ────────────────────

@dataclass(frozen=True, slots=True)
class Searchable(SchemaAxisCapability):
    boost: float = 1.0

    # YOUR language
    def compile_index(self, ctx: IndexFieldCtx) -> IndexFieldCtx:  
        return replace(ctx, searchable=True, boost=self.boost)

    # emergent's!
    def compile_openapi(self, ctx: OpenAPIContext) -> OpenAPIContext:
        return openapi_schema(
            ctx, **{"x-searchable": True, "x-search-boost": self.boost}
        )


@dataclass(frozen=True, slots=True)
class Facet(SchemaAxisCapability):
    def compile_index(self, ctx: IndexFieldCtx) -> IndexFieldCtx:
        return replace(ctx, facet=True)

    def compile_openapi(self, ctx: OpenAPIContext) -> OpenAPIContext:
        return openapi_schema(ctx, **{"x-facet": True})


# ── Your compiler: to_search_index — same shape as to_pydantic

@dataclass(frozen=True, slots=True)
class IndexField:
    name: str
    searchable: bool = False
    boost: float = 1.0
    facet: bool = False


@dataclass(frozen=True, slots=True)
class SearchIndex:
    entity: str
    fields: tuple[IndexField, ...]


INDEX_COMPILER = SchemaCompiler(phases=(INDEX_PHASE,))


def to_search_index(cls: type, axes: Axes) -> SearchIndex:
    ec = INDEX_COMPILER.compile(cls, axes)
    return SearchIndex(
        entity=cls.__name__,
        fields=tuple(
            IndexField(
                name=c.field_name,
                searchable=c.searchable,
                boost=c.boost,
                facet=c.facet,
            )
            for fc in ec
            if (c := fc[INDEX_PHASE]).searchable or c.facet
        ),
    )


# ── Entity ───────────────────────────────────────────────────

@dataclass
class Product:
    id: Annotated[int, Identity]
    name: Annotated[str, MaxLen(200), Searchable(boost=3.0)]
    description: Annotated[str, Searchable()]
    category: Annotated[str, Facet()]
    price: Annotated[float, Min(0), Facet()]


# ── Same axes, three compilers ───────────────────────────────

axes = Axes.default()

index  = to_search_index(Product, axes)   # YOUR compiler   → SearchIndex
model  = to_pydantic(Product, axes)       # emergent's       → Pydantic BaseModel
schema = to_json_schema(Product, axes)    # emergent's       → OpenAPI JSON Schema
index → SearchIndex(entity='Product', fields=(
    IndexField(name='name', searchable=True, boost=3.0),
    IndexField(name='description', searchable=True, boost=1.0),
    IndexField(name='category', facet=True),
    IndexField(name='price', facet=True),
))

model → <class 'Product'>  (Pydantic BaseModel with MaxLen, Min validation)

schema → {
    "name": {"type": "string", "maxLength": 200, "x-searchable": true, "x-search-boost": 3.0},
    "category": {"type": "string", "x-facet": true},
    "price": {"type": "number", "minimum": 0, "x-facet": true},
    ...
}

to_search_index has the same shape as to_pydantic — takes a class and axes, returns a typed artifact. Searchable implements both compile_index and compile_openapi, so it participates in both compilers. MaxLen has no compile_index — your compiler never sees it.

Execution: nodnod

emergent has two co-equal primitives. fold compiles descriptions. nodnod executes dependency graphs:

@G.node
class FetchPrice:
    @classmethod
    async def __compose__(cls, product: Product, db: Database) -> float:
        return await db.get_price(product.id)

@G.node
class FetchStock:
    @classmethod
    async def __compose__(cls, product: Product, db: Database) -> int:
        return await db.get_stock(product.id)

# FetchPrice and FetchStock have no dependency on each other → run in parallel.
# No asyncio.gather. No concurrency code. The type signatures ARE the graph.
result = await G.compose(BuildReport, product, db)

Types are the specification. fold reads capabilities from Annotated. nodnod reads dependencies from __compose__ signatures. Both: the structure IS the program.


Why this matters

A User in Django lives in a model, a serializer, a view, a URL config, a migration. Five files that must agree. When you change one, you grep for the others and hope. When a new developer joins, they trace the chain by hand. When a coding agent tries to add a field, it hallucinates a signal that doesn't exist because the pattern looked right from the three files it saw.

This is the scattered meaning problem. It is not a tooling problem — it is a language problem. The absence of a primitive that lets you state a fact once and have every consumer derive what it needs.

emergent is that primitive. Four properties make it work:

Property What it means
Locality All concerns for a field live on the field. Annotated[str, MaxLen(255), Unique, sql.Index()] — validation, schema, DDL in one place.
Defunctionalization Behavior is data. MaxLen(255) is a frozen dataclass you can print, compare, serialize. Not a closure. Not a decorator. Data.
Open-world dispatch New capabilities participate via isinstance. No registration. No plugin system. Implement the protocol → fold picks you up.
Algebraic composition Compilers compose: FASTAPI_SCHEMA + YOUR_PHASE. Capabilities compose: (MaxLen(255), Unique). No interference between independent concerns.

The result: any observer — human, LLM, or your future self at 3 AM — reads one declaration and knows everything. One change propagates to all targets. Not by convention. By construction.


Self-describing

emergent's IR reads itself. Every axis has an explain system — structured dicts for tools, human-readable text for you:

explain_schema(User)
# === User ===
#   [SchemaName('users')]
#
#   id (int):
#     [Identity]
#
#   email (str):
#     [Unique, MaxLen(255)]
#     cli: Help('Email address')
#     openapi: Description('User email'), Format('email')
#     sql: Index('idx_email')

explain_application(app)
# === Application (3 endpoints) ===
#
#   Endpoint #1 (2 exposures):
#     [POST /api/v1/players] RequestResponseCodec
#       request: RegisterRequest, response: RegisterResponse
#     [register (cli)] RequestResponseCodec
#       request: RegisterRequest, response: RegisterResponse

explain_full(User)  # 3-layer trace
# === User (full trace) ===
#
#   Schema: 3 fields (1 identity)
#     id (int) [Identity]
#     name (str)
#     email (str) [Unique]
#
#   Derivation: 1 pattern
#     Pattern #1: Dialect (11 steps), provider=Users
#       steps: InspectEntity → RequireIdentity → BindProvider → ...
#       ops: List, Get, Create, Update, Patch, Delete
#
#   Surface: 6 exposures across 1 endpoint
#     GET /api/users [ListUserRequest → ListUserResponse]
#     GET /api/users/{id} [GetUserRequest → GetUserResponse]
#     ...

Schema, query, storage, surface, compilation trace, derivation pipeline — all explainable. No execution, no side effects. The program describes itself from its own IR.


Verified at import time

verify catches semantic contradictions before your code runs:

from emergent.wire.verify import verify_raising

@dataclass
class Sensor:
    id: Annotated[int, Identity]
    name: Annotated[str, MinLen(50), MaxLen(10)]       # ERROR: MinLen > MaxLen
    temp: Annotated[float, Min(200), Max(125)]          # ERROR: Min > Max
    secret: Annotated[str, ReadOnly(), WriteOnly()]      # ERROR: inaccessible field

verify_raising(Sensor)  # VerificationError with all issues

Three built-in phases — numeric constraints, length constraints, semantic flags — each running as a standard compilation phase. Open-world: add your own verification phases without modifying emergent.


Four levels of control

# Level 1 — auto CRUD: one decorator, full API
@schema_meta(http_crud("/products", Store))
@dataclass
class Product:
    id: Annotated[int, Identity]
    name: str
    price: float

# Level 2 — CRUD + methods: derive CRUD, hand-write domain logic
@schema_meta(http_crud("/bounties", Board, ops=(LIST, GET, CREATE)), Methods())
@dataclass
class Bounty:
    id: Annotated[int, Identity]
    title: str
    reward: int

    @classmethod
    @post("/bounties/{bounty_id}/claim")
    async def claim(cls, db: ..., bounty_id: int, hunter: str) -> Result[Bounty, DomainError]: ...

# Level 3 — hand-written methods: every endpoint explicit, full control
@schema_meta(Methods())
@dataclass
class Auth:
    @classmethod
    @post("/login")
    async def login(cls, creds: Credentials) -> Result[Token, AuthError]: ...

# Level 4 — raw wire: one endpoint, three targets
endpoint(runner)
    .expose(HTTPRouteTrigger("POST", "/register"), rrc(Req, Resp))
    .expose(CLITrigger("register"), rrc(Req, Resp))
    .expose(TelegrindTrigger(Command("register")), rrc(Req, Resp))

Pick what fits. Mix in one app. Drop down a level when you need control, stay high when you don't.


Semantic transforms

Transforms in emergent dispatch on domain meaning, not syntax. This is a novel mechanism — no existing macro system combines domain-semantic awareness with compositional algebra:

@schema_meta(
    http_crud("/posts", Posts),
    WithoutDelete(),    # ← removes DELETE op across all targets
    Paginated(20),      # ← adds pagination to ops with Pageable effect
    Sorted(),           # ← adds sorting to LIST
    Filtered("author"), # ← adds exact-match filter
)
@dataclass
class Post:
    id: Annotated[int, Identity]
    title: str
    body: str
    author: str

Capabilities stack as arguments in @schema_meta() — frozen data in, frozen data out. They compose because they operate on algebraic structure, not string templates or AST nodes.


Examples

Example What it shows
roulette/ HTTP + CLI + Telegram from one codebase
cross_compile/ Bridge legacy FastAPI → CLI
full_stack/ Full-stack example
wiring.py Raw wire: endpoint + trigger + codec

Tutorial

A story-driven, 27-chapter walkthrough — from first API to handing your codebase to a coding agent.

Start here → docs/tutorial/00-intro.md

Part Chapters What you'll build
I–IV 01–14 CRUD, methods, transforms, auth, nested resources, multi-target, custom dialects, raw wire, bridge
V–VI 15–18 Query axis, providers, ops & runners, scope & DI
VII–VIII 19–24 Enrichers, storage, compilation internals, stateful codecs, roulette walkthrough, design philosophy
IX 25–27 Verify & explain, why emergent is LLM-native, agent workflows

Docs

docs/intro.md Introduction (EN)
docs/intro_ru.md Введение (RU)
docs/essence.md The essence — one function, one operator
docs/philosophy.md Design philosophy
docs/theory/architecture.md Architecture — theory, invariants, algebraic properties
docs/internal/wire-reference.md Wire reference — axes, capabilities, compile, bridge
docs/internal/cheatsheet.md Cheatsheet — all axes, every import, every pattern
docs/theory/universal-derivation.md Derivation — entity → endpoints via fold
docs/theory/compiler-deep-dive.md Compiler deep-dive — developing custom compilers
docs/emergent-and-ai.md emergent + AI agents
docs/book/ Book — 5-chapter SICP-style deep dive into compilation thinking

Where we are

emergent is young — started January 2026. Already runs in production. The core architecture (fold, CompilationPhase, SchemaCompiler, TargetCompiler, verify, explain, derive) is stable. What's still growing: the ecosystem, the stdlib, the number of built-in targets and dialects. Breaking changes happen, but we keep them well-motivated.


Stack

Layer What
deployme.py Application → infrastructure (compose, k8s)
emergent platform: fold, CompilationPhase, SchemaCompiler, TargetCompiler
emergent.wire libraries: axes (schema, surface, storage, query), derive, bridge, verify
emergent.graph runtime: Composer, ScopeFamily, RuntimePolicy (Cooperative / WorkStealing)
emergent.ops/saga/cache patterns: data-driven dispatch, compensation chains, tiered caching
nodnod typed dependency graphs, auto-parallelization, Either, Scope
combinators.py retry, timeout, fallback, race
kungfu Result, Option, LazyCoroResult

Define a context. Define a protocol. Call fold.

You just created a compilation language.

Metadata

Release files for emergent-py 0.9.5

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for emergent-py 0.9.5
File Size Uploaded Provenance Actions
emergent_py-0.9.5.tar.gz 393.4 kB Without provenance Details for emergent_py-0.9.5.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for emergent-py 0.9.5
File Interpreter ABI Platform Actions
emergent_py-0.9.5-py3-none-any.whl Python 3 none any Details for emergent_py-0.9.5-py3-none-any.whl

Total release size: 888.0 kB

Release files / emergent_py-0.9.5.tar.gz

Download URL emergent_py-0.9.5.tar.gz
Size 393.4 kB
Tags Source
SHA-256 checksum
How to use checksums
d80eabb02192ad00aee1882067ce77f648dbe4b610e766353625ceafa7bd798f
BLAKE2b-256 checksum
How to use checksums
bacc166fe3a04d622907ccbf2ad8a73407a38669a18bf6fcb54bf48b0aaf6019
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.8.1

Release files / emergent_py-0.9.5-py3-none-any.whl

Download URL emergent_py-0.9.5-py3-none-any.whl
Size 494.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
97491ce249af1a3aa072542ef52878b185b9786a2680fd1897587bcb3659b1a8
BLAKE2b-256 checksum
How to use checksums
54df5785831ba73a40e599c4cadcd6b4fde59847acc7d20641ccba43c786feed
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.8.1

Security for emergent-py 0.9.5

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.9.5 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page