Replace the synchronous local-control daemon and the shepherd-spawned
tribes-deploy-apply one-shot with a single in-process broker reused by
the HTTP and CLI transports. All Guix calls (pull, system build,
switch-generation) now go through tribes-guix-helper, which emits
NDJSON phase/done/error frames so the broker surfaces typed error
codes (channel_commit_unreachable, signature_invalid, build_failed,
switch_failed, helper_crashed, ...) instead of regex-parsing guix
stderr.
Long operations run on a single POSIX worker thread fed by an
ice-9-q queue; the HTTP request thread stays free, returning 202
Accepted with a job_id while the build runs. Status is held in an
atomic-box snapshot polled at GET /v1/deployment/status. Same
plan_hash → idempotent re-queue; different plan_hash while busy →
409. Generations log writes go through a mutex-guarded state-store
with tempfile + rename + fsync atomics.
Module layout under tribes/deploy:
json, config, state, worker, guix-helper,
operations (was runtime), handlers, http (was local-control),
cli, entry.
Drops request.json / tribes-deploy-apply / tribes-deploy-shepherd-
service entirely. Packaging stays on trivial-build-system but ships
four wrapper binaries (tribes, tribes-deploy-exec,
tribes-local-control, tribes-guix-helper) wrapped with the guix /
guile-gcrypt / guile-gnutls module paths the helper needs.
Tests: tribes-deploy-runtime renamed to tribes-deploy-operations and
rewritten against a fake helper-backend; the pure-resolver test in
tribes-deploy-executor is unchanged. Schema for the BEAM follow-up
is documented in docs/LOCAL_CONTROL_API.md.