Chassis
Chassis is a transactional runtime composition layer for dynamic agent systems.
It owns the runtime environment in which agents execute — plugins, capabilities, scoped resources, reversible effects, immutable runtime generations, policy, budgets, secrets, configuration, diagnostics, and observability metadata — and hands an immutable view of that environment to an execution engine.
Composition may change over time; every in-flight run stays pinned to the immutable runtime generation it started with. LangGraph is the first-class execution engine mounted within Chassis. Chassis is not a graph framework.
Install
pip install chassis-harness # the core lifecycle kernel
pip install "chassis-harness[langgraph]" # the LangGraph adapter
pip install "chassis-harness[langsmith]" # LangSmith telemetry + evaluation
pip install "chassis-harness[langgraph,langsmith]"
Python 3.12+. The import package is chassis.
The core has no dependency on langgraph, langchain-core, or langsmith:
importing chassis, the plugin lifecycle, generations, budgets, and diagnostics all
work without them. The extras add the LangGraph adapter, the langchain-core test
doubles, and the LangSmith backend; using an integration whose extra is missing
raises a MissingExtraError that names the extra to install.
Quickstart
from chassis import MODEL, Harness
from chassis.langgraph import AgentDefinition, GraphBuildInputs, LangGraphAgent
from chassis.runtime import HarnessRunContext
harness = Harness(name="quickstart")
harness.provide(MODEL, my_chat_model) # a langchain-core chat model
harness.register_agent(
LangGraphAgent(
AgentDefinition(name="research-agent", version="1", state_schema=ChatState, build=build_agent),
checkpointer=InMemorySaver(),
)
)
async with harness:
result = await harness.agents.invoke(
"research-agent", {"messages": [HumanMessage("hi")]}, thread_id="thread-1"
)
print(result.text, result.generation_id) # hello … gen_0001
A run acquires one immutable generation and keeps it: swapping a provider publishes a new generation without mutating the environment underneath an in-flight run.
The LangGraph quickstart needs the adapter extra:
pip install "chassis-harness[langgraph]".
Runnable end to end — no credentials, scripted model, asserts its own output:
uv run python examples/quickstart.py
Full walkthrough: docs/getting-started.md.
A plugin in ten lines
from chassis import DATABASE, PluginContext, plugin
from chassis.tools import ToolPolicy
@plugin(name="web-search", version="1.0.0", provides={"tools": "1.0.0"}, requires={"database": ">=1,<2"})
async def web_search(ctx: PluginContext) -> None:
store = ctx.require(DATABASE) # resolved for this composition
ctx.tools.register(search_tool, policy=ToolPolicy(permissions=("network.fetch",)))
ctx.create_task(warm_cache(store), name="cache-warmer")
No activation logic, no deregistration calls, no task bookkeeping: the harness orders plugins by declared capabilities, owns every effect through the plugin's scope, and cancels its tasks on unload.
Hierarchical composition scopes
Composition can be a tree, not a list. A scope inherits the providers visible from its ancestors, adds its own, narrows what it exposes, and owns what it declares — and it is still only desired state until a generation is published:
harness.install(postgres, entry_id="postgres") # shared, at the root
tier = harness.composition.child("tier-a", capabilities=[MODEL, DATABASE])
research = tier.child("research")
research.install(search, entry_id="search")
research.require(MODEL, ">=1,<2")
async with harness:
harness.diagnostics.explain_requirement("agent", "database").to_text()
harness.diagnostics.explain_scope("/tier-a/research").to_dict()
harness.diagnostics.diff_generations(old_id, new_id).to_text()
Sibling-local composition stays invisible, an ambiguity between a local and an inherited provider stays explicit until a preference resolves it, and a run that acquired a scope tree keeps observing exactly that tree.
Full guide: docs/scopes.md. Runnable:
uv run python examples/scoped_composition.py.
Versioned agent composition
A logical agent is described once, versioned, and materialized into the same composition scopes — without Chassis becoming an agent framework:
from chassis.agent_spec import AgentSpec
harness.agents.install(AgentSpec(
name="finance",
revision="17",
runtime_ref="finance-graph", # a registered AgentRuntime
capabilities=["model", "database", "tools"], # visibility, not authority
requires={"model": ">=1,<2", "database": ">=1,<2"},
tools=["spreadsheet"],
profile="reasoning",
plugins={"ledger": {}}, # resolved via the catalog
))
result = await harness.agents.invoke("finance", {"messages": [...]})
assert (result.agent, result.agent_revision) == ("finance", "17")
harness.diagnostics.explain_agent("finance", revision="17").to_text()
harness.diagnostics.diff_agents("finance", "17", "18").to_text()
A revision, once published, is immutable; a run keeps the revision (and generation) it started under even when a newer revision is published; retiring an agent stops new runs without destroying old executions; and unchanged contributions are reused across a revision change by the same semantic-identity machinery everything else uses.
AgentSpec is what composition an agent sees; LangGraph's AgentDefinition stays how
a specific engine builds and runs a graph. The core imports without LangGraph.
Full guide: docs/agent-composition.md. Runnable:
uv run python examples/agent_composition.py.
The core proposition
Chassis allows agent runtime composition to change over time while active runs retain a coherent environment, dependencies react correctly, resources have explicit ownership, and obsolete components are disposed only when they are no longer reachable.
Guarantees and deliberate absences states what that buys you, and what Chassis refuses to promise.
What Chassis is not
- not a graph engine — LangGraph owns graph execution and durability;
- not a replacement for
langchain-coremodels, tools, or runnables; - not a replacement for LangSmith tracing or experiment management;
- not a sandbox — in-process Python plugins are trusted code, and policy is not presented as isolation;
- not a system that claims deterministic replay of arbitrary clocks, networks, databases, or external services.
Status
Pre-1.0 (0.5.0). The surface covered by tests/test_public_api.py may break in a
minor release; every break is recorded in CHANGELOG.md, and
migrations lists the 0.1 → 0.2, 0.2 → 0.3, 0.3 → 0.4, and
0.4 → 0.5 changes.
Development
Chassis uses uv as its canonical project manager. Python ≥ 3.12 is required.
uv sync
uv run pytest
uv run ruff check .
uv run ruff format --check .
uv run pyright
uv build
pyproject.toml and uv.lock are the canonical dependency state. Do not introduce
alternative project managers or parallel requirements.txt files.
Releasing
- record the change in
CHANGELOG.mdunder a## [x.y.z]heading; - bump
versioninpyproject.tomlto the same number; - tag and push:
git tag vX.Y.Z && git push origin vX.Y.Z.
.github/workflows/release.yml runs the full check suite, then refuses to build
unless the tag, the project version, and the changelog agree; it builds the
distribution, installs the wheel into a clean environment, runs the quickstart
against it, and publishes through PyPI trusted publishing (no token is stored).
workflow_dispatch verifies all of that without publishing.
Examples
uv run python examples/quickstart.py # smallest useful app
uv run python examples/basic_agent.py # LangGraph agent end to end
uv run python examples/reactive_cascade.py # database → memory → extension
uv run python examples/safe_provider_replacement.py # generations across a provider swap
uv run python examples/scoped_composition.py # hierarchical composition scopes
Each example asserts what it prints, so running it verifies the behaviour. The test suite runs all of them.
Documentation
- Rendered docs: https://andreolli-davide.github.io/chassis/
docs/getting-started.md— install and first run.docs/recipes.md— behind a web service, per-tenant composition, hot provider swaps, budgets, durable runs.docs/troubleshooting.md— symptom, cause, and the exact diagnostics output for each.docs/migration.md— the 0.3 → 0.4, 0.2 → 0.3, and 0.1 → 0.2 changes and how to migrate.docs/incremental-composition.md— how composition changes incrementally: semantic identity, impact analysis, structural sharing, reuse diagnostics.docs/— lifecycle, scoped composition, plugin authoring, LangGraph, observability, security assumptions, replay limitations, configuration, and design guarantees.
Layout
src/chassis/
core/ scopes, effects, generations, errors
composition.py composition scopes and resolved scope trees
capabilities/ versioned contracts, provider registry, snapshots
plugins/ manifests, author API, resolver, registry
hooks/ scope-owned hook registry
tasks/ scope-owned background work
tools/ tool registry and the execution boundary
policy/ permissions and the evaluation boundary
budget/ hierarchical budget governor
secrets/ providers and redaction
langgraph/ agent definitions, graph cache, LangGraph runtime
telemetry/ instrumentation protocol, LangSmith, recording
persistence/ canonical hashing and runtime snapshots
replay/ bounded record/replay
config/ declarative configuration and reconciliation
testing/ TestHarness and fakes
Contributing
Issues and pull requests are welcome. Start with CONTRIBUTING.md for the gates a change must pass and what a reviewable commit looks like. Questions belong in Discussions; security reports go through private advisories instead of a public issue — see SECURITY.md for what is in scope.
License
Apache-2.0 — see LICENSE.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file chassis_harness-0.5.0.tar.gz.
File metadata
- Download URL: chassis_harness-0.5.0.tar.gz
- Upload date:
- Size: 399.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1e1ac8707b37701b4180aea54dff5a8240ac07f7472b0df3f8b6279bc5dce2e3
|
|
| MD5 |
9b5039b400651a43f38e52d8b030e2d0
|
|
| BLAKE2b-256 |
39f119200bb578a669e80a2a7a3895ecbb25eb9f5eb31fe26a92b26cf7ed182f
|
Provenance
The following attestation bundles were made for chassis_harness-0.5.0.tar.gz:
Publisher:
release.yml on andreolli-davide/chassis
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
chassis_harness-0.5.0.tar.gz -
Subject digest:
1e1ac8707b37701b4180aea54dff5a8240ac07f7472b0df3f8b6279bc5dce2e3 - Sigstore transparency entry: 2880246086
- Sigstore integration time:
-
Permalink:
andreolli-davide/chassis@ee3184e43c3ebd650652ebed98c4de3db499e10e -
Branch / Tag:
refs/tags/v0.5.0 - Owner: https://github.com/andreolli-davide
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@ee3184e43c3ebd650652ebed98c4de3db499e10e -
Trigger Event:
push
-
Statement type:
File details
Details for the file chassis_harness-0.5.0-py3-none-any.whl.
File metadata
- Download URL: chassis_harness-0.5.0-py3-none-any.whl
- Upload date:
- Size: 191.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
01c2e68e07e46371ea51a96e47e54a929b2021b4ce6bcc8ab9c95d03b740dae4
|
|
| MD5 |
1652aa835d2cabc15842540b2b155c19
|
|
| BLAKE2b-256 |
bb5fc8096206ec370f98b4615eeef0573ef8b0f95415d411b415d826a7d39c0f
|
Provenance
The following attestation bundles were made for chassis_harness-0.5.0-py3-none-any.whl:
Publisher:
release.yml on andreolli-davide/chassis
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
chassis_harness-0.5.0-py3-none-any.whl -
Subject digest:
01c2e68e07e46371ea51a96e47e54a929b2021b4ce6bcc8ab9c95d03b740dae4 - Sigstore transparency entry: 2880246148
- Sigstore integration time:
-
Permalink:
andreolli-davide/chassis@ee3184e43c3ebd650652ebed98c4de3db499e10e -
Branch / Tag:
refs/tags/v0.5.0 - Owner: https://github.com/andreolli-davide
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@ee3184e43c3ebd650652ebed98c4de3db499e10e -
Trigger Event:
push
-
Statement type: