Commit graph

68 commits

Author SHA1 Message Date
3ce2840823 Merge pull request 'feat(mcp): confine external MCP servers in a jail (reuse spawner primitive)' (#38) from feat/jail-external-mcp into main
Some checks are pending
CI / markdown (push) Waiting to run
CI / rust (push) Waiting to run
Reviewed-on: #38
2026-06-13 20:35:26 +02:00
Sam & Claude
87c075d6ba feat(mcp): confine external MCP servers in a jail (reuse spawner primitive)
Some checks failed
CI / rust (pull_request) Has been cancelled
CI / markdown (pull_request) Has been cancelled
External MCP servers are arbitrary third-party binaries — at least as untrusted
as the agents the spawner already jails — but the #36 prototype spawned them
directly on the host. Close that gap by reusing the existing confinement
primitive instead of growing a second one.

- ExternalMcpServer gains `jail: Option<JailConfig>` (#[serde(default)]).
- ExternalMcpSession::start routes Command::new through
  colibri_daemon::spawner::jail_wrap with the shared COLIBRI_JAIL_PRIV_MODE
  policy (mdo live / helper deploy). No jail => unchanged. stdio (incl. the
  piped JSON-RPC stdin/stdout) flows through jexec/jail/mdo unaffected.
- docs/COLIBRI-EXTERNAL-MCP-PROTOTYPE: document the `jail` field + confinement.
- 3 tests (no-jail passthrough, jexec wrap, registry jail deserialize).

colibri-mcp already depends on colibri-daemon, so no new dep. Build/test/clippy/
fmt green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 20:08:24 +02:00
Sam & Claude
c2f631b53c feat(socket): accept jail on the spawn-agent command
Wires JailConfig through the control-plane socket so a jailed agent spawn is
reachable end-to-end:

- ColibriCommand::SpawnAgent gains `jail: Option<JailConfig>` (#[serde(default)],
  so existing/raw JSON clients are unaffected).
- socket dispatch + cmd_spawn_agent thread it onto AgentSpawnConfig.jail, where
  jail_wrap applies it.
- colibri-client::spawn_agent sets jail: None (signature unchanged); the typed
  CLI keeps its own separate Command enum. A client/CLI flag to actually request
  a jail is a follow-up — the socket now carries the field for internal callers
  (scheduler/supervisor) and any JSON client.

daemon+client build clean; daemon lib tests (58) green; clippy -D warnings clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 20:01:02 +02:00
5ce93206b2 feat(mcp): prototype external MCP host tools (Sam & Codex)
Some checks failed
CI / rust (pull_request) Has been cancelled
CI / markdown (pull_request) Has been cancelled
Adds stdio external MCP server registry support to colibri-mcp with read-only discovery by default and explicit COLIBRI_MCP_EXTERNAL_CALL gating for proxying external tools. Also smooths the merged jail-spawn formatting/FreeBSD command parameter edge so repository gates pass.\n\nChecks: cargo test -p colibri-mcp --all-targets; cargo fmt --check; ./scripts/check-format.sh; git diff --check; fake stdio MCP server smoke via colibri-mcp --external-config --external-call
2026-06-13 19:53:21 +02:00
Sam & Claude
66cbc76a5b feat(spawner): JailConfig + jail_wrap for jailed agent spawn
Some checks failed
CI / rust (pull_request) Has been cancelled
CI / markdown (pull_request) Has been cancelled
Implements the spawner half of docs/COLIBRI-JAILED-AGENT-SPAWN-DESIGN.md so
Colibri can confine a spawned agent (e.g. pi) in a FreeBSD jail. zot untouched.

- PrivMode {Mdo, Helper, None}: how the (unprivileged) daemon gets the root that
  jail attach/create needs. Resolved from COLIBRI_JAIL_PRIV_MODE (default mdo —
  the live-USB posture); deployed hosts set helper. Only consulted when a spawn
  requests a jail.
- JailConfig {name, path, ip4, user}: `name` enters a persistent jail (jexec,
  precedence); `path` makes an ephemeral `jail -c command=` that self-cleans on
  exit. Neither set = no-op. (Refines the design's `ephemeral` flag into the
  clearer name-vs-path choice.)
- jail_wrap(): pure (binary,args)->(program,argv) wrapper. No-op without a jail.
  jexec runs without -l so injected COLIBRI_*/provider env is inherited; stdio
  flows through mdo/jexec/jail so glasspane ingestion is unchanged.
- AgentSpawnConfig gains `jail: Option<JailConfig>` (#[serde(default)]); spawn()
  resolves PrivMode/helper once and routes the command through jail_wrap.
- kill(): documented jail teardown semantics + the in-jail process-group reaping
  follow-up.
- 7 jail_wrap unit tests. Full daemon lib suite (58) green; clippy -D warnings clean.

Not wired through the SpawnAgent socket command yet (it builds AgentSpawnConfig
with jail=None) — that protocol field is the next small step.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 19:31:09 +02:00
Sam & Claude
1f2377d4dd cleanup: drop the experimental clawdie mini-binary
Some checks failed
CI / markdown (pull_request) Has been cancelled
CI / rust (pull_request) Has been cancelled
The `clawdie` crate (Telegram+DeepSeek mini-agent over the control-plane core)
was an experimental operator-lane candidate. Per the agent-harness
consolidation, the live USB runs colibri_daemon + the zot agent, and the
deployed `service clawdie` is a reserved name, not this binary — so the
mini-binary is dead weight. Remove it and its now-orphaned docs.

- delete crates/clawdie (leaf crate; nothing depended on it)
- delete packaging/freebsd/clawdie.in (its rc.d candidate)
- delete docs/CLAWDIE-AGENT-WIKI.md + docs/CLAWDIE-BUILD.md (only described it)
- drop it from workspace members + Cargo.lock; tidy the strip-profile comment
- README: 11 → 10 crates, remove the clawdie row
- COLIBRI-TOKENOMICS-TRIFECTA: drop the stale clawdie-lane scope note

No "relay" existed in this repo (already gone). zot is untouched. The Clawdie
brand, the clawdie operator user, and the reserved deployed `service clawdie`
name are unaffected — this only removes the experimental Rust mini-binary.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 19:19:07 +02:00
Sam & Claude
21800a8775 feat(mcp): add colibri-mcp crate — MCP bridge for editor integration (Sam & Claude)
Some checks failed
CI / rust (pull_request) Has been cancelled
CI / markdown (pull_request) Has been cancelled
- 7 Phase 1 tools: status, snapshot, list_tasks, list_skills,
  create_task, intake_task, set_cost_mode
- Write tools gated behind COLIBRI_MCP_WRITE=1 (default read-only)
- stdio JSON-RPC server for MCP protocol compliance
- 10 integration tests with mock Unix socket server
- Uses ColibriCommand/ColibriResponse (post-rename from PR #30)
- Design doc: docs/CLAWDIE-STUDIO-PROPOSAL.md
2026-06-13 12:53:43 +02:00
6e78ea630d docs: clarify Herdr as optional Linux display (Sam & Codex)
Some checks failed
CI / rust (pull_request) Has been cancelled
CI / markdown (pull_request) Has been cancelled
Cleans stale Herdr socket/API naming after the Colibri socket rename, preserves Herdr as an optional Linux/macOS display client, marks the clawdie mini-binary service as experimental rather than ISO/deployed-service contract, and removes old internal session logs.\n\nChecks: ./scripts/check-format.sh; cargo fmt --check; git diff --check; sh -n packaging/freebsd/colibri_daemon.in packaging/freebsd/clawdie.in
2026-06-13 12:29:11 +02:00
Sam & Claude
b11bff2b00 refactor: rename the daemon socket API Herdr* -> Colibri* (Sam & Claude)
Some checks failed
CI / rust (pull_request) Has been cancelled
CI / markdown (pull_request) Has been cancelled
The colibri-daemon's own control-plane socket was named after Herdr (the AGPL
Linux supervision tool whose protocol shape it borrowed), which made logs/types
("Herdr socket API listening", HerdrCommand/HerdrResponse) look like a Herdr
dependency. There is none — no herdr crate, process, or network call. zot is the
agent; this is Colibri's control-plane socket.

Renamed Colibri's OWN API only:
- HerdrCommand -> ColibriCommand, HerdrResponse -> ColibriResponse (daemon defs +
  socket.rs + colibri-client usages).
- log/doc/Cargo strings: "Herdr socket API"/"operator dashboard"/"Herdr Unix
  socket" -> "Colibri control-plane socket" (daemon, clawdie).

Wire-compatible: the JSON `cmd` values come from #[serde(rename=...)] and are
unchanged. Kept legitimate references to Herdr *the tool* (glasspane lineage
"reimplements Herdr's glasspane capability", "Herdr's 5-state model"; client
"display clients (Herdr on Linux…)"; tui "herdr-like").

build + test + clippy -D warnings + fmt --check clean; runtime confirms the
daemon now logs "Colibri control-plane socket listening".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 11:07:58 +02:00
Sam & Claude
6c6420ff2a feat(glasspane): runtime-aware ingestion so zot panes parse precisely (Sam & Claude)
Some checks failed
CI / rust (pull_request) Has been cancelled
CI / markdown (pull_request) Has been cancelled
The daemon already spawns agents and streams stdout JSONL into the glasspane
(cmd_spawn_agent: take_stdout + attach_pane + stream_agent_stdout_to_glasspane),
but the streaming ingestor was Pi-only — zot panes were only *incidentally*
correct (shared lifecycle names) and dropped zot's tool/streaming events to the
default arm.

- PiJsonlIngestor: add a `runtime` field; ingest_line_at normalizes via
  zot_event_type for Zot panes (tool_use_* -> tool_execution_*, response
  success:false -> error, response/usage -> skipped), raw type for Pi/Local.
- SupervisedPane::new_with_runtime + PaneSupervisor::attach_pane_with_runtime
  (existing new/attach_pane_at delegate with Pi — no behavior change).
- socket.rs cmd_spawn_agent: derive runtime from the binary basename
  (`zot` -> Zot, else Pi) and attach the pane with it. The stdout streamer is
  unchanged — it now ingests with the pane's runtime.

Tests: 34 pass incl. a new runtime-aware test feeding RAW zot lines (tool_use_*,
turn_end) through PaneSupervisor.ingest_line_at -> Working mid tool-loop, Done at
turn_end. clippy -D warnings clean.

Completes the daemon spawn -> glasspane wiring for zot (ADR migration step).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 10:48:21 +02:00
Sam & Claude
813ace9237 fix(daemon): make the Herdr socket group-writable (0770) (Sam & Claude)
Re-landed on current main (the earlier branch never merged — main moved under
it). Operators hit "permission denied" connecting to the colibri daemon from
colibri-tui / the `clawdie` helper: socket.rs binds the Unix socket but never
sets its mode, so it stays at the umask default (0755 = owner-only write).
Connecting needs WRITE perm, so a colibri-group member (clawdie) gets EACCES.
chmod the socket to 0770 after bind. Shared socket::serve, so it covers both
colibri-daemon and the clawdie agent.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 10:53:01 +02:00
fd0d83e053 fmt: format current main Rust sources (Sam & Codex) 2026-06-04 20:59:20 +02:00
454c23db34 fix(colibri): add ListSkills + RegisterSkill to Command enum (Hermes)
The colibri CLI's parse_args and run handler referenced Command::ListSkills
and Command::RegisterSkill, but both variants were missing from the enum
definition. This caused a build failure on Linux (rustc 1.95.0).

Added the missing variants with their fields (name, description, category
for RegisterSkill). Build + test + format gate verified green.

Co-Authored-By: Hermes Agent <hermes@clawdie.si>
2026-06-04 20:17:58 +02:00
Clawdie Operator
c422f16697 Add USB live runtime inventory golden test
Manifest captured from the clawdie-iso operator USB:
- FreeBSD 15.0-RELEASE, Node v24.14.1, pi 0.78.0
- Validates the RuntimeInventory contract parses live USB data
2026-06-04 12:46:34 +00:00
Clawdie Operator
6e7c4c022b Add list-skills + register-skill to colibri CLI
- DaemonClient: list_skills() and register_skill() methods
- CLI: list-skills and register-skill subcommands
- Parsing: --description and --category options for register-skill
- Usage text and examples updated

The daemon socket already had ListSkills and RegisterSkill commands;
this exposes them through the colibri CLI binary.
2026-06-04 12:12:19 +00:00
Sam & Claude
25c7f16600 Merge remote-tracking branch 'origin/main'
# Conflicts:
#	Cargo.toml
2026-06-02 09:26:46 +02:00
Sam & Claude
aea2fbe60e feat: add clawdie — simplified operator agent in one small binary (Sam & Claude)
New `clawdie` crate: the operator-friendly face of Colibri. One small Rust
binary that reuses the proven control-plane core (glasspane supervision +
Herdr Unix-socket API + coordination loop — the "split brain") and puts a
DeepSeek-backed Telegram bot in front of it. That is the entire out-of-the-box
surface.

Deliberately lifted vs. the full control plane: cost modes, quota accounting,
context budgets, multi-provider fallback (OpenRouter/Anthropic), per-user
limits. One DeepSeek key serves both the chat lane and the daemon routing.

- crates/clawdie: main (core + bridge wiring), telegram (long-poll bridge),
  deepseek (minimal one-key chat), build.rs (bakes CLAWDIE_TG_TOKEN +
  CLAWDIE_DEEPSEEK_KEY build flags; runtime env overrides).
- packaging/freebsd/clawdie.in: rc.d service, daemon(8)-supervised, restart on
  crash, dedicated clawdie user — starts as a service like Clawdie-AI.
- release profile strips symbols (binary ~7.6 MB stripped).
- docs/CLAWDIE-AGENT-WIKI.md (mindmap), docs/CLAWDIE-BUILD.md (build + ISO +
  next-build XFCE USB fixes), README workspace table.

Build/clippy/fmt green; headless start smoke-tested (socket + sessions bind).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 08:31:59 +02:00
123kupola
24c1fbfd13 fix: restore PR11 Linux preflight 2026-05-31 17:36:35 +02:00
b06d244e85 feat: cache warming on daemon startup + periodic re-warm (T1.4 PR3b)
Disabled by default. Enables DeepSeek prefix cache warming:
- Fire-and-forget probe on daemon startup
- Optional periodic re-warming (COLIBRI_CACHE_WARMING_INTERVAL_HOURS)
- Cache warming metrics exposed in daemon status
- Fix: Box<dyn Error> -> Box<dyn Error + Send + Sync> in deepseek crate

Config: COLIBRI_CACHE_WARMING=0|1, COLIBRI_CACHE_WARMING_INTERVAL_HOURS=N
Status: cache_warming.enabled, .last_warm_at, .last_warm_cache_hit,
        .last_warm_hit_tokens

Build: pass | Tests: workspace green | Fmt: clean
2026-05-31 17:33:53 +02:00
a42e6b76ff feat: add local_args to spawn-agent for argv-capable Pi spawn
Adds local_args field to HerdrCommand::SpawnAgent, enabling the
Local provider to pass argv to the agent binary without a wrapper
script. Backward-compatible — local_args defaults to None.

Real Pi spawn on FreeBSD is now:
  spawn-agent provider=local model=pi local_args=['--mode','json','--no-tools','-p','task']

Previously required a wrapper script because only an executable
path was accepted. This closes the OSA wrapper caveat from PR #9.

Build: pass | Tests: workspace green
2026-05-31 17:21:25 +02:00
1f550a4b5c feat: scheduler prompt injection (T1.4 PR3a)
When scheduler_prompt_injection is enabled and a session_id is
provided on spawn-agent, the daemon builds a PromptAssembly from
the session, serializes it as COLIBRI_SESSION_CONTEXT env var,
and passes COLIBRI_COST_MODE to the spawned agent process.

Config-gated (default: disabled) via COLIBRI_SCHEDULER_PROMPT_INJECTION.
No cache warming — that's PR3b (separate).

Build: pass | Tests: workspace green | Clippy: clean | Fmt: clean
2026-05-31 17:05:32 +02:00
3c10fc098e test: add Pi spawn path proof integration test
Validates: Colibri spawns agent process (fake-pi-agent.py) → reads
JSONL stdout → glasspane ingests → snapshot shows Done state with
correct session ID.

Uses scripts/fake-pi-agent.py which emits the colibri-pi-events
JSONL taxonomy (session, agent_start, turn_start, turn_end,
agent_end). Proves the spawn→ingest→glasspane pipeline without
requiring the real pi binary.

The real Pi binary path (when installed) follows the same pattern:
pi --mode json is spawned with identical spawner code.

Build: pass | Tests: 1/1 green | Workspace: all green
2026-05-31 16:23:11 +02:00
79c15cd4df feat: cost-aware trimming + auto-escalation (T1.4 PR 2)
Behavior changes for cache-first prompt discipline:

- PromptAssembly::trim_to_budget(CostMode): trims volatile scratch
  first, then oldest appendable log entries, to fit within cost mode
  budget. Prefix is never trimmed. Returns count of items removed.

- EscalationTrigger enum: BudgetExceeded + CompactionInsufficient
  variants for auto-escalation decisions.

- auto_escalate(): returns Some(next_mode) if trigger warrants
  escalation, None if already at ceiling or trigger doesn't apply.

- 11 new tests: trim budget scenarios (under/over/deterministic/
  prefix-preserved), escalation chain (fast→smart→max→ceiling),
  compaction triggers.

No scheduler injection, no cache warming — PR 3 follows.

Build: pass | Tests: 51/51 green | Clippy: clean | Fmt: clean
2026-05-31 16:13:11 +02:00
880da14662 Merge pull request 'feat: add zot runtime event normalization scaffold' (#3) from feat/zot-runtime-event-adapter into main 2026-05-31 16:03:51 +02:00
65e304a1e0 Merge pull request 'feat: scaffold colibri-skills crate — split-brain read consumer' (#2) from feat/colibri-skills-scaffold into main 2026-05-31 16:03:12 +02:00
7c1a9d886a feat: add PromptAssembly + CacheMetrics structs (T1.4 PR 1)
Structural only — no behavior change. Introduces:

- PromptAssembly: named 3-region wrapper around build_prompt_messages()
  with to_messages(), immutable_prefix, appendable_log, volatile_scratch,
  total_bytes, estimated_tokens.

- CacheMetrics: per-session cache-hit tracking with hit_rate() and
  record().

- Session::build_prompt_assembly() wraps existing build_prompt_messages()
  with no logic change.

- 5 golden tests: assembly structure, empty volatile, hit rate
  calculations, record accumulation.

- Linked T1.4-PROMPT-DISCIPLINE-PLAN.md from COLIBRI-CUTOVER-PLAN.md.

No trimming, no escalation, no scheduler changes — PR 2 and 3 follow.

Parked branches (colibri-skills, zot harness) untouched.

Build: pass | Tests: 41/41 green (+5 new) | Clippy: clean | Fmt: clean
2026-05-31 15:30:38 +02:00
b0e94fd514 feat: add zot runtime event normalization scaffold
Phase 1 of zot agent harness integration. Adds:

- AgentRuntime enum (Pi, Zot, Local) with serde support
- zot_event_type() — parse zot RPC NDJSON lines to normalized
  Colibri event types. Permissive: handles tool_use_start,
  tool_use_args, tool_use_end in addition to documented events.
- apply_zot_event() / fold_zot_events() — state transitions
  reusing the existing apply_pi_event() logic via normalization.
- Critical: assistant_message does NOT end the pane (zot may
  emit them during tool loops). Only turn_end/done signals
  completion.
- 16 new tests using real captured zot RPC event lines, including
  tool call sequences and error/success response handling.
- PiJsonlIngestor and existing tests unchanged (33/33 green).

No spawner, socket, or process changes — this is the event
taxonomy foundation only. Wrapper + daemon integration follow
in Phase 2.

Build: pass | Tests: 33/33 green | Clippy: clean | Fmt: clean
2026-05-31 15:03:39 +02:00
1da49eac4f fix: satisfy clippy for skill status default (Sam & Codex)
Validation: cargo fmt --check; cargo clippy -p colibri-skills --all-targets -- -D warnings; cargo test -p colibri-skills.
2026-05-31 14:38:28 +02:00
5267f97f52 feat: scaffold colibri-skills crate — split-brain read consumer
Phase 1: structs + type system + 12 tests. No IO, no SQLite yet.
Compiles against full workspace (9 crates now, up from 8).

The colibri-skills crate is the read-only runtime consumer for
skill artifacts authored in Clawdie-AI. It does NOT store or author
skills — it indexes committed, reviewed skill bundles.

Seeded from the astro-howto artifact (PR #6 in clawdie-ai):
  - Skill, SkillManifest, SkillArtifact, SkillChunk structs
  - ArtifactType classifier (document, image, script, transcript, etc.)
  - ImportSummary + SearchResult types
  - SQLite schema documented in doc/COLIBRI-SKILLS-PLAN.md

Build: pass | Tests: 12/12 green | Clippy: pending
2026-05-31 14:36:43 +02:00
123kupola
6f957d3d72 test: add Slovenian multibyte truncation test (Sam & Hermes)
"Cene že še češnje je" — š/č/ž are 2-byte UTF-8, same family as
German umlauts. Complements the existing CJK+umlaut test. Ensures
compact_tool_result never panics on Slavic diacritics in FreeBSD
locale output, pkg descriptions, and agent logs.
2026-05-27 23:22:10 +02:00
0dd7cf70af fix: UTF-8-safe truncation in compact_tool_result
cost.rs:124 sliced at a raw byte boundary which panics on multibyte
UTF-8 characters (ä, ö, ü, CJK, etc). FreeBSD tool output and agent
logs regularly contain non-ASCII.

Fix: use str::floor_char_boundary() to round down to the nearest valid
char boundary before slicing. This never panics and produces valid
UTF-8 output at or below the requested byte limit.

Added test: multibyte truncation with CJK + umlaut input.
2026-05-27 23:09:34 +02:00
123kupola
0b24a7c7d1 feat: richer status + cost thresholds in session rotation (Sam & Hermes)
ISO-ready improvements:
- cmd_status now returns paths, cost mode/thresholds, task counts
  by status, pane count, scheduler interval
- session_rotation reads CostMode thresholds instead of static
  DaemonConfig fields — Fast/Smart/Max affect compaction now
- Debug log includes active cost_mode
2026-05-27 22:48:42 +02:00
2883151b5f fix: keep cost mode changes clippy-clean 2026-05-27 22:31:31 +02:00
123kupola
ffee0c655a docs: note set-cost-mode is runtime-only for T1.4 (Sam & Hermes) 2026-05-27 22:29:09 +02:00
123kupola
c97b44ad3e feat: add cost discipline — Fast/Smart/Max modes + escalation (Sam & Hermes)
Phase 5: cache-first prompt discipline. New cost module with:
- CostMode enum: Fast (500K/5 turns/4KB tool cap), Smart (2M/20/16KB), Max (8M/100/none)
- escalate() — Fast→Smart→Max, each step logged visibly
- compact_tool_result() — truncate oversized tool outputs with byte annotation
- set-cost-mode socket command + COLIBRI_COST_MODE env var
- 9 unit tests (defaults, thresholds, escalation path, parsing, compaction)
2026-05-27 22:27:30 +02:00
eaa2f27680 feat: add colibri task commands 2026-05-27 22:24:16 +02:00
8d4bb3a916 refactor: rename colibri-ctl binary to colibri (Sam & Claude)
The operator CLI was already a subcommand dispatcher; drop the -ctl suffix so
it reads `colibri status` / `colibri snapshot` / `colibri spawn-local …` — one
`colibri` entrypoint (same single-binary-with-subcommands shape as just, but a
control plane). Renames the bin + its source file, updates usage strings, and
points the forward-looking docs at `colibri`. Dated session/handoff records
are left as historical. (Task-board subcommands intake/create/list are the
follow-up T1.3c slice.)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 21:50:36 +02:00
4f81e49983 test: intake-task drains to SQLite via run_loop (Sam & Claude)
Regression guard for the scheduler store deadlock fixed upstream in d760536:
scheduler.tick held a non-reentrant std::sync::Mutex (state.store) across the
match scrutinee, so relocking inside the arm deadlocked the first time any
intake/scheduled task fired.

This test (independently authored on domedog) submits an intake-task over the
real Unix socket, runs run_loop with a fast scheduler tick, and asserts the
task is drained into SQLite. It hangs (>8min) against the pre-fix code and
passes in 0.09s against d760536 — so it cross-validates that fix and guards the
regression. Closes osa-smoke rec #2.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 21:48:47 +02:00
d760536fe1 fix: avoid scheduler store deadlock on intake drain 2026-05-27 21:02:01 +02:00
af8c0110d7 fix: improve daemon loop wiring — log scheduler interval, join both tasks
Two improvements to Hermes' run_loop wiring (9717ce7):

1. Add scheduler_secs to the daemon loop startup log — the most
   important interval for the OSA re-smoke was missing from the
   heartbeat/rotation/handoff log line.

2. Replace tokio::select! with tokio::join! in main.rs — select!
   returned when the first task finished, leaving the other dangling.
   join! waits for both the socket server and the daemon loop to
   complete before proceeding to shutdown.

89 tests pass, clippy clean.
2026-05-27 20:21:41 +02:00
123kupola
9717ce70f6 fix: start daemon::run_loop from main.rs (Sam & Hermes)
The scheduler tick was wired into daemon::run_loop but main.rs only
started the socket server, never the background loop. intake-task
commands queued into the scheduler's in-memory queue were never
processed. Fix: spawn daemon::run_loop as a second tokio task
alongside the socket server.
2026-05-27 20:16:02 +02:00
a48afa1c0a fix: harden scheduler tests and FreeBSD store isolation 2026-05-27 19:13:20 +02:00
123kupola
ceaeaee658 feat: add scheduler — cron/interval/one-shot execution (Sam & Hermes)
T1.3 Phase 4: execution & scheduling. New scheduler module with:
- Schedule types: Once (ISO timestamp), Interval (every N secs), Cron (5-field)
- 12 scheduler unit tests (cron matching, interval firing, leader/delegate)
- Leader/delegate: capability-match agent selection (pick_agent)
- Intake-task socket command for Telegram/remote task submission
- 4th daemon loop tick (scheduler_tick, every 30s)
- Plan baseline updated: ebc1b99, 72 tests, 8 crates
2026-05-27 19:05:20 +02:00
123kupola
ebc1b99b7e feat: add colibri-store — embedded SQLite coordination database (Sam & Hermes)
Phase 3 coordination core: task board (queued→claimed→started→done/failed),
agent registry, skills catalog. WAL mode, VACUUM INTO backup, JSON export.
DaemonConfig gains db_path; DaemonState gains Mutex<Store>.
9 coordination socket commands: list-tasks, create-task, transition-task,
claim-task, list-agents, register-agent, list-skills, register-skill.
8 crates, 72 tests, clippy-clean. README updated to reflect 8-crate workspace.
2026-05-27 16:40:19 +02:00
5d45a0f74b Fix clippy collapsible_match in colibri-tui session nav (Sam & Claude)
The Tab / BackTab key arms wrapped their whole body in
`if !app.sessions.is_empty()`. clippy (-D warnings) flagged both as
collapsible_match; lift the guard onto the match arm. Empty `sessions` now
falls through to the catch-all — same no-op as before, behavior unchanged.

Gates on c2655d1 + this: build ok, cargo test --workspace 65 passed/0 failed,
cargo clippy --workspace --all-targets -- -D warnings clean, cargo fmt --check clean.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 15:15:15 +02:00
dd55533b5d Fix colibri harness rustfmt drift 2026-05-27 13:56:31 +02:00
Sam & Hermes
eb37784f97 Upgrade colibri-glasspane-tui → colibri-harness (Sam & Hermes)
Herdr-like supervision TUI built on Colibri primitives:

Step 1-2: spawn/kill keybindings + pane detail popup
  - s: spawn agent (local smoke agent)
  - x: kill selected pane
  - Enter: detail popup (pane ID, state, session, CWD, stalled, last event)
  - Esc: close detail / quit

Step 3: Wire colibri-smoke-agent as local provider
  - TUI calls DaemonClient::spawn_agent('local', 'colibri-smoke-agent')
  - Status messages for spawn/kill results

Step 4: Multi-session grouping
  - tab/shift+tab: cycle session filter
  - Header shows active session and count
  - Panes filtered by pi_session_id
  - Rebuild session list on every snapshot refresh

Step 5: Harness double-spawn smoke test
  - Two agents, same session, verify both reach Done
  - Kill one, verify stopped
  - Full lifecycle: spawn→working→blocked→done→kill

65 tests, 0 warnings, 0 clippy errors.
2026-05-27 13:49:24 +02:00
07d04efa8a Add Colibri operator smoke CLI helpers 2026-05-27 12:19:24 +02:00
668697e727 Tighten glasspane TUI defaults and tests 2026-05-27 10:09:38 +02:00
Sam & Claude
0bc858b9be Fix four rough corners in colibri-glasspane-tui (Sam & Claude)
1. Terminal cleanup on all exit paths: move disable_raw_mode +
   LeaveAlternateScreen to main() with a restore_terminal() helper,
   install a panic hook so raw mode is never left active on panic or
   io::Error early-return from run().

2. Safe observed_at slice: replace &snap.observed_at[..19] (panics on
   short strings) with .get(..19).unwrap_or(&snap.observed_at).

3. Stalled state unified in icon column: state_color() and state_icon()
   now accept a stalled bool — stalled panes show magenta ⚠ in the icon
   cell instead of a green/yellow primary state icon, so the eye lands on
   a single column. Separate Stalled column kept for redundancy.

4. Footer j/k navigation hint added alongside q and r.

---
Build: pass | Tests: pass — 50 passed
2026-05-27 09:53:49 +02:00