- New page: docs/guide/sl/reference/okrajsave.md (15 abbreviations
explained in plain Slovenian — LLM, JSON, JSONL, ACL, PF, NAT,
ZFS, API, HTTPS, TCP, SHA, BCP, ISO, UI, VPS)
- Linked from reference index
- First occurrence of each abbreviation in every Slovenian page
now links to the glossary
- Fix broken ./install/ link (English + Slovenian)
- requirements title: 'Zahteve' → 'Osnovne zahteve'
- prettier-clean
- colibri.md: complete rewrite — was TypeScript "event fabric" with pi-centric
ingestion modules, proof gates, Herdr evaluation. Now describes the actual
v0.12 Rust control plane: crate map, agent model (zot/pi), mother MCP flow,
links to wiki decision pages.
- "broken" → "inconsistent" (docs-publishing), "compromised" (install),
"not suitable" (sdk-deep-dive)
- Zero remainders: no kill, smoke, fake, hacky, TODOs, stale pi patterns
Document the cross-host control-plane bridge (socat TCP on tailscale0 →
colibri-daemon Unix socket): FreeBSD rc.d vs Linux systemd parity, the
interface-scoped firewall gate (pf / ufw), the "tailnet boundary is the auth"
security model (no socket auth; scope :9190 via Tailscale ACL), and config
notes (TAILSCALE_IP_REQUIRED placeholder, socket-path parity, 0770 group).
Points at packaging/{freebsd,linux}/ for install. Linked from the architecture
index next to Control Plane. No real tailnet IPs (placeholders only).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The terminal-capture / signature-triage layer (colibri-glasspane terminal.rs
+ signatures.rs, driven by the daemon poll loop) had no guide coverage. Document
it: content-hash dedup history, edge-triggered signature alerts, per-OS
signature sets, the COLIBRI_TERMINAL_* / TELEGRAM_* config, and the
terminal-watch/unwatch/list/history/poll socket commands. Linked from the
operate index.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Convert std::process::Command → tokio::process::Command so SSH
spawn/wait_with_output yield the tokio worker instead of blocking it.
Stdin write uses AsyncWriteExt::write_all. This, combined with the
earlier tokio::time::sleep fix, makes the entire mother registration
async — no thread is ever blocked during SSH retries.
Replace three std:🧵:sleep calls with tokio::time::sleep().await
in try_register_hw_with_mother. The function is now async so retry
delays yield the tokio worker instead of blocking it — the detached
task won't starve the daemon's other async work (scheduler, heartbeat,
socket handlers) during SSH retries.
Layer 1 — rc.d ordering:
- Add tailscaled to colibri_daemon REQUIRE so the daemon doesn't start
before the tailscale daemon is running.
Layer 2 — autospawn hook:
- After agent spawn, if clawdie-hw-probe was collected, read
external-mcp.json to detect a 'mother' server entry.
- If configured, SSH to mother and call node_register via colibri-mcp
with 3 retries / 5s backoff (tailscale auth can lag).
- Runs in a detached tokio task so SSH retries never block the daemon.
The probe data is already collected at autospawn time and passed to
the agent via CLAWDIE_HW_PROFILE; this addition closes the loop by
actually sending it to the mother node as a best-effort side effect.
Sam & Claude
main's fmt gate was red (over-indented host/last_seen in scheduler test
fixtures + drift in socket/store/tests from earlier Phase-3 merges). This
brings the branch to fmt --all --check clean so the gate passes; kept
separate from the dedup fix commit.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The merged #206 created a regression: cmd_intake_task created a task
at submit time (returning id A) AND the scheduler tick created a second
task from the same request (id B, which got claimed). Two tasks per
intake, with the returned id pointing to the orphaned copy.
Fix: TaskRequest now carries the pre-created task_id. The tick drops
its create_task() call and only does pick_agent + claim_task on the
existing id. One task, one id, the returned id is the one the
scheduler routes.
- TaskRequest gains task_id field
- cmd_intake_task creates once, pushes id to queue
- scheduler tick claims via req.task_id (no create_task)
- scheduler unit test pre-creates task before submit
- multi_agent_board test verifies id in response
Previously intake-task returned {"status":"queued"} with no task_id,
breaking runbook steps 3-4 that needed the id for transition-task.
Now cmd_intake_task creates the task in the store immediately (like
create-task) and returns the full Task object including id, status,
title, etc. The scheduler queue path is unchanged — the task is still
pushed to the intake queue for the tick to route/claim.
Test updated: scheduler_routes_intake verifies response contains id.
Agent struct now carries host (where the agent registered from) and
last_seen (last heartbeat/re-register timestamp). The SQL columns
existed since the Phase 3 schema commit but were never read or
written — Rust ignored them.
Changes:
- Agent struct: +host: Option<String>, +last_seen: Option<String>
- register_agent: un-underscore host arg, INSERT/read it
- get_agent / list_agents: SELECT host + last_seen columns
- Store::heartbeat(): UPDATE last_seen + COALESCE host
- ColibriCommand: +Heartbeat variant, +host on RegisterAgent
- cmd_heartbeat: socket dispatch → store heartbeat
- run_migrations: actually execute MIGRATIONS (was defined but never run)
- All test Agent constructions updated
What's still open per the plan: lease/TTL semantics, daemon heartbeat
tick calling the store, offline detection for stale agents.
Those are follow-up slices — the schema + wiring foundation is in.
Per the no-real-100.x-IPs-in-git policy: env.example now ships
COLIBRI_BRIDGE_LISTEN_ADDR=TAILSCALE_IP_REQUIRED (operator fills in via
tailscale ip -4 at deploy time), and the README uses placeholders/commands
instead of literal addresses for both domedog and hermes.
Linux peer of packaging/freebsd/colibri_bridge.in: bridge the colibri-daemon
control-plane Unix socket to TCP 9190 on the Tailscale interface so mesh hosts
can reach the control plane.
- colibri-bridge.service: systemd unit running socat under sandboxing, BindsTo
the daemon, freebind so it can bind the tailnet IP before tailscaled is up.
- colibri-bridge.env.example: tunables (systemd parallel to the rc.d sysrc vars).
- colibri-bridge.nft: nftables ruleset for hosts WITHOUT ufw.
- README: install steps + the verified domedog host facts (tailnet IP, ufw
default-deny posture, 8443=CloudPanel and its public exposure) + open
questions for the cross-host (hermes) review.
Network gate already applied on domedog: `ufw allow in on tailscale0 to any
port 9190 proto tcp`. The systemd unit is proposed pending the hermes review.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Three bugs in the FreeBSD rc.d script:
- colibri_bridge_health() had an empty body — health check did nothing
- tcolibri_bridge_health — stray 't' prefix, typo
- Stray closing/opening braces scrambled colibri_bridge_status()
into two detached blocks, breaking the pgrep + nc check
health now delegates to status; status runs the full health check
(pgrep for socat + nc smoke to the socket).
Replace the real default 100.72.229.63 with TAILSCALE_IP_REQUIRED.
The operator must now set the listen address explicitly in rc.conf
before the service will start. The prestart guard fails with a clear
error message if the placeholder is still present.
This ensures no real Tailscale IPs leak into the git history or
shipped config files. Per MULTI-AGENT-HOST-PLAN Phase 5 acceptance.
- Update register_agent callers added on main after Phase 3 diverged
(live_socket_check + claim_task tests), pass None for host
- Prefix unused host param with underscore (WIP — wiring in next slice)
- Allow dead_code on MIGRATIONS constant (schema not yet wired)
Rebase conflict resolution only — no behavioral changes.
Verify that toggling attention_only ON then OFF returns
filtered_panes() to the exact same list (order, count, IDs)
— no state corruption from the retain() partial filter.
agent-harness.md listed only zot's end-to-end proof (zot_rpc_smoke.rs,
ignored, ZOT_BIN-gated). pi now has better default CI coverage via
pi_spawn_live.rs (unignored, runs every test run), plus the new
default_agent_args unit tests proving the autospawn argv contract.
Also moves the autospawn argv reference into its own bullet for clarity.
Three unit tests for the autospawn contract function that determines
whether a spawned agent gets rpc-args (zot) or self-driving JSON args
(pi / everything else):
- default_agent_args_zot_gets_rpc — basename 'zot' → ["rpc"]
- default_agent_args_pi_gets_mode_json — basename 'pi' → ["--mode","json"]
- default_agent_args_unknown_gets_mode_json — safe default for
unknown harnesses (sample-pi-agent.py, colibri-test-agent)
Also covers path-prefixed variants (/usr/local/bin/zot, /usr/local/bin/pi)
to verify basename extraction works through the spawner pipeline.
This closes the untested gap: if someone adds a third harness or changes
the basename check, these tests catch the pi-path regression before it
reaches the operator who flips from zot to pi.
workspace green (0 failures).
Two new decisions captured, one page corrected:
terminal.md — the terminal-capability decision. Why colibri-tui and the agents
it supervises need modified-key reporting (Tab vs Shift-Tab, n vs N, Enter),
why the choice fell on Kitty, the tmux extended-keys + csi-u passthrough for
the in-tmux workflow, raw-vs-tmux distinction, the SSH xterm-kitty terminfo
gotcha, and pi's identical requirement. The decision is about capability;
Kitty is the instance.
operator-attention.md — the shipped attention system as one decision. Attention
as a derived view over the state machine (not a sixth variant), the TUI
bar/jump/filter/row-highlight, and the #193 terminal-capture + signature-triage
+ edge-triggered alerts. Records the has_attention session-filter bug and fix.
Lists what is still open (outbound push, answer-from-dashboard).
glasspane.md — corrected drift. The real AgentState enum is {Idle, Working,
Blocked, Done, Error}; Stalled is a derived flag, not a variant (the page's
diagram omitted Blocked and listed Stalled as a variant). The "Usability
roadmap (TODO)" listed the attention half as not-yet-built; it shipped via
#191/#193, so those items move to operator-attention.md and the roadmap keeps
only the genuinely-unbuilt direction.
index.md — two table rows (also satisfies the orphan-page check).
Verified: prettier-clean on all 4 files; wiki-lint --strict clean (144 pass /
0 fail, up from 137); no dangling refs, no orphans, no resurrected names.
(Sam & Claude)
scripts/ci-checks.sh runs five gates; .forgejo/workflows/ci.yml ran only four
— wiki-lint --strict was missing. quality-gates.md states "ci.yml encodes the
same checks" as local, which was not quite true. Add the wiki-lint step to the
markdown job so CI matches local the day a runner is registered.
wiki-lint is pure POSIX sh (grep/awk/sed/find), so it runs in the existing
node:20 container — no new image or job.
This does not by itself stop drift reaching main: as quality-gates.md notes,
no Forgejo Actions runner is registered, so nothing enforces CI server-side
today. The local pre-push hook remains the active enforcement layer; this
change ensures CI is ready to take over once a runner exists. Verified
wiki-lint passes clean on main (137 pass / 0 fail).
(Sam & Claude)
Once any pane carried a session_id, rebuild_session_list() forced
session_filter = Some(first), so the operator could never get back to the
aggregated "All sessions" view — Tab only cycled individual sessions.
Documented as a known bug in GLASSPANE-TUI-ENHANCEMENTS.md.
Model the session cycle as [All, s1, s2, ...]: index 0 is a synthetic "All
sessions" entry (filter = None), any other index scopes to sessions[i-1].
Two helpers encode the mapping:
- session_count() = sessions.len() + 1 (All is always present)
- apply_session_filter() maps session_idx -> filter (0 => None)
Behavior changes:
- On connect, the operator now lands on "All sessions" (was: the
alphabetically-first session). The aggregated view is the more useful
default and is always reachable via Tab/BackTab.
- The position indicator now shows for any cycle > 1 item, so the
"All (1 of 2)" hint appears even with a single session.
self.sessions still holds real session ids only (no sentinel string), so the
sorted/deduped invariant is unchanged.
Tests:
- rebuild_session_list_dedupes_and_sorts: updated for the new default +
offset mapping (index 1 => s1, index 2 => s2).
- all_sessions_view_is_reachable_with_sessions_present: new regression test
covering connect-defaults-to-All and the Tab cycle All -> s1 -> s2 -> All.
- attention_bar_ignores_other_session_panes: comment corrected (rebuild no
longer selects the first session).
19/19 TUI tests pass; fmt + clippy (-D warnings) clean.
(Sam & Claude)
cargo fmt --all --check failed on main with 5 drift sites in
crates/colibri-glasspane-tui/src/main.rs — all inside the tests added by #195,
which hand-packed Pane struct literals as `id: .., agent: .., state: ..,` on a
single line. rustfmt wants one field per line. Pure formatting: 91 insertions
/ 38 deletions, no logic change (only structural additions are field
continuations and one assert!(...) arg wrap).
Unblocks the fmt half of the workspace gate; second merged PR in a row (#193,
#195) to land fmt-red — worth tightening the CI fmt check as a required gate.
Verified: fmt clean workspace-wide, clippy -D warnings clean, 18/18 tests pass.
(Sam & Claude)