The problem
Hermes has living documentation. Skills and memory are also living systems. The tempting but wrong move is to scrape the docs and immediately patch the agent’s long-term behavior. That would be fast, and it would also be a foot-gun.
The build I landed is intentionally conservative. It turns documentation deltas into Kanban review cards. A reviewer or worker can then extract operational knowledge, propose skill patches, and mark memory candidates. Nothing durable happens just because a page changed.
The implementation shape
The core code lives in a thin, testable module: hermes_cli/learn.py. It owns discovery, state, task body generation, result formatting, CLI parsing, and slash-command parsing. The rest of Hermes only routes into it.
The first useful slice was deliberately narrow: hermes docs, not a general crawler, not a self-modifying learner, not a new model tool.
The bug that mattered
The subtle failure was Kanban semantics. Creating a task with initial_status="blocked" stored status='blocked', but it did not make the block sticky. The list and dispatcher paths call kb.recompute_ready(). That promotion pass can move non-sticky blocked tasks back to ready when they have no parent dependency.
For a review gate, that is exactly wrong. The fix was to create the task with the normal path, then call the official block API with kind="needs_input". That emits a real blocked event, which recompute_ready() respects.
task_id = kb.create_task(..., idempotency_key=key, tenant="hermes-docs")
task = kb.get_task(conn, task_id)
if task and task.status in {"ready", "running"}:
kb.block_task(
conn,
task_id,
reason="Review gate: inspect docs delta before any skill/memory/config write",
kind="needs_input",
)
What landed
hermes_cli/learn.py owns docs discovery, page hashing, state persistence, task generation, CLI parsing, and slash parsing.
CLI, TUI, and gateway surfaces route exact hermes docs requests to the command path and leave generic /learn alone.
The test slice covers sitemap discovery, fallback docs links, idempotency, changed-page detection, slash routing, and sticky review gates.
Verification
The feature was built test-first. The important regression test fails if a review card becomes ready after kb.recompute_ready(). That is the guardrail that caught the original mistake.
uv run --extra dev python -m pytest tests/hermes_cli/test_learn_docs.py -q -o 'addopts='
# 5 passed
uv run --extra dev python -m pytest tests/tui_gateway/test_protocol.py -q -o 'addopts='
# 72 passed
uv run --extra dev ruff check hermes_cli/learn.py tests/hermes_cli/test_learn_docs.py
# All checks passed
A real write-path smoke test ran in a temporary HERMES_HOME. It created two learning cards, verified both remained blocked after recompute, and verified both had blocked events with needs_input.
What this is not
- It is not autonomous skill mutation.
- It is not a general web crawler.
- It is not a background daemon yet.
- It is not a replacement for a human reviewer deciding what belongs in durable agent memory.
The right next step is a review command that opens one blocked docs-learning card, extracts a clean candidate patch, and keeps the final write behind an explicit approval step.