Hermes native features fit our fleet (2026-10-20)
Hermes native features fit our fleet (2026-10-20)
Young: "Study with Tam and Echo. Here are more sources from Hermes, see it good fit for our team."
Verdict: excellent fit. We were about to build most of this ourselves. Every mechanism the ground rules call for — work-queue, one board, heartbeat, staleness detection, polling without re-prompting — already ships in Hermes, and is already installed on LAB. The work is configuration, not construction.
1. The measurement that changes the plan
We specced a work-queue in a2a-agent-reporting-system-2026-10-20.md as
"A2A + a tasks.db row per instruction". Hermes already has it, and it is the
same database we already point at.
/home/lab/.hermes/kanban.db → tables: tasks, task_links, task_comments,
task_events, task_runs, task_attachments,
kanban_notify_subs
The tasks table already carries every column our design needed:
| Column | Our design wanted | Status |
|---|---|---|
assignee | owner | ✅ exists |
status | state (ready/running/review/blocked/done) | ✅ exists |
last_heartbeat_at | heartbeat + staleness | ✅ exists |
claim_lock, claim_expires | one-owner enforcement | ✅ exists |
created_by, created_at, started_at, completed_at | timeline | ✅ exists |
goal_mode, goal_max_turns | the Ralph loop per card | ✅ exists |
completion_contract | exit-proof contract | ✅ exists |
tenant | per-tenant scoping | ✅ exists |
consecutive_failures, last_failure_error | circuit breaker | ✅ exists |
block_kind, block_recurrences | blocker reporting | ✅ exists |
workspace_kind, workspace_path | isolated worker workspace | ✅ exists |
model_override, provider_override, skills | per-card lane config | ✅ exists |
result | agent's report back | ✅ exists |
idempotency_key | dedup on re-dispatch | ✅ exists |
Measured state: 0 tasks, 0 rows. The board exists and is empty — the schema is ready, nothing has ever been dispatched through it. That is the gap, not the tooling.
CLI surface (measured live, hermes kanban --help):init · boards · create · swarm · list · show · assign · reclaim · reassign · diagnostics · link · unlink · claim · comment · attach · complete · edit · block · schedule · unblock · request-review · request-changes · reopen-review · promote · archive · tail · dispatch · daemon · watch · stats · notify-subscribe · log · runs · heartbeat · assignees · context · specify · decompose · gc · repair
2. Feature-by-feature fit
2.1 Goals (/goal) — judge-driven continuation
A standing objective that survives turns. After each turn a lightweight judge
model decides done / continue / blocked; if continue, Hermes feeds a
continuation prompt back into the same session and keeps working.
| Command | What it does |
|---|---|
/goal <text> | Set the goal; first turn fires immediately |
/goal draft <text> | Draft a structured completion contract, then set it |
/goal show | Print the active goal's completion contract |
/goal status / /goal | Show goal, status, turns used |
/goal pause / /goal resume / /goal clear | Lifecycle control |
/goal wait <pid> [reason] | Park the loop on a background process; auto-resume on exit |
/goal gate add <command> | Quality gate — a shell command that must pass before "done" |
Why this is the single best fit for our fleet: the gate feature is our exit-proof
rule. "Nothing is live until the exit proof passes" (prod-implementation-stages) becomes
/goal gate add "curl -sf http://127.0.0.1:8091/health". The judge cannot mark done
until the gate passes — enforced by Hermes, not by an agent's good intentions.
Boundary (documented, important): /goal is single-session. It never creates a
kanban card, never assigns work to another profile, never fans out. The overlap is
deliberate and small: a kanban card created with --goal runs the same continuation
engine inside that one card's worker session. It borrows the engine, not the board.
Our use: per-card iteration on "build the Docker images until arm64 pulls succeed and the health gate passes" — with a shell gate, not a prose claim.
2.2 Heartbeat (/heartbeat) — idle-only recurring prompt
One recurring instruction for the current session. Fires when the session is idle and the interval elapsed, as a normal user-role turn — same conversation, same context, same prompt cache.
| Command | What it does |
|---|---|
/heartbeat every <interval> <prompt> | Set/replace the heartbeat. Intervals 90s, 10m, 2h, 1d (min 60s) |
/heartbeat status | Show heartbeat, interval, time to next fire |
/heartbeat pause / /heartbeat resume / /heartbeat clear | Lifecycle |
/hb | Alias |
Behaviour that matters for us:
- Idle-only — never interrupts a running turn; fires at next idle poll
- Missed ticks coalesce — one turn, not a backlog; timer re-anchors on every fire
- User messages win — a queued user message always takes priority
- Owned by the setting surface — a heartbeat set from a Telegram chat fires from the gateway and replies into that chat
- Gateway recovery — startup restores active heartbeats using persisted conversation and thread routing; no new chat message required
- Persistence — state in
SessionDB.state_metakeyedheartbeat:<session_id>; follows context-compression session rotations - Quiet when nothing to say — a scheduled turn may end with
NO_REPLY/[SILENT]; gateway sends nothing - Don't-invent-work guard — the injected prompt tells the agent to reply briefly and stop when nothing meaningful changed
Our use: this is the "poller that doesn't re-prompt" we wanted. Min's /heartbeat every 5m Poll each agent's A2A endpoint and report only status changes — the
don't-invent-work guard is exactly the "no busywork" rule the ground rules need.
2.3 Loops (/loop) — timer-driven recurrence
Re-runs a prompt on a recurring cadence inside the current session. Each wakeup is a real agent turn: Hermes reads current state fresh, does the work, reports back.
Two cadence modes:
- Fixed interval — you set the clock (
/loop 2m poll the build) - Self-paced — omit the interval; starts at 1 min floor, backs off exponentially (2m, 4m, 8m, up to 15 min ceiling) while replies stop changing, snaps back to floor when a reply differs. Change detection is a local digest comparison (timestamps ignored), so idle waits cost nothing extra.
Stop conditions: agent ends reply with LOOP_COMPLETE · --times N run cap ·
--until <condition> (judge-gated, fail-open) · /loop stop · backstop
loops.max_ticks (default 100).
Our use: the self-paced mode is the "watch a deploy and tell me when it changes"
pattern. The --until condition is a quality gate on the loop itself.
2.4 Heartbeat vs Loops vs Cron vs Goals vs Kanban — the decision table
| Surface | Runs in | Survives restart | How many | Use for |
|---|---|---|---|---|
/heartbeat | This conversation (full context) | Yes (SessionDB) | One per session | "Keep an eye on X in this thread while we work" |
/loop | This conversation (real turn each tick) | Session-lifetime | One per session | Polling external state, iterate-until-green |
hermes cron | Fresh isolated session per tick | Yes (durable scheduler) | Unlimited | Standing jobs, reports, watchdogs, deliveries |
/goal | This conversation (judge-driven loop) | Session | One per session | Keep iterating on ONE objective until achieved |
| Kanban | Board of many tasks (own worker process + session each) | Yes (SQLite rows forever) | Unlimited | Many independent tasks, dependencies, handoffs, multiple profiles |
The rule of thumb, stated by Hermes: if the recurring prompt needs the
conversation's context → /heartbeat. If it's a self-contained job → cron. If it's
one objective with a definition of done → /goal. If work crosses agent boundaries
or needs discoverability → Kanban.
We are using all five, for five different jobs. That is not confusion — that is the right decomposition.
2.5 Kanban — the board we already specced
Durable SQLite-backed task board shared across all Hermes profiles.
Two surfaces, one database:
- Agents drive it through a dedicated
kanban_*toolset —kanban_show,kanban_list,kanban_complete,kanban_request_review,kanban_request_changes,kanban_block,kanban_heartbeat,kanban_comment,kanban_attach,kanban_attach_url,kanban_attachments,kanban_create,kanban_link,kanban_unblock - Humans/scripts/cron drive it through
hermes kanban …on the CLI,/kanban …as a slash command, or the dashboard
The distinction that matters for our ground rules: delegate_task is a function
call (RPC fork→join, anonymous subagent, no resumability, audit trail lost on
compression). Kanban is a work queue where every handoff is a row any profile or
human can see and edit — named profiles with persistent memory, block→unblock→re-run,
crash→reclaim, human-in-the-loop, N agents over a task's life, durable audit trail
forever, peer coordination.
What we get that we specced by hand:
- One owner per task — enforced by
assignee+ atomicclaim(claim_lock) - Blocked → unblock → re-run, with
block_kindandblock_recurrences - Crash → reclaim (
hermes kanban reclaim) - Audit trail in
task_events+task_comments— never lost to compression kanban_heartbeat— the heartbeat mechanism, on the boardhermes kanban watch— a live event stream (--assignee,--tenant,--kinds,--interval) — this is Min's pollerhermes kanban dispatch— spawns workers (--dry-run,--max,--failure-limit)hermes kanban daemon— the dispatcher as a servicehermes kanban swarm— parallel workers → verifier → synthesizer graphhermes kanban boards— one board per project/workstream — this is how we get the "one board" rule right without one board for everything
Completion checkpoint before the iteration cap: dispatcher-owned workers get one
checkpoint notice near 90% of their finite iteration budget, attached to a fresh tool
result while another tool-capable call remains. A commit or diff alone never
automatically completes a task — the worker must call kanban_complete after verifying
the task contract, or persist a progress comment and continue.
Deadlock guard worth knowing: do NOT kanban_link a support card to the card it
is meant to unblock — the link makes the support card a child of the blocked parent,
so it is gated behind the parent it exists to unblock and neither ever runs. Reference
the parent id in the support card's body instead.
2.6 Kanban worker lanes — the contract for Heart agents
This is the piece that makes Heart Fleet on every host implementable in Hermes terms rather than bespoke.
Hermes Kanban = canonical task lifecycle + audit trail
Worker lane = implementation executor for one assigned card
Reviewer = human or human-proxy that gates "done"
GitHub PR = upstreamable artifact (optional, for code lanes)
A lane provides three things:
- An assignee string — matched against a Hermes profile name (default) or a
registered non-spawnable identifier (plugin lane shape). Tasks whose assignee
doesn't resolve are left on
readywith askipped_nonspawnableevent — not silently dropped or executed by an arbitrary fallback. That is exactly our "emptiness-is-loud" rule, built in. - A spawn mechanism — for profile lanes:
hermes -p <assignee> chat -q <prompt>inside the task's pinned workspace, with env vars set:HERMES_KANBAN_TASK,HERMES_KANBAN_DB,HERMES_KANBAN_BOARD,HERMES_KANBAN_WORKSPACES_ROOT,HERMES_KANBAN_WORKSPACE,HERMES_KANBAN_RUN_ID,HERMES_KANBAN_CLAIM_LOCK,HERMES_PROFILE,HERMES_TENANT.
For non-Hermes lanes (plugin-registered), the plugin supplies its ownspawn_fn. ← This is how Heart (SolOS V3 / opencode) becomes a lane: a plugin lane whose spawn_fn runs the V3 worker on a host with opencode v2. - A lifecycle terminator — every claim ends in exactly one of:
kanban_complete(summary=..., metadata=...)→ done ·kanban_request_review(summary=..., metadata=..., reviewer=...)→ review ·kanban_block(reason=...)→ blocked (dispatcher respawns onkanban_unblock).
The scope fence (directly relevant to our uid-boundary work): a task assignment
belongs to the dispatcher worker, not to every program it starts. Hermes subprocess
helpers carry a non-owner fence into shells, execution kernels, cron deliveries,
hooks, language servers, and ordinary stdio MCP servers. Later children remain fenced
even when a script removes the inherited task ID: CLI and tool mutations are rejected
rather than treating that script as an orchestrator. Honest limit, stated by Hermes:
this is cooperative runtime scoping, not OS confinement — it does not stop arbitrary
code deliberately erasing lineage metadata or opening SQLite directly. That is why our
uid-boundary fence (Sol's t_2b2636f9) is still a separate, real requirement.
Reviewer role: the dispatcher loads the bundled sdlc-review skill unless
kanban.review_dispatch is disabled. A reviewer approves with kanban_complete,
returns actionable rework with kanban_request_changes, or escalates a genuine
external blocker with kanban_block. This is our "one voice to Young" with a
mechanism — Young is the reviewer, Min is the orchestrator.
2.7 Automation blueprints — ready-to-run automations
Blueprints are ready-to-run automations. Pick one, fill a couple of fields, Hermes
schedules it as a cron job — no cron syntax required. Works from every surface:
dashboard Cron page → Blueprints tab, CLI/TUI/messengers (/blueprint <name>),
desktop "Send to App". The name match is forgiving (prefix or near-spelling resolves).
Never schedules anything silently — you always confirm before the job is created.
The 16 built-ins (measured from cron/blueprint_catalog.py, 791 lines):
| Key | Title | Fit for us |
|---|---|---|
morning-brief | Morning briefing: calendar, weather, agenda | Yes — Pulse daily brief |
workday-start | Weekday nudge with agenda + top priorities | Yes — Brain workday start |
evening-winddown | End-of-day check-in, tomorrow at a glance | Maybe — Pulse EOD |
weekly-review | Weekly recap: done, open, priorities | Yes — fleet weekly review (Min's Rule 10) |
competitor-watch | Track named companies for material news | Yes — Echo's competitor monitor |
price-watch | Watch a product, flight, hotel, listing | Yes — Pulse/investing price alerts |
bill-renewal-watch | Heads-up before recurring payment/subscription | Yes — our OpenCode subs + Oracle + domain renewals |
important-mail | Check inbox, ping ONLY about mail that matters | Maybe — needs O365 wiring |
news-digest | Topic news digest, deduped | Yes — Pulse news |
learn-daily | One bite-sized lesson a day | Maybe — agent skill drip |
meal-plan | Meal planning | No (personal) |
hydration-move | Health nudges | No (personal) |
gratitude-journal | Gratitude journaling | No (personal) |
habit-checkin | Habit check-ins | No (personal) |
custom-reminder | One-off reminder | Maybe |
on-this-day | Recall this day in history | No (personal) |
Plugin blueprints are prefixed with the plugin name (/blueprint teamtools:standup)
so a plugin can never replace a built-in. Plugins load per profile, so a plugin
blueprint is listed only in profiles where that plugin is enabled. A job created from
a plugin blueprint is an ordinary cron job; disabling the plugin removes it from the
catalog but jobs already created keep running.
Writing our own: a blueprint is just a skill with a metadata.hermes.blueprint
block in its SKILL.md frontmatter. Plugin authors can register one directly with
ctx.register_automation_blueprint().
2.8 Cron — the durable scheduler
A single cronjob_manage tool with action-style operations. Jobs can be one-shot or
recurring, attach zero-to-many skills, deliver to origin chat / local files / platform
targets, run in fresh agent sessions, run in no-agent mode (script on a schedule,
stdout delivered verbatim, zero LLM involvement), and fire on external events (a
webhook route with cron_job set fires the job the moment something happens).
Model resolution at fire time: per-job pin → cron.model in config.yaml → the main
agent model. Per-job reasoning effort is separately pinnable (none…ultra).
Cron-run sessions cannot recursively create more cron jobs — runaway scheduling
loops are disabled.
Our measured current cron state (LAB):
| Profile | Jobs |
|---|---|
| default | sol-persona-sync (every 30m) |
| mira | solos-backup-github (03:00), restore-proof-daily (03:30), spend-ledger-daily (09:00) |
| sol | kanban-obsidian-sync (every 30m) |
| nova | none |
| echo | none |
Gap: no agent has a coordination cron. Nobody is polling the board, nobody is probing the fleet, nobody is reporting to Min on a schedule. That is the missing mechanism, and cron is the tool for it.
3. What this changes in our design
| We specced | Hermes already has | Change |
|---|---|---|
tasks.db row per instruction | kanban.db tasks table | Drop the spec. Use kanban.db. |
One Kanban file (fleet-board.md) | hermes kanban boards (one per workstream) | Board = the DB. Keep the .md as a projection for the vault, not the source. |
| Min's work-queue dispatcher cron | hermes kanban dispatch + daemon | Use the built-in. |
| Polling agents for staleness | hermes kanban watch + kanban_heartbeat | Use the built-in. |
| "Nothing is live until the exit proof passes" | /goal gate add <command> | Enforce it in Hermes, not in prose. |
| "Iterate until done" (agent keeps working) | /goal + judge | Use it. |
| "Poller that doesn't re-prompt" | /heartbeat (idle-only, don't-invent-work guard) | Use it. |
| "Watch a deploy, tell me when it changes" | /loop (self-paced) | Use it. |
| Standalone daily/weekly jobs | cron + blueprints | Use blueprints for the ready-made ones. |
| Heart as a worker lane | plugin lane + spawn_fn | This is how Heart-on-every-host works. |
| "One voice to Young" | reviewer role (kanban_request_review) | Young reviews, Min orchestrates. |
| Empty agents show RED, not silent | skipped_nonspawnable event | Built in. |
4. The fit is not perfect — three real gaps
- Heart agents are not Hermes profiles. They are SolOS V3 workers on opencode.
A plugin lane with a custom
spawn_fnis the documented path, but it is not built yet. Until then, Heart cards cannot be dispatched by the board. This is Sol's lane. - The A2A dialect split is still open (Heart=v1.0, Pulse=v0.3, Brain=both). A board that dispatches across planes needs one dialect. Until fixed, a card dispatched to Pulse or Heart can be accepted and then silently fail.
- Kanban is a single-host SQLite file.
~/.hermes/kanban.dbon LAB. The Pulse laptop and prod have their own. A board row created on LAB is not visible to Tam on the laptop. Our A2A reporting design (Min polls each agent) is still needed for cross-host state — the board covers same-host coordination, A2A covers cross-host.
Honest note: the earlier a2a-agent-reporting-system-2026-10-20.md spec is
not superseded — it covers the cross-host case Hermes Kanban does not. But its
same-host half is now redundant with the board.
5. Recommended adoption order
| # | Action | Owner | Why now |
|---|---|---|---|
| 1 | Create the fleet board: hermes kanban boards → one board per workstream | Min | Board exists (empty). Without a board, nothing dispatches. |
| 2 | Move the 19 fleet-board.md cards into real kanban cards | Min | The .md is currently the register; the DB should be canonical. |
| 3 | Wire hermes kanban dispatch --dry-run into a Min cron | Sol | Proves dispatch without spawning |
| 4 | hermes kanban daemon as a systemd user unit on LAB | Sol | The dispatcher becomes a service, not a cron tick |
| 5 | /goal gate on the Docker-image cards (exit proof as a shell command) | Sol | Turns "done" into a measured claim |
| 6 | /heartbeat on Min: poll A2A endpoints, report only changes | Min | The poller Young asked for |
| 7 | Heart plugin lane (spawn_fn → V3 worker) | Sol + Tam | Unblocks Heart on every host |
| 8 | Blueprints: morning-brief (Pulse), weekly-review (fleet), bill-renewal-watch (subs), competitor-watch (Echo) | Mira + Echo | Ready-made, no cron syntax |
| 9 | Unify A2A dialect; record dialect: per peer in the registry | Sol | Blocked for cross-plane dispatch |
| 10 | Bridge cross-host board state via A2A (or accept the split) | Min + Tam | Young's ruling needed on board topology |
6. Open questions for Young
- Board topology — one board per workstream (SolOS, Pulse, Prod-tenants) or one
fleet board? Hermes supports both (
kanban boards). The ground rules say "one board"; the reality is 3 units. My rec: one board per workstream, one fleet board as the view. - Does the vault
.mdboard stay? The ground rules made it the register. Hermes says the DB is canonical and the .md a projection. My rec: DB canonical, .md generated by the existingkanban-obsidian-synccron (Sol's, already every 30m). No manual .md editing. - Who reviews?
kanban_request_reviewneeds a reviewer identity. My rec: Young (one voice, real gate) — Min is the orchestrator, not the approver.
🎯 Min's Read: the fit is real and larger than I expected — Hermes Kanban is
the work-queue we specced, with the columns already in the schema, and /goal gate
is our exit-proof rule made enforceable rather than aspirational. The construction
work is done; what remains is wiring, and three honest gaps (Heart is not a Hermes
lane yet, the A2A dialect split blocks cross-plane dispatch, and the board is
single-host so cross-host state still needs A2A). The spec I wrote yesterday is
half-redundant now — and that is worth saying plainly rather than defending the doc.
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.
Medusa × EVE integration pattern (2026-10-13)
Young's synthesis direction: Medusa + EVE is a great match. Verified every claim in the brief against live sources — all check out, including two I initially doubted and then confirmed wrong. This note registers the combined architecture on top of hermes-eve-cohosting-study and medusa-hosting-docker-study.

