readme: manager mcp surface picks up update; operator-surface recap mentions /model + last-turn + model chip + the three collapsibles (inbox / journald / agent.nix). web-ui.md: details-restore-key story under shape; port-conflict banner mention on containers; agent.nix viewer alongside journald; notifications use per-event tags + console.debug log on block/show; deny endpoint takes note=<reason>; data-prompt / data-prompt-field generalisation noted. conventions.md: data-prompt and snapshot/restoreOpenDetails added to the async-forms section. persistence.md: operator_questions row picks up deadline_at (ttl) column with a migration note. todo.md: new 'Bugs' section captures the manager-question not-rendering issue with three suspect paths to chase. claude.md scratchpad rewritten as a clean handoff for the compaction + the upcoming config-git overhaul. flags the two-repo (proposed/ + applied/) split as the thing to reconsider.
2.9 KiB
Conventions
Code-style and process expectations across the workspace. Most of these exist because something already went wrong without them.
Naming
- Containers are length-bounded by
nixos-container(≤ 11 chars). - Sub-agents are
h-<name>with<name>≤ 9 chars. - The manager is
hm1nd(noh-prefix, fixed name). MAX_AGENT_NAMEinlifecycle.rsenforces the cap.- Per-agent web UI port =
WEB_PORT_BASE + FNV1a(name) % WEB_PORT_RANGE(8100..8999); manager fixed at 8000; dashboardcfg.dashboardPort(default 7000).
Identity = socket
There are no auth tokens on the per-agent unix sockets. The socket
path identifies the principal; perms come from "who has the
bind-mount." A sub-agent only sees its own /run/hive/mcp.sock; the
manager has access to its privileged socket; hive-c0re owns the host
admin socket.
Wire protocol
JSON line-delimited over unix sockets in both directions (host admin
/ manager / agent). SSE streams (/messages/stream,
/events/stream) are text/event-stream. Request/response types
live in hive-sh4re — change them in one place.
Async forms
Dashboard + per-agent mutating forms carry data-async; a delegated
submit listener in assets/app.js intercepts, shows a spinner,
POSTs application/x-www-form-urlencoded (axum's Form extractor
rejects multipart), calls refreshState() on success. New mutating
forms should add data-async and optionally data-confirm (for a
JS-side confirm() prompt) or data-prompt="…" (for a
window.prompt() whose answer goes into a hidden input named by
data-prompt-field, default note).
refreshState defers automatically when document.activeElement
sits inside a managed section so the operator's typing isn't lost;
collapsible <details data-restore-key=…> survive the re-render
via snapshotOpenDetails / restoreOpenDetails.
rebuild is the reconcile verb
lifecycle::rebuild idempotently rewrites
/etc/nixos-containers/<C>.conf (PRIVATE_NETWORK=0, clears
HOST_ADDRESS / LOCAL_ADDRESS, sets EXTRA_NSPAWN_FLAGS),
regenerates applied/<name>/flake.nix, writes the systemd limits
drop-in, then nixos-container update + stop + start.
Anything that changes per-container state on the host should be
re-applied here so a manual ↻ R3BU1LD from the dashboard is
sufficient to recover.
Actions are factored
approve / deny / destroy (and the lifecycle helper) live in
actions.rs / dashboard.rs. The admin socket and the dashboard
POST handlers both call into them so the two surfaces never drift.
Commit messages
Short, lowercase, no Co-Authored-By trailer. Imperative mood, no
period. Body explains why if non-obvious; otherwise the subject
alone is fine. Wrap at ~72 cols.
Commit before test
Stage and commit when work looks ready, then run validation
(cargo check, nix flake check, real deploy). Failures get a
follow-up commit rather than an amend. The commit history is the
work log; rewriting it loses signal.