layered-soul/skills/herdr-deployment/references/colibri-harness.md
Sam & Claude 4d8ce07fa7 docs: apply Prettier to current markdown (Sam & Codex)
Normalize markdown formatting after the latest main updates.\n\nChecks: python3 scripts/layered_soul.py validate .; npx --yes prettier@3 --check '**/*.md'; git diff --check.
2026-06-14 01:48:32 +02:00

3.9 KiB

Colibri-Harness: Herdr-like TUI on Colibri Primitives

The colibri-glasspane-tui crate (binary: colibri-tui) was upgraded to a herdr-like supervision dashboard — now called colibri-harness — on commit eb37784 (2026-05-27, Sam & Hermes).

Relationship to Herdr

Herdr colibri-harness
What Terminal workspace manager (full tmux) Single-screen pane supervision dashboard
Protocol Herdr binary frame diffs over SSH Colibri GlasspaneSnapshot JSON over Unix socket
FreeBSD Parked — Linux/macOS only Native (via colibri-daemon on FreeBSD)
Agent spawn Native PTY spawn Calls daemon to spawn agents
Session mgmt Workspaces, tabs, BSP splits Session filter cycling (tab key)
State model PTY lifecycle 5-state (Idle/Working/Blocked/Done/Error)

Boundary (decided 2026-05-27): Herdr = Linux/macOS display/remote plane behind Colibri's socket API; colibri-glasspane = native FreeBSD answer. No herdr vendoring, no herdr-on-FreeBSD in the core.

Keybindings

 q      quit / close detail
 s      spawn agent (local provider → colibri-smoke-agent)
 x      kill selected pane
 Enter  open/close detail popup
 tab    next session filter
 shift+tab  previous session filter
 j/k    navigate pane rows
 r      manual refresh

App Architecture

The App struct holds:

  • DaemonClient — talks to daemon socket
  • GlasspaneSnapshot — polled every 2s
  • TableState — ratatui row selection
  • status_msg + TTL — auto-decaying cyan feedback
  • detail_pane — index into filtered panes for Enter popup
  • session_filter — optional pi_session_id filter
  • sessions / session_idx — cycled via tab

Ratatui borrow-checker pattern

render_table() calls filtered_panes() which borrows self immutably, then render_stateful_widget() borrows self.table_state mutably. Fix: collect panes as owned Vec<Pane> before rendering:

fn render_table(&mut self, f: &mut Frame, area: Rect) {
    let panes: Vec<Pane> = self.filtered_panes().iter().map(|p| (*p).clone()).collect();
    // ... use panes, then render_stateful_widget with self.table_state
}

Lifetime issues with inline closures capturing &str → fix by using format!() inside the closure instead of passing the reference:

fn render_footer(&self, f: &mut Frame, area: Rect) {
    let key = |label: &str| -> Span {
        Span::styled(
            format!(" {label}"),  // format! avoids lifetime issue
            Style::default().add_modifier(Modifier::BOLD).fg(Color::White).bg(Color::DarkGray),
        )
    };
    // key("q"), key("s"), etc.
}

Smoke Agent Integration

The TUI spawns colibri-smoke-agent as a local provider via the daemon socket. The smoke agent defaults to session ID "manual-smoke" (overridable with --session-id). Key behaviors:

  • Spawn → daemon spawns process → smoke agent writes JSONL → PiJsonlIngestor feeds state machine → snapshot reflects Idle→Working→Blocked→Done
  • Kill sends KillAgent command → daemon stops process
  • Snapshot may briefly retain killed panes (async cleanup)

Test Coverage

  • Unit tests: 5 (TUI helpers: short_observed_at, state_color, state_icon, filtered_panes, rebuild_session_list)
  • Integration smoke: live_socket_smoke.rs — 2 tests (single agent lifecycle
    • double spawn session isolation)
  • Workspace: 65 tests total (as of eb37784), clippy-clean
  • herdr-deployment skill (this skill)
  • clawdie-ai/docs/internal/sessions/2026-05-27-herdr-tailscale-remote-smoke.md
  • Colibri repo: git@codeberg.org:Clawdie/Colibri.git