Blog

Keeping Topic Routing Explicit with Hybrid Sync

Why Eva-CLI should keep config/routes/topics.yaml as the production route source of truth while using agent.yaml to generate route proposals, validation reports, and explicit sync patches.

Published: Category: Runtime

Topic routing is one of the places where an Agent runtime can accidentally become hard to audit. If every Agent manifest can silently reshape the global delivery table, a harmless-looking subscription change can widen the event surface. If every route is handwritten with no tooling, Agent creation becomes repetitive and easy to miss.

Eva-CLI should use a hybrid model: keep config/routes/topics.yaml as the production routing source of truth, but let tools read config/agents/**/agent.yaml to generate route proposals, consistency reports, and explicit write-back patches.

Topic routing hybrid sync diagram showing Agent manifests, route proposals, validation gate, explicit write step, production topics.yaml, generated diagnostics, and Scheduler RouteTable.
The important boundary is that runtime reads and validates; only explicit tooling writes the production route file.

Two files, two different jobs

agent.yaml is still the right place for an Agent to describe itself: identity, parent intent, subscriptions, child route hints, and emitted Topic permissions. But those declarations do not automatically become Scheduler delivery authority. The Scheduler should load a validated route table from config/routes/topics.yaml.

File Owns Runtime rule
config/agents/**/agent.yaml Agent identity, subscriptions, children, local route hints, and permissions.emit. Input to discovery, policy checks, mailbox registration, and route proposal generation.
config/routes/topics.yaml Global Topic pattern matching, delivery mode, target Agents, and match order. Production source of truth for Scheduler delivery.
.eva/generated/routes/topics.generated.yaml Generated suggestions inferred from Agent manifests. Diagnostic artifact only; not a production route table.
.eva/reports/config/routes-diff.json Conflicts, missing routes, orphan targets, and suggested fixes. Evidence for review, CI, and IDE diagnostics.

The command surface should make writes explicit

The safest user experience is to make every write visible. Validation and diff generation can run automatically in CI or an IDE. Updating topics.yaml should require a command or quick-fix that the user intentionally invokes.

Command Behavior Writes files
eva config validate Validate schemas, Agent references, Topic patterns, permissions, and route consistency. No
eva config routes sync --check Generate proposals from Agent manifests and compare them with topics.yaml. No
eva config routes preview Show the merged route table plus conflicts before changing source files. No
eva config routes sync --write Write safe, explainable, conflict-free proposals back to topics.yaml. Yes
eva config routes dump-effective Show the RouteTable actually used by Scheduler after validation. No

Why not generate everything dynamically?

Automatic generation is attractive for simple fanout subscriptions, but production routing has more semantics than "who subscribed to this string." Delivery mode, priority, wildcard shadowing, fallback behavior, and target override rules are global runtime decisions. Those decisions belong in a reviewed route table.

Approach Good at Weak spot
Fully handwritten routes Audit, rollback, complex matching rules, security review. Easy to forget updates when adding or renaming Agents.
Fully dynamic generation Fast Agent scaffolding and simple subscription coverage. Ambiguous source of truth and unsafe expansion when manifests are wrong.
Hybrid sync Explicit production routing with machine-generated drift detection. Requires good validation rules and careful write-back behavior.

Validation is the real product

The hybrid model only works if validation is strict enough to catch drift. A useful validator should flag route targets that do not exist, disabled Agents, delivery conflicts, subscriptions that never receive events, global routes that target Agents outside their declared subscriptions, and emitted Topics outside permissions.emit.

agent.yaml
  -> scan manifests
  -> build route proposals
  -> compare with topics.yaml
  -> report missing, orphaned, conflicting, or unsafe routes
  -> write only through explicit sync command

This keeps the runtime startup path small: parse topics.yaml, validate schemas and policy, then build a new Scheduler RouteTable generation. Startup may read Agent manifests for cross-checking, but it must not silently mutate the route file.

The full specification is available in Topic Routing Hybrid Sync, with related configuration context in Project Configuration.