Skip to content

conductor

conductor

Conductor commands — mzt start/stop/restart/conductor-status.

These commands consolidate all daemon lifecycle management into the main marianne CLI. The core logic lives in marianne.daemon.process (shared functions); this module provides thin Typer command wrappers.

Functions

start

start(config_file=Option(None, '--config', '-c', help='YAML config file'), foreground=Option(False, '--foreground', '-f', help='Run in foreground'), log_level=Option('info', '--log-level', '-l', help='Log level'), profile=Option(None, '--profile', '-p', help='Daemon operational profile (dev, intensive, minimal). Overrides config file defaults.'), conductor_clone=None)

Start the Marianne conductor.

Source code in src/marianne/cli/commands/conductor.py
def start(
    config_file: Path | None = typer.Option(None, "--config", "-c", help="YAML config file"),
    foreground: bool = typer.Option(False, "--foreground", "-f", help="Run in foreground"),
    log_level: str = typer.Option("info", "--log-level", "-l", help="Log level"),
    profile: str | None = typer.Option(
        None, "--profile", "-p",
        help="Daemon operational profile (dev, intensive, minimal). "
        "Overrides config file defaults.",
    ),
    conductor_clone: Annotated[
        str | None,
        typer.Option(
            "--conductor-clone",
            help="Start a clone conductor for safe testing. "
            "Use --conductor-clone= (with equals) for default clone, "
            "or --conductor-clone=NAME for a named clone. "
            "Overrides global --conductor-clone if both are given.",
        ),
    ] = None,
) -> None:
    """Start the Marianne conductor."""
    from marianne.daemon.clone import get_clone_name, is_clone_active, set_clone_name
    from marianne.daemon.process import start_conductor

    # Determine which clone name to use
    clone_to_use: str | None = None
    if conductor_clone is not None:
        # Command-level --conductor-clone overrides global flag
        set_clone_name(conductor_clone)
        clone_to_use = conductor_clone
    elif is_clone_active():
        # Use global --conductor-clone
        clone_to_use = get_clone_name()

    start_conductor(
        config_file=config_file,
        foreground=foreground,
        log_level=log_level,
        profile=profile,
        clone_name=clone_to_use,
    )

stop

stop(pid_file=Option(None, '--pid-file', help='PID file path'), force=Option(False, '--force', help='Send SIGKILL instead of SIGTERM'), conductor_clone=None)

Stop the Marianne conductor.

When jobs are actively running, warns and asks for confirmation. Use --force to skip the safety check and send SIGKILL.

Source code in src/marianne/cli/commands/conductor.py
def stop(
    pid_file: Path | None = typer.Option(None, "--pid-file", help="PID file path"),
    force: bool = typer.Option(False, "--force", help="Send SIGKILL instead of SIGTERM"),
    conductor_clone: Annotated[
        str | None,
        typer.Option(
            "--conductor-clone",
            help="Stop a clone conductor. "
            "Use --conductor-clone= (with equals) for default clone, "
            "or --conductor-clone=NAME for a named clone. "
            "Overrides global --conductor-clone if both are given.",
        ),
    ] = None,
) -> None:
    """Stop the Marianne conductor.

    When jobs are actively running, warns and asks for confirmation.
    Use --force to skip the safety check and send SIGKILL.
    """
    from marianne.daemon.clone import get_clone_name, is_clone_active, set_clone_name
    from marianne.daemon.process import stop_conductor

    # Determine which clone name to use
    clone_name_to_use: str | None = None
    if conductor_clone is not None:
        # Command-level --conductor-clone overrides global flag
        set_clone_name(conductor_clone)
        clone_name_to_use = conductor_clone
    elif is_clone_active():
        # Use global --conductor-clone
        clone_name_to_use = get_clone_name()

    socket_path: Path | None = None
    if clone_name_to_use is not None and pid_file is None:
        from marianne.daemon.clone import resolve_clone_paths

        clone = resolve_clone_paths(clone_name_to_use)
        pid_file = clone.pid_file
        socket_path = clone.socket

    stop_conductor(pid_file=pid_file, force=force, socket_path=socket_path)

restart

restart(config_file=Option(None, '--config', '-c', help='YAML config file'), foreground=Option(False, '--foreground', '-f', help='Run in foreground'), log_level=Option('info', '--log-level', '-l', help='Log level'), pid_file=Option(None, '--pid-file', help='PID file path'), profile=Option(None, '--profile', '-p', help='Daemon operational profile (dev, intensive, minimal). Overrides config file defaults.'), conductor_clone=None)

Restart the Marianne conductor (stop + start).

Source code in src/marianne/cli/commands/conductor.py
def restart(
    config_file: Path | None = typer.Option(None, "--config", "-c", help="YAML config file"),
    foreground: bool = typer.Option(False, "--foreground", "-f", help="Run in foreground"),
    log_level: str = typer.Option("info", "--log-level", "-l", help="Log level"),
    pid_file: Path | None = typer.Option(None, "--pid-file", help="PID file path"),
    profile: str | None = typer.Option(
        None, "--profile", "-p",
        help="Daemon operational profile (dev, intensive, minimal). "
        "Overrides config file defaults.",
    ),
    conductor_clone: Annotated[
        str | None,
        typer.Option(
            "--conductor-clone",
            help="Restart a clone conductor. "
            "Use --conductor-clone= (with equals) for default clone, "
            "or --conductor-clone=NAME for a named clone. "
            "Overrides global --conductor-clone if both are given.",
        ),
    ] = None,
) -> None:
    """Restart the Marianne conductor (stop + start)."""
    from marianne.daemon.clone import get_clone_name, is_clone_active, set_clone_name
    from marianne.daemon.process import (
        start_conductor,
        stop_conductor,
        wait_for_conductor_exit,
    )

    # Determine which clone name to use
    clone_name: str | None = None
    if conductor_clone is not None:
        # Command-level --conductor-clone overrides global flag
        set_clone_name(conductor_clone)
        clone_name = conductor_clone
    elif is_clone_active():
        # Use global --conductor-clone
        clone_name = get_clone_name()

    # When clone is active, redirect PID file to clone's PID
    if clone_name is not None:
        from marianne.daemon.clone import resolve_clone_paths

        if pid_file is None:
            pid_file = resolve_clone_paths(clone_name).pid_file

    # Stop (ignore exit if not running)
    try:
        stop_conductor(pid_file=pid_file)
    except SystemExit:
        pass

    # Wait for old process to fully exit before starting the new one.
    # Without this, start_conductor sees the dying process and says
    # "already running" (race condition).
    if not wait_for_conductor_exit(pid_file, timeout=30.0):
        output_error(
            "Old conductor did not exit within 30 seconds.",
            hints=["Try 'mzt stop --force' to send SIGKILL."],
        )
        raise typer.Exit(1)

    start_conductor(
        config_file=config_file,
        foreground=foreground,
        log_level=log_level,
        profile=profile,
        clone_name=clone_name,
    )

conductor_status

conductor_status(pid_file=Option(None, '--pid-file', help='PID file path'), socket_path=Option(None, '--socket', help='Unix socket path'))

Check Marianne conductor status.

Source code in src/marianne/cli/commands/conductor.py
def conductor_status(
    pid_file: Path | None = typer.Option(None, "--pid-file", help="PID file path"),
    socket_path: Path | None = typer.Option(None, "--socket", help="Unix socket path"),
) -> None:
    """Check Marianne conductor status."""
    from marianne.daemon.clone import get_clone_name, is_clone_active
    from marianne.daemon.process import get_conductor_status

    # When clone is active, redirect to clone PID and socket
    if is_clone_active():
        from marianne.daemon.clone import resolve_clone_paths

        clone_paths = resolve_clone_paths(get_clone_name())
        if pid_file is None:
            pid_file = clone_paths.pid_file
        if socket_path is None:
            socket_path = clone_paths.socket

    get_conductor_status(pid_file=pid_file, socket_path=socket_path)