The AI era needs typed data, states, and relations. CMSes almost got there first.
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.