ADR 0003 — DataBuffer<T> and DataRegistry in core¶
Status: Accepted
Sprint: 13 (landed as DataRing<T>); revised same release to DataBuffer<T> (ring concept removed — see note below)
Date: 2026-05-14
Context¶
FrameRing (Sprint 7) and PixelRegistry (Sprint 6) live in src/modules/lights/. Sprint 13 replaces both with generic types. The question is whether those generic types belong in src/core/ or stay in src/modules/.
The core boundary from architecture/system.md:
In core:
Module,ModuleManager,Scheduler,Pal(timing / GPIO / fs / rtos primitives / heap query — nothing more).
Not in core: networking, HTTP / WebSocket / REST, OTA, NTP, state persistence, lighting.
Decision¶
DataBuffer<T> and DataRegistry are placed in src/core/.
Justification:
DataBuffer<T>is a concurrency primitive — a single-slot SPSC buffer with acquire/release memory ordering. It is as domain-neutral asstd::atomic. It belongs alongsideSchedulerwhich already manages cross-core task dispatch.DataRegistryis a typed singleton store keyed by string id. It is structurally identical toModuleManager(owns resolution by name) and belongs in the same layer.- Neither type references
RGB, pixel geometry, lighting, networking, or any other domain. The template parameterTis supplied by the leaf module (RipplesEffectusesDataBuffer<RGB>); core never seesRGB. - The alternative — keep them in
src/modules/lights/— would mean a future audio or sensor module wanting the same SPSC pattern would either duplicate the buffer or take a dependency on the lights module. That is the v1 drift pattern.
Note on ring depth. The original Sprint 13 design used a depth-configurable ring (DataRing<T>, allocate(count, depth)). This was removed in the same release: a ring with depth > 1 within one module is overdesign — the double-buffer boundary belongs between two separate modules (producer owns one DataBuffer<T> slot, consumer owns its own DataBuffer<T> slot). The file was renamed DataBuffer.h and the depth parameter dropped. See architecture — hot-path data sharing and backend — data sharing for the rationale.
Consequences¶
src/core/gains two new files:DataBuffer.handDataRegistry.h.src/modules/lights/FrameRing.handPixelRegistry.hare deleted.check_loc.pycore budget must be bumped to account for the two new files. The bump is explicit and PR-visible, consistent with the minimalism rule.- Future non-lights modules that need SPSC producer/consumer buffers use
DataBuffer<T>directly from core — no cross-domain dependency.