ActiveGraph v1.2.0 is live – x30 speedup ActiveGraph v1.2.0 is now live, introducing a pluggable graph projection seam with FalkorDB as the first external backend, achieving up to a 30x speedup on structural queries. The release, driven by external contributor @dudizimber, adds native edges, Cypher query push-down, and a conformance suite for future backends, while making the full test suite a CI gate and standardizing on Apache 2.0 licensing. ← back to blog /blog Active Graph v1.2.0: The Graph Projection Becomes Pluggable v1.2.0 makes the materialized graph a pluggable seam with FalkorDB as the first external backend — native edges, Cypher query push-down, a conformance suite for future backends — plus a CI gate for the full test suite and one license everywhere. - release - activegraph - graph - falkordb - community - announcement Active Graph https://activegraph.ai v1.2.0 is now live on PyPI https://pypi.org/project/activegraph/ . This release has a different origin story than the last one. v1.1.0 was a planned tightening pass. v1.2.0 arrived from the outside: an external contributor, @dudizimber https://github.com/dudizimber , opened four issues over one week, then shipped the pull requests that answered them. The result is the biggest architectural addition since packs — the materialized graph projection is now a pluggable seam, with FalkorDB as the first external backend. TL;DR - New GraphStore seam: choose where the current-state graph materializes. The event log remains the source of truth; the projection is now a pluggable, disposable view of it. FalkorDBGraphStore backs the projection with a real graph database — native edges, Cypher queries, a connected graph you can actually see in FalkorDB Browser.- Structural queries push down into the database. A 2-hop pattern match over 20,000 objects drops from ~8.9 s to ~300 ms, because cost now scales with the result, not the graph. GraphStoreConformance is the extension contract: any future backend passes the same executable suite.- The full test suite is now a CI gate, on a Python 3.11 + 3.12 matrix, with the FalkorDB and Postgres conformance suites running instead of skipping. - Every architectural decision is locked in CONTRACT.md § v1.2, and one license — Apache 2.0 — now appears everywhere, including the website. Install or upgrade: pip install --upgrade activegraph Where This Release Came From Active Graph has always kept the materialized graph — objects, relations, patches — in process memory, rebuilt from the event log on every run. That is the right default. But it left three things hard once a run's current-state view got interesting: you couldn't query it with a graph query language, you couldn't share it across processes, and it had to fit on the heap. The event log already had a seam for this — EventStore , with SQLite and Postgres behind it. The projection had no equivalent. Issue 38 named the gap, and the first PR introduced the GraphStore seam: an interface for where the projection lives, with InMemoryGraphStore as the unchanged default and FalkorDBGraphStore as the first external backend. python from activegraph import Graph, Runtime, FalkorDBGraphStore store = FalkorDBGraphStore host="localhost", port=6379, graph name="run-42" graph = Graph graph store=store rt = Runtime graph=graph, behaviors= ... or rematerialize a recorded run into FalkorDB: rt = Runtime.load "runs.db", run id="run-42", graph store=store The invariant that makes this safe: the event log stays the source of truth, and the projection is disposable. Wiping a GraphStore loses nothing — replaying the log rebuilds it. Durability and audit continue to come from the EventStore . That asymmetry is what let the rest of this release move fast. Install the backend with pip install 'activegraph falkordb ' server client or pip install 'activegraph falkordb-embedded ' embedded engine, Python 3.12+ . Neither is pulled in by all — opting into a graph database is always explicit. Relations Became Real Edges The first FalkorDB implementation stored each relation as a standalone node carrying source/target properties. That preserved a subtle Active Graph feature — dangling relations, where a relation can reference objects that don't exist yet — but it defeated the point of a graph database. The Browser rendered a cloud of disconnected nodes. Native traversal and graph algorithms didn't apply. Every hop was a manual property join. Issue 41 called this out, and the follow-up PR remodeled the store. Relations are now native edges with a single fixed relationship type, carrying the relation's kind as an edge property: php MATCH s:AGObject - r:AGRelation {type: 'knows'} - t:AGObject RETURN t.id, t.data Dangling relations survive the remodel through a shared :AGNode endpoint label: real objects are :AGNode:AGObject , and a dangling endpoint is a bare :AGNode placeholder that promotes when its object arrives. The fixed relationship type also keeps every value a bound parameter — relation kinds never enter query text, so there is no injection surface in the write path. And because the projection is disposable, the layout change needed no migration tooling. Point Runtime.load at the event log and it rematerializes in the new shape. Queries Push Down Into the Database Backing the projection with FalkorDB is only useful if queries run there. Initially they didn't: every Graph query pulled the whole projection out of the database and filtered in Python, so cost scaled with graph size rather than result size. Issues 43 and 45 closed that gap. GraphStore now exposes optional query hooks — find objects , find objects in types , find relations , neighborhood , and match chain — with Python defaults that define the semantics. A backend may override a hook to push the work into the database, but results must be identical to the default. On FalkorDB, type filters, relation lookups, neighborhood walks, and whole pattern chains now run as index-backed Cypher. A pattern that used to walk the projection hop-by-hop collapses into a single query. The numbers, from the benchmark script that ships in the repo indicative, one machine, read the ratios : a 2-hop pattern match over 20,000 objects takes ~8.9 s on the in-memory matcher's whole-projection walk and ~300 ms pushed down — roughly 30× — and the gap widens with graph size, because FalkorDB's cost scales with matches, not nodes. In-memory still wins on small graphs and always wins on writes; the FalkorDB guide has the full table and the rule of thumb. The parts that can't be translated faithfully stayed in Python on purpose. The where predicate language evaluates over the pushed-down candidate set, because the structured payload is stored as JSON and a faithful translation isn't possible today. Structural filters push down; everything else stays in one place. Native where push-down is named follow-on work in the roadmap. A Conformance Suite Is the Extension Contract How do you keep two backends — and any future ones — honest about "results must be identical"? With an executable contract. GraphStoreConformance is a pytest-collectable suite covering round-trips, cascade deletes, the query-hook semantics, placeholder promotion and demotion, and cycle-safe neighborhood walks. InMemoryGraphStore and FalkorDBGraphStore both inherit it. That is also the door for what comes next. A Neo4j or Postgres-graph backend doesn't need a design debate; it needs to subclass the conformance suite and go green. The contract is the tests, not the prose. The Test Suite Is Now a CI Gate Here is an uncomfortable thing this release surfaced: Active Graph had six CI workflows — docs build, docstring coverage, strict typing, wheel completeness, deploy verification — and none of them ran the test suite. Roughly 700 tests ran only by convention, locally, by whoever was driving. The FalkorDB PRs merged with no automated test execution at all. That contradicted the project's own gate philosophy, so v1.2.0 closes it. A new workflow runs the suite on every push and pull request, on a Python 3.11 + 3.12 matrix. The 3.12 leg installs the embedded FalkorDB engine so the store and conformance tests execute instead of skipping, and a Postgres service container does the same for the Postgres event-store tests. The locked invariant: every non-slow test in the repository executes on at least one leg. A new optional-dependency skip that no leg exercises is a gate regression, not a neutral skip. Locking Decisions After the Fact, Honestly Active Graph's development discipline is contract-first: decisions get locked in CONTRACT.md in the same commit as the code. The FalkorDB arc inverted that — the code came from the community at the community's pace, and merging good work mattered more than queueing it behind contract prose. The debt got paid before release. CONTRACT.md § v1.2 locks every architectural decision the arc introduced — the seam and its source-of-truth invariant, the connection model, the native-edge shape, the query-hook semantics down to which hooks promise ordering, and the conformance rule — with a provenance note stating plainly that the lock was written after the code, and why. If a future contribution lands the same way, the pattern is now named: merge, then lock before release. The same pass restored the planning cadence around it. The roadmap now scopes the next cycle type-completeness burndown, native structured-output mode, where push-down design, community surface , every deferred idea carries an explicit revisit trigger so deferrals can't silently expire, and docstring coverage on the public surface reached 100% — Runtime , View , and Budget were, embarrassingly, among the last symbols without one. One License, Everywhere A sharp-eyed user noticed that the website said MIT while the repository said Apache 2.0 — and asked, reasonably, whether an AI made an accidental edit somewhere. No accident, but a real inconsistency. Active Graph switched from MIT to Apache 2.0 before the v1.0 launch, for the explicit patent grant and the precise contribution terms. The repository has been consistent since — LICENSE , NOTICE , wheel metadata, and a CI test that enforces it. The website was the stale artifact, and it now says Apache 2.0 like everything else. If you're building on Active Graph commercially: Apache 2.0, with a patent grant, verified in CI. Migration Notes No migration is required. - Runs that don't pass graph store= behave byte-for-byte as before. InMemoryGraphStore remains the default. - The FalkorDB backend is opt-in via the falkordb or falkordb-embedded extras. - If you tried the pre-release relations-as-nodes layout: no tooling needed. The projection is disposable — reload the run with graph store= and it rematerializes as native edges. - v1.2.0 adds no new event types, reason codes, or error classes. Why This Release Matters Two reasons, one technical and one social. Technical: the current-state view of a long-running agent system is now a real graph you can point real tools at. Cypher over live state, dashboards and sidecar inspectors sharing one projection, structural queries that cost what the answer costs — while the event log keeps being the boring, durable, replayable source of truth underneath. Social: this is the first Active Graph release whose core capability was designed, argued, and built by someone outside the project, through the issues-first process the project asks contributors to follow. Four issues, each stating a problem, a workaround, and a proposal; then the PRs that resolved them. The process held up under its first real test, and the contract, conformance suite, and CI gate that shipped alongside it are what make the next outside contribution easier than this one. PyPI: https://pypi.org/project/activegraph/1.2.0/ https://pypi.org/project/activegraph/1.2.0/ GitHub release: https://github.com/yoheinakajima/activegraph/releases/tag/v1.2.0 https://github.com/yoheinakajima/activegraph/releases/tag/v1.2.0 FalkorDB guide: https://docs.activegraph.ai/guides/using-falkordb/ https://docs.activegraph.ai/guides/using-falkordb/ ← back to blog /blog