Terminal capture, signature triage, and edge-triggered alerts #193

Merged
clawdie merged 3 commits from terminal-capture-and-alerts into main 2026-06-25 21:28:26 +02:00
Owner

What

Adds the terminal-capture + signature-triage half of Glasspane — the complement to its event-state model. Ports the brain of the clawdie-ai tmux-screenshot skill (not the PNG) into the Rust core and wires it into the daemon as an autonomous poll loop with edge-triggered alerts.

colibri-glasspane (new modules)

  • terminal.rsstrip_ansi, content-hash frame ids (sha256[:12]), CapturedFrame, and TerminalRecorder: a deduped ring buffer that drops frames identical to the previous one (so polling a near-static pane collapses into a compact log of real transitions) with edge-triggered alerting (a failure fires once on its rising edge; re-fires only after it clears). capture_tmux_pane is a thin seam so the dedup/triage core is testable without a terminal.
  • signatures.rs — data-driven Severity/Signature/Detection/SignatureSet matcher + a high-value linux_default() set (systemd/oom/disk/docker/forwarding). The per-OS set is the hook into capability-routing.

colibri-daemon (wiring)

  • DaemonState.terminal map of per-pane recorders; poll tick in run_loop gated on COLIBRI_TERMINAL_CAPTURE, seeded from COLIBRI_TERMINAL_WATCH.
  • capture_and_record() shares the blocking tmux capture (on spawn_blocking) + brief-lock fold between the loop and the socket; env-gated Telegram alert routing that no-ops cleanly when unconfigured.
  • Socket commands: terminal-watch / terminal-unwatch / terminal-list / terminal-history / terminal-poll.
  • env_bool helper: forgiving truthy parsing (1/true/yes/on) so COLIBRI_TERMINAL_CAPTURE=1 is not silently false (Rust's bool::from_str only accepts true/false).

Testing

  • 17 new glasspane unit tests + daemon socket/config tests. Whole workspace green, clippy clean.
  • Verified live on Linux (domedog): an autonomous 1s poll loop deduped ~5 ticks into 2 frames and fired one edge-triggered alert at the moment a pane's state broke, then latched it in active_alerts.

Notes / follow-ups (not in this PR)

  • PNG render intentionally stays the Python script's job (lazy human view); Colibri owns capture + history + triage.
  • Only linux_default() signatures exist so far — a FreeBSD set is the next per-OS addition.
  • Pre-existing bool flags (COLIBRI_CACHE_WARMING, COLIBRI_HEADROOM_ENABLED) share the bool::from_str footgun; left as-is (out of scope).

🤖 Generated with Claude Code

## What Adds the **terminal-capture + signature-triage** half of Glasspane — the complement to its event-state model. Ports the *brain* of the clawdie-ai `tmux-screenshot` skill (not the PNG) into the Rust core and wires it into the daemon as an autonomous poll loop with edge-triggered alerts. ## colibri-glasspane (new modules) - **`terminal.rs`** — `strip_ansi`, content-hash frame ids (`sha256[:12]`), `CapturedFrame`, and `TerminalRecorder`: a deduped ring buffer that drops frames identical to the previous one (so polling a near-static pane collapses into a compact log of *real* transitions) with **edge-triggered** alerting (a failure fires once on its rising edge; re-fires only after it clears). `capture_tmux_pane` is a thin seam so the dedup/triage core is testable without a terminal. - **`signatures.rs`** — data-driven `Severity`/`Signature`/`Detection`/`SignatureSet` matcher + a high-value `linux_default()` set (systemd/oom/disk/docker/forwarding). The per-OS set is the hook into capability-routing. ## colibri-daemon (wiring) - `DaemonState.terminal` map of per-pane recorders; poll tick in `run_loop` gated on `COLIBRI_TERMINAL_CAPTURE`, seeded from `COLIBRI_TERMINAL_WATCH`. - `capture_and_record()` shares the blocking tmux capture (on `spawn_blocking`) + brief-lock fold between the loop and the socket; **env-gated Telegram** alert routing that no-ops cleanly when unconfigured. - Socket commands: `terminal-watch` / `terminal-unwatch` / `terminal-list` / `terminal-history` / `terminal-poll`. - `env_bool` helper: forgiving truthy parsing (`1`/`true`/`yes`/`on`) so `COLIBRI_TERMINAL_CAPTURE=1` is not silently `false` (Rust's `bool::from_str` only accepts `true`/`false`). ## Testing - 17 new glasspane unit tests + daemon socket/config tests. **Whole workspace green, clippy clean.** - Verified **live on Linux (domedog)**: an autonomous 1s poll loop deduped ~5 ticks into **2 frames** and fired **one** edge-triggered alert at the moment a pane's state broke, then latched it in `active_alerts`. ## Notes / follow-ups (not in this PR) - PNG render intentionally stays the Python script's job (lazy human view); Colibri owns capture + history + triage. - Only `linux_default()` signatures exist so far — a FreeBSD set is the next per-OS addition. - Pre-existing bool flags (`COLIBRI_CACHE_WARMING`, `COLIBRI_HEADROOM_ENABLED`) share the `bool::from_str` footgun; left as-is (out of scope). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
clawdie added 1 commit 2026-06-25 20:51:31 +02:00
feat(glasspane): terminal capture, signature triage, and edge-triggered alerts
Some checks are pending
CI / rust (pull_request) Waiting to run
CI / markdown (pull_request) Waiting to run
CI / port (pull_request) Waiting to run
CI / agent-jail-pkgs (pull_request) Waiting to run
0509ed76bc
Add the screen-scraping half of Glasspane to complement its event-state
model. Ports the *brain* of the clawdie-ai tmux-screenshot skill (not the
PNG) into the Rust core and wires it into the daemon.

colibri-glasspane:
- terminal.rs: strip_ansi, content-hash frame ids (sha256[:12]),
  CapturedFrame, and TerminalRecorder — a deduped ring buffer that drops
  frames identical to the previous one (so polling a static pane collapses
  to a log of real transitions) with edge-triggered alerting (a failure
  fires once on its rising edge, re-fires only after it clears). Thin
  capture_tmux_pane seam keeps I/O out of the testable core.
- signatures.rs: data-driven Severity/Signature/Detection/SignatureSet
  matcher with a high-value linux_default() set (systemd/oom/disk/docker/
  forwarding). Per-OS set is the hook for capability-routing.

colibri-daemon:
- DaemonState.terminal map of per-pane recorders; poll tick in run_loop
  gated on COLIBRI_TERMINAL_CAPTURE, seeded from COLIBRI_TERMINAL_WATCH.
- capture_and_record() shares the blocking tmux capture (on spawn_blocking)
  + brief lock fold between the loop and the socket; env-gated Telegram
  alert routing that no-ops cleanly when unconfigured.
- socket cmds: terminal-watch/unwatch/list/history/poll.
- env_bool helper: forgiving truthy parsing (1/true/yes/on) so
  COLIBRI_TERMINAL_CAPTURE=1 is not silently false like bool::from_str.

Tests: 17 new glasspane unit tests + daemon socket/config tests; whole
workspace green, clippy clean. Verified live on Linux (domedog): autonomous
loop deduped ~5 ticks into 2 frames and fired one edge-triggered alert.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
claude-domedog added 1 commit 2026-06-25 20:54:35 +02:00
fix(config): forgiving bool parsing for pre-existing feature flags
Some checks are pending
CI / rust (pull_request) Waiting to run
CI / markdown (pull_request) Waiting to run
CI / port (pull_request) Waiting to run
CI / agent-jail-pkgs (pull_request) Waiting to run
b2f5d8f355
scheduler_prompt_injection, cache_warming_enabled, and headroom_enabled
used env_parse::<bool>, i.e. bool::from_str, which accepts only "true"/
"false". Any other truthy spelling (1/yes/on/TRUE) silently parsed to
false — the feature failed closed with no error or log. Switch them to
the same env_bool helper added for terminal capture so =1/yes/on now work
as operators expect. Backward compatible: true/false keep their meaning.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
claude-domedog added 1 commit 2026-06-25 21:01:27 +02:00
style: rustfmt — fix fmt drift introduced by terminal-capture commit
Some checks are pending
CI / rust (pull_request) Waiting to run
CI / markdown (pull_request) Waiting to run
CI / port (pull_request) Waiting to run
CI / agent-jail-pkgs (pull_request) Waiting to run
7980b09ddb
cargo fmt --all --check flagged 10 drift sites across 4 files, all from the
terminal-capture commit (0509ed7): config.rs (env_bool matches! one-liner),
socket.rs (serde_json! block), signatures.rs + terminal.rs (long arg lists and
json! blocks). Pure line-wrapping — no logic changes.

Unblocks the fmt half of the workspace gate (fmt/clippy/test/release) for PR
#193.

Verified: fmt clean, clippy -D warnings clean, config tests pass (env_bool +
defaults + from_env_vars).

(Sam & Claude)
clawdie merged commit 4bdcea18d5 into main 2026-06-25 21:28:25 +02:00
clawdie deleted branch terminal-capture-and-alerts 2026-06-25 21:28:28 +02:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: clawdie/colibri#193
No description provided.