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.
[Unreleased]¶
[0.8.0] - 2026-05-13¶
Added¶
target_modelfield onTemplateManifest: Templates that generate YAML/JSON config can now declare which Pydantic model the output should validate against. Automated tests verify template output matches the declared model.Settings file validation in
madsci validate: The validate command now supportssettings.yamland*.settings.yamlfiles, validating against per-manager settings classes (Lab, Event, Experiment, Resource, Data, Workcell, Location).Pydantic-first data modeling guidance: Added to CLAUDE.md, AGENTS.md, and agent skills to codify the rule that every YAML/JSON config format must have a corresponding Pydantic model.
SiLA2 Native Node Client (Experimental)¶
Status: Experimental. This is a preview of native SiLA2 integration. The client surface, optional-dependency name, and binary/error handling may change. The broader migration is scoped in
openspec/changes/sila2-native-node-design/(umbrella issue #293). What ships now is client-side consumption only — authoring a MADSci node as a SiLA2 server is not yet supported.
SilaNodeClient(madsci.client.node.sila_node_client): NewAbstractNodeClientsubclass that connects to SiLA2 servers over gRPC via thesila2SDK. Supportssila://host:portURL dispatch, observable + unobservable command execution, server introspection (get_info/get_status/get_state),FeatureName.CommandNamedot-notation resolution, binary responses surfaced asActionFiles(with path-traversal hardening), and structured connection-error diagnostics with classification (dns_resolution,connection_refused,connection_timeout,tls_error,grpc_error).madsci.client[sila]extra: Optional install (pip install "madsci.client[sila]") that pulls in a compatiblesila2SDK version.SilaNodeClientraises a clearImportErrorif the extra is not installed.Example SiLA2 server (
examples/example_lab/example_modules/sila_example_server/): Minimal SiLA2 server exposing anExampleDeviceFeature (Greetunobservable,CountDownobservable,GenerateDatabinary response,ServerUptimeProperty). Runs as a Docker Compose service onsila://localhost:50052with a TCP-socket healthcheck. Wired intoworkcell_nodesinexamples/example_lab/settings.yamlassila_example.sila_node_notebook.ipynb: End-to-end walkthrough exercising every supportedSilaNodeClientcapability. Doubles as the SiLA validation harness — runnable viajust validate_nb_sila(papermill against the live compose service).Documentation: New “Consuming SiLA2 Devices (Experimental)” section in
docs/guides/node_development.md. Example lab README documents the newsila_example_serverservice and notebook.OpenSpec: Design-only change
sila2-native-node-designpublished, scoping the broader native SiLA2 migration (~12 downstream implementation issues #A–#M tracked against #293).
Layered Location Ownership Model¶
NodeIntrinsicLocationDefinition: Nodes declare locations intrinsic to their hardware via theintrinsic_locationsClassVar. Location names are auto-prefixed with{node_name}.for uniqueness (e.g.,deck_1becomesliquidhandler_1.deck_1).LocationManagementenum: Every location now tracks whether it is managed by a node (NODE) or by the lab configuration (LAB) via themanaged_byfield onLocation.ownerfield onLocation:OwnershipInfoprovenance tracking. Node-managed locations automatically setowner.node_id.POST /location/initendpoint: Idempotent get-or-create for location registration, used by nodes during startup.GET /locations?managed_by=node|labfiltering: Query locations by management type.GET /reconciliation/statusendpoint: Returns last reconciliation timestamp, results, and configuration.LabLocationConfig: Reconcilable lab config file format (locations.yaml) withrepresentation_templates,location_templates,training, andlocationssections. Replaces the oldseed_locations_filemechanism.RepresentationTrainingEntry: Teaches a node how to access locations it does not own (e.g., robot arm coordinates for a liquid handler deck slot). Defined in thetrainingsection of the lab config file.lab_config_filesetting:LocationManagerSettingsfield (default:locations.yaml) for the lab config file path, discovered via walk-up search. Replacesseed_locations_file.Health endpoint enrichment:
LocationManagerHealthnow includesnum_node_managed_locations,num_lab_managed_locations, andlast_reconciliation_atfields.intrinsic_location_handler()onAbstractNode: Lifecycle method that registers intrinsic locations with the Location Manager at startup, aftertemplate_handler()and beforestartup_handler().NodeInfo.intrinsic_locationsfield: Exposes a node’s declared intrinsic locations through its/infoendpoint.Location Manager schema upgrade 2.0.0 to 3.0.0: Adds
managed_by(default"lab") andowner(defaultnull) fields to all existing location documents, plus amanaged_by_idxindex.
CLI/TUI Buildout¶
madsci addcommand with 8 subcommands for adding components to existing module projects (docs, drivers, notebooks, gitignore, compose, dev_tools, agent_config, all)8 addon templates for optional project components:
addon/docs,addon/drivers,addon/notebooks,addon/gitignore,addon/compose,addon/dev_tools,addon/agent_config,addon/all8 new CLI command groups for direct manager interaction, each with short aliases:
madsci workflow(wf): list, show, submit, pause, resume, cancel, retry, resubmitmadsci resource(res): list, get, create, delete, restore, tree, lock, unlock, quantity, template, historymadsci location(loc): list, get, create, create-from-template, delete, resources, attach, detach, set-repr, remove-repr, transfer-graph, plan-transfer, export, import, template, rep-templatemadsci node(nd): list, info, status, state, log, admin, action, action-result, action-history, config, set-config, add, shellmadsci experiment(exp): list, get, start, run, pause, continue, cancel, endmadsci campaign(camp): create, getmadsci data(dt): list, get, metadata, submit, querymadsci events(ev): query, get, archive, purge, backup
4 new TUI main screens: experiments, resources, locations, data browser — all with search/filter, detail panels, and action bars
5 TUI detail/modal screens: resource tree, transfer graph, workflow detail, step detail, action executor
TUI node enhancements: admin command panel and interactive action executor
TUI workflow enhancements: retry, resubmit, step details, and filtering
Shared CLI/TUI utility layer:
cli_utils.py(formatting, health checks, output helpers) and reusable TUI widget library (7 widgets and screen mixins)httpx client migration: All 8 service clients migrated from
requeststohttpxwithDualModeClientMixinfor sync/async support, configurable retry transports, and rate-limit tracking viahttpx_factory.pyResourceClient async methods: 16 new async methods for TUI integration
LocationClient async methods: Missing async methods added for TUI integration
Transfer graph detailed view:
GET /transfer/graph/detailedendpoint on Location Manager returningTransferGraphDetailedResponsewith per-edge node names;get_detailed_transfer_graph()/async_get_detailed_transfer_graph()onLocationClient;TransferGraphDetailedEdgeandTransferGraphDetailedResponsemodels; dashboard redesign with location grouping, resource-fill colors, edge tooltips, location index numbers, andtransferEdgespolling on the location store.Bundled templates
_shared/directory: 13 canonical files (.gitignore,CLAUDE.md.j2,AGENTS.md.j2,justfile.j2,ruff.toml.j2,.pre-commit-config.yaml.j2,docker-compose.yaml.j2, notebooks, docs, drivers) deduplicated across all bundled templates.TemplateEngineextended with_resolve_shared_dir()(walk-up + importlib fallback),_resolve_source_path()(local-first fallback), multi-pathFileSystemLoader, and expanded path-traversal check. Removes ~105 duplicated files across 14 template directories (6 module + 8 addon), net -4,678 lines.Node
try/exceptpre-commit checker (scripts/precommit_check_try_catch_blocks.py): pre-commit hook that fails when a node module catches an exception without re-raising or returning anActionResult, enforcing the node error-handling contract.
Node Location Template System (PR #228, #258)¶
template_handler()lifecycle method onAbstractNodefor declarative registration of resource templates, location representation templates, and location templates at node startuplocation_representation_templatesandlocation_templatesclass variables onAbstractNodefor declarative template definitionsNodeInfo.location_templatesfield exposing a node’s registered location templates through its info endpointLocation Templates panel on the Squid Dashboard with schema-aware forms for creating locations from templates
Resource Templates panel on the Squid Dashboard Resources tab
Database auto-initialization for document database collections not yet tracked by the migration system
Agent Skills (PR #242)¶
Four bundled agent skills (
madsci-cli,madsci-nodes,madsci-managers,madsci-experiments) providing AI coding agents with MADSci domain knowledgeSkills are automatically copied into generated projects via
madsci newandmadsci init
Dashboard Workflow and Node Modal Redesign (PR #259)¶
Redesigned workflow modal with tabbed layout (Overview, Steps, Results tabs) and step indicators
Workflow retry and resubmit UX with confirmation dialogs and snackbar notifications
Redesigned node modal with tabbed layout (Overview, Info, Actions tabs)
resubmit_workflowendpoint on workcell manager and corresponding client method
Experiment Ownership Propagation (PR #260)¶
Experiment ownership context (experiment_id, campaign_id, user) is now automatically propagated to workflows started within
manage_experiment()
FOSS Infrastructure Migration (Issue #212)¶
New FOSS migration tool (
madsci.common.foss_migration) for automated data migration from proprietary infrastructure (MongoDB, Redis, MinIO) to FOSS alternatives (FerretDB, Valkey, SeaweedFS)FossMigrationToolorchestrator with 20+ methods covering prerequisite checks, Docker lifecycle management, database-specific migrations, and post-migration verificationFossMigrationSettingswith environment variable support and customizable compose file/service namesFossMigrationStepResultandFossMigrationReportresult models for structured migration reporting
New
madsci migrate fossCLI command with--dry-run/--applymodes, per-step execution (--step),--skip-backup,--skip-docker, URL overrides, and Rich table outputDocker Compose migration overlay (
compose.migration.yaml) for running old containers on alternate ports during migration (MongoDB:27018, PostgreSQL:5433, Redis:6380, MinIO:9002)Automated Location Manager data migration from v0.7.x Redis to FerretDB document database as part of the FOSS migration pipeline — auto-discovers manager IDs, reads location data (resource attachments, node representations) from old Redis, and writes to FerretDB via
LocationMigratorComprehensive FOSS migration guide (
docs/guides/foss_migration.md) with prerequisites, quick start, Location Manager migration, troubleshooting, and data directory referenceFOSS migration test suites: CLI tests (129 lines) and tool unit tests (536 lines)
Changed¶
madsci validatedocstring: Updated to reflect support for settings files alongside definitions and workflows.CLI commands refactored to use shared utility layer for consistent output formatting and error handling
TUI screens migrated to shared widget library for consistent styling and behavior
Example lab location management: Inline
location_locationsdefinitions insettings.yamlreplaced by a standalonelocations.yamllab config file usingLabLocationConfigformat. Node-intrinsic locations (liquid handler deck slots) are now auto-created by nodes on startup. Lab-managed locations (storage rack, plate carriage) and training entries (robot arm coordinates for LH deck slots) are defined inlocations.yaml.Location Manager reconciliation: Now processes the lab config file (
LabLocationConfig) on each cycle, syncing representation templates, location templates, locations, and training entries with the live database.
Location Manager Dual-Handler Architecture (PR #228)¶
Location Manager migrated from Redis-only to dual-handler architecture (document database + cache)
Location data persisted in document database with cache-backed fast reads
Dashboard UX Improvements (PRs #255, #257)¶
Copyable workflow step output now uses
js-yamlfor YAML formatting with scoped toggle per actionJSON renderer for workflow steps has a depth limit to prevent browser freezes on deeply nested data
Action argument values in the node modal are now initialized from their declared defaults
Default Infrastructure: FOSS Alternatives¶
MongoDB → FerretDB v2: Default document database switched to FerretDB (MongoDB wire protocol, backed by PostgreSQL with DocumentDB extension); Python
pymongoclient unchangedRedis → Valkey 8: Default key-value store switched to Valkey (drop-in API-compatible); Python
redisclient unchangedMinIO → SeaweedFS 4.17: Default object storage switched to SeaweedFS (S3-compatible); Python
minioSDK unchangedPostgreSQL split: Two separate PostgreSQL instances —
madsci_postgres(port 5432,postgres-documentdb-dev:17-ferretdbfor FerretDB) andmadsci_postgres_resources(port 5434,postgres:17for Resource Manager) — replacing the single shared instance
FOSS Terminology Audit¶
BREAKING:
DocumentDBBackupSettingsenv prefix changed fromMONGODB_toDOCUMENT_DB_(e.g.,MONGODB_BACKUP_DIR→DOCUMENT_DB_BACKUP_DIR)BREAKING:
DocumentDBMigrationSettingsenv prefix changed fromMONGODB_MIGRATION_toDOCUMENT_DB_MIGRATION_(e.g.,MONGODB_MIGRATION_DATABASE→DOCUMENT_DB_MIGRATION_DATABASE)BREAKING: Settings fields
redis_host,redis_port,redis_passwordrenamed tocache_host,cache_port,cache_passwordonWorkcellManagerSettingsandLocationManagerSettings(old names accepted viavalidation_aliasfor backward compatibility)BREAKING: Health model fields
redis_connectedrenamed tocache_connectedonWorkcellManagerHealthandLocationManagerHealthBREAKING: Docker types field
REDIS_PORTrenamed toCACHE_PORT(old name accepted viavalidation_alias)Default backup directory for document database migrations changed from
.madsci/backups/mongodbto.madsci/backups/document_dbBackup metadata
backup_typevalue changed from"mongodb"to"document_db"for document database backupsComprehensive terminology updates across all comments, docstrings, schema descriptions, and documentation to use vendor-neutral terms (“document database” instead of “MongoDB”, “cache” instead of “Redis”)
Deleted stale auto-generated API doc files referencing old module names (
mongo_cli.md,mongodb_backup.md,mongo_handler.md, etc.)Test directory renamed:
test_mongodb_backup_tools/→test_document_db_backup_tools/Manual test file renamed:
manual_test_minio.py→manual_test_object_storage.py
Vendor-Neutral Renames¶
Handler files:
mongo_handler.py→document_storage_handler.py,minio_handler.py→object_storage_handler.pyHandler classes:
PyMongoHandler→PyDocumentStorageHandler,InMemoryMongoHandler→InMemoryDocumentStorageHandler,RealMinioHandler→RealObjectStorageHandler,InMemoryMinioHandler→InMemoryObjectStorageHandlerBackup tools:
mongodb_backup.py→document_db_backup.py,mongo_cli.py→document_db_cli.py,MongoDBBackupTool→DocumentDBBackupTool,MongoDBBackupSettings→DocumentDBBackupSettingsMigration tools:
mongodb_migration_tool.py→document_db_migration_tool.py,mongodb_version_checker.py→document_db_version_checker.pyHelper function:
create_minio_client()→create_object_storage_client()(with new_normalise_endpoint()for URL scheme stripping)Manager constructor parameters:
mongo_handler→document_handler,minio_handler→object_storage_handlerSettings fields:
mongo_db_url→document_db_urlacross all managers (backward-compatible viaAliasChoicesvalidation aliases)Docker types:
MONGODB_PORT→DOCUMENT_DB_PORT,MINIO_PORT→OBJECT_STORAGE_PORT(8333),MINIO_CONSOLE_PORT→OBJECT_STORAGE_CONSOLE_PORT(9333)CLI entry point:
madsci-mongodb-backup→madsci-document-db-backupBackup settings files: now check for both
document_db_backup.*and legacymongodb_backup.*filenames
Configuration and Port Changes¶
Default object storage ports changed from MinIO conventions (9000/9001) to SeaweedFS defaults (8333/9333)
Resource Manager PostgreSQL moved to port 5434 (FerretDB backend occupies 5432)
Default
POSTGRES_DBchanged fromresourcestopostgres(FerretDB requirement)SeaweedFS S3 credentials configured via
AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEYenvironment variablesNew
public_endpointfield onObjectStorageSettingsfor customizable public-facing URLs (replaces hardcoded port rewriting)
Templates¶
Lab templates (
standard,distributed) updated: service names (mongodb→ferretdb,redis→valkey), environment variables, and dependencies aligned with FOSS stack
Documentation¶
Operator guides updated for FOSS stack: backup/recovery, troubleshooting, updates/maintenance
Manager READMEs updated with FerretDB/Valkey/SeaweedFS references and new handler names
Tutorials updated with new service names, ports, and compose configuration
madsci_commonREADME updated with new backup tool class names
Deprecated¶
LabManagerDefinition: Now emitsMadsciDeprecationWarningon instantiation (v0.7.0 removal). UseLabManagerSettingsfor configuration.
Removed¶
madsci new workcellsubcommand: The workcell template generated an orphaned YAML format that didn’t correspond to any Pydantic model. Workcell configuration is handled byWorkcellManagerSettingsviasettings.yaml.LabClient.get_definition(): The Lab Manager no longer serves a/definitionendpoint. Useget_lab_context()orget_lab_health()instead.WORKCELLtemplate category: Removed fromTemplateCategoryenum.NodeLocationTemplateDefinition: Replaced byNodeIntrinsicLocationDefinitionfor declaring node-intrinsic locations. Location templates are now defined asLocationTemplateobjects (via lab config file or API) rather than on node classes.seed_locations_filesetting: Replaced bylab_config_fileonLocationManagerSettings. The newLabLocationConfigformat supports representation templates, location templates, training entries, and locations in a single reconcilable file.workcell_manager.compose.yaml(redundant compose file)MongoDB data volume from
compose.yaml(FerretDB uses PostgreSQL backend)Hardcoded MinIO console port rewriting logic in
object_storage_helpers.py(replaced by configurablepublic_endpoint)
Fixed¶
TUI UX improvements: scrollable layouts, clickable ActionBar, screen discoverability
Dashboard scroll behavior and full ULID ID display
Transfer graph names, delete confirmation dialog, inventory button label
Double-slash URL bug in node client health checks
httpx migration fixes: stale
requestsimports, config attribute access, response closing in retry transports, async health checks, broken sync.close()callObject storage overwrites on repeated uploads (closes #274): added
ULID_PREFIXEDvalue toObjectNamingStrategyand incorporateddatapoint_idinto S3 object keys ({ulid}_{label}) in both server-side and client-side upload paths, matching the local filesystem backend.madsci new <subcommand>ignored--namein interactive mode:collect_parameters_interactive()now accepts anoverridesdict, andgenerate_from_template()passes CLI-provided names through it, somadsci new lab -n my_labproduces a lab namedmy_labinstead of prompting.Generated lab compose templates referenced nonexistent local images (
madsci-squid:latest,madsci-event-manager:latest): updatedstandardanddistributedlab templates to use the published GHCR images (ghcr.io/ad-sdl/madsci_dashboard:latestfor the lab manager,ghcr.io/ad-sdl/madsci:latestfor all other services). Removed the stalebuild:section from the lab manager service.SiLA connection diagnostics:
SilaNodeClientnow raises connection errors enriched with classification (dns_resolution,connection_refused,connection_timeout,tls_error,grpc_error) and remediation hints.
Dashboard and UI Fixes (PRs #255, #256, #257, #259)¶
Fixed dashboard freeze caused by
@vue:updatedevent handler creating infinite update loops; replaced with@update:modelValueTyped action endpoints now return a failed
ActionResultinstead of HTTP 500 when actions raise exceptionsAction argument checkbox and input values in the node modal now correctly initialized from their declared defaults
Workflow steps that error before execution now show failed status instead of remaining pending
Experiment Ownership (PR #260)¶
Restricted ownership context overrides to experiment-level fields only (
experiment_id,campaign_id,user), preventing unintended overwrites of workflow-level ownership
Template Import Fixes¶
Fixed
RestNodeConfigimport in all 6 module templates (was importing frommadsci.node_modulewhich does not export it; now imports frommadsci.common.types.node_types)Fixed
SquidServer/SquidSettingsimports in all 3 lab templates (classes do not exist; replaced withLabManagerfrommadsci.squid.lab_serverandLabManagerSettingsfrommadsci.common.types.lab_types)
FOSS Migration Location Data Loss¶
Fixed FOSS migration tool silently skipping Location Manager data stored in Redis, treating it as ephemeral; v0.7.x Location Manager stores primary location data (resource attachments, node representations, transfer state) in Redis, which is now automatically migrated to the document database during
madsci migrate foss --apply
Documentation URL Fixes¶
Fixed broken GitHub Pages documentation URLs in CLI output and generated project files
CLI Scaffolding Fixes (madsci new)¶
Fixed
--templateargument being ignored inmadsci new module(template selection prompt was always shown even when--templatewas explicitly provided)Fixed default module name
my_modulecreating amy_module_moduledirectory; default changed tomy_deviceFixed agent skills being placed in the current working directory instead of inside the generated project directory (e.g.,
my_device_module/.agents/instead of./.agents/)Fixed next-steps output using hardcoded fallback names (e.g.,
your_module_module) instead of the actual names entered during interactive prompts;generate_from_templatenow returns theGeneratedProjectresultFixed documentation link in
madsci new modulepointing to non-existent URL
[0.7.1] - 2026-03-10¶
Added¶
Database Handler Abstractions¶
New
madsci.common.db_handlerspackage with abstract base classes (DocumentStorageHandler,CacheHandler,PostgresHandler,ObjectStorageHandler) and both real and in-memory implementationsReal implementations:
PyMongoHandler,PyCacheHandler,SQLAlchemyHandler,RealObjectStorageHandlerIn-memory implementations:
InMemoryDocumentStorageHandler,InMemoryCacheHandler,SQLiteHandler,InMemoryObjectStorageHandlerAll 6 database-backed managers now accept optional handler constructor parameters (
document_handler,object_storage_handler,cache_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 Object Storage Handler Consolidation¶
All object storage operations now routed through
ObjectStorageHandlerabstraction; removed directself.minio_clientusage_setup_object_storage()wraps legacyMinioclients inRealObjectStorageHandler(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)
InMemoryDocumentStorageHandler _client Safety¶
InMemoryDocumentStorageHandler.__init__now setsself._client = Nonewhen an externaldatabaseis provided, preventingAttributeErroron_clientaccess
Handler ABC Return Type Annotations¶
Improved return type annotations on handler ABC methods:
DocumentStorageHandler.get_collection() -> Collection | Any,CacheHandler.create_dict() -> MutableMapping,CacheHandler.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 document database)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 document database and Valkey 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)Document database 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 document database 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