Skip to content

hives: every README held to its code, and what was broken fixed - #100

Merged
adiled merged 7 commits into
mainfrom
hives-sweep
Oct 27, 2026
Merged

adiled merged 7 commits into
mainfrom
hives-sweep

Conversation

@adiled

@adiled adiled commented Oct 27, 2026 •

Copy link
Copy Markdown
Owner

A sweep of every hive's README against what its code does turned up code that was broken outright, some of it unsafe. That is fixed here, each fix pinned by a test, and the docs describe the fixed behaviour. Separately, Claude could not be reached through the openai-server hive on an installed humd; the last two commits fix why, and make the failure say so when it happens.

Security

  • humd heard any tone from a connection that never said hello, so anything that could reach the socket could prompt without a key. A connection is now heard for nothing but its hello until a breath admits it.
  • humd sent a sid's tones to every idle bee, so one bee could see another's blooms. Tones reach only the connections that prompted or attached under the sid.
  • grpc listened on every interface with no auth, which reached a bash builder. It listens on 127.0.0.1 by default.
  • twilio-sms never verified Twilio's signature; it does, against HUM_TWILIO_URL.
  • paid-oracle took pending or reverted payments, and one payment bought any number of calls.

Could not start, or never worked

  • gsm-modem opened its serial port twice. paid-oracle's installed bee got none of its settings. ollama-server's nurse was never started, and its port clashed with the forager's. vercel-ai's installed service restarted forever. wifi-p2p did not compile.
  • Every forager's default model matched no nurse. With no model named, a prompt goes to any nurse with room.

Hangs, leaks, lost history

  • anthropic-server always streamed (breaking the SDK's create()), dropped history, and could not carry a tool call through. ollama-server dropped history, hung on tool calls and leaked a connection per request. vercel-ai's tool calls stalled about 10 minutes.
  • humd: a refused tool call hung its cell; a bloom whose peer left the ensemble hung until it expired, and now fails with a retryable nurse-gone; per-sid state grew forever; a second connection on one key did not close the first.
  • hum-nest: wallClockMs was never enforced, and a cleanup race could kill a replacement cell.
  • Smaller fixes in openai-server, web2, grpc, twilio-sms, dwarfstar and claude-repl.

Claude behind openai-server
On an installed humd, a stale openai-server process was refused by humd four times a second for three days (its hello named no role), and hum bee --list called its bees did-not-return. Restarted, it nestled, and every request for a claude model failed with spawn claude: the claude-cli nurse runs under launchd's PATH, which leaves out ~/.local/bin, where Claude Code's installer puts claude.

  • hum_paths::program_dirs / find_program: PATH, then ~/.local/bin, ~/.cargo/bin, ~/.bun/bin, /opt/homebrew/bin, /usr/local/bin. claude-cli and claude-repl look there and in ~/.claude/local, take CLAUDE_CLI_PATH first (empty is unset, and their Orchfiles pass it from the environment of hum hive <hive> install), and name every place they looked when claude is in none. claude-cli logs which claude it found when it starts.
  • A refused hello that names a bee entered here is kept as a refused advisory with its reason, counts toward reconnect-storm, and counts the bee as returned. hum bee --list prints the reason under a bee that is not nestled. A key nobody entered leaves nothing behind.
  • humd logged at trace when HUM_LOG_LEVEL was unset, every iroh frame included: one machine's log passed 6 GB and buried the warnings hum doctor reads. It logs at info. HUM_LOG_LEVEL and the claude lookup are in hum(1).

Through this branch's openai-server and a nestled claude-cli, POST /v1/chat/completions for claude-haiku-4-5 answers, and so does wryme with a shop at http://127.0.0.1:14620/v1.

Left for a decision

  • hum bee exit does not stick: a hive that enters itself re-enters on restart. A test pins it as intended.
  • Any humd can join as a peer; there is no peer allowlist.
  • A paid-oracle payment seen in public can be redeemed first by whoever sees it. The README says so.
  • The nurse library and the docs name different propensity values; the TS client cannot release its link, so vercel-ai closes an idle one after 1 s.
  • On the socket, a hello gets an echo with ok: true as it arrives and, when refused, a second under the same rid with ok: false.

Not run for real: no phone, GSM modem, live chain or Twilio account; those paths are tested with fakes.

claude-cli's models: the nurse advertised five models from May, so nothing newer than Opus 4.7, Sonnet 4.6 or Haiku 4.5 reached it through humd. It advertises every model served to every account, claude-fable-5-1 to claude-haiku-4-5 (14); the Mythos models and the deprecated claude-sonnet-4-5 are for CLAUDE_MODELS to name, which hum hive claude-cli install now passes from its environment (empty is the built-in list).

Also

  • The openai-server tests' scripted upstream exits when its parent does; one killed run had left it serving for two days.
  • vercel-ai approves esbuild's build in pnpm-workspace.yaml, as anthropic-server does, so a fresh pnpm install under pnpm 11 succeeds.
  • The four field doc comments the sweep put in hum-nest/src/limits.rs moved into its //! header (tools/speech.py).

Verification, on this branch, macOS arm64:

  • codegen --check, tools/speech.py, tools/paths.py: pass.
  • Core: humd, ensemble, thrum-core, sim, hum-identity, hum-nest, hum-paths, hum, codegen, hum-thrum: pass. sim --test liveness failed once under load (a handshake timed out while every suite was building at once) and passed 5 of 5 alone; this branch does not touch it.
  • Hives: bash, bp7, claude-cli, claude-repl, grpc, gsm-modem, ollama-server, paid-oracle, web2 (cargo test); twilio-sms (go test -race); openai-server, anthropic-server, vercel-ai (from a clean node_modules) (bun test); wifi-p2p (gradlew test); dwarfstar (make check): pass.
  • CI: rust, mesh, conform, c, go, python, ts: pass.
  • Installed from this branch on macOS (humd, hum, humctl by cargo install; claude-cli and openai-server by hum hive <hive> install, no CLAUDE_CLI_PATH): claude-cli finds ~/.local/bin/claude under launchd's PATH, openai-server lists all 14 claude models, and wme -p on a station at http://127.0.0.1:14620/v1 answers on claude-haiku-4-5, claude-sonnet-4-6, claude-haiku-5-5, claude-opus-5-5 and claude-fable-5-1.

A sweep of each hive's README against what its code does, which turned
up code that was broken outright; that is fixed here too, each pinned by
a test.

humd
- A connection is heard for nothing but its hello until a breath admits
  it; before, a connection that never said hello could prompt.
- A sid's tones reach only the connections that prompted or attached
  under it, not every idle bee.
- A bloom whose peer leaves the ensemble fails with a retryable
  `nurse-gone` instead of hanging until it expires.
- A refused tool call no longer hangs its cell; per-sid state and sigil
  claims are freed; a second connection on one key closes the first.

hum-nest
- `wallClockMs` is enforced; cleanup and eviction act only on their own
  cell, so a race no longer kills a replacement.

hives
- grpc listens on 127.0.0.1 by default (it served a bash builder to
  every interface, unauthenticated).
- twilio-sms verifies Twilio's signature.
- paid-oracle refuses pending or reverted payments and redeems each once.
- gsm-modem, ollama-server, vercel-ai and wifi-p2p start (or build).
- anthropic-server, ollama-server and vercel-ai keep history, carry tool
  calls through, and cancel on disconnect.
- Foragers name no model by default, so any nurse with room grows.
- Fixes in openai-server, web2, grpc, twilio-sms, dwarfstar, claude-repl.

docs
- Every hive README, hives/README.md and THRUM.md describe what the code
  does; foragers.md goes.
- hum bee's help names --role.
…md logs at info

Installed, a nurse runs under orchd with launchd's PATH, which leaves out
~/.local/bin, where Claude Code's installer puts claude. The nurse
nestled and advertised its models, and every prompt failed with "spawn
claude". hum_paths::program_dirs looks on PATH and then in ~/.local/bin,
~/.cargo/bin, ~/.bun/bin, /opt/homebrew/bin and /usr/local/bin; both
claude hives look there and in ~/.claude/local, take CLAUDE_CLI_PATH
first (an empty one is unset), name every place they looked when claude
is in none, and take CLAUDE_CLI_PATH from the environment of `hum hive
<hive> install`. claude-cli logs which claude it found when it starts.

humd logged at trace when HUM_LOG_LEVEL was unset, every iroh frame
included, which grew one machine's log past 6 GB and buried the
warnings `hum doctor` reads from its tail. It logs at info.
A bee from an older build said hello with no role, humd refused it four
times a second for days, and `hum bee --list` showed it as entered, not
nestled, advised did-not-return: it had returned, and was refused. Every
hello humd refuses now goes through one place that, when the hello names
a bee entered here, notes `refused` with the reason and counts the hello
toward reconnect-storm, and counts the bee as returned. `hum bee --list`
prints the latest reason under a bee that is not nestled. A key nobody
entered leaves nothing behind.
…hat started it

It was stopped only by a test's close, so a run killed or aborted before
then left it serving, reparented to launchd; one had been up for two
days. It now reads the pipe its parent holds and exits when that ends.
…e's header

The kernel speaks in code: tools/speech.py refuses a field's doc comment.
pnpm 11 refuses an install whose dependencies' build scripts no one
approved, and esbuild's is one; anthropic-server already says so.
…he environment of its install

The nurse advertised five models from May, so humd routed nothing newer
than Opus 4.7, Sonnet 4.6 or Haiku 4.5 to it. It advertises every model
served to every account, Fable 5.1 to Haiku 4.5; the Mythos models and
the deprecated claude-sonnet-4-5 are for CLAUDE_MODELS to name. The
Orchfile takes CLAUDE_MODELS from the environment of `hum hive
claude-cli install`, and a value that names no model is unset, so the
empty one an install without it passes leaves the built-in list.
@adiled
adiled merged commit 51eea2f into main Oct 27, 2026
7 checks passed
adiled added a commit that referenced this pull request Oct 28, 2026
… words, and a call keeps claude's own id (#102)

## Why

Testing #101 in wryme showed one conversation spread over four claude sessions in seven minutes. No claude process piled up, and nothing was lost (each new session got the whole conversation stitched in), but every request that missed began a new sigil and a cold claude session.

openai-server (and, since #100, anthropic-server and vercel-ai) found a conversation's sigil by a hash of its whole history: system prompt, every turn's words, every tool id. That is not what a sigil was meant to be (the forager picks the sid), and clients reshape history all the time. What made wryme miss:
- on the Responses API, wryme sends claude's tool calls between rounds of a turn but not the words claude said before them, then replays the turn with them;
- its `[bookkeeping]` note rides in the system prompt for one request;
- `tinker_keep` drops older tool pairs, and a turn can come back squashed into one message.

## What changes

**A sigil is found by what the client echoes faithfully** (openai-server, anthropic-server, vercel-ai)
1. A held call's sid, then `previous_response_id` / `conversation`, as before.
2. A line the client names: `X-Hum-Sid` (vercel-ai: `providerOptions.hum.sid`), scoped to the caller.
3. The newest tool call id in the request that this caller was given. Ids are unique, and every client echoes them because it must to answer.
4. The words of the last two turns, which compaction of older turns leaves alone; also tried without trailing answers, so a delivery or a late answer finds the sigil it had before them.
5. Otherwise a new sigil.

A sigil continues only from where it last left off: the request must still hold the sigil's newest call (never a call the server ran, which a client may not echo), or match its latest tail. A copy of a conversation taken earlier begins its own. No key reads the system prompt.

**claude-cli tells a changed system prompt** at the head of the turn it changes in, and reads what a session was told from its transcript when a cell is raised on it. Before, only a new session was told, so a changed prompt reached claude only by starting a new session.

**A call keeps the id its cell gave it.** The nest asks the nurse, `Nurse::call_id` (default none), and knows nothing of any cell. claude-cli reads Claude Code's `_meta["claudecode/toolUseId"]`. A caller, the forager and claude's transcript now name a call alike. Held tool results are matched by call and answer, then by tool and answer; a late answer to a call claude already holds an answer for is stitched on as a call of its own, since a call takes one answer.

## Testing
- hum-nest: 34 pass, `tools/speech.py` clean, clippy clean. New: a call keeps the id its cell gave it.
- claude-cli: all pass. New: the call id read from `_meta`; a changed system prompt told, an unchanged one not; a transcript says what it was told last; a late answer to a held call stitched as its own call.
- openai-server 69, anthropic-server 29, vercel-ai 22, end to end included. `Sigils` tests: dropped older pairs, a squashed turn, mid-turn words missing then present, a changed system prompt, a made-up delivery, a tool-less tail, reordered calls, a server-run last call with `mcp_calls` dropped, all keep the sigil; strangers, a growing sigil, and copies taken earlier (with and without tools) do not; `X-Hum-Sid` names a line per caller.
- Live (`HUM_LIVE_CLAUDE=1`, `claude-haiku-4-5`): **20/20**, new: a changed system prompt obeyed on the same sigil (`nurse.cell.reused`); `X-Hum-Sid` carrying a codeword across two unrelated requests; every caller tool call arriving as `toolu_…`.

## Known limits
- A client that edits an older message keeps its session, so claude remembers the unedited version.
- A copy of a tool-less conversation, taken after its latest turn, shares the original's sigil.
- A compaction summary sent as a user message right before the last two turns changes the tail; as a system message it does not.
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