You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Part 2 of 2 of the runtime-decoupling work from docs/design/034-technical-debt-review.md (§3.2, §3.3) — builds on #130 (dyn-safe RuntimeOps + builder de-erasure). This completes the program started in designs 028/029: Producer<T>/Consumer<T> are already R-free; this issue removes R from the rest of the object graph and deletes the dyn Any plumbing that existed only to escape it.
Breaking changes are explicitly fine — desired, even. The aim is a clean structure. No compatibility shims, no phantom-parameter aliases, no deprecation cycles. Downstream code (examples, tools, connectors) is updated mechanically in the same change series.
Target state
The runtime is a value with a capability interface (Arc<dyn RuntimeOps> from #130), not a type parameter. The only generic parameter left on the user-facing object graph is T — the record types, where static typing actually earns its keep.
Tasks
1. Non-generic RuntimeContext
RuntimeContext becomes a concrete struct wrapping Arc<dyn RuntimeOps>; delete the duplicated std/no_std impl blocks and the panicking extract_from_any.
Known observable break: ctx.time().now() returns u64 nanos instead of R::Instant (e.g. embassy_time::Instant). Call sites that store instants switch to nanos or unix_time().
2. Strip R from the record graph
TypedRecord<T, R> → TypedRecord<T>; stored service closures take (Producer<T>/Consumer<T>, RuntimeContext) instead of Arc<dyn Any + Send + Sync>.
RecordRegistrar<'a, T, R> → RecordRegistrar<'a, T>; source()/tap() become inherent methods (no more per-adapter downcasting wrappers).
ConnectorBuilder<R> → ConnectorBuilder (all in-tree connectors already implement it blanket-for-all-R and never use R).
Consumer/producer factories take &AimDb instead of Arc<dyn core::any::Any> + downcast-or-panic.
Delete the RuntimeForProfiling marker-trait workaround in lib.rs (profiling calls now_nanos()).
4. Delete JoinFanInRuntime
Replace the GAT-based JoinFanInRuntime/JoinQueue/JoinSender/JoinReceiver family and the three per-adapter join_queue.rs files with one bounded async-channel queue in core — the same primitive the session engine already uses on tokio, Embassy, and WASM.
Open decision — .buffer(BufferCfg): buffer construction is the one genuinely adapter-specific piece left. Options:
(a) keep one tiny per-adapter extension trait for .buffer() only, or
(b) have the builder take a buffer factory at .runtime() time, or
(c) runtime-neutral buffer impls in core (largest win — deletes ~2.5k adapter lines — but SPMC-ring lag semantics need a hand-rolled implementation; likely a follow-up issue).
Recommendation: (a) or (b) here, (c) as follow-up.
6. Data plane: erase to bytes, not dyn Any (stretch goal — may split out)
Outbound: fuse the serializer into the typed side so connectors receive (topic, Payload) already serialized — deletes AnyReader::recv_any + SerializerFn(&dyn Any) downcasts per message.
Inbound: store Fn(&[u8]) -> Result<(), Error> closures that deserialize and produce in one typed step — deletes ProducerTrait::produce_any.
Acceptance criteria
No core::any import remains in aimdb-core outside TypeId-based registry lookup and DynBuffer::as_any
Context
Part 2 of 2 of the runtime-decoupling work from
docs/design/034-technical-debt-review.md(§3.2, §3.3) — builds on #130 (dyn-safeRuntimeOps+ builder de-erasure). This completes the program started in designs 028/029:Producer<T>/Consumer<T>are alreadyR-free; this issue removesRfrom the rest of the object graph and deletes thedyn Anyplumbing that existed only to escape it.Breaking changes are explicitly fine — desired, even. The aim is a clean structure. No compatibility shims, no phantom-parameter aliases, no deprecation cycles. Downstream code (examples, tools, connectors) is updated mechanically in the same change series.
Target state
The runtime is a value with a capability interface (
Arc<dyn RuntimeOps>from #130), not a type parameter. The only generic parameter left on the user-facing object graph isT— the record types, where static typing actually earns its keep.Tasks
1. Non-generic
RuntimeContextRuntimeContextbecomes a concrete struct wrappingArc<dyn RuntimeOps>; delete the duplicated std/no_std impl blocks and the panickingextract_from_any.ctx.time().now()returnsu64nanos instead ofR::Instant(e.g.embassy_time::Instant). Call sites that store instants switch to nanos orunix_time().2. Strip
Rfrom the record graphTypedRecord<T, R>→TypedRecord<T>; stored service closures take(Producer<T>/Consumer<T>, RuntimeContext)instead ofArc<dyn Any + Send + Sync>.RecordRegistrar<'a, T, R>→RecordRegistrar<'a, T>;source()/tap()become inherent methods (no more per-adapter downcasting wrappers).TransformDescriptor<T, R>→TransformDescriptor<T>; build fns receiveArc<dyn RuntimeOps>+Arc<AimDb>.3. Strip
Rfrom the database and connectorsAimDb<R>→AimDb; deleteruntime_any();runtime_arc()returnsArc<dyn RuntimeOps>.ConnectorBuilder<R>→ConnectorBuilder(all in-tree connectors already implement it blanket-for-all-Rand never useR).&AimDbinstead ofArc<dyn core::any::Any>+ downcast-or-panic.RuntimeForProfilingmarker-trait workaround inlib.rs(profiling callsnow_nanos()).4. Delete
JoinFanInRuntimeJoinFanInRuntime/JoinQueue/JoinSender/JoinReceiverfamily and the three per-adapterjoin_queue.rsfiles with one boundedasync-channelqueue in core — the same primitive the session engine already uses on tokio, Embassy, and WASM.5. Dissolve
ext_macros.rsRuntimeContextnon-generic and the lifetime fix from refactor(executor/core): dyn-safe RuntimeOps trait + de-erase builder internals & registrar lifetimes (R-removal part 1) #130,TokioRecordRegistrarExt/EmbassyRecordRegistrarExt/wasm equivalents lose their reason to exist. Primary user API becomes plain, rustdoc-visible inherent methods..buffer(BufferCfg): buffer construction is the one genuinely adapter-specific piece left. Options:(a) keep one tiny per-adapter extension trait for
.buffer()only, or(b) have the builder take a buffer factory at
.runtime()time, or(c) runtime-neutral buffer impls in core (largest win — deletes ~2.5k adapter lines — but SPMC-ring lag semantics need a hand-rolled implementation; likely a follow-up issue).
Recommendation: (a) or (b) here, (c) as follow-up.
6. Data plane: erase to bytes, not
dyn Any(stretch goal — may split out)(topic, Payload)already serialized — deletesAnyReader::recv_any+SerializerFn(&dyn Any)downcasts per message.Fn(&[u8]) -> Result<(), Error>closures that deserialize and produce in one typed step — deletesProducerTrait::produce_any.Acceptance criteria
core::anyimport remains inaimdb-coreoutsideTypeId-based registry lookup andDynBuffer::as_anygrep -rn "extract_from_any\|runtime_any\|RuntimeForProfiling\|JoinFanInRuntime" aimdb-*/srcreturns nothingR/runtime type parameters appear in no public core type except viaArc<dyn RuntimeOps>ext_macros.rsdeleted; adapter crates reduced to executor glue +RuntimeOpsimpl (+ buffer wiring per the §5 decision)make check,make examples)AimDb<R>→AimDb,RuntimeContextsignature,now()→ nanos, ext-trait removal