Hermes as the Brain — what we studied, what we enabled, what is still open
Hermes as the Brain — what we studied, what we enabled, what is still open
Young, 2026-10-11: "Utilise and optimise Hermes for a better Brain... Review, study and enable it these feature on Hermes."
And, on the fleet: "I saw Brain Unit stopped due to 429 on Hermes, now how do we fix this issue and switch to different model when it happen and resume tasks."
One file, as asked — "register all your study on vault and keep it on single md file." This is that file. The feature study, the 429 diagnosis, and the finding it produced are all here.
Part 1 — The seven features, each with what it gives the Brain
| Feature | What it does | Verdict for SolOS |
|---|---|---|
Goals (/goal) | a standing objective survives turns; a judge model checks after each turn and feeds continuation prompts until done | Adopt for the Brain. This is the "resume tasks" primitive Young asked for |
Kanban (hermes kanban) | a durable task board in ~/.hermes/kanban.db; every worker is a full OS process with its own identity | Adopt as the Heart's dispatch surface. kanban_* tools, CLI, and dashboard all share kanban_db — one read of the board, no drift |
Heartbeats (/heartbeat) | one recurring instruction per session, idle-only, fires as a plain user turn | Adopt for watchdogs — "keep an eye on X in this thread" |
Recurring loops (/loop) | re-runs a prompt on a cadence; each wakeup is a real agent turn reading current state | Adopt over heartbeats for polling. Self-paced mode backs off 2m→4m→8m→15m and snaps back when state changes |
Code execution (execute_code) | Python scripts calling Hermes tools over a Unix socket; only print() returns to the LLM | Adopt — this is the token saver. Intermediate results never enter context |
Batch processing (batch_runner.py) | runs N prompts as isolated sessions; JSONL in, ShareGPT trajectories out | Defer. Built for training data. Not our shape yet |
| Event hooks (4 systems) | gateway hooks, plugin hooks, shell hooks in config.yaml, outbound webhooks | Adopt shell hooks — drop-in scripts that can block/fail-closed on pre_tool_call |
Automation blueprints (/blueprint) | ready-to-run automations scheduled as cron; one is just a skill with a metadata.hermes.blueprint block | Adopt. A blueprint is a skill, which is our cortex pattern already |
Goal vs Kanban — the boundary, stated because it is easy to get wrong
/goalis single-session. It never creates a kanban card and never fans out.- Kanban is a board of many tasks, each in its own worker process.
- A kanban card created with
--goalborrows the continuation engine for that one card — it does not join the two systems.
Heartbeat vs Loop vs Cron
- Context-dependent, in-thread →
/heartbeat - Polling external state on a clock →
/loop - Unattended, survives restart →
hermes cron
Part 2 — The 429: root cause, measured
Young reported the Brain stopped on 429. The measurement says it was worse — and simpler.
What was wrong
All three Brain profiles carried:
model:
default: space-bunny-free # ← sol and mira
provider: opencode-go
fallback_providers: <absent> # ← no chain at all
Probed live against https://opencode.ai/zen/go/v1/chat/completions:
space-bunny-free HTTP 400 "Model is unavailable."
longcat-2.5-preview-free HTTP 200 OK
step-5-preview-free HTTP 200 OK
muse-spark-1.3-contributor-free HTTP 400 "Model is unavailable."
So the "429" was two faults stacked:
- Brain's primary was a model the API does not serve — every turn failed at the provider
- No fallback chain existed — nothing to try next, so the run stopped
A rate limit alone recovers. A dead primary with no chain does not.
The diagnostic trap, recorded
The first probe returned 401 Missing API key for every model. That is not a
credential fault — the key is fine. The endpoint requires an x-opencode-session
header, and without it every call fails identically. An auth-looking error that
is really a missing header is exactly the shape that sends someone to rotate a
working key.
What is applied now — verified by Hermes' own reader, not by our config file
Primary: longcat-2.5-preview-free (via opencode-go)
Fallback chain (4 entries):
1. step-5-preview-free (via opencode-go) [https://opencode.ai/zen/go/v1]
2. glm-5.3-flash (via opencode-go) [https://opencode.ai/zen/go/v1]
3. qwen3.8-flash (via opencode-go) [https://opencode.ai/zen/go/v1]
4. glm-5.3-flash (via opencode-zen) [https://opencode.ai/zen/v1]
Identical across sol, min and mira — one unit, one chain. Backups at
config.yaml.bak-429 for all three.
The order is free first, then GO paid, then ZEN paid. Money is unrecoverable, work is not; but the Brain only reaches paid when every free option has failed.
⚠️ A sibling's work, preserved. Min had already written a 4-entry chain for his profile with no
base_url, so Hermes could not resolve the route. I kept his model order and his escalation and added only the missing URLs. My first writer had silently replaced it with a free-only chain — caught by reading the backup before committing, and reverted.
Part 3 — Why Hermes' fallback is only half the answer
Hermes' fallback_providers chain tries another model when the provider
refuses. That fixes Young's symptom.
It does not decide which model a task should use — by capability, confidentiality, or cost tier. That is V3's job, and it is where the two systems meet:
Hermes fallback_providers → "this provider is refusing; try the next one"
V3 route() + ladder → "which model should serve this task at all,
and is a failure the CHOICE's or the TASK's?"
PENDING-APPROVAL P10 is that seam. Min built route() and proved it (12
attempts, 0 paid calls, success on a free model). It had zero callers. This week I
built the invocation layer, operation/runtime/invoke.py, which is the invoke:
boundary route() asks for.
Part 4 — What I enabled, and what I did not
Enabled / applied
fallback_providerson all three Brain profiles — verified livemodel.defaultrepointed to a model the API servesoperation/runtime/invoke.py— the invoke seam (Stage 7 / P10)
Not enabled, deliberately
- Batch processing — training-data tooling, not our shape
- Event hooks / shell hooks — powerful, and a
pre_tool_callhook can fail closed. Enabling a blocking hook needs its own decision, not a study - Kanban for the Brain — the Heart owns dispatch; the Brain dispatching onto the same board would be two dispatchers
A note on the three-of-us problem
Young is right that we converged on the same problem and proposed different
solutions. The concrete instance: Min registered P10 asking me to wire route(),
while I was building a fallback chain in Hermes. Both are correct; they are
different layers, and neither of us knew the other was on it.
The fix that would actually work is not a rule — it is that PENDING-APPROVAL.md
is read before starting work in a shared lane. It has 11 items and most answer a
question before it gets asked. The register is now ONE file; the second one I
created is archived with a pointer.
References
| Topic | Where |
|---|---|
| Fallback providers | docs/user-guide/features/fallback-providers |
| Provider routing | docs/user-guide/features/provider-routing |
| Credential pools | docs/user-guide/features/credential-pools |
| Goals · Kanban | docs/user-guide/features/goals · /kanban |
| Heartbeat · Loops | docs/user-guide/features/heartbeat · /loops |
| Code execution | docs/user-guide/features/code-execution |
| Hooks | docs/user-guide/features/hooks |
| Blueprints | docs/reference/automation-blueprints-catalog |
CLOSED — P11: the prose-refusal defect (fixed by commit fdecd2f, 2026-10-11)
Resolution: classify() now returns Outcome.REFUSAL for every polite refusal
with rc=0. Min added five refusal patterns to _SIGNALS in
core/config/router.py, placed last so they only fire when nothing else matched.
The test passes — 9/9 in tests/test_invoke_route.py.
Outcome.REFUSAL is declared, listed as switchable, and can never be produced.
core/config/router.py documents V2's prose-refusal defect at line 37 and puts
REFUSAL in SWITCHABLE. But _SIGNALS contains no pattern that yields it, so
classify() returns SUCCESS for every polite refusal:
"I'm sorry, I can't help with that." -> success
"I cannot assist with this request." -> success
"This violates my usage policy." -> success
That is V2's defect, live in V3. A provider that declines in prose with rc=0
would be recorded as a completed run and not switched.
Owner: Min — resolved by commit fdecd2f (2026-10-11). His work is complete,
NOT pending. The failing test that named him now passes.
Hermes As A Subsystem
Hermes × Medusa co-hosting + EVE framework study (2026-10-13)
Follow-on to medusa-hosting-docker-study (Oracle ARM node: aarch64 × 4 OCPUs, 23Gi RAM, 180G disk free, measured live). Two questions answered: how many Hermes Agents co-host with the Medusa tenant stacks, and EVE framework vs Hermes Agent.

