feat: add supervisor abstraction with native dev fallback for Docker#63
feat: add supervisor abstraction with native dev fallback for Docker#63edgarriba merged 13 commits intokornia:mainfrom
Conversation
- Add Supervisor dispatcher with systemd and native backends - Implement NativeSupervisor for Docker/non-systemd development - Migrate NodeManager lifecycle/build/status flows to Supervisor - Clarify systemd-only journalctl behavior in CLI and docs - Document Linux/systemd as supported production path
- native_supervisor: 5 tests (lifecycle, autostart, stale PID, errors, signals) - supervisor dispatcher: 2 tests (is_native, full lifecycle delegation) - node_manager: 2 tests (get_logs native message, start/stop/restart e2e) - lifecycle CLI: regression guard for systemd error message - daemon_client: regression guard for systemd error message
- fix(native_supervisor): replace drop(child) with child.wait().await in
watcher task — drop sends SIGKILL immediately, killing the spawned node
- fix(native_supervisor): redirect child stdout/stderr to
~/.bubbaloop/procs/{name}.{stdout,stderr} to avoid polluting daemon logs
- fix(native_supervisor): remove misleading JobRemoved event emitted on
start_unit; refresh_node handles state update
- fix(native_supervisor): document PID-race caveat in stop_unit doc comment
- fix(docker-compose): bind ports to 127.0.0.1 instead of 0.0.0.0
- fix(docker-compose): remove unused BUBBALOOP_ALLOW_NO_SYSTEMD env var
- test: remove tautological string tests in daemon_client and node lifecycle
that verified hardcoded strings against themselves
docs: add code review notes to docs/plans/
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Review Summary by QodoAdd supervisor abstraction with native fallback for Docker development
WalkthroughsDescription• Add supervisor abstraction layer with systemd and native backends - Supervisor::detect() auto-selects systemd (Linux) or native (Docker/dev) - NativeSupervisor manages processes via tokio::process with PID persistence • Migrate NodeManager lifecycle/build/status to use Supervisor instead of direct systemd - Removes systemd coupling from node start/stop/restart/install/uninstall flows - Simplifies service name handling by delegating to backend • Clarify systemd-only features in CLI and documentation - journalctl logs now explicitly marked as systemd backend only - Native fallback returns helpful message instead of cryptic errors • Add comprehensive test coverage for native supervisor and dispatcher - 5 native supervisor tests (lifecycle, autostart, stale PID, errors, signals) - 2 supervisor dispatcher tests (backend detection, full lifecycle delegation) - 2 node manager integration tests (native logs message, e2e start/stop/restart) • Add devcontainer with Docker Compose for development without systemd - Ubuntu 22.04 base with Rust, GStreamer, D-Bus, protobuf, Node.js - SSH key mounting and port forwarding for local development Diagramflowchart LR
A["NodeManager"] -->|uses| B["Supervisor"]
B -->|detect| C{"D-Bus available?"}
C -->|yes| D["SystemdClient"]
C -->|no| E["NativeSupervisor"]
D -->|manages| F["systemd services"]
E -->|manages| G["tokio::process + PID files"]
F -->|provides| H["journalctl logs"]
G -->|provides| I["stdout/stderr files"]
File Changes1. crates/bubbaloop/src/daemon/supervisor.rs
|
Code Review by Qodo
1.
|
There was a problem hiding this comment.
Pull request overview
Introduces a unified supervisor abstraction to decouple node/service lifecycle management from systemd, adding a native (non-systemd) development fallback primarily for Docker environments.
Changes:
- Added
Supervisordispatcher that selects a systemd backend when D-Bus is available and otherwise falls back to a native supervisor. - Implemented
NativeSupervisorto manage node processes viatokio::processwith on-disk PID/config persistence. - Refactored
NodeManagerlifecycle/status/logs paths to route throughSupervisor, and updated docs/devcontainer assets to clarify systemd vs Docker behavior.
Reviewed changes
Copilot reviewed 14 out of 15 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| README.md | Clarifies that Linux+systemd is the supported production path and Docker uses a dev-only fallback. |
| docs/getting-started/installation.md | Updates platform support matrix and explains non-systemd fallback limitations. |
| crates/bubbaloop/src/daemon/supervisor.rs | Adds supervisor dispatcher and basic tests for native delegation. |
| crates/bubbaloop/src/daemon/node_manager/mod.rs | Switches signal subscription/state queries to Supervisor and returns explicit logs guidance in native mode. |
| crates/bubbaloop/src/daemon/node_manager/lifecycle.rs | Routes lifecycle install/start/stop/restart/autostart through Supervisor. |
| crates/bubbaloop/src/daemon/node_manager/build.rs | Uses Supervisor for pre-build stop-if-running behavior. |
| crates/bubbaloop/src/daemon/native_supervisor.rs | Implements native process supervisor with config/PID persistence and lifecycle signaling. |
| crates/bubbaloop/src/daemon/mod.rs | Exposes new supervisor modules and generalizes signal-listener log messages. |
| crates/bubbaloop/src/cli/node/lifecycle.rs | Clarifies that journalctl -f log following is systemd-only and improves error text. |
| crates/bubbaloop/src/cli/daemon.rs | Documents daemon logs command as systemd-only. |
| crates/bubbaloop/src/cli/daemon_client.rs | Improves journalctl failure messaging for daemon logs. |
| Cargo.lock | Bumps bubbaloop version to 0.0.12-dev. |
| .devcontainer/Dockerfile | Adds devcontainer image for building/running in Docker. |
| .devcontainer/docker-compose.yml | Defines dev service plus a zenoh router dependency. |
| .devcontainer/devcontainer.json | Configures VS Code devcontainer extensions, ports, and setup commands. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
Summary comment fixes (#5, #6, #7) #5 — Stop may misreport success: After SIGKILL, #6 — Native drops depends_on: #7 — Native autostart not applied: Added |
- pixi.toml: add --features dashboard to `pixi run dash` and `build-release`
tasks (was missing after dashboard was moved from default features)
- native_supervisor: make procs_dir configurable via new_with_root(PathBuf)
so tests use tempfile::tempdir() instead of writing to ~/.bubbaloop/procs
- native_supervisor: guard stop_unit against PID <= 1 (corrupted PID file
could otherwise signal init or a process group)
- native_supervisor: derive default command from node_type when command is
None, matching systemd backend (rust: target/release/<name>, python:
venv/bin/python main.py)
- native_supervisor: make stop_unit idempotent — installed-but-stopped nodes
now return Ok(()) instead of ServiceNotFound
- native_supervisor: check SIGTERM exit status; return error if kill fails
- native_supervisor: fix misleading comment about kill-on-drop behavior
(tokio does not send SIGKILL on drop by default)
- native_supervisor: after SIGKILL, re-check liveness before claiming success;
return OperationFailed without removing PID file if process survives
- native_supervisor: persist depends_on in ProcConfig and emit a warn! at
start time — native backend does not enforce ordering (dev-only fallback)
- native_supervisor: add list_installed_names() and start_autostart_units()
to apply autostart flag at daemon startup (systemd handles this externally,
native mode needs it explicitly)
- supervisor: forward depends_on to native backend; add start_native_autostart()
- node_manager: call start_native_autostart() after initial refresh so
autostart works across daemon restarts in Docker/native mode
- node_manager: fix get_logs() fallback message to point to actual log files
(~/.bubbaloop/procs/{name}.stdout/stderr) instead of suggesting foreground
- node_manager: generalize start_signal_listener() doc comment to cover both
backends; scope D-Bus deadlock warning to systemd only
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- pixi.toml: add --features dashboard to pixi run dash and build-release
tasks (was missing after dashboard was moved from default features)
- native_supervisor: make procs_dir configurable via new_with_root(PathBuf)
so tests use tempfile::tempdir() instead of writing to ~/.bubbaloop/procs
- native_supervisor: guard stop_unit against PID <= 1 (corrupted PID file
could otherwise signal init or a process group)
- native_supervisor: derive default command from node_type when command is
None, matching systemd backend (rust: target/release/<name>, python:
venv/bin/python main.py)
- native_supervisor: make stop_unit idempotent — installed-but-stopped nodes
return Ok(()) instead of ServiceNotFound
- native_supervisor: check SIGTERM exit status; return error if kill fails
- native_supervisor: fix misleading comment about kill-on-drop behavior
(tokio does not send SIGKILL on drop by default)
- native_supervisor: after SIGKILL, re-check liveness before claiming success;
return OperationFailed without removing PID file if process survives
- native_supervisor: persist depends_on in ProcConfig and emit a warn! at
start time; native backend does not enforce ordering (dev-only fallback)
- native_supervisor: add list_installed_names() and start_autostart_units()
to apply autostart flag at daemon startup
- supervisor: forward depends_on to native backend; add start_native_autostart()
- node_manager: call start_native_autostart() after initial refresh so
autostart works across daemon restarts in Docker/native mode
- node_manager: fix get_logs() fallback message to point to actual log files
(~/.bubbaloop/procs/{name}.stdout/stderr)
- node_manager: generalize start_signal_listener() doc comment; scope D-Bus
deadlock warning to systemd backend only
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 15 out of 15 changed files in this pull request and generated 6 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Suppress duplicate JobRemoved event from watcher task when stop_unit already removed the PID file (intentional stop). Watcher now only emits on unexpected exits (crashes), preventing a spurious 'failed' event racing with stop_unit's 'done' event. - Expose NativeSupervisor::procs_dir_path() and Supervisor::native_procs_dir() so callers can use the actual storage path (including /tmp fallback) instead of reading $HOME from the environment. - Fix get_logs() native fallback message to use supervisor.native_procs_dir() instead of $HOME, so the hint is accurate when NativeSupervisor falls back to /tmp. - Refresh NodeManager cache after start_native_autostart() so newly started nodes appear as Running immediately rather than Stopped until next poll. - Document split_whitespace limitation in start_unit (quoted/escaped args and paths with spaces are not supported in the native backend). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 15 out of 15 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…tect()
Add SystemdClient::probe() which calls GetUnit("basic.target") via D-Bus.
Any systemd-level response (including NoSuchUnit) proves the service is
present; a transport/FDO error (ServiceUnknown etc.) means systemd is not
on this bus.
Supervisor::detect() now calls probe() after opening the session connection
so environments with D-Bus but without systemd (containers with a user
dbus-daemon but no systemd PID 1) correctly fall back to NativeSupervisor
instead of selecting a broken systemd backend.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Summary
This PR introduces a supervisor dispatcher for node/service lifecycle management and adds a native fallback backend for non-systemd environments (primarily Docker development).
The goal is to keep Linux + systemd as the supported production path, while allowing daemon/node flows to work in Docker without crashing when D-Bus/systemd is unavailable.
Changes
Why
Validation
Notes