Skip to content

hum tee dum tee#43

Open
adiled wants to merge 12 commits into
mainfrom
hum-paths-crate
Open

hum tee dum tee#43
adiled wants to merge 12 commits into
mainfrom
hum-paths-crate

Conversation

@adiled
Copy link
Copy Markdown
Owner

@adiled adiled commented May 30, 2026

No description provided.

adiled added 12 commits May 30, 2026 15:15
New hum-paths crate. init() sets XDG defaults at startup; every callsite
routes through it. No /tmp fallback. Socket moves to $XDG_STATE_HOME/hum.
Issue #41 root cause was Arc<Mutex<Option<Child>>> + try_lock in
claude-cli — wait task held the lock forever, kill closure's try_lock
always failed, SIGKILL never reached the child.

- nest::lifecycle::supervise(AsyncGroupChild) using tokio::select! over
  child.wait() vs CancellationToken. command-group for tree-kill so
  claude's descendants die too. Reaps with timeout.
- Cell.kill: Arc<dyn Fn> -> Cell.cancel: CancellationToken.
- Drop on CellBundle cancels on map removal; idle reaper + LRU evict
  + map clear all kill correctly.
- claude-cli + claude-repl + mock + nest::pool + serve.rs migrated.
- lru::LruCache replaces HashMap + linear-scan eviction (O(1)).
- nest::metrics swapped /proc parser for sysinfo (cross-platform RSS/CPU).
- metrics + metrics-exporter-prometheus on humd; /metrics on 127.0.0.1:9909
  (HUM_METRICS_ADDR override). Counters at evict + kill sites; gauge for
  active cells.
- governor token bucket on thrumd accept loop (100/s).
- Reconnect jitter on serve_worker reconnect to spread thundering herds.
- 3 lifecycle tests: cancel kills, natural exit propagates, tree-kill
  takes grandchild.
DaemonConfig::from_env() (and a few other early callers) hit hum_paths
functions before any init() ran. Tests panicked. Making xdg() call init()
on first miss makes init() optional everywhere; explicit init() at bin
entry stays as an eager warmup so child processes inherit the env.
Dead modules removed (1500 LOC):
- nest::pool (Nest, never used)
- nest::mock (only used by pool tests)
- nest::health (tiered eviction policy, no callers)
- nest::budget (token/tool-call caps, no callers)
- nest::Listener + nest::ForagerBee traits (Listener impl in serve.rs
  was empty stubs; ForagerBee had zero impls)

Wired:
- nest::metrics::spawn_sampler now drives a hum_cell_rss_bytes /
  hum_cell_cpu_ms gauge labelled by pid from inside lifecycle::supervise.
  Per-cell observability stops being a half-built feature.
- humd.metricsAddr config knob (hum.json) replaces the hardcoded
  127.0.0.1:9909. Schema entry added.

Naming standardized: hum_cells_active -> hum_cell_count.

Dead code removed in hives/common/src/serve.rs (HashMap import,
WireListener Listener impl), humd/src/lib.rs (HumdSink.cli_path),
hum/src/main.rs (home() helper).

Tests added:
- serve::tests::drop_cancels_token — RAII fires on drop
- serve::tests::lru_pop_drops_bundle_and_cancels — LRU eviction kills
- serve::tests::map_clear_cancels_all — shutdown kills everything
- humd::prometheus_endpoint — /metrics actually serves
- thrumd::accept_rate_limit — governor paces accepts at quota
EOF
…urged, flaky test budgeted

- nest::ResourceLimits now actually applied: claude-cli calls
  apply_pre_exec on the std::process::Command (via cmd.as_std_mut())
  before group_spawn. SpawnSpec.resource_limits stops being decorative.
- CatalogueSlot.sid: stored but never read. Field + setter param dropped.
- humfs::tools::read::is_code_file: production-unused fn with tests
  testing nothing the codebase uses. Both purged.
- partition_then_heal_converges_wane: budget raised 1s -> 10s so the
  test is no longer load-flaky under parallel runs.

cargo check + cargo check --tests: zero warnings.
Rendezvous file (P1 #40 fix):
- hum_paths::RuntimeInfo + HumnestRuntimeInfo (atomic write, read, remove).
- thrumd::serve_with_hook fires on_bound after UnixListener::bind. humd
  uses it to publish runtime.json with socket + pid + version + bound_at_ms.
- hum_paths::thrum_sock_resolved prefers env > runtime.json > default.
  Daemons keep thrum_sock; bees/CLI use _resolved. Socket-path drift is
  gone — clients always reach whatever humd actually bound.
- hum doctor connect-tests with a real hello tone, 1s timeout. exists()
  check replaced. doctor surfaces humnest runtime.json too.
- hum bee --list flags "⚠ crash-looping (exit N)" via new svc_last_exit
  reading systemctl ExecMainStatus / launchctl 'last exit code'.

humnest crate — bee supervisor sibling daemon:
- Reads humnest.bees[] from hum.json.
- Each bee spawned via nest::lifecycle::supervise (group-kill, RAII).
- Restart policy per bee: always | on-failure (max_retries, backoff) | never.
- Crash-loop state surfaced through humnest-list RPC.
- Control socket at humnest.sock (NDJSON): humnest-spawn|humnest-kill|humnest-list.
- Sibling to humd: humd crash != humnest crash, bees stay alive.

humctl crate — service-manager 0.7 wrapper:
- humctl {install|start|stop|restart|status|uninstall} {humd|humnest}.
- Pure Rust, ServiceLevel::User, cross-platform via service-manager crate.
- scripts/svc.sh DELETED. 300 lines of bash, gone.
- ./install + every hives/*/install rewritten to call humctl, drop svc.sh
  sourcing. Hive install now appends to hum.json humnest.bees + restarts
  humnest instead of generating per-bee systemd/launchd units.

config gained humnest.bees[] schema. hum CLI: new 'hum nest' subcommand
lists humnest bees with state + restart count. 'hum bee enter|exit|reenter'
routes through humnest first for kinds in hum.json, falls back to legacy
svc paths for unknown / 'all' targets.

247 tests pass.
Same priority order as hum_paths::thrum_sock_resolved on the Rust side:
HUM_THRUM_SOCK > runtime.json > computed default. Closes the last gap
where non-Rust clients would miss humd's published socket path after
restart.
humctl shrinks to humd-only operator (no install/uninstall — bootstrap
owns service registration). New verbs: start, stop, restart, status,
logs, health. Status shells systemctl/launchctl native; health does a
real connect + hello/breath probe through hum_paths::thrum_sock_resolved.

hum_paths::macos_log is replaced by daemon_logs(name) returning a typed
DaemonLogs { Journald{unit} | Files{stdout,stderr} } so callers dispatch
on the enum instead of #[cfg]-ing per platform. humctl logs and hum's
print_recent_logs now share the same path.

install bash generates humd.service + humnest.service inline (Linux) or
humd.plist + humnest.plist (macOS), coupling expressed in the unit
files themselves (Wants= / PartOf= / RunAtLoad). svc.sh stays deleted.

SIGHUP reload handler I had added to humnest is reverted — it was a
coarse 'reconcile against disk' plaster. surgical RPC (humnest-spawn /
humnest-kill via the existing control socket) is the right primitive
and hum_route_verb already uses it.
humnest is gone. orchd (sibling Rust project; user-scope systemd +
launchd platform contributed upstream this round) replaces it.

Architecture now:
  launchd/systemd
    └── humd (one user service; humctl operates; ./install registers)
  orchd
    └── per-bee user units (one each, generated from per-hive Orchfile)

Hive surface: each hive ships an Orchfile at its root. `hum hive install
<target>` resolves the target, builds (cargo install --path), copies the
Orchfile into ~/.config/hum/orch.d/, re-assembles ~/.config/hum/Orchfile,
and runs `orchd up <kind>`. No more per-hive install bash scripts.

What dies:
- humnest crate (entire thing — 4 modules, supervisor + control + log + lib)
- hum_paths::humnest_sock, humnest_runtime, HumnestRuntimeInfo
- config::HumnestSection, BeeConfig (orch's Orchfile owns this declaration)
- hum.schema.json humnest section
- 4 hives/*/install bash scripts (claude-cli, claude-repl, humfs, paid-oracle)
- ./install humnest unit generation (no second user unit)

What lives:
- humd, humctl, hum CLI: unchanged in role
- humctl: humd operator only (start/stop/status/logs/health)
- orch + orchd: pulled as git deps by ./install via cargo install

What's new:
- hives/{claude-cli,claude-repl,humfs,paid-oracle}/Orchfile (declarative)
- hum CLI: hive_install does build + register-via-Orchfile + orchd up
- hum CLI: bee enter/exit/reenter routes through orchd up/down/restart
- hum CLI: 'hum nest' delegates to 'orchd status'
- ./install pulls orch + orchd from github via cargo install

openai-server (TS) still has its bash install — TS build pipeline
(pnpm install + tsup) not yet automated through hum hive install. Follow-up.

247 tests pass.
Detects build kind by marker file:
- Cargo.toml         → cargo install --path
- package.json       → pnpm/npm install + build; writes ~/.local/bin/<kind>
                       as a node wrapper exec'ing the produced dist/index.js
                       (honors pre-built dist if no pnpm/npm in PATH)
- go.mod             → go build -o ~/.local/bin/<kind>
- 'build' script     → execute it (escape hatch for exotic hives)

Orchfiles added for the remaining hives (every hive now ships one):
- bp7, grpc, gsm-modem, ollama-server (Rust foragers; bp7-forager,
  grpc-forager, ollama-server, gsm-modem binaries)
- openai-server, anthropic-server, vercel-ai (TS HTTP foragers)
- twilio-sms (Go forager)

12 hives total, all surface-uniform: ~/.local/bin/<kind> + Orchfile.

hives/openai-server/install bash deleted — the build pipeline (pnpm
install + tsup build + node wrapper) now lives in hum CLI's
build_node(). One code path for all TS hives.

247 tests pass.
Constitution-level rename. Where hum owns the names, they sing.

  SpawnSpec      → Egg                  (the thing-to-be a worker raises)
  WorkerBee::spawn → ::raise            (workers raise brood)
  Cell.pid       → Cell.mark            (beekeeper's mark)
  Cell.stdin     → Cell.feed            (nurses feed larvae through open cells)
  Cell.events    → Cell.mmm             (the sounds from inside the cell)
  Cell.exited    → Cell.emerged         (adult bee chews out the cap and emerges)
  Cell.cancel    → Cell.silence + Cell.still()  (token field, verb method)
  ResourceLimits → Bounds
  resource_limits→ bounds
  CellMetrics    → Vitals
  Attachment     → Pollen               (what a forager carries home)
  encode_prompt_with_attachments → encode_prompt_with_pollen
  nest::lifecycle::supervise → ::tend   (nurse bees tend brood cells)

Foreign types stay foreign (CancellationToken, mpsc::Sender,
AsyncGroupChild, tokio::process::Command, oneshot::Receiver). We don't
own those words. Our surface either reads like apiary biology or stays
out of the way.

247 tests pass.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant