Item 4 from the rc.d/release-gate audit. The colibri repo carried its own copy
of scripts/stage-colibri-iso.sh that had drifted behind clawdie-iso's — the ISO
build uses clawdie-iso's copy, and nothing in colibri executes this one, so the
colibri copy was dead split-brain (a maintenance trap). Deleted it and pointed
the PRIORITY-HANDOFF references at clawdie-iso as the canonical owner.
Checks: ./scripts/check-format.sh.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Return an error from the socket server when another daemon owns the Unix socket or bind setup fails, and broadcast shutdown so the daemon does not stay alive without a control socket. Also format the PR docs.\n\nChecks: cargo fmt --check; ./scripts/check-format.sh; git diff --check; cargo test -p colibri-daemon clear_stale_socket -- --nocapture; cargo test -p colibri-daemon --test sigterm_shutdown -- --nocapture.
The rc.d "rm stale socket on prestart" fix (07e4660) was a band-aid over
two daemon-side defects that surfaced on the live FreeBSD host:
1. colibri-daemon never handled SIGTERM. main.rs awaited only ctrl_c()
(SIGINT), so `service stop`/`restart` — which sends SIGTERM via
daemon(8) to the child — killed it on the default disposition with no
cleanup. The graceful path (socket removal, agent reaping) never ran,
leaking the socket file and orphaning spawned agents across restarts.
Now wait_for_shutdown_signal() selects on SIGTERM or SIGINT, so the
same graceful path runs on a normal service stop. New integration test
(tests/sigterm_shutdown.rs) spawns the binary, sends SIGTERM, and
asserts the socket is removed.
2. Stale-socket cleanup had no liveness check — both the daemon
(socket.rs) and the rc prestart would unconditionally rm the socket
before bind, which could delete a *running* instance's socket if
rc.subr's pid detection misfires and starts a second daemon. Cleanup
now probes first (clear_stale_socket): connect succeeds -> refuse to
start; refused/dead -> remove and bind. Unit-tested for absent, stale,
and live cases.
With the daemon owning safe socket cleanup, the rc prestart no longer
removes the socket (only stale pidfiles), eliminating the restart-time
clobber hazard. This also makes the SIGTERM shutdown described in
ISO-SERVICE-LAYOUT.md (PR #75) actually true.
Gates: cargo fmt --check, clippy -D warnings, cargo test --workspace all
green on Linux; sh -n on the rc script OK. FreeBSD runtime validation
still pending per FREEBSD-BUILD-LANE-HANDOFF.md.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Six bugs found in deep-dive analysis of FreeBSD rc.d/rc.conf after the
live-copy-safe fix (7d23905):
1. colibri_cost_mode → colibri_daemon_cost_mode: naming broke rc.subr
${name}_ convention — operator setting colibri_daemon_cost_mode=fast
in rc.conf was silently ignored. Fixed in rc.d, staging script,
rc.conf.sample, and all docs.
2. Removed redundant chmod 660 on socket in poststart: Rust code already
sets 0770 with documented rationale. The poststart override to 0660
was conflicting, fragile, and had no comment.
3. Removed unnecessary chmod 644 on pidfile in poststart: pidfile lives
in a 0750 directory — world-readable permission is pointless and
security-negative.
4. Fixed ISO-SERVICE-LAYOUT.md: socket perms were wrong (said 750, actual
770), colibri-daemon.pid was labeled supervisor pidfile (it's the
child), supervisor pidfile was missing entirely, shutdown behavior
didn't mention custom stop_cmd targeting the supervisor.
5. health_cmd now checks for non-empty daemon response instead of just
connectvity — a hung daemon accepting connections but returning
garbage was reported healthy.
6. rc.conf.sample hostname path: $ (hostname) → $(/bin/hostname) for
consistency with rc.d script and early-boot PATH safety.
Checks: sh -n OK, cargo fmt --check OK, cargo clippy clean,
cargo test --workspace 207 passed.
Keep the FreeBSD rc.d comments focused on the intended service contract instead of historical repair details.\n\nChecks: sh -n packaging/freebsd/colibri_daemon.in; git diff --check.
Ensure rc.d-launched colibri-daemon can resolve local provider commands such as colibri-test-agent from /usr/local/bin.\n\nChecks: sh -n packaging/freebsd/colibri_daemon.in; git diff --check.
Rename the local deterministic launch helper from colibri-smoke-agent to colibri-test-agent, update CLI/TUI/tests/docs, and teach the FreeBSD rc.d service to source /usr/local/etc/colibri/provider.env plus set a service PATH for local spawns.\n\nChecks: cargo fmt --check; ./scripts/check-format.sh; git diff --check; cargo check -p colibri-daemon -p colibri-client -p colibri-glasspane-tui; cargo check -p colibri-client --bins; cargo test -p colibri-client --test live_socket_check -- --nocapture.
Remove stale socket and pidfiles in prestart while rc.d still has root privileges. This handles live USB repair cases where a previous corrupt/root-started daemon left /var/run/colibri/colibri.sock owned by root, causing the colibri user to fail unlink with EPERM and bind with EADDRINUSE.\n\nChecks: sh -n packaging/freebsd/colibri_daemon.in; git diff --check.
Make the FreeBSD rc.d source safe to copy directly onto the live USB: avoid rc.subr's *_program command override, avoid double privilege drop via daemon(8) -u, and keep pid/socket chmod fixes in the source script.\n\nChecks: sh -n packaging/freebsd/colibri_daemon.in; git diff --check.
Priority 3 — Cost mode enforcement:
- Removed session_max_bytes/max_uncompacted_turns from DaemonConfig; cost
mode string is now the single source of truth for all thresholds
- maybe_compact_or_rollover() derives thresholds from CostMode, not static
config fields
- compact_oldest_turns() takes a keep parameter (derived from cost mode)
- compact_tool_result() wired into build_prompt_messages() — tool results
are truncated when cost mode says to compact
- trim_to_budget() called in build_prompt_assembly()
- auto_escalate() wired into session_rotation() — escalates cost mode
when compaction is insufficient
- set-cost-mode socket command now updates runtime cost_mode (RwLock on
DaemonState) instead of just acknowledging
Priority 2 — Pi spawn path end-to-end:
- poll_tasks() now queries claimed tasks, spawns the configured agent
binary (COLIBRI_AGENT_BINARY), creates a session, wires stdout to
glasspane, and transitions the task to Started
- stream_agent_stdout_to_glasspane made pub for cross-module access
- poll_tasks called from scheduler_tick_fn after the scheduler runs
- New integration test: poll_tasks_spawns_agent_for_claimed_task validates
the full path: create task → claim → poll_tasks spawns → glasspane
observes Idle → Working → Blocked → Done lifecycle
Gates: fmt/clippy/test all green (207 tests, 0 failures).
The count drifted repeatedly (8/10/11/12, plus README-vs-AGENTS mismatches)
because it was hardcoded in three places. Drop the number from README (status +
heading) and AGENTS; the crate table + Cargo.toml members are the source of
truth, so adding/removing a crate only touches the table.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The layered-soul skills importer globbed skills/**/*.md, pulling references/ and
templates/ in as separate skills. Import each skills/**/SKILL.md instead
(frontmatter name/description, category 'soul'); supporting files are not skills.
Verified against the populated layered-soul: 9 skills imported, idempotent,
curated memory deferred. Doc updated to match.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Match the docs to the shipped staged-env code: add the JailConfig root_path
field, a 'Staged env payloads' section (prepare_spawn_command writes env.sh/
launch.sh under /var/run/colibri-stage/<id>/), resolve the mdo-env-passthrough
open item, and add root_path to the external-MCP example.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Unbreaks the workspace clippy gate: prepare_spawn_command has 8 args (8/7), so
clippy::too_many_arguments fails under -D warnings on main.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- scripts/headroom-sidecar.py: Unix socket server (from headroom import compress)
- cost.rs: HeadroomSidecar struct with connect/compress methods
- session.rs: build_prompt_messages() now accepts optional sidecar
- daemon.rs: spawns sidecar on startup if COLIBRI_HEADROOM_ENABLED=true
- config.rs: headroom_enabled + headroom_socket_path config fields
- socket.rs: cmd_status reports headroom status, cmd_get_session uses sidecar
- All test fixtures updated with new DaemonConfig fields
40-50% token savings on tool outputs with zero accuracy loss.
Disabled by default (COLIBRI_HEADROOM_ENABLED=false).
Works identically on Linux and FreeBSD.
Add real FreeBSD 15 read-only validation output and the hardening findings so Linux-side reviewers can evaluate the installer follow-up without needing host access.\n\nChecks: ./scripts/check-format.sh; git diff --check
Use the clawdie service user in the generated FreeBSD rc.d script, chown state directories after the user is created, and reject unknown existing ZFS pools before rendering/applying a plan. Update the FreeBSD validation handoff to cover these checks.\n\nFreeBSD checks: cargo fmt --check; ./scripts/check-format.sh; git diff --check; cargo test -p clawdie -- --nocapture; cargo clippy -p clawdie --all-targets -- -D warnings; cargo build -p clawdie --release; target/release/clawdie discover; target/release/clawdie plan; target/release/clawdie apply --pool zroot (dry-run); target/release/clawdie plan --pool does-not-exist (expected error).
Codex validates the disk-touching + service-install paths (zfs/zpool create,
pw/rc.d service) that can't be exercised off-host. Includes read-only checks,
destructive provisioning steps for a scratch pool, teardown, and acceptance
criteria.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
clawdie chooses storage per host:
- FreeBSD: ZFS required (datasets under the pool)
- Linux with ZFS + a pool: datasets under the pool
- Linux without ZFS: plain-dir fallback, reporting ZFS benefits + spare disks
- --create-pool /dev/DEV runs `zpool create` (needs --pool NAME)
Pool creation is destructive and guarded: refused unless the disk is detected
empty (no partitions/filesystem/mount, not the root disk) or --force is given,
and only with --yes. `discover` lists block devices with candidacy. New
disk-candidacy parser + storage resolver are unit-tested (13 tests).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>