ad-sdl/madsci/madsci-experiments

Claude Code and agent skill by ad-sdl/madsci

Trust tier 2
  • community
LLM review pending
Source check pending

Checking that the SKILL.md file is still in the repository.

Working with MADSci experiment modalities and the experiment lifecycle. Use when creating, modifying, or debugging experiments using ExperimentScript, ExperimentNotebook, ExperimentTUI, or ExperimentNode.

Install

npx vskill@latest install ad-sdl/madsci/madsci-experiments

SKILL.md

MADSci Experiments

MADSci provides four experiment modalities, all built on ExperimentBase which uses MadsciClientMixin (composition, not RestNode inheritance). Choose the right modality for your use case, then implement run_experiment().

Key Files

FilePurpose
src/madsci_experiment_application/madsci/experiment_application/experiment_base.pyBase class, lifecycle, context manager
src/madsci_experiment_application/madsci/experiment_application/experiment_script.pySimple run-once modality
src/madsci_experiment_application/madsci/experiment_application/experiment_notebook.pyJupyter notebook modality
src/madsci_experiment_application/madsci/experiment_application/experiment_tui.pyTerminal UI modality
src/madsci_experiment_application/madsci/experiment_application/experiment_node.pyREST server modality
src/madsci_common/madsci/common/types/experiment_types.pyExperimentDesign, Experiment, ExperimentStatus
src/madsci_client/madsci/client/experiment_client.pyExperiment Manager HTTP client
src/madsci_client/madsci/client/client_mixin.pyMadsciClientMixin with 7 lazy client properties
src/madsci_common/madsci/common/bundled_templates/experiment/Templates for all 4 modalities

Choosing a Modality

ModalityBest ForEntry PointInteractive?
ExperimentScriptBatch processing, automated pipelinesrun() or main()No
ExperimentNotebookExploratory analysis, Jupyter workflowsstart() / end()Yes (cell-by-cell)
ExperimentTUIOperator-attended experiments needing pause/cancelrun_tui()Yes (terminal)
ExperimentNodeRemote-controlled experiments via REST APIstart_server()Via HTTP

Architecture

ExperimentBase (MadsciClientMixin)
├── ExperimentScript # run() -> manage_experiment() -> run_experiment()
├── ExperimentNotebook # start() / end() with Rich display
├── ExperimentTUI # Textual TUI with threading.Event pause/cancel
└── ExperimentNode # Wraps RestNode internally, exposes run_experiment as action

Key design choice: ExperimentBase uses composition (MadsciClientMixin), not inheritance from RestNode. Only ExperimentNode creates a RestNode internally when it needs server capabilities.

ExperimentDesign vs Experiment

  • ExperimentDesign: Template/blueprint. Defines experiment_name, description, resource_conditions. Reusable across runs. Can be loaded from YAML.
  • Experiment: Runtime instance. Has experiment_id (ULID), status, timestamps, ownership. Created by start_experiment_run().
python
from madsci.common.types.experiment_types import ExperimentDesign
design = ExperimentDesign(
experiment_name="Synthesis Optimization",
experiment_description="Optimize reaction conditions for compound X",
)
# Or load from YAML
design = ExperimentDesign.from_yaml("experiment_design.yaml")

ExperimentScript (Simplest Modality)

python
from madsci.experiment_application.experiment_script import ExperimentScript
from madsci.common.types.experiment_types import ExperimentDesign
class SynthesisExperiment(ExperimentScript):
experiment_design = ExperimentDesign(
experiment_name="Synthesis Run",
experiment_description="Automated synthesis workflow",
)
def run_experiment(self, sample_id: str = "default", cycles: int = 3) -> dict:
"""Core experiment logic. Called within manage_experiment() context."""
results = []
for i in range(cycles):
result = self.workcell_client.run_workflow(
"synthesis", parameters={"sample_id": sample_id, "cycle": i}
)
results.append(result)
self.logger.info("Cycle completed", cycle=i, result=result)
return {"sample_id": sample_id, "results": results}
if __name__ == "__main__":
SynthesisExperiment.main(sample_id="ABC123", cycles=5)

Entry points:

  • run(*args, **kwargs): Instance method. Merges config args with passed args.
  • main(*args, **kwargs): Class method. Creates instance and calls run().

Config class: ExperimentScriptConfig adds run_args and run_kwargs fields for CLI/env configuration.

ExperimentNotebook (Jupyter)

Designed for cell-by-cell interactive use in Jupyter notebooks.

python
# Cell 1: Setup
from my_experiment import MyNotebookExperiment
exp = MyNotebookExperiment()
# Cell 2: Start
exp.start(run_name="Exploration Run 1")
# Cell 3: Execute
result = exp.run_workflow("characterize", parameters={"sample": "S1"})
# Cell 4: Visualize
exp.display(result, title="Characterization Results")
# Cell 5: End
exp.end()

Or use as context manager:

python
with MyNotebookExperiment() as exp:
result = exp.run_workflow("synthesis")
exp.display(result, title="Results")

Key methods:

  • start(run_name, run_description): Starts experiment, displays status. Returns self.
  • end(status): Ends experiment, displays summary. Returns self.
  • run_workflow(workflow_name, parameters, display_result): Convenience wrapper for workcell_client.run_workflow().
  • display(data, title): Rich-formatted display (falls back to plain print).

Config class: ExperimentNotebookConfig adds rich_output: bool = True and auto_display_results: bool = True.

ExperimentTUI (Terminal UI)

Interactive terminal interface with pause/cancel controls via Textual.

python
from madsci.experiment_application.experiment_tui import ExperimentTUI
from madsci.common.types.experiment_types import ExperimentDesign
class OperatorExperiment(ExperimentTUI):
experiment_design = ExperimentDesign(
experiment_name="Operator-Assisted Synthesis",
)
def run_experiment(self) -> dict:
results = []
for step in range(10):
self.check_experiment_status() # Handles pause/cancel locally
result = self.workcell_client.run_workflow("step", parameters={"n": step})
results.append(result)
return {"steps_completed": len(results)}
if __name__ == "__main__":
OperatorExperiment().run_tui()

Thread-safe controls:

  • request_pause() / request_resume() / request_cancel(): Called from TUI thread
  • check_experiment_status(): Called in experiment thread. Uses threading.Event (no server round-trips). Raises ExperimentCancelledError if cancelled. Blocks while paused.

Config class: ExperimentTUIConfig adds refresh_interval: float = 1.0 and show_logs: bool = True.

Requires: pip install textual (optional dependency).

ExperimentNode (REST Server)

Exposes run_experiment() as a node action, controllable via the workcell manager.

python
from madsci.experiment_application.experiment_node import ExperimentNode
from madsci.common.types.experiment_types import ExperimentDesign
class RemoteExperiment(ExperimentNode):
experiment_design = ExperimentDesign(
experiment_name="Remote Synthesis",
)
def run_experiment(self, sample_id: str, temperature: float = 25.0) -> dict:
return self.workcell_client.run_workflow(
"process", parameters={"sample_id": sample_id, "temp": temperature}
)
if __name__ == "__main__":
RemoteExperiment().start_server()

Internals: Creates a RestNode instance internally. Wraps run_experiment as a non-blocking action inside manage_experiment() context.

Config class: ExperimentNodeConfig adds server_host: str = "0.0.0.0", server_port: int = 6000, cors_enabled: bool = True.

Lifecycle and Context Manager

manage_experiment() (Recommended)
python
with self.manage_experiment(run_name="Run 1") as exp:
# Experiment started, logging context established
result = exp.workcell_client.run_workflow("my_workflow")
# On success: end_experiment(COMPLETED) called automatically
# On exception: handle_exception() called, then re-raised

The context manager:

  1. Calls start_experiment_run() -> registers with Experiment Manager
  2. Sets up hierarchical logging context (experiment_id, experiment_name, run_name, experiment_type)
  3. Yields self
  4. On success: end_experiment(COMPLETED)
  5. On exception: handle_exception() then re-raises
Manual lifecycle
python
exp.start_experiment_run(run_name="Manual Run")
try:
exp.run_experiment()
exp.end_experiment(status=ExperimentStatus.COMPLETED)
except Exception as e:
exp.handle_exception(e)
raise
Exception handling

Override handle_exception() for custom behavior:

python
def handle_exception(self, exception: Exception) -> None:
if isinstance(exception, RecoverableError):
self.logger.warning("Recoverable error, retrying", error=str(exception))
return # Don't end experiment
super().handle_exception(exception) # Logs error + ends with FAILED

ExperimentStatus

python
class ExperimentStatus(str, Enum):
IN_PROGRESS = "in_progress"
PAUSED = "paused"
COMPLETED = "completed"
FAILED = "failed"
CANCELLED = "cancelled"
UNKNOWN = "unknown"

Exception Types

python
from madsci.common.exceptions import (
ExperimentCancelledError, # Raised when experiment cancelled externally
ExperimentFailedError, # Raised when experiment fails externally
ExperimentPauseTimeoutError, # Raised when pause exceeds max_pause_wait
)

Client Access (via MadsciClientMixin)

All experiment modalities provide 7 lazy-initialized client properties:

python
self.event_client # EventClient - logging and events
self.logger # Alias for event_client
self.resource_client # ResourceClient - inventory
self.data_client # DataClient - data storage
self.experiment_client # ExperimentClient - experiment lifecycle
self.workcell_client # WorkcellClient - workflow execution
self.location_client # LocationClient - locations
self.lab_client # LabClient - lab coordination

Clients are created on first access. URLs resolved from config or lab context (service discovery).

Configuration

All configs inherit from ExperimentBaseConfig (MadsciBaseSettings, env prefix EXPERIMENT_):

python
# Common fields (ExperimentBaseConfig):
lab_server_url: Optional[AnyUrl] # Lab manager for service discovery
event_server_url: Optional[AnyUrl] # Override Event Manager URL
experiment_server_url: Optional[AnyUrl] # Override Experiment Manager URL
workcell_server_url: Optional[AnyUrl] # Override Workcell Manager URL
data_server_url: Optional[AnyUrl] # Override Data Manager URL
resource_server_url: Optional[AnyUrl] # Override Resource Manager URL
location_server_url: Optional[AnyUrl] # Override Location Manager URL
max_pause_wait: Optional[float] # Max seconds to wait while paused (None = forever)

Config files searched via walk-up: settings.yaml, experiment.settings.yaml, .env, experiment.env.

Creating Experiments from Templates

bash
madsci new experiment
# Choose modality: script, notebook, tui, node
# Provide: experiment_name, description

Templates in src/madsci_common/madsci/common/bundled_templates/experiment/:

  • script/ -> {name}.py
  • notebook/ -> {name}.ipynb
  • tui/ -> {name}_tui.py
  • node/ -> {name}_node.py

Checking Experiment Status (Pause/Cancel)

Call check_experiment_status() at natural checkpoints in long-running experiments:

python
def run_experiment(self):
for batch in batches:
self.check_experiment_status() # Blocks if paused, raises if cancelled
process(batch)

ExperimentTUI behavior: Uses local threading.Event (no network calls). Other modalities: Polls Experiment Manager with exponential backoff (5s -> 60s). Logs "Still waiting" every 5 minutes.

Common Pitfalls

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 100 of 100Pattern checks on the skill's files.
  • Model reviewNot runA model review is scheduled for the next rescan.

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
82
Repository forks
11
7-day trend score
22

Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/ad-sdl/madsci/madsci-experiments/badge)](https://verified-skill.com/skills/ad-sdl/madsci/madsci-experiments)

Something wrong with this skill or its page? Tell us.