Blog
Designing eva-core as Eva-CLI's Contract Layer
Why eva-core should be the first implementation module: stable Topics, IDs, Events, invocation contracts, and structured errors before runtime behavior spreads across crates.
eva-core is the right first implementation module because it defines the contracts every other crate will depend on. Before Eva-CLI grows EventBus delivery, Scheduler routing, Lua Agent execution, Adapter transports, memory, hardware, and runtime generation switching, it needs a small shared vocabulary that does not perform side effects.
The point is not to make eva-core powerful. The point is to make it boring, stable, and hard to misuse: Topics are validated before routing, IDs cannot be mixed accidentally, Events have trace linkage, invoke requests have predictable shapes, and errors carry structured fields instead of loose strings.
eva-core should stay at the contract boundary: shared by everyone, owned by no runtime side effect.The first module should reduce future rewrites
Starting with a transport or an Agent runtime can feel more visible, but those modules need common types immediately. If each crate invents its own Topic strings, request identifiers, payload wrappers, and error categories, the project will spend its second implementation pass deleting duplicate contracts.
eva-core prevents that by fixing the lowest-level shapes first. It should not decide policy, execute Lua, call providers, read configuration files, or publish events. It should only make the data moving through those modules explicit and testable.
| Contract | Why it belongs in eva-core |
Who uses it first |
|---|---|---|
| Topic | Routing, subscriptions, emitted topics, and policy checks all need the same validation rules. | eva-scheduler, eva-policy, eva-config |
| ID newtypes | Agent, Adapter, Capability, Event, and Request identifiers should not be interchangeable strings. | All runtime crates |
| Event | EventBus, Scheduler, AgentRuntime, audit, and recovery all need one event envelope. | eva-eventbus, eva-scheduler, eva-agent |
| Invoke | Agent and capability calls need a stable request/response boundary before transports exist. | eva-agent, eva-capability, eva-adapter |
| Error | Cross-crate failures need kind, message, retryability, provider code, and trace linkage. | Every crate boundary |
Keep the crate deliberately narrow
A good eva-core design is defined as much by what it rejects as by what it owns. It should not load YAML, evaluate permissions, persist events, run Lua, own Adapter manifests, speak MCP, or manage lifecycle generations. Those are runtime responsibilities. Putting them in eva-core would make the base layer too heavy and eventually create dependency cycles.
eva-core
-> Topic, TopicPattern
-> AgentId, AdapterId, CapabilityName, RequestId, EventId
-> Event, EventTarget, trace linkage fields
-> AgentInvokeRequest, CapabilityInvokeRequest, InvokeResponse
-> EvaError, ErrorKind, retryable, provider_code
A practical first milestone
The first milestone should be small enough to finish and strict enough to unblock the next modules. Topic parsing and pattern validation should come first because configuration, routing, permissions, and Agent emission all depend on it. ID newtypes come next because they prevent later APIs from accepting arbitrary strings. Events and invoke contracts then give EventBus, Scheduler, AgentRuntime, and AdapterRouter a shared surface.
| Priority | Deliverable | Acceptance check |
|---|---|---|
| 1 | Topic and TopicPattern |
Accepts /input/user, rejects empty segments, restricts ** to the final segment. |
| 2 | ID newtypes | Agent IDs, Adapter IDs, and Request IDs cannot be passed to the wrong API by accident. |
| 3 | Event |
Includes event_id, topic, payload, optional target, and trace linkage fields. |
| 4 | Invoke request/response | Agent and capability calls have one stable shape before transports are implemented. |
| 5 | Structured errors | Every cross-module error includes category, message, retryability, and optional provider context. |
What it unlocks next
Once eva-core is stable, eva-config can parse YAML into strongly typed references, eva-eventbus can publish a real Event, eva-scheduler can match Topics without string ad hoc logic, and eva-agent can receive a clear event envelope. That is the smallest foundation that makes the rest of Eva-CLI implementation feel incremental instead of speculative.
The detailed module document is available in eva-core Module Design.