# 106 — Give API handlers a real way to address the primary vs. the profile actor ## Summary By the time [[104-move-metric-computation-to-profile-actor]] and [[105-move-alert-evaluation-to-profile-actor]] land, metric/alert messages need to route to the profile actor while param/log messages stay on the primary — today's single `EngineHandle` (one `mpsc::Sender` + one `IndexReader`) can't express that. This ticket gives `api.rs`'s handlers, and the runtime profile-switch path, a real API surface for both actors, per [[0001-actor-split]]'s resolved decision to decide during implementation whether that's two handle types or one internally-fanning-out handle. ## Where - `src/engine/handle.rs:74-78` (`EngineHandle { tx, reader }`) — the single handle type `ApiState` holds today. - `src/api/mod.rs` — `ApiState`'s definition (wherever it stores the `EngineHandle`) and every handler that sends an `EngineMessage`; metric/alert-related routes need to target the profile actor's channel instead of the primary's. - `src/api/mod.rs:812-852` (`reload_engine_declarations`, `activate_profile`) — today removes/redeclares params *and* metrics against one engine. After this split, param removal/redeclare stays primary-side, metric removal/redeclare targets the profile actor — this function needs to send to both. - `src/config/profile.rs`'s `load_into_engine` (referenced in [[0004-profile-switch-reindexing-cost]] at `:332-384`) — same dual-target concern at startup load time as well as runtime reactivation. - `src/api/mcp_server.rs` — check whether the MCP server's query surface holds its own `EngineHandle` reference that also needs the updated shape. ## Scope - Decide and implement the concrete shape: either (a) `ApiState` holds two handles (`primary: EngineHandle`, `profile: ProfileHandle`) and each handler picks the right one, or (b) one combined handle type that internally holds both channels/readers and routes by message kind. Document the choice in the PR description since [[0001-actor-split]] deliberately left it open. - Update every `api.rs` handler that currently sends a `DeclareMetric`/`UpdateMetric`/`RemoveMetricDeclaration` or alert- related message to target the profile actor; param-related messages (`DeclareParam`/`RemoveParamDeclaration`/`ClearParams`/ `SetMaxLogs`/`EnsureSortHelperIndex`) stay on the primary. - Update `reload_engine_declarations`/`activate_profile` and `Profile::load_into_engine` to split their remove-then-redeclare sequence across both actors correctly, preserving today's behavior (still fully synchronous from the caller's perspective — this ticket doesn't change the blocking nature of profile switching, only which actor(s) the messages go to; see [[0004-profile-switch-reindexing-cost]] for the separate question of making that async). - Out of scope: the query fan-out layer for requests needing both raw logs and profile-derived data in one response — that's [[107-query-fan-out-across-primary-and-profile]]. Read-only query handlers can be touched here only insofar as they need updating to compile against the new handle shape, not to actually merge two data sources yet.