Skip to content

refactor(executor/core): dyn-safe RuntimeOps + de-erased builder & registrar lifetimes (034 Phase 2, #130)#137

Merged
lxsaah merged 4 commits into
mainfrom
refactor/034-phase2-130-runtime-ops
Jun 10, 2026
Merged

refactor(executor/core): dyn-safe RuntimeOps + de-erased builder & registrar lifetimes (034 Phase 2, #130)#137
lxsaah merged 4 commits into
mainfrom
refactor/034-phase2-130-runtime-ops

Conversation

@lxsaah

@lxsaah lxsaah commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Closes #130. Part of design 034 Phase 2 (review doc §3.2/§3.5); first of three stacked PRs (#130#133#134).

What

1. Dyn-safe RuntimeOps capability trait (aimdb-executor) — groundwork for #131, not yet consumed by core.

  • name() / now_nanos() (monotonic, arbitrary epoch) / unix_time() / boxed sleep(core::time::Duration) / log(LogLevel, &str).
  • Implemented for TokioAdapter (OnceLock<Instant> anchor), EmbassyAdapter (boot-anchored uptime, µs granularity), WasmAdapter (Performance.now(), wall clock from Date.now()).
  • Per-adapter impls are intentional: a blanket impl over TimeOps is impossible (now_nanos needs an epoch anchor; TimeOps::Instant is opaque).
  • Shared behavioral contract test (aimdb_executor::test_support) run by all three adapters under their own executors. The embassy host time-driver stub now wakes immediately on schedule_wake so Duration::ZERO sleeps complete (host clock is pinned at 0).
  • BoxFuture's canonical definition moves to aimdb-executor; aimdb-core re-exports it (same type).

2. De-erased builder internals (aimdb-core/src/builder.rs) — no behavior change.

  • spawn_fns/start_fns stored as Vec<(StringKey, SpawnFnType<R>)> / Vec<StartFnType<R>> instead of Box<dyn Any + Send>; both panicking downcasts in build() deleted.
  • AimDb<R>'s struct-level R: RuntimeAdapter + 'static bound moved to its impls (strictly more permissive) so the field types are well-formed on the NoRuntime typestate, where both vectors are provably empty.

3. Registrar lifetime fix (aimdb-core/src/typed_api.rs) — breaking for code that names the old signatures; closure-based configuration compiles unchanged.

  • All fluent methods: &'a mut self -> &'a mut Self&mut self -> &mut Self. A configure closure can now write reg.source_raw(…); reg.tap_raw(…); as separate statements and reuse the registrar after a chain.
  • configure's bound drops the HRTB: FnOnce(&mut RecordRegistrar<'_, T, R>).
  • OutboundConnectorBuilder/InboundConnectorBuilder: double-'a split into <'r, 'a, T, R>; finish() returns &'r mut RecordRegistrar<'a, T, R> (chains still work).
  • RecordT::register, impl_record_registrar_ext!, EmbassyRecordRegistrarExtCustom, and RecordRegistrarPersistExt follow.

Acceptance criteria (from #130)

  • Arc<dyn RuntimeOps> constructible from all three adapters; shared behavioral test
  • No Box<dyn Any> fields in AimDbBuilder; no downcasts in build()
  • reg.source_raw(…); reg.tap_raw(…); as separate statements (new registrar_allows_separate_statements test; multiple_link_from_allowed extended)
  • No behavior change: make check green (fmt, clippy matrix, full test matrix, embedded thumbv7em, wasm, deny)

Notes for reviewers

🤖 Generated with Claude Code

lxsaah and others added 4 commits June 10, 2026 04:00
Adds the object-safe counterpart to the RuntimeAdapter/TimeOps/Logger
family: concrete time types (u64 nanos, core::time::Duration), a boxed
sleep future, and LogLevel-based logging, so a runtime can travel as
Arc<dyn RuntimeOps> instead of a type parameter. Implemented by all
three adapters with a shared behavioral contract test; per-adapter
impls are intentional (now_nanos needs an epoch anchor that the opaque
TimeOps::Instant cannot provide).

BoxFuture's canonical definition moves to aimdb-executor; aimdb-core
re-exports it. The embassy host-test time driver now wakes immediately
on schedule_wake so already-expired timers (Duration::ZERO sleeps)
complete on the pinned-at-0 host clock.

Groundwork only: core does not consume RuntimeOps yet (issue #131).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…130)

AimDbBuilder<R> is already generic over R, and configure()/on_start()
are only callable once .runtime() fixes R — the Box<dyn Any + Send>
round-trip through spawn_fns/start_fns bought nothing and cost two
panicking downcasts in build(). Store SpawnFnType<R>/StartFnType<R>
directly and delete the downcasts.

The struct-level bound on AimDb<R> moves to its impls so the field
types stay well-formed for the builder's NoRuntime typestate (where
both vectors are provably empty). PhantomData field dropped.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
)

RecordRegistrar methods took &'a mut self and returned &'a mut Self
with 'a = the struct's own lifetime parameter — the classic fluent-API
lifetime bug: the first call borrowed the registrar for its entire
remaining lifetime, forcing one unbroken chain per configure closure
(the old test admitted as much).

- All fluent methods now take &mut self -> &mut Self (fresh borrows);
  separate statements like 'reg.source_raw(…); reg.tap_raw(…);' work.
- configure()'s closure bound drops the HRTB:
  FnOnce(&mut RecordRegistrar<'_, T, R>).
- Outbound/InboundConnectorBuilder split the double-'a into <'r, 'a>:
  'r borrows the registrar, 'a is the registrar's record borrow;
  finish() returns &'r mut RecordRegistrar<'a, T, R> so chains still
  work. RecordT::register signature updated accordingly.
- ext_macros and the hand-written extension traits
  (EmbassyRecordRegistrarExtCustom, RecordRegistrarPersistExt) follow.

BREAKING: trait signatures changed for RecordT::register and the
extension traits; the connector builders gained a lifetime parameter.
Existing closure-based user code compiles unchanged (the new bounds
are strictly more permissive).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@lxsaah lxsaah merged commit bc04541 into main Jun 10, 2026
5 checks passed
@lxsaah lxsaah deleted the refactor/034-phase2-130-runtime-ops branch June 10, 2026 04:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

refactor(executor/core): dyn-safe RuntimeOps trait + de-erase builder internals & registrar lifetimes (R-removal part 1)

1 participant