All notable changes to the MADSci framework are documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[0.7.1] - 2026-03-10¶
Added¶
Database Handler Abstractions¶
New
madsci.common.db_handlerspackage with abstract base classes (MongoHandler,RedisHandler,PostgresHandler,MinioHandler) and both real and in-memory implementationsReal implementations:
PyMongoHandler,PyRedisHandler,SQLAlchemyHandler,RealMinioHandlerIn-memory implementations:
InMemoryMongoHandler,InMemoryRedisHandler,SQLiteHandler,InMemoryMinioHandlerAll 6 database-backed managers now accept optional handler constructor parameters (
mongo_handler,minio_handler,redis_handler,postgres_handler), enabling dependency injection for testingLocalRunnerupdated to use handler abstractions instead of raw in-memory clientsInMemoryCollectiongained projection support,replace_one(),clientproperty, andlist_collection_names()
Node Registry Resolution¶
Nodes now resolve stable IDs from the ID Registry at startup, matching the manager pattern
Added
enable_registry_resolution,registry_lock_timeout, andlab_urlfields toNodeConfigAbstractNode.__init__()callsIdentityResolver.resolve_with_info()to look up or create a stablenode_idatexithandler releases the registry lock on node shutdown for graceful handoffNodes that fail registry resolution fall back to a generated ULID (non-fatal) unless lock contention exhausts the retry window (fatal
RegistryLockError)
Node Lifecycle Management¶
AbstractNode.close()method for explicit registry lock cleanup, recommended for notebook users who reassign node variablesAbstractNode.__del__()andAbstractManagerBase.__del__()for GC-based identity release as a fallback
Changed¶
EventClient Retry Removal¶
Removed the async retry queue from
EventClient(background thread,_event_buffer,_retrying,_shutdownstate, OTEL buffer-size gauge and retry counter metrics)Event delivery is now synchronous and fire-once; callers should handle failures explicitly
Added
madsci.eventclient.send_failuresOTEL counter and upgraded failure logging fromwarningtoerrorwith structured kwargs (event_type,event_id)
DataManager MinIO Handler Consolidation¶
All MinIO operations now routed through
MinioHandlerabstraction; removed directself.minio_clientusage_setup_object_storage()wraps legacyMinioclients inRealMinioHandler(same pattern as other managers wrapping raw connections)
Legacy Constructor Parameter Deprecation¶
Legacy database connection parameters (
db_connection,db_client,redis_connection,mongo_connection) now emitDeprecationWarningacross all 6 managers and 2 state handlers
OpenTelemetry Logging Migration¶
Migrated from deprecated
opentelemetry.sdk._logs.LoggingHandlertoLoggingInstrumentorfrom theopentelemetry-instrumentation-loggingpackageAdded
opentelemetry-instrumentation-loggingas a dependency ofmadsci_commonEliminates 44+ deprecation warnings from the OTEL SDK
Test Infrastructure¶
Isolated test suite from shared registry via root
conftest.pythat patchesenable_registry_resolutiondefaults toFalseand redirectsMADSCI_REGISTRY_PATHto a temp fileReplaced
nbconvertwithpapermillfor notebook validation; CI workflow renamed frome2e_teststovalidate_notebooksTestcontainer fixtures now skip gracefully (
pytest.skip()) when Docker containers are unavailable instead of hard-failingAdded
PortWaitStrategyto fix testcontainer host port-forwarding race condition on macOS/Rancher DesktopRemoved
pytest-mock-resourcesdependency; all tests use in-memory database handlers (no Docker required for unit/integration tests)
Fixed¶
Weakref atexit Handlers¶
Manager and node
atexithandlers now useweakref.refto avoid preventing GC of discarded instances (e.g. in notebook scenarios)Added
_atexit_registeredguard to prevent accumulating duplicate handlers on repeated calls
LoggingInstrumentor Double-Instrumentation Guard¶
configure_otel()now checksis_instrumented_by_opentelemetrybefore callingLoggingInstrumentor().instrument(), preventing duplicate instrumentation when called multiple times (e.g. in test suites)
EventManager Lazy pymongo Imports¶
Moved top-level
import pymongoandfrom pymongo import errorsbehindTYPE_CHECKINGguard and into methods, allowing the module to be imported without pymongo installed (in-memory-only usage)
InMemoryMongoHandler _client Safety¶
InMemoryMongoHandler.__init__now setsself._client = Nonewhen an externaldatabaseis provided, preventingAttributeErroron_clientaccess
Handler ABC Return Type Annotations¶
Improved return type annotations on handler ABC methods:
MongoHandler.get_collection() -> Collection | Any,RedisHandler.create_dict() -> MutableMapping,RedisHandler.create_lock() -> ContextManager,PostgresHandler.get_engine() -> Engine | Any
Manager Registry Lock Retry + Shutdown Release¶
AbstractManagerBase._resolve_identity_from_registry()now retries lock acquisition forregistry_lock_timeoutseconds (default 60s) before raising, surviving ungraceful container restarts where the previous lock hasn’t expired yetAdded
registry_lock_timeoutfield toManagerSettings(default 60.0s, should be at least 2x the lock TTL of 30s)RegistryLockErroris now fatal (re-raised) — managers cannot start without a stable identityatexit.register(self.release_identity)ensures the registry lock is released on process exitIdentityResolver.resolve()andresolve_with_info()now accept aretry_timeoutparameter, passed through toLocalRegistryManager.resolve()LocalRegistryManager.resolve()implements retry loop: onRegistryLockError, retries every 2s untilretry_timeoutelapses
Other Fixes¶
Fixed timezone-naive datetime comparison bug in
ResourceInterfacelock checksFixed
DatabaseVersionCheckerto only createSchemaVersionTable(not all tables) during version checks
[0.7.0] - 2026-03-04¶
Added¶
.madsci/ Sentry Directory¶
Canonical
madsci.common.sentrymodule for all.madsci/directory path resolutionWalk-up resolution:
.madsci/sentinel ->.git/boundary ->~/.madsci/fallbackmadsci initscaffolds.madsci/with standard subdirs andregistry.json.git/now acts as a walk-up boundary for settings file discovery
Settings Directory with Walk-Up Discovery¶
_settings_dirkeyword argument on allMadsciBaseSettingssubclasses for overriding the walk-up starting directoryMADSCI_SETTINGS_DIRenvironment variable for overriding the walk-up starting directory--settings-dirCLI option onmadsci start,madsci start manager,madsci start node, andmadsci config exportWalk-up resolves each filename independently: shared
settings.yamlin a parent dir coexists withnode.settings.yamlin a child dirWalk-up discovery is always active from CWD by default;
_settings_dirandMADSCI_SETTINGS_DIRoverride the starting directory for walk-up, not whether walk-up is used
CLI (17 commands)¶
madsci init- Interactive lab initialization wizardmadsci new- Component scaffolding from templates (module, interface, node, experiment, workflow, workcell, lab subcommands)madsci start- Start lab services (Docker Compose or local mode)madsci start manager <name>- Start a single manager as a subprocessmadsci start node <path>- Start a node module as a subprocess--wait/--no-waitflag for health polling after detached start
madsci stop- Stop lab servicesmadsci stop manager <name>- Stop a background manager processmadsci stop node <name>- Stop a background node process
madsci status- Service health checking with--watchand--jsonsupportmadsci doctor- Environment diagnostic checks (python, docker, ports)madsci logs- Event log viewing with--follow,--level,--grep,--sincefiltersmadsci run- Workflow and experiment executionmadsci validate- Configuration and definition file validationmadsci config- Configuration management (export, create)madsci backup- Database backup creation (PostgreSQL and MongoDB)madsci registry- Service registry managementmadsci migrate- Database migration toolingmadsci tui- Interactive terminal user interfacemadsci completion- Shell completion generation (bash, zsh, fish)madsci commands- Command listingmadsci version- Version displayCommand aliases:
n,s,l,doc,val,ui,cmd,cfgLazy command loading for fast CLI startup
Templates (26 templates)¶
6 module templates: basic, device, instrument, liquid_handler, camera, robot_arm
4 interface templates: fake, real, sim, mock
1 node template: basic
4 experiment templates: script, notebook, tui, node
2 workflow templates: basic, multi_step
1 workcell template: basic
3 lab templates: minimal, standard, distributed
5 communication templates: serial, socket, rest, sdk, modbus
Template engine with Jinja2, parameter validation, conditional files, and post-generation hooks
Template registry with bundled, user, and remote template sources
TUI (Terminal User Interface)¶
Dashboard screen with service overview and quick actions
Status screen with detailed service health and auto-refresh
Logs screen with filterable log viewer
Nodes screen with node status and management
Workflows screen with workflow monitoring and control
CSS theming with light/dark mode support
Trogon integration for CLI-to-TUI command exploration
Local Mode¶
madsci start --mode=localruns all managers in-process without DockerIn-memory drop-in backends for MongoDB and Redis operations; SQLite drop-in for PostgreSQL (Resource Manager)
Local data storage for development and testing without external database dependencies
Configuration Management¶
madsci config exportfor exporting configuration to YAML with secret redactionmadsci config createfor generating configuration files from defaultsSecret classification with
json_schema_extra={"secret": True}model_dump_safe()method on MadsciBaseModel and MadsciBaseSettings for secret redactionExplicit configuration management (no auto-writing of config files)
Experiment Application¶
ExperimentBasepropagates lab context URLs to instance attributes for robust client creation across async boundaries and Jupyter cellsExperimentScriptfor run-once experiment scriptsExperimentNotebookfor Jupyter-friendly experiments with cell-based executionExperimentTUIthread-safe pause/cancel controls usingthreading.Eventfor safe cross-thread state managementExperimentNodeREST node modality for workcell-managed experiment executionExample experiments:
example_experiment.py(ExperimentScript) andexample_experiment_tui.py(ExperimentTUI) inexamples/
Node Module Framework¶
NodeInfo.from_config()for creating node info from configurationMigration tools for database schema management
Context and Ownership Systems¶
MadsciContextsettings class replacingMadsciCLIConfigfor unified server URL configurationmadsci_context()context manager for propagating server URL configuration across componentsGlobalMadsciContextsingleton for application-wide server URL configuration@with_madsci_contextand@madsci_context_classdecorators for function and class-level contextmadsci.common.ownershipmodule for tracking ownership metadata (user, experiment, workflow, node)ownership_context()context manager for hierarchical ownership propagation@with_ownershipand@ownership_classdecorators for function and class-level ownership context
Registry Subsystem¶
IdentityResolverfor resolving component names to stable ULIDs (local registry -> lab registry -> generate new)LocalRegistryManagerwith walk-up.madsci/discovery for file-based identity persistenceLockManagerwith heartbeat-based stale lock detection for process-level coordination
Event Manager Analytics and Retention¶
Event archiving via
/events/archiveendpoint (by event IDs or date cutoff)MongoDB TTL index for automatic hard-deletion of archived events
Background retention task for periodic event archiving
UtilizationAnalyzerfor session-based system and node utilization analysisTimeSeriesAnalyzerfor timezone-aware time-series utilization reports (daily, hourly, weekly)CSVExporterfor exporting utilization, user, and session reports to CSV
Workflow Admin Commands¶
pause_workflow()andcancel_workflow()workflow engine utilitiesAdminCommandsenum expanded with PAUSE, RESUME, CANCEL, LOCK, UNLOCKWorkflow cancellation retries until reaching a cancellable node or timeout
Retry workflow from last failed step
Testing¶
2600+ automated tests
150+ template validation tests
CLI tests using Click’s CliRunner
E2E test harness with tutorial validation
YAML-driven
E2ETestRunnerframework inmadsci.common.testingwith validator registry and conditional step execution
Observability¶
OpenTelemetry integration for distributed tracing, metrics, and log correlation
structlogintegration for per-instance structured logging in EventClientStructured logging with hierarchical context propagation
event_client_context()andget_event_client()for context managementPer-manager OTEL configuration via environment variables
structlog_config.pymodule with configurable processor pipelines (JSON/console output, OTEL context, hierarchy context)
Workflow Status Display¶
New
WorkflowDisplayclass inmadsci.client.workflow_displaywith three rendering backends:Rich Live (default terminal): In-place updating table with progress bar, step icons, and colored status panels
Jupyter/IPython: HTML table with styled status cells and CSS progress bar, updated in-place via
display_idPlain text: Simple line-based output for environments without Rich or IPython
Auto-detection of display environment (Jupyter notebook vs terminal vs plain), overridable via
display_modeparameterPer-step timing: running steps show live elapsed time, completed steps show final duration
Step key annotations shown alongside step names when available
Paused/queued workflow indicators in all three backends
Formatted error prompts for workflow failure/cancellation with retry options
Developer Experience¶
Automatic Rich traceback handler installed on
madsci.commonimport for prettier exception outputMadsciDeveloperSettingswithMADSCI_DISABLE_RICH_TRACEBACKSandMADSCI_RICH_TRACEBACKS_SHOW_LOCALSenvironment variablesOpenAPI spec auto-export and Redoc REST API documentation for all 7 managers (
docs/api-specs/)devbox.jsonfor reproducible development environments via Nix-based devbox
New Type Modules¶
interface_types.py: Base settings for hardware interfaces (Serial, Socket, USB)module_types.py: Module and node settings hierarchy for module developmentregistry_types.py: Registry entries, locks, and component type definitionsmigration_types.py: Migration status and output format typesbackup_types.py: PostgreSQL and MongoDB backup settingsclient_types.py:MadsciClientConfigwith standardized retry, timeout, and pooling configurationcontext_types.py:MadsciContextunified server URL settings
Changed¶
Minimum Python version raised from 3.9.1 to 3.10.0 across all packages
Workcell Node Reconnect Behavior¶
Replaced disruptive
reset_disconnects()(which reset all nodes toinitializing) with a non-disruptivereconnect_disconnected_nodes()daemon thread that retries only disconnected nodesReconnect attempts run in a separate background thread, so the main engine loop is never blocked by reconnect activity
On a successful reconnect,
update_node()naturally restores the node’s status from the node’s own response; on failure, the node stays disconnected and is retried on the next intervalDefault
reconnect_attempt_intervalreduced from 1200 s → 30 s, safe now that retries are non-disruptive
Workcell Client¶
WorkcellClient.await_workflow()now usesWorkflowDisplayfor rich progress output instead of rawprint()calls with flush hacksNew
display_modeparameter onawait_workflow()(default"auto") to control rendering backend_handle_workflow_error()uses display-aware prompt formatting when a display instance is availableAll changes are backward-compatible; existing method signatures are preserved
Default paths for manager runtime data (PIDs, logs, backups, workcell files, datapoints) now resolve to a project-local
.madsci/directory via walk-up instead of always~/.madsci/. If no.madsci/or.git/directory is found in the directory tree,~/.madsci/is still used as the fallback. SetMADSCI_SETTINGS_DIRto override the resolution start directory.Backup subdirectory layout changed:
.madsci/mongodb/backups->.madsci/backups/mongodb,.madsci/postgresql/backups->.madsci/backups/postgresqlDefinition files fully purged from runtime code: all managers now use settings-only configuration (
AbstractManagerBase[Settings]pattern)update_node_filessetting removed from production code (wasTrueby default; remains only in test mocks)Settings consolidation for structural config overrides
Opt-in registry resolution in manager base
Docker reorganization: Dockerfiles and entrypoint scripts moved to
docker/directory; compose files split intocompose.yaml,compose.infra.yaml, andcompose.otel.yamlReorganized repository documentation and examples:
Moved
example_lab/toexamples/example_lab/Moved example notebooks to
examples/notebooks/Moved general-purpose guides (Node Development, Workflow Development, Observability, Troubleshooting) from
example_lab/todocs/guides/Updated MyST TOC, compose files, and all cross-references
Example lab now uses modern
settings.yaml+.envdual-layer configuration instead of*_MANAGER_DEFINITIONenvironment variablesExample lab definition files fully deprecated: structural data (locations, transfer capabilities, resource templates, workcell nodes) extracted into standalone YAML files and inline settings;
*_manager_definitionkeys removed fromsettings.yamlMigration tests decoupled from the live example lab using versioned fixture data (
fixtures/migration/v0.6/)Added required dependencies:
structlog,rich,opentelemetry-api,httpxAdded optional dependency groups:
otel-exportersandtui(madsci.client),otelandotel-instrumentation(madsci.common)
Removed¶
example_app.pyremoved (used deprecatedExperimentApplicationandNodeDefinition)lab_definition_pathparameter removed fromLocalRunner(was accepted but never used)load_or_create_definition()andload_definition()removed entirely from the codebase
Deprecated¶
Definition files hard-deprecated in v0.7.0
NodeDefinitionfiles (useNodeInfo.from_config()instead)NodeConfigreplacesNodeDefinitionas the primary node configuration typeManagerDefinitionfiles (useManagerSettingsinstead)WorkcellManagerDefinitiondeprecated in favor ofWorkcellInfofor runtime state andWorkcellManagerSettingsfor configuration