An experimental Python foundation for durable multi-entity applications.
Entarium 1.0.1 is a complete, embeddable runtime: persistent independent entities that maintain their own state, receive different information, hold different beliefs, use different capabilities, and interact under flexible orchestration. An entity may be controlled by an AI model, a human, or deterministic code, and remains the same entity across all of them.
It is infrastructure meant to be installed and depended on — not a mandatory always-on Entarium server. An application that uses it is its own project; that application owns process lifetime and when interactions advance. Examples in this repository stay deliberately trivial. That boundary is architecture §14.1 and ADR 0010. What is public is enumerated rather than implied (ADR 0045), and what a version number promises about that list is ADR 0052.
Entarium 1.0.1 is complete. The project is currently inactive and experimental. This repository is published as a finished experimental foundation and reference implementation. There is no active development roadmap, and nothing beyond 1.0.1 is promised. 1.0.1 is a publication-closeout of 1.0.0: same runtime and public API. Future work may occur; it would require explicit new direction and should not be inferred from historical planning documents.
What the version number commits to is ADR 0052 and no more: each part of MAJOR.MINOR.PATCH commits to the public census, enforced by the validation command against tests/declared_surface.py. There is no stability grade inside public. Durable state is promised separately and more strongly — a persisted document changes by migration, never by silent reinterpretation (ADR 0019) — and no version increment licenses breaking it. Deprecation does not exist until a published name is scheduled for removal (ADR 0053). What a released version carried is recorded in CHANGELOG.md; the number itself lives only in pyproject.toml.
Publishing to a public package index is optional and intentionally deferred (ADR 0059 fitness; owner decision recorded there and in docs/development/backlog.md as MANUAL-REACTIVATION-ONLY). Release artifacts are produced offline by scripts/release.py (ADR 0054); no credential enters this repository. Obtain the wheel or sdist from a gated build, or depend on a git/path reference — neither path requires PyPI.
A checkout of this repository also includes packaged consumer materials under vendor/ for starting a derivative application without treating Entarium's internals as the application contract. vendor/entarium_consumer.tgz extracts to entarium-1.0.1/ (consumer README, architecture, public API reference, changelog, licence, and the seven examples). vendor/cursor_prompt.md is optional tooling guidance for an AI-assisted derivative session; it is not part of the runtime. Neither file is installed by the wheel.
These facilities exist in the library and are what a consumer can actually use:
- Durable entities with Entarium-owned presence, independent of model, provider, controller, and process.
- Replaceable controllers — deterministic code, a human, or a model-backed adapter — without changing entity identity.
- Interactions with addressable scopes, structural visibility, and host-driven advancement.
- Context assembly as the only path to a controller, with an auditable exposure record per invocation.
- Epistemic tiers as containers: exposure, observation, interpretation, and retained belief. Higher tiers do not silently rewrite lower ones. Automatic promotion is not provided; the host writes tiers 2 and 3.
- Persistence and resumability through SQLite document stores (and POSIX file-backed reference stores), including resume after discarding execution/checkpoint state.
- Capabilities with deterministic authorization evaluated outside the controller.
- Orchestration policy rather than a fixed turn loop: phases, subgroups, concurrent participation, selection invariance.
- Operational seams a host may attach: event sink, provenance recorder, exposure delivery recording, call budgets.
Entarium is not a chatbot, an agent framework, a workflow engine, a simulation engine, a world engine, or a game engine. It does not provide autonomous personality growth, and it does not ship finished showcase applications. Role-playing campaigns, hidden-information games, expert panels, and similar settings were design forcing functions; they are not products in this repository.
Architectural motivation, deferred decisions, and historical roadmap material describe why the system is shaped as it is. They are not a list of extra proven capabilities.
Most multi-agent frameworks treat the actor as a conversation with a personality attached. Entarium inverts that. The entity is durable; the model, provider, controller, and interaction are replaceable structures around it.
Three properties follow, and they are implemented:
Durable state belongs to the entity, not to a controller or a provider. An entity is fully reconstructible from Entarium-owned state without querying anyone. Swapping a model, or handing control from an AI to a human mid-interaction, costs nothing durable.
Information isolation is structural and auditable. Everything an entity's controller sees passes through one context assembly step that produces an inspectable exposure record. "Entity B does not know X" is a testable assertion about one artifact, not a hope about the whole codebase. Telling a model to pretend it does not know something is not isolation.
Truth and belief are separate, and belief is tiered. An entity may be exposed to something and not register it, register it and misread it, or carry a belief that contradicts both the record and another belief it holds. Correcting authoritative state does not correct anyone's mind. At this version, the tiers are containers and a citation rule, not an automatic ladder.
ADR 0055 is the commitment. "Supported" means exercised by CI on every push and pull request — not a hope about an untested combination.
| Python | CPython 3.14 (requires-python = ">=3.14,<3.15") |
| Platform | Linux (CI runs on ubuntu-latest) |
macOS is expected to behave like Linux for the SQLite stores but is untested, so it is not supported. Windows is expected to run those stores and is known not to run the file-backed reference stores (fcntl.flock); neither fact makes it supported. Widening either column is a change that adds a CI matrix entry in the same edit.
Store constraints a consumer must honour. The SQLite stores (ADR 0022) serve one host against a local filesystem. WAL shares memory between processes on that host and does not work over a network filesystem. Pointing a data directory at NFS, SMB, or similar is outside what the store supports. The file-backed stores remain a POSIX-only reference implementation; they are not the consumer path's platform limit.
Install the wheel into an environment that satisfies the support window above. The only runtime dependency is pinned in pyproject.toml.
A release is produced offline by scripts/release.py (ADR 0054). Public package-index publication is deferred (ADR 0059; MANUAL-REACTIVATION-ONLY); no credential is in this repository. Install from a gated wheel or sdist, or from a git/path reference.
Entarium is offered under the Apache License 2.0, copyright 2026 Ken McDonald (ADR 0058). The full text is in LICENSE, which both the wheel and the source distribution carry, and the packaging metadata states the same choice as the SPDX expression Apache-2.0 and as a trove classifier.
Apache-2.0 rather than a shorter permissive licence for the explicit patent grant in its §3, which matters for infrastructure meant to be embedded in someone else's product. No NOTICE file is shipped, so §4(d) obliges a redistributor to reproduce nothing beyond the licence itself. Nothing in this tree is copied from another project, so no upstream obligation qualifies the grant.
The wheel installs the library. The source distribution also carries the consumer documentation below (ADR 0056). Relative paths named here are present in the sdist and in a repository checkout; they are not installed into site-packages by the wheel.
| Path | What it is |
|---|---|
docs/architecture/generalized-multi-entity-system.md |
Authoritative architecture. Defines the invariants and decision precedence. Start here. |
docs/reference/public-api.md |
The API reference: every published name with its signature, fields, enum members, protocol methods, and whether it is awaited. Generated from the code. |
docs/decisions/ |
ADRs for durable choices (including 0010, 0045, 0052, 0054, 0055, 0056). |
examples/ |
Trivial one-concept programs that document supported usage — not applications (architecture §14.1). |
CHANGELOG.md |
What each released version carried. |
Two artifacts describe the surface, and they answer different questions.
The census answers may I depend on this name. It is every name a public module publishes in __all__ (ADR 0045), it is what a version increment makes a promise about (ADR 0052), and an installed environment can read it with nothing but the artifact's declared dependencies:
python -c "import entarium.orchestration as m; print(m.__all__)"The reference answers how do I call it, which a list of names structurally cannot: docs/reference/public-api.md carries the signature, the keyword-only parameters and their defaults, the dataclass fields, the enum members, the protocol methods, and the async. It is generated from the package's own objects and docstrings, so it cannot drift from the code without the repository's validation command failing.
This repository additionally enforces the census with a pytest module that is not shipped; pytest is not a runtime dependency, so that enforcement file is not a document an install environment can run.
After obtaining the source distribution (or a checkout), the programs under examples/ each demonstrate one concept — including structural isolation and truth-versus-belief, the two properties named above as reasons the project exists. Start with examples/README.md.
python examples/entity_with_persona.py
python examples/structural_isolation.py
python examples/truth_and_belief.pyArchitecture §14.1 and ADR 0010 say the host owns process lifetime and when interactions advance. Three more divisions of labour follow from decisions already in the code; each was discovered in the v1 proof by looking for a method that is not there. They are deliberate. They are not gaps to fill casually.
Keep your own index of the identifiers you minted. EntityStore and SnapshotStore do not enumerate: they create, load, and (for snapshots) delete by identifier you already hold. InteractionStore is the exception — it exposes identifiers so retention can list what it may remove (ADR 0020), and that list is not a query surface. An application that needs to reopen entities or snapshots after a restart must remember what it minted; nothing in those two stores will invent the list.
Promote observations to beliefs yourself. ingest stops at tier 1: it registers every item an exposure carried as an observation, and leaves interpretations and retained beliefs untouched. A controller cannot reach durable state. There is no public promote step — DurablePresence.with_interpretation, with_belief, and consolidation's optional RetainedBeliefDraft are how tiers 2 and 3 enter presence, and the host constructs those values. At this version, "belief is tiered" is containers and a citation rule, not an automatic ladder.
Runner-driven participation registers everything its exposure carried. InteractionRunner calls ingest on every successful act. That is why a second hand-written ingest against the same delivery raises ObservationAlreadyRecordedError. "Exposed but not registered" is expressed by not driving that entity through a runner for that delivery — assemble and invoke yourself, and skip ingest — not by asking the runner to filter.
To audit the exposures a run actually showed each entity, attach an ExposureDeliveryRecorder to InteractionRunner (exposures=…). After every successful invoke the runner records a DurableExposure: metadata always, content only when you also pass recording=ExposureRecording(enabled=True). Default is silence and content off (ADR 0057). That trail is process-local unless you write it into an ExposureArchive.
audit_exposures is a different question. It re-assembles contexts from an InteractionRecord and checks a visibility policy over that record. It does not read what a run delivered. Use both when you need both halves.
This section is for a checkout of this repository. Paths below resolve in the tree; they are not part of an installed wheel. Consumer documentation that a release carries is listed above and ships in the sdist (ADR 0056).
Derivative applications should depend on Entarium 1.0.1 as an external library and use the packaged consumer materials rather than this tree's internals. See docs/consumer-packet.md for vendor/entarium_consumer.tgz and vendor/cursor_prompt.md.
uv is the only prerequisite. It provisions the interpreter — CPython 3.14, per ADR 0004 and ADR 0055 — along with every pinned tool, so no separate Python installation is needed.
uv sync # provision: fetch the interpreter and the pinned tools
uv run --frozen --offline python scripts/check.py # validate: formatting, linting, strict types, testsThe second command is the one that answers whether the repository is healthy. It runs ruff format --check, ruff check, pyright in strict mode, and pytest, and fails if any of them fails. A type error is a failure, not a warning: under a language without module privacy, part of what enforces the architecture lives in static checking.
Provisioning and validating are deliberately separate. uv sync is the step that is allowed to reach the network; validation runs --offline against what is already installed, so touching the network during a check is an error rather than an accident. --frozen means the committed uv.lock is used exactly as it stands.
The same command also runs on every push and pull request via .github/workflows/validate.yml. CI provisions with uv sync --frozen, then runs uv run --frozen --offline python scripts/check.py — no credentials, no live-provider suite, and no network access during the check itself. A failure shows on the change without anyone remembering to run it locally. The workflow matrix is the support claim (ADR 0055): today one cell, CPython 3.14 on ubuntu-latest.
Hosted CI is the chosen mechanism rather than a pre-commit hook: the suite is large enough that a local hook would be skipped routinely, which is the failure mode automatic validation exists to stop. The workflow comment records the same reasoning.
One test file, or one test:
uv run --frozen --offline python -m pytest tests/test_scaffolding.py
uv run --frozen --offline python -m pytest tests/test_scaffolding.py::test_interpreter_meets_the_adr_0004_floorThe fast suite under tests/ is provider-free: no credentials, no network, and no live model call. A separate real-provider suite lives in provider_suite/ and is outside pytest's configured testpaths, so scripts/check.py never collects it. Run it only when you mean to, with a credential in the environment:
ANTHROPIC_API_KEY=… uv run python -m pytest provider_suite/Without the key, live-call tests skip; the suite-separation sentinel still runs. The live suite includes a one-call adapter probe and a Phase 1 scenario with one AI-controlled participant (test_provider_scenario_live.py) whose assertions are structural, not payload-equal.
Live calls in that suite will need credentials for a chosen provider; the sentinel file there makes none. The fast suite asserts the separation by collection, not by convention.
A second separable command proves the package is installable outside this tree. It builds a wheel, installs it into a fresh environment whose working directory is not this repository, and runs a minimal program from consumer_suite/ that imports only public names, advances an interaction, and resumes in a second process:
uv run --frozen --offline python scripts/consumer_smoke.pyLike the provider suite, it is not part of scripts/check.py. It runs offline against the already-synced environment and lockfile.
A third separable command runs a review-panel domain against that same kind of installed artifact. That panel is an optional sibling repository (../panel when present), not part of Entarium and not required to use it. The command builds the wheel, installs it outside both trees, and runs the panel's suite:
uv run --frozen --offline python scripts/panel_consumer.pyA fourth separable command puts one entity in both a test-tree domain (Climb) and that optional panel at once. It is separable because the panel is a sibling repository and the fast suite must not require one:
uv run --frozen --offline python scripts/cross_domain_entity.pyA fifth separable command produces a release. It refuses unless tests/declared_surface.py already records this version's surface (re-record with record first), refuses unless CHANGELOG.md has a section for the version with no deprecation subsection, builds the wheel and sdist twice and checks they are byte-identical, gates that the sdist carries the documentation the README promises (ADR 0056), then installs the wheel outside the tree and gates that install: every public name imports, durable fixtures load through the public stores, and the consumer smoke advances and resumes. It does not publish — nothing is uploaded, and no credential is involved (ADR 0054):
uv run --frozen --offline python scripts/release.py record # when the surface moved
uv run --frozen --offline python scripts/release.py # build, gate, write dist/The API reference is generated rather than written, and the validation command fails when the committed file stops matching the code. After changing a public signature, field, enum member, or docstring, regenerate it in the same change:
uv run --frozen --offline python scripts/reference.py # rewrite it
uv run --frozen --offline python scripts/reference.py --check # what the suite assertsFormatting is checked rather than applied by the validation command. To apply it:
uv run --frozen --offline python -m ruff format .The implementation language is settled — Python, per ADR 0004. Development historically ran one work item at a time; docs/development/state.md records that Entarium 1.0.1 is complete and that there is currently no work item.
| Architecture | Authoritative for 1.0.1. Defines the invariants and decision precedence. Start here. |
| API reference | Generated. Every published name with its shape. Do not edit by hand. |
| Decisions | ADRs for durable choices. |
| Consumer packet | Packaged consumer docs and optional AI prompt for derivative projects. |
| Current state | Development is complete and idle; this file records that. |
| Roadmap | Historical record of completed 1.0.0 phases. Not an active plan. |
| Development system | How work items, sessions, and the ratchet operated. |
| AGENTS.md | Instructions for AI coding agents. |
| External reuse policy | When to depend on, copy from, or merely learn from external work. |
| Reference repository strategy | Optional local prior-art checkouts used during development. Not required to use Entarium. |
| External reference records | Citations and analysis of those projects. Entarium does not include or own them. |
During development, mature external projects were kept as read-only local prior art in sibling directories outside this repository (../references/ when present). They are not dependencies, are not published with Entarium, and are not required to build, test, or use it. Entarium never imports from them. What was concluded after reading each one is in docs/external-references/.