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

100 lines
3.9 KiB
Markdown

# 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:
```rust
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
}
```
### Footer keybinding pattern
Lifetime issues with inline closures capturing `&str` → fix by using
`format!()` inside the closure instead of passing the reference:
```rust
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
## Related
- 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`