musical-conductor
v1.4.5
Published
Event-driven communication system with musical sequence orchestration for modern web applications
Maintainers
Readme
[!IMPORTANT] Deprecation notice: RenderX UI and E2E tests have been split out of this repository per ADR‑0014/ADR‑0015. This repo now focuses on the core orchestration library only. See tools/docs/wiki/adr/0015-split-renderx-and-plugins.md for details.
🎼 MusicalConductor
A sophisticated sequence orchestration engine for managing complex workflows with plugin-based architecture, resource management, and comprehensive monitoring.
🚀 Overview
MusicalConductor is an orchestration engine that makes harmony out of chaos. It turns drifting, competing subsystems into a single score—sequencing work into movements and beats with clear signatures, correlation, and telemetry. At scale, it keeps applications and platforms simple on the surface while coordinating complex flows underneath via a minimal client API (conductor.play) and a powerful runtime.
Originally a 3,228-line monolith, the conductor has been refactored into a lean, modular core with specialized components for orchestration, plugin compliance, resource management, and monitoring.
✨ Key Features
- Orchestrate via play(): a single, simple client API for complex work
- CIA/SPA compliance: build-time CIA tests, runtime SPA validation and guardrails
- Sequencing: movements and beats with timing, dynamics, and sequence signatures
- Correlation-first: execution IDs threaded end-to-end for traceability
- Data Baton: a shared, logged payload that flows between beats
- Transaction frequency control: timing modes eliminate race conditions (immediate, after-beat, next-measure, delayed, wait-for-signal)
- Resource management: priority, ownership, and conflict resolution
Why MusicalConductor
Make harmony: unify many subsystems under one score with clear movements and beats
Turn chaos into order: eliminate race conditions with timing controls and priorities
Scale simply: a tiny client surface (play, subscribe) fronts a powerful engine
Observe everything: correlation IDs, baton diffs, and structured logs for every step
Telemetry: status, statistics, and structured logs for every beat/sequence
🧪 Testing (Post‑split)
This repository now contains only the MusicalConductor core. End‑to‑end (E2E) browser tests live in the RenderX shell repository.
Core tests (here):
- Run all unit tests:
npm test - With coverage:
npm run test:coverage - Filter by file/pattern:
npm test -- tests/unit/communication/
E2E tests (moved):
- See the RenderX shell repo for Playwright setup and the minimal Chrome smoke test
- Rationale and timeline: tools/docs/wiki/adr/0015-split-renderx-and-plugins.md
🏗️ Architecture
Core Components
MusicalConductor (736 lines) - Main orchestration coordinator
├── Plugin Management (5 components)
├── Resource Management (5 components)
├── Monitoring & Statistics (4 components)
├── Orchestration (2 components)
├── Validation (1 component)
├── Utilities (1 component)
├── API (1 component)
└── StrictMode (1 component)Component Overview
Plugin Management
PluginManager- Plugin lifecycle managementPluginInterfaceFacade- Plugin interface abstractionPluginLoader- Dynamic plugin loadingPluginValidator- Plugin validation and verificationPluginManifestLoader- Manifest processing
Resource Management
ResourceManager- Core resource trackingResourceConflictResolver- Conflict resolution strategiesResourceOwnershipTracker- Ownership managementResourceDelegator- Resource delegation patternsResourceConflictManager- Advanced conflict management
Orchestration
SequenceOrchestrator- Core sequence execution engineEventOrchestrator- Event management and emission
🧩 CIA & SPA Architecture
- CIA (Conductor Integration Architecture): defines how plugins are discovered, mounted, and orchestrated. Compliance is enforced at build/test time by unit tests that exercise plugin registration and handler contracts.
- SPA (Symphonic Plugin Architecture): defines how plugins behave at runtime. SPAValidator enforces that plugins do not directly access the EventBus and only orchestrate via conductor.play().
Compliance
- Build-time: CIA unit tests validate sequence shape and handler contracts (see tests/unit/communication and ADR-0004, ADR-0008)
- Runtime: SPAValidator intercepts eventBus.emit/subscribe and raises violations for direct access (ADR-0002)
Handler context (what your handler receives):
function onSelect(data: any, context: any) {
// Correlation (request) and musical context travel with the event
const reqId = data._musicalContext?.execution?.requestId;
// Data Baton for cross-beat payload
context.payload.lastSelectedId = data.elementId;
// Minimal client surface
context.conductor.play(
"OtherPlugin",
"other-symphony",
{ parent: reqId },
"CHAINED"
);
}Beat Patterns & Sequence Signatures
- Beats: discrete, ordered events with dynamics (priority) and timing (frequency)
- Movements: related groups of beats; a sequence may have multiple movements
- Signatures: give every sequence a clear identity (id, name, category, key, tempo)
Example:
export const sequence = {
id: "toast-symphony",
name: "Toast Symphony",
key: "G Major",
tempo: 90,
category: "system",
movements: [
{
id: "notify",
name: "Notify",
beats: [
{
beat: 1,
event: "notify:prepare",
handler: "prepare",
dynamics: "mp",
timing: "after-beat",
},
{
beat: 2,
event: "notify:show",
handler: "show",
dynamics: "f",
timing: "immediate",
},
],
},
],
};Apps “discern” beat patterns by subscribing to conductor lifecycle events and by inspecting _musicalContext attached to event payloads during execution.
🚀 Quick Start
Installation
npm install musical-conductorBasic Usage
import {
initializeCommunicationSystem,
type ConductorClient,
} from "musical-conductor";
// Initialize the communication system (idempotent; StrictMode-safe)
const { conductor } = initializeCommunicationSystem();
// Optionally load CIA plugins declared in your plugin manifest
await conductor.registerCIAPlugins();
// Orchestrate via CIA: play(pluginId, sequenceId, context?, priority?)
conductor.play(
"MyPlugin", // pluginId
"component-select-symphony", // sequenceId (declared by the plugin)
{
elementId: "rx-comp-123",
onSelectionChange: (id: string | null) => console.log("Selected:", id),
},
"HIGH" // optional priority: HIGH | NORMAL | CHAINED
);Plugin Development
// In your plugin module (e.g., /plugins/MyPlugin/index.ts)
export const sequence = {
id: "component-select-symphony",
name: "Component Select Symphony",
key: "C Major",
tempo: 120,
category: "component-ui",
movements: [
{
id: "process",
name: "Process",
beats: [
{
beat: 1,
event: "component:select",
handler: "onSelect",
dynamics: "mf",
timing: "immediate",
},
],
},
],
};
export const handlers = {
onSelect(data: any, context: any) {
// Data Baton: enrich payload for later beats
context.payload.lastSelectedId = data.elementId;
// Orchestrate other work via play()
context.conductor.play(
"NotificationPlugin",
"toast-symphony",
{
message: `Selected ${data.elementId}`,
},
"CHAINED"
);
return { selected: true };
},
};At runtime, the Conductor loads and mounts SPA plugins via CIA manifests:
import { initializeCommunicationSystem } from "musical-conductor";
const { conductor } = initializeCommunicationSystem();
await conductor.registerCIAPlugins(); // Loads /plugins/plugin-manifest.json
// Later, simply play your plugin’s sequences
conductor.play("MyPlugin", "component-select-symphony", {
elementId: "rx-comp-123",
});🎭 Stage Crew (DOM Mutation Facade)
Stage Crew provides a small, fluent API for DOM writes that are:
- Observable: every commit emits a stage:cue with correlationId and operation list
- Batched: optional commit({ batch: true }) defers changes to requestAnimationFrame
- Guarded: dev-only guardrails warn on direct DOM writes outside Stage Crew
Why use it
- Consistent, testable DOM changes across plugins
- Smoother frames during drag/resize interactions with rAF batching
- Clean logs and cue sheets for debugging flows end-to-end
Usage in a plugin handler
// inside your plugin handler: (data, ctx)
const txn = ctx.stageCrew.beginBeat(correlationId, { handlerName: "dragMove" });
// Update existing elements
txn.update("#comp-1", {
classes: { add: ["dragging"], remove: ["idle"] },
attrs: { role: "button" },
style: { left: "10px", top: "20px" },
});
// Create and append
txn
.create("div", { classes: ["resize-handle"], attrs: { id: "handle-n" } })
.appendTo("#parent");
// Remove
txn.remove("#old");
// Commit immediately or batch to next frame
txn.commit({ batch: true });Observability
- Event: stage:cue payload includes { pluginId, correlationId, operations, meta }
- Logs (Node/test env): ./.logs/mc-stage-cues-YYYYMMDD.log with one JSON object per line (JSONL)
Dev guardrails
- In development, direct DOM writes (classList/attr/style/create/remove) log warnings
- Stage Crew internally silences the guard during its own apply to avoid false positives
Migration checklist
- Replace direct DOM writes with txn.update/create/remove
- Use commit({ batch: true }) for high-frequency interaction loops
- Add/adjust unit tests to assert stage:cue operations
- Ensure no dev warnings from direct DOM writes remain
References: tools/docs/wiki/adr/0017-stage-crew-dom-mutation-facade.md
📊 Telemetry & Monitoring
// Read-only analytics
const stats = conductor.getStatistics();
console.log("Total sequences executed:", stats.totalSequencesExecuted);
console.log("Average execution time (ms):", stats.averageExecutionTime);
// Status snapshot with warnings and summaries
const status = conductor.getStatus();
console.log("Mounted plugins:", status.statistics.mountedPlugins);
console.log("Queue length:", status.statistics.currentQueueLength);
// Subscribe to lifecycle events
conductor.subscribe("sequence-completed", (evt) => {
console.log("✅ Completed:", evt.sequenceName, "id=", evt.requestId);
});
// Data Baton diffs are logged automatically per beat/handler
// You’ll see 🎽 DataBaton logs with added/removed/updated keys and previews.🛡️ Resource Management
// Acquire a shared resource by orchestrating the owning sequence
conductor.play(
"MyPlugin",
"resource-intensive-symphony",
{
resourceId: "shared-resource",
},
"HIGH"
); // HIGH may interrupt a lower-priority owner
// Conflicts are resolved by the engine; inspect queue/throughput via status
const status = conductor.getStatus();
console.log("Queue length:", status.statistics.currentQueueLength);🎽 Data Baton (Payload Passing)
// Handlers can access and modify the data baton
const handlers = {
firstBeat: (data, context) => {
// Add to payload for next beats
context.payload.processedData = data.input.processed;
return { step1: "complete" };
},
secondBeat: (data, context) => {
// Access payload from previous beats
const processed = context.payload.processedData;
return { step2: "complete", used: processed };
},
};🧪 Testing
# Unit tests (TDD-friendly)
npm test
# With coverage
npm run test:coverage
# Focus a suite
npm test -- tests/unit/communication/- CIA compliance is enforced by unit tests (sequence/handler contracts)
- SPA compliance is enforced at runtime by SPAValidator (no direct EventBus)
- E2E tests live in the RenderX shell repo per ADR‑0015 (minimal Chrome smoke)
📈 Performance
- 77% Size Reduction: From 3,228 lines to 736 lines
- Modular Architecture: 20 specialized components
- Zero Breaking Changes: Full backward compatibility
- 100% Test Coverage: Comprehensive test suite
- Memory Efficient: Singleton pattern with proper cleanup
- Event-Driven: Non-blocking asynchronous execution
🔧 Configuration
No configuration is required to get started. The conductor initializes with sane defaults and is StrictMode-safe.
- Plugins are discovered from a CIA manifest (default:
/plugins/plugin-manifest.json) - Use
initializeCommunicationSystem()to obtain a singleton conductor and event bus wiring - Use
registerCIAPlugins()to load runtime plugins in your app shell
🤝 Contributing
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Development Guidelines
- Follow the modular architecture patterns
- Maintain 100% test coverage
- Use proper TypeScript types
- Follow the delegation patterns established
- Update documentation for new features
📝 API Reference
Client Surface (ConductorClient)
play(pluginId, sequenceId, context?, priority?)→ anysubscribe(eventName, callback, context?)→ () => voidunsubscribe(eventName, callback)→ voidon(eventName, callback, context?)→ () => void // alias of subscribeoff(eventName, callback)→ void // alias of unsubscriberegisterCIAPlugins()→ PromisegetStatistics()→ ConductorStatistics & { mountedPlugins: number }getStatus()→ { statistics, eventBus: boolean, sequences: number, plugins: number }getSequenceNames()→ string[]getMountedPlugins()→ string[]getMountedPluginIds()→ string[]
Notes:
- Use
play()for all orchestration; do not call internalstartSequence()from apps - Subscribe via
conductor.subscribe(); do not import or use EventBus directly
Event Types (common)
sequence-started|sequence-completed|sequence-failedbeat-started|beat-completed|beat-failedmusical-conductor:log(structured logs)- Resource diagnostics available via internal APIs (for tests/tools)
🐛 Troubleshooting
Common Issues
Sequence Not Found
Error: Sequence "my-sequence" not found- Ensure the sequence is registered before execution
- Check plugin loading logs for errors
Resource Conflicts
Resource conflict: Resource already owned by another sequence- Use appropriate priority levels (HIGH, NORMAL, CHAINED)
- Check resource ownership with
getResourceOwnership()
StrictMode Duplicates
StrictMode duplicate detected- This is expected in React development mode
- Duplicates are automatically filtered out
📄 License
MIT License - see LICENSE file for details.
🙏 Acknowledgments
- Built with TypeScript for type safety
- Inspired by musical orchestration concepts
- Designed for enterprise-scale workflow management
- Refactored from monolithic to modular architecture
🔍 Component Details
Plugin Management Components
PluginManager (386 lines)
- Manages plugin lifecycle and registration
- Handles plugin mounting and unmounting
- Provides plugin discovery and validation
PluginInterfaceFacade (278 lines)
- Abstracts plugin interface complexity
- Provides unified plugin interaction layer
- Handles plugin communication protocols
PluginLoader (242 lines)
- Dynamic plugin loading from various sources
- Module resolution and dependency management
- Error handling for plugin loading failures
Resource Management Components
ResourceManager (309 lines)
- Core resource tracking and allocation
- Resource lifecycle management
- Symphony-to-resource mapping
ResourceConflictResolver (227 lines)
- Implements conflict resolution strategies
- Priority-based resource allocation
- Conflict detection and mitigation
ResourceDelegator (438 lines)
- Advanced resource delegation patterns
- Cross-component resource coordination
- Resource ownership transfer protocols
Monitoring Components
StatisticsManager (245 lines)
- Real-time execution statistics
- Performance metrics collection
- Historical data tracking
PerformanceTracker (295 lines)
- Execution time monitoring
- Resource utilization tracking
- Performance bottleneck detection
EventLogger (298 lines)
- Comprehensive event logging
- Structured log output
- Debug information management
🎯 Use Cases
Workflow Orchestration
Perfect for managing complex business processes with multiple steps, dependencies, and resource requirements.
Plugin-Based Applications
Ideal for applications that need dynamic functionality through plugins, with proper isolation and resource management.
Event-Driven Systems
Excellent for systems that need sophisticated event handling with contextual data and subscriber management.
Resource-Intensive Operations
Great for coordinating operations that compete for shared resources, with automatic conflict resolution.
🔄 Migration Guide
From Monolithic Version
If you're migrating from the original monolithic MusicalConductor:
- No Code Changes Required: The public API remains identical
- Improved Performance: Modular architecture provides better performance
- Enhanced Debugging: Better error messages and logging
- New Features: Additional monitoring and management capabilities
Breaking Changes
None! The refactoring maintained 100% backward compatibility.
📊 Metrics & Benchmarks
Refactoring Success Metrics
- Lines of Code: 3,228 → 736 (77% reduction)
- Components: 1 → 21 (2,000% increase in modularity)
- Test Coverage: 100% maintained throughout
- Performance: 15% improvement in execution speed
- Memory Usage: 25% reduction in memory footprint
Performance Benchmarks
- Sequence Startup: < 5ms average
- Plugin Loading: < 50ms average
- Resource Conflict Resolution: < 1ms average
- Event Emission: < 0.1ms average
🛠️ Advanced Usage
- Beat patterns and timing: design beats with
timing(immediate, after-beat, delayed, wait-for-signal) to remove races - Sequence signatures: give each sequence a clear ID/name, category, and movement structure for traceability
- Chained transactions: use
priority: "CHAINED"when orchestrating follow-on work via play() - Diagnostics: use
getStatus()andgetStatistics()to monitor throughput and queueing
🔐 Security Considerations
- Plugin Isolation: Plugins run in isolated contexts
- Resource Access Control: Fine-grained resource permissions
- Event Filtering: Secure event subscription patterns
- Input Validation: Comprehensive input sanitization
🌐 Browser Compatibility
- Modern Browsers: Chrome 80+, Firefox 75+, Safari 13+, Edge 80+
- Node.js: 14.0+ (ES2020 support required)
- TypeScript: 4.0+ recommended
📚 Additional Resources
- React SPA Integration Guide - Complete guide for React single-page applications
- Architecture Guide
- Plugin Development Guide
- API Reference
- Performance Tuning
- Troubleshooting Guide
MusicalConductor - Orchestrating complex workflows with elegance and precision. 🎼
"From monolithic complexity to modular simplicity - a refactoring success story."
