nexum_sdk::config covers lookup and parsing (get_required, get_optional, ConfigError, fixed-point helpers) but not storage: every module that parses [config] in init and reads it in later dispatches hand-rolls the same static RwLock<Option<T>>, a store_config, a typed not-initialised refusal, and a test-only guard/clear pair. Two consumers already duplicate it verbatim (cow-venue's AdapterConfig, twap-monitor's KeeperConfig) and every future configured module adds another copy.
Why
The supervisor's lifecycle makes the pattern universal: call_init runs with the manifest config on every (re)instantiation against a fresh Store, so instance-memory storage re-seeded by init is the correct home for operator wiring, and each module reinventing the slot invites drift (poison handling, refusal type, test-isolation shape). The lookup half already lives in the SDK; the storage half should sit beside it.
Scope
config::Slot<T> (name open): const fn new, store(T), get() -> Result<T, ConfigError> (or a dedicated not-initialised variant), poison-tolerant via PoisonError::into_inner.
cfg(test) helpers: clear and a serialising guard, so refusal tests are runner-independent (nextest process-per-test and plain libtest both pass).
- Rustdoc states the lifecycle contract:
init re-seeds on every (re)instantiation; the slot is instance memory, never persisted.
- Migrate consumers to
get_required/ConfigError opportunistically when they adopt the slot; not part of this change.
Done when
nexum_sdk::configcovers lookup and parsing (get_required,get_optional,ConfigError, fixed-point helpers) but not storage: every module that parses[config]ininitand reads it in later dispatches hand-rolls the samestatic RwLock<Option<T>>, astore_config, a typed not-initialised refusal, and a test-only guard/clear pair. Two consumers already duplicate it verbatim (cow-venue'sAdapterConfig, twap-monitor'sKeeperConfig) and every future configured module adds another copy.Why
The supervisor's lifecycle makes the pattern universal:
call_initruns with the manifest config on every (re)instantiation against a freshStore, so instance-memory storage re-seeded byinitis the correct home for operator wiring, and each module reinventing the slot invites drift (poison handling, refusal type, test-isolation shape). The lookup half already lives in the SDK; the storage half should sit beside it.Scope
config::Slot<T>(name open):const fn new,store(T),get() -> Result<T, ConfigError>(or a dedicated not-initialised variant), poison-tolerant viaPoisonError::into_inner.cfg(test)helpers:clearand a serialising guard, so refusal tests are runner-independent (nextest process-per-test and plain libtest both pass).initre-seeds on every (re)instantiation; the slot is instance memory, never persisted.get_required/ConfigErroropportunistically when they adopt the slot; not part of this change.Done when
Faultboundary conversion.