The content management systems of the 2000s and 2010s solved a real problem. They gave
content a shape: defined types, structured fields, lifecycle states. A blog post was
not just a row in a table. It was a thing with a title, a body, a status, a
publication date. It could be in Draft or Published or Archived. You could list all
published posts. You could prevent a draft from being served to readers.
This was good thinking. Typed data and explicit states make software more correct and
more predictable. The CMS world figured that out early.
But CMSes were built for one kind of operator: a human working through a browser. The
API was an afterthought. Relations between content items were baked into the UI or
bolted on as plugins. The whole system assumed a person was making the decisions.
That assumption is changing. I started thinking about this in February, and started
building.
The premise was simple: agents need the same things CMSes were built around, typed
data, explicit states, relations. But through a protocol, not a UI, and with enforcement, not just convention. An agent that talks to a typed content layer knows
what fields exist, knows what state transitions are valid, and gets a typed error when
it tries something illegal. The lifecycle is not in the prompt. It is in the layer.
Here is what that looks like in practice. An agent calls preview_impact before archiving a source document:
tool: preview_impact
target_type: source
target_id: "src-42"
→ { "count": 4, "dependents": [...] }
The agent sees that four published posts cite this source before it acts. That is not a prompt instruction. It is a query against the typed relation graph.
State transitions are enforced the same way. Calling publish_post on an Archived item returns a typed error: invalid state transition. The agent does not need to be told this in the prompt. The layer refuses.
Custom state machines, where you define your own lifecycle stages like Draft, Review, Approved, Published, are on the roadmap. The built-in states are the foundation that makes them possible.
I had been building this for a couple of months when Andrej Karpathy posted about his
LLM Wiki. His pattern: instead of running RAG over documents every time, have an agent
build and maintain a persistent wiki of structured, interlinked markdown files that
compounds over time. His framing was: "Obsidian is the IDE. The LLM is the programmer.
The wiki is the codebase."
It was a good confirmation that the need is real. Agents need persistent, structured,
queryable knowledge. Not a raw database. Not a chat history. Something shaped.
But it also showed clearly where markdown stops. There is no enforcement in a markdown
file. There is no state machine. There is no way to say "this decision is Active and
blocks these goals" and have that mean something to the agent beyond text it was told.
Relations are backlinks, great for a human reading a graph view, but not a typed edge
a system can query and act on. And it is local, one developer, one machine.
What if you took those same ideas and built them with enforcement instead of convention?
Typed content means the agent knows what fields exist and what they accept. Explicit
state machines mean invalid transitions are rejected at the protocol level: an agent
cannot publish a draft that has not been through review, because the framework refuses
the transition, not because the prompt told it not to. Typed relations between items
form a persistent edge graph the agent can query: what depends on this item, what
decisions are related to this goal, what jobs produced this output.
The access layer is MCP. The agent calls tools, not endpoints. The tools carry the
shape and the constraints.
A few things this is not. Graphiti (from Zep) is a temporal knowledge graph that extracts entities and relations from conversational episodes using an LLM. Good for agent memory derived from unstructured input, built on Neo4j. LangGraph orchestrates how an agent reasons, not what it operates on. New MCP servers expose APIs, they do not enforce state machines.
The distinction that matters: Graphiti derives structure. Smeldr enforces it. If you need to extract a graph from past conversations, Graphiti is the right tool. If you need a typed, stateful layer that an agent operates through, with transitions that are rejected at the protocol level rather than in a prompt, that is a different problem.
These are complements for most systems, not competitors.
This is where the use cases go beyond content management.
Relations and edges can model "this article cites this source," but also "this decision
blocks this goal" or "this agent job produced this output." The same primitives that
power a content graph can power orchestration, memory, context management. An agent
that starts a session can query the graph for active goals, related constraints, and
decisions that are still live. It works within a structure that was built for it to
reason over, not a structure it has to interpret from prose.
CMSes touched this space and stopped at the browser. Karpathy's wiki takes it further
but stays in flat files. The next step is typed edges with enforced lifecycle, queryable
by agents through a protocol designed for them.
That is what I am building with Smeldr: an open source Go framework where content
types have explicit lifecycle states, relations between items are typed edges in a
persistent graph, and agents interact through MCP tools that enforce the rules rather
than expose raw data.
It started as a content backend. The further I get into it, the more it looks like
something more general: a typed, stateful graph that agents can safely operate a whole
system through.
The CMS people were onto something. Karpathy is onto something. I think the next piece
is putting enforcement and typed relations underneath it.
Smeldr is open source, written in Go, no build pipeline required.
Top comments (0)