mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-06-23 10:42:00 +00:00
refactor(kanban): fold worker/orchestrator skills into injected guidance (#50473)
The kanban-worker and kanban-orchestrator bundled skills existed only to be force-loaded into dispatcher-spawned workers, gated by environments:[kanban] so they wouldn't leak into normal CLI listings. That gating was fragile (the leak that #50443 patched) and the --skills auto-load was already best-effort — most workers ran without it because the bundled skill isn't present in profile-scoped skills dirs. Remove the skills entirely and promote their load-bearing content (workspace kinds, deliverable artifacts, created-card integrity, profile discovery) into KANBAN_GUIDANCE, which is already injected into every kanban worker's system prompt. Net result: every worker reliably gets the guidance, nothing can leak into a CLI/blank-slate session, and the gating machinery is gone. - agent/prompt_builder.py: promote the 4 load-bearing rules into KANBAN_GUIDANCE - hermes_cli/kanban_db.py: drop --skills kanban-worker auto-injection + _kanban_worker_skill_available probe - hermes_cli/kanban_swarm.py: drop skills=[kanban-orchestrator] on the root card - hermes_cli/kanban.py: drop kanban-init skill seeding; fix help text - delete skills/devops/kanban-{worker,orchestrator} - docs: delete the two skill pages (EN+zh), fix sidebars/catalog/kanban.md/kanban-worker-lanes.md and the video-orchestrator + codex-lane references - tests: update spawn-argv expectations; re-bound the guidance-size guard Supersedes the skill-leak half of #50443 (credit @helix4u for flagging the area).
This commit is contained in:
parent
e5e2583635
commit
84e1d31e54
32 changed files with 160 additions and 1575 deletions
|
|
@ -238,6 +238,23 @@ KANBAN_GUIDANCE = (
|
|||
"of the decomposition. Do NOT execute the work yourself; your job is "
|
||||
"routing, not implementation.\n"
|
||||
"\n"
|
||||
"## Reference details that change outcomes\n"
|
||||
"\n"
|
||||
"- **Workspace.** `cd $HERMES_KANBAN_WORKSPACE` first. For a `worktree` kind "
|
||||
"with no `.git`, `git worktree add <path> "
|
||||
"${HERMES_KANBAN_BRANCH:-wt/$HERMES_KANBAN_TASK}` from the main repo, then "
|
||||
"cd there.\n"
|
||||
"- **Deliverables.** Files a human wants go in "
|
||||
"`kanban_complete(artifacts=[<absolute paths>])` (top-level param; paths in "
|
||||
"`metadata` are NOT uploaded). Files must exist at completion.\n"
|
||||
"- **Created cards.** List ids in `kanban_complete(created_cards=[...])` "
|
||||
"ONLY when captured from a successful `kanban_create` return — never invent "
|
||||
"or paste ids; the kernel rejects the completion on any phantom id.\n"
|
||||
"- **Orchestrating: discover profiles first.** The dispatcher SILENTLY "
|
||||
"drops a card with an unknown assignee (it sits in `ready` forever). Ground "
|
||||
"every assignee in a real profile (`hermes profile list`, or ask the user), "
|
||||
"and express dependencies via `parents=[...]` on `kanban_create`, not prose.\n"
|
||||
"\n"
|
||||
"## Do NOT\n"
|
||||
"\n"
|
||||
"- Do not shell out to `hermes kanban <verb>` for board operations. Use "
|
||||
|
|
|
|||
|
|
@ -280,9 +280,9 @@ def skill_matches_environment(frontmatter: Dict[str, Any]) -> bool:
|
|||
This is an OFFER-time filter: it controls whether a skill shows up in the
|
||||
skills index / autocomplete / slash-command list. It is intentionally NOT
|
||||
enforced by ``skill_view`` or ``--skills`` preloading — an explicit load is
|
||||
explicit consent, and load-bearing force-loads (e.g. the kanban dispatcher
|
||||
injecting ``--skills kanban-worker``) must always succeed regardless of how
|
||||
the offer surfaces filter the skill.
|
||||
explicit consent, and load-bearing force-loads (e.g. a dispatcher pinning
|
||||
a task to a specialist skill via ``--skills``) must always succeed
|
||||
regardless of how the offer surfaces filter the skill.
|
||||
|
||||
A skill matches when ANY of its declared environments is currently active
|
||||
(OR semantics, mirroring ``platforms``). Unknown env tags fail open.
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ from typing import Any, Optional
|
|||
|
||||
from hermes_cli import kanban_db as kb
|
||||
from hermes_cli import kanban_swarm as ks
|
||||
from hermes_cli.profiles import get_active_profile_name, get_profile_dir, seed_profile_skills
|
||||
from hermes_cli.profiles import get_active_profile_name
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
|
@ -330,8 +330,8 @@ def build_parser(parent_subparsers: argparse._SubParsersAction) -> argparse.Argu
|
|||
help="Author name recorded on the task (default: user)")
|
||||
p_create.add_argument("--skill", action="append", default=[], dest="skills",
|
||||
help="Skill to force-load into the worker "
|
||||
"(repeatable). Appended to the built-in "
|
||||
"kanban-worker skill. Example: "
|
||||
"(repeatable). The kanban lifecycle is already "
|
||||
"injected automatically. Example: "
|
||||
"--skill translation --skill github-code-review")
|
||||
p_create.add_argument("--max-retries", type=int, default=None,
|
||||
metavar="N",
|
||||
|
|
@ -1223,21 +1223,6 @@ def _cmd_init(args: argparse.Namespace) -> int:
|
|||
path = kb.init_db()
|
||||
print(f"Kanban DB initialized at {path}")
|
||||
|
||||
# Seed bundled skills (e.g. kanban-worker) into the active profile so
|
||||
# the kanban dispatcher can use them without a separate `hermes profile
|
||||
# create` step. This is best-effort — a missing or broken profile is
|
||||
# not fatal to `kanban init`.
|
||||
try:
|
||||
profile_name = get_active_profile_name() or "default"
|
||||
profile_dir = get_profile_dir(profile_name)
|
||||
result = seed_profile_skills(profile_dir, quiet=True)
|
||||
if result:
|
||||
copied = result.get("copied", [])
|
||||
if copied:
|
||||
print(f"Seeded skill(s) into profile {profile_name}: {', '.join(copied)}")
|
||||
except Exception:
|
||||
pass # best-effort
|
||||
|
||||
print()
|
||||
# Enumerate profiles on disk so the user knows what assignees are
|
||||
# already addressable. Multica does this auto-detection on its
|
||||
|
|
@ -1461,8 +1446,7 @@ def _cmd_show(args: argparse.Namespace) -> int:
|
|||
parents = kb.parent_ids(conn, args.task_id)
|
||||
children = kb.child_ids(conn, args.task_id)
|
||||
runs = kb.list_runs(conn, args.task_id, **rsk)
|
||||
# Workers hand off via ``task_runs.summary`` (kanban-worker skill);
|
||||
# ``tasks.result`` is left NULL unless the caller explicitly passed
|
||||
# Workers hand off via ``task_runs.summary``; ``tasks.result`` is left NULL unless the caller explicitly passed
|
||||
# ``result=``. Surfacing the latest summary here keeps ``show`` from
|
||||
# looking like a no-op when the worker actually did real work.
|
||||
latest_summary = kb.latest_summary(conn, args.task_id)
|
||||
|
|
|
|||
|
|
@ -804,10 +804,9 @@ class Task:
|
|||
current_run_id: Optional[int] = None
|
||||
workflow_template_id: Optional[str] = None
|
||||
current_step_key: Optional[str] = None
|
||||
# Force-loaded skills for the worker on this task (appended to the
|
||||
# dispatcher's built-in `kanban-worker` via --skills). Stored as a
|
||||
# JSON array of skill names. None = use only the defaults; empty
|
||||
# list = explicitly no extra skills.
|
||||
# Force-loaded skills for the worker on this task (passed via
|
||||
# --skills). Stored as a JSON array of skill names. None = use only
|
||||
# the defaults; empty list = explicitly no extra skills.
|
||||
skills: Optional[list] = None
|
||||
model_override: Optional[str] = None
|
||||
# Per-task override for the consecutive-failure circuit breaker.
|
||||
|
|
@ -1045,8 +1044,7 @@ CREATE TABLE IF NOT EXISTS tasks (
|
|||
workflow_template_id TEXT,
|
||||
current_step_key TEXT,
|
||||
-- Force-loaded skills for the worker on this task, stored as JSON.
|
||||
-- Appended to the dispatcher's built-in `--skills kanban-worker`.
|
||||
-- NULL or empty array = no extras.
|
||||
-- Passed to the worker via `--skills`. NULL or empty array = no extras.
|
||||
skills TEXT,
|
||||
-- Per-task model override. When set, the dispatcher passes -m <model>
|
||||
-- to the worker, overriding the profile's default model. NULL = use
|
||||
|
|
@ -1848,8 +1846,7 @@ def _migrate_add_optional_columns(conn: sqlite3.Connection) -> None:
|
|||
)
|
||||
if "skills" not in cols:
|
||||
# JSON array of skill names the dispatcher force-loads into the
|
||||
# worker (additive to the built-in `kanban-worker`). NULL is fine
|
||||
# for existing rows.
|
||||
# worker via --skills. NULL is fine for existing rows.
|
||||
_add_column_if_missing(conn, "tasks", "skills", "skills TEXT")
|
||||
|
||||
if "max_retries" not in cols:
|
||||
|
|
@ -2285,9 +2282,8 @@ def create_task(
|
|||
|
||||
``skills`` is an optional list of skill names to force-load into
|
||||
the worker when dispatched. Stored as JSON; the dispatcher passes
|
||||
each name to ``hermes --skills ...`` alongside the built-in
|
||||
``kanban-worker``. Use this to pin a task to a specialist skill
|
||||
(e.g. ``skills=["translation"]`` so the worker loads the
|
||||
each name to ``hermes --skills ...``. Use this to pin a task to a
|
||||
specialist skill (e.g. ``skills=["translation"]`` so the worker loads the
|
||||
translation skill regardless of the profile's default config).
|
||||
"""
|
||||
assignee = _canonical_assignee(assignee)
|
||||
|
|
@ -2348,7 +2344,7 @@ def create_task(
|
|||
f"{quoted} {noun}, not skill name(s). "
|
||||
"Put toolsets in the assignee profile's `toolsets:` config "
|
||||
"instead of per-task skills. Skills are named skill bundles "
|
||||
"(e.g. `kanban-worker`, `blogwatcher`); toolsets are runtime "
|
||||
"(e.g. `blogwatcher`, `github-code-review`); toolsets are runtime "
|
||||
"capabilities (e.g. `web`, `browser`, `terminal`)."
|
||||
)
|
||||
skills_list = cleaned
|
||||
|
|
@ -6994,11 +6990,11 @@ def _dispatch_once_locked(
|
|||
if claimed.workspace_kind == "worktree":
|
||||
set_branch_name(conn, claimed.id, resolved_branch_name or (claimed.branch_name or "").strip() or f"wt/{claimed.id}")
|
||||
_maybe_emit_scratch_tip(conn, claimed.id, claimed.workspace_kind)
|
||||
# Force-load sdlc-review skill for review agents. The
|
||||
# _default_spawn function already auto-loads kanban-worker, and
|
||||
# appends task.skills via --skills. Setting task.skills here
|
||||
# means the review agent gets both kanban-worker (lifecycle)
|
||||
# and sdlc-review (review logic: AC verification, merge, etc.).
|
||||
# Force-load the sdlc-review skill for review agents — it carries
|
||||
# the review logic (AC verification, merge, etc.). The mandatory
|
||||
# kanban lifecycle is already injected into every worker's system
|
||||
# prompt via KANBAN_GUIDANCE, so this is the only extra skill the
|
||||
# review agent needs.
|
||||
claimed.skills = ["sdlc-review"]
|
||||
_spawn = spawn_fn if spawn_fn is not None else _default_spawn
|
||||
try:
|
||||
|
|
@ -7223,41 +7219,6 @@ def _resolve_hermes_argv() -> list[str]:
|
|||
return _module_hermes_argv()
|
||||
|
||||
|
||||
def _kanban_worker_skill_available(hermes_home: Optional[str]) -> bool:
|
||||
"""True if the bundled ``kanban-worker`` skill resolves for the home the
|
||||
spawned worker will run under.
|
||||
|
||||
The dispatcher injects ``--skills kanban-worker`` into every worker. When
|
||||
the worker activates a profile (``hermes -p <name>``), its ``SKILLS_DIR``
|
||||
becomes ``<profile_home>/skills`` — which on many profiles does NOT contain
|
||||
the bundled skill (it ships in the *default* root home, not every
|
||||
profile-scoped skills dir). Preloading a missing skill is fatal at CLI
|
||||
startup (``ValueError: Unknown skill(s): kanban-worker``), aborting the
|
||||
worker before the agent loop runs. Gate the flag on actual resolvability;
|
||||
the kanban lifecycle contract is still injected via ``KANBAN_GUIDANCE``, so
|
||||
omitting the flag only drops the supplementary pattern library.
|
||||
"""
|
||||
from pathlib import Path as _Path
|
||||
|
||||
# An unset HERMES_HOME means the worker falls back to the default root
|
||||
# home (``~/.hermes``), which ships the bundled skill.
|
||||
base = _Path(hermes_home) if hermes_home else (_Path.home() / ".hermes")
|
||||
skills_root = base / "skills"
|
||||
if not skills_root.is_dir():
|
||||
return False
|
||||
# Canonical bundled location first (cheap), then a bounded scan for
|
||||
# profiles that have it nested elsewhere.
|
||||
if (skills_root / "devops" / "kanban-worker" / "SKILL.md").is_file():
|
||||
return True
|
||||
try:
|
||||
for skill_md in skills_root.rglob("kanban-worker/SKILL.md"):
|
||||
if skill_md.is_file():
|
||||
return True
|
||||
except OSError:
|
||||
pass
|
||||
return False
|
||||
|
||||
|
||||
def _worker_terminal_timeout_env(
|
||||
max_runtime_seconds: Optional[int],
|
||||
current_timeout: Optional[str],
|
||||
|
|
@ -7440,32 +7401,14 @@ def _default_spawn(
|
|||
# profile-local worker sessions still register configured hooks.
|
||||
"--accept-hooks",
|
||||
]
|
||||
# Auto-load the kanban-worker skill so every dispatched worker
|
||||
# has the pattern library (good summary/metadata shapes, retry
|
||||
# diagnostics, block-reason examples) in its context, even if
|
||||
# the profile hasn't wired it into skills config. The MANDATORY
|
||||
# lifecycle is already in the system prompt via KANBAN_GUIDANCE;
|
||||
# this skill is the deeper reference. Users can point a profile
|
||||
# at a different/additional skill via config if they want —
|
||||
# --skills is additive to the profile's default skill set.
|
||||
#
|
||||
# Only add the flag when the skill actually resolves for the home
|
||||
# the worker runs under: the bundled skill is absent from many
|
||||
# profile-scoped skills dirs, and preloading a missing skill is
|
||||
# fatal at CLI startup. Omitting it is safe — the lifecycle
|
||||
# contract still ships via KANBAN_GUIDANCE.
|
||||
if _kanban_worker_skill_available(env.get("HERMES_HOME")):
|
||||
cmd.extend(["--skills", "kanban-worker"])
|
||||
# Per-task force-loaded skills. Each name goes in its own
|
||||
# `--skills X` pair rather than a single comma-joined arg: the CLI
|
||||
# accepts both forms (action='append' + comma-split), but
|
||||
# per-name pairs are easier to read in `ps` output and avoid any
|
||||
# quoting ambiguity if a skill name ever contains unusual chars.
|
||||
# Dedupe against the built-in so we don't double-load kanban-worker
|
||||
# if a task author asks for it explicitly.
|
||||
if task.skills:
|
||||
for sk in task.skills:
|
||||
if sk and sk != "kanban-worker":
|
||||
if sk:
|
||||
cmd.extend(["--skills", sk])
|
||||
if task.model_override:
|
||||
cmd.extend(["-m", task.model_override])
|
||||
|
|
@ -8322,7 +8265,7 @@ def latest_run(conn: sqlite3.Connection, task_id: str) -> Optional[Run]:
|
|||
def latest_summary(conn: sqlite3.Connection, task_id: str) -> Optional[str]:
|
||||
"""Return the latest non-null ``task_runs.summary`` for ``task_id``.
|
||||
|
||||
The kanban-worker skill writes its handoff to ``task_runs.summary``
|
||||
The worker writes its handoff to ``task_runs.summary``
|
||||
via ``complete_task(summary=...)``; ``tasks.result`` is left empty
|
||||
unless the caller passes ``result=`` explicitly. Dashboards and CLI
|
||||
"show" views need this value to surface what a worker actually did
|
||||
|
|
|
|||
|
|
@ -124,7 +124,6 @@ def create_swarm(
|
|||
idempotency_key=idempotency_key,
|
||||
workspace_kind=workspace_kind,
|
||||
workspace_path=workspace_path,
|
||||
skills=["kanban-orchestrator"],
|
||||
)
|
||||
|
||||
# If idempotency returned an existing non-archived root, do not duplicate the
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ platforms: [linux, macos, windows]
|
|||
metadata:
|
||||
hermes:
|
||||
tags: [video, kanban, multi-agent, orchestration, production-pipeline]
|
||||
related_skills: [kanban-orchestrator, kanban-worker, ascii-video, manim-video, p5js, comfyui, touchdesigner-mcp, blender-mcp, pixel-art, ascii-art, songwriting-and-ai-music, heartmula, songsee, spotify, youtube-content, claude-design, excalidraw, architecture-diagram, concept-diagrams, baoyu-comic, baoyu-infographic, humanizer, gif-search, meme-generation]
|
||||
related_skills: [ascii-video, manim-video, p5js, comfyui, touchdesigner-mcp, blender-mcp, pixel-art, ascii-art, songwriting-and-ai-music, heartmula, songsee, spotify, youtube-content, claude-design, excalidraw, architecture-diagram, concept-diagrams, baoyu-comic, baoyu-infographic, humanizer, gif-search, meme-generation]
|
||||
credits: |
|
||||
The single-project workspace layout, profile-config patching pattern,
|
||||
SOUL.md-per-profile model, TEAM.md task-graph convention, and
|
||||
|
|
@ -174,8 +174,9 @@ task graphs. See **[references/examples.md](references/examples.md)**.
|
|||
6. **The director never executes.** Even with the full `kanban + terminal +
|
||||
file` toolset, the director's `SOUL.md` rules forbid it from executing
|
||||
work itself. It decomposes and routes only — every concrete task becomes
|
||||
a `hermes kanban create` call to a specialist profile. The
|
||||
`kanban-orchestrator` skill spells this out further.
|
||||
a `hermes kanban create` call to a specialist profile. The kanban
|
||||
orchestration guidance auto-injected into every kanban worker's system
|
||||
prompt spells this out further.
|
||||
|
||||
7. **Don't over-decompose.** A 30-second product video does NOT need 20 tasks.
|
||||
Aim for the smallest task graph that still parallelizes well and exposes the
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ echo "═══ Configuring profiles ═══"
|
|||
configure_profile() {
|
||||
local profile="$1"
|
||||
local toolsets_json="$2" # JSON array string, e.g. '["kanban","terminal","file"]'
|
||||
local skills_json="$3" # JSON array string, e.g. '["kanban-worker","ascii-video"]'
|
||||
local skills_json="$3" # JSON array string, e.g. '["ascii-video"]'
|
||||
python3 - "$profile" "$toolsets_json" "$skills_json" "$WORKSPACE" <<'PY'
|
||||
"""Patch a Hermes profile config.yaml using PyYAML so we don't depend on the
|
||||
exact default-config string format. Validates the patch took effect and exits
|
||||
|
|
|
|||
|
|
@ -39,8 +39,8 @@ T8 reviewer final QA (parent: T7)
|
|||
**Key choices:**
|
||||
- Local ComfyUI via `comfyui` skill is preferred over external API for
|
||||
cost/control — but external APIs are fine if ComfyUI isn't installed
|
||||
- `editor` profile is ffmpeg-only, no Hermes skill required beyond
|
||||
`kanban-worker`
|
||||
- `editor` profile is ffmpeg-only, no Hermes skill required (kanban guidance
|
||||
is auto-injected into every kanban worker)
|
||||
- Storyboarder produces `storyboard.excalidraw` alongside the markdown
|
||||
|
||||
## Example 2 — Product / marketing teaser
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ default-config schema drift:
|
|||
configure_profile() {
|
||||
local profile="$1"
|
||||
local toolsets_json="$2" # JSON array, e.g. '["kanban","terminal","file"]'
|
||||
local skills_json="$3" # JSON array, e.g. '["kanban-worker","ascii-video"]'
|
||||
local skills_json="$3" # JSON array, e.g. '["ascii-video"]'
|
||||
python3 - "$profile" "$toolsets_json" "$skills_json" <<'PY'
|
||||
import json, os, sys, yaml
|
||||
profile, ts_json, sk_json = sys.argv[1:4]
|
||||
|
|
@ -133,16 +133,16 @@ the entire production. **Critical content for the director's SOUL.md:**
|
|||
|
||||
- **Anti-temptation rules:** "Do not execute the work yourself. For every
|
||||
concrete task, create a kanban task and assign it. Decompose, route, comment,
|
||||
approve — that's the whole job." (The `kanban-orchestrator` skill provides
|
||||
the deeper playbook; load it.)
|
||||
approve — that's the whole job." (The kanban orchestration guidance is
|
||||
auto-injected into every kanban worker's system prompt — no skill to load.)
|
||||
- **Decomposition steps:** Read `brief.md`, `TEAM.md`, `taste/`. Use the team
|
||||
graph in `TEAM.md` to fan out tasks.
|
||||
- **The workspace_path rule** (see below).
|
||||
|
||||
Other profiles' SOUL.md is briefer; mostly mechanical: who you are, what you
|
||||
read, what you produce, what skills/tools to use, where to write outputs.
|
||||
Most non-director profiles should `always_load: kanban-worker` for the
|
||||
deeper-than-baseline kanban guidance.
|
||||
The kanban lifecycle guidance is auto-injected into every kanban worker's
|
||||
system prompt, so no profile needs to load a kanban skill.
|
||||
|
||||
### Initial kanban task
|
||||
|
||||
|
|
|
|||
|
|
@ -18,15 +18,16 @@ The vision-holder. Reads the brief and brand guide, decomposes into a task
|
|||
graph, comments to steer creative direction, approves the final cut.
|
||||
|
||||
- **Toolsets:** kanban, terminal, file
|
||||
- **Skills:** `kanban-orchestrator`. The kanban plugin auto-injects baseline
|
||||
orchestration guidance for free; `kanban-orchestrator` is the deeper
|
||||
decomposition playbook. Add `creative-ideation` if the brief is wide-open
|
||||
and needs framing help.
|
||||
- **Skills:** no extra skill needed — the kanban orchestration guidance
|
||||
(decomposition playbook, "decompose, don't execute" discipline) is
|
||||
auto-injected into every kanban worker's system prompt. Add
|
||||
`creative-ideation` if the brief is wide-open and needs framing help.
|
||||
- **Personality:** Tied to the brand voice — see `assets/soul.md.tmpl`
|
||||
|
||||
The director has the same toolset as everyone else, but its `SOUL.md` rules
|
||||
**forbid** execution. The "decompose, don't execute" discipline is enforced
|
||||
by personality + the kanban-orchestrator skill, not by missing tools.
|
||||
by personality + the auto-injected kanban orchestration guidance, not by
|
||||
missing tools.
|
||||
|
||||
## Pre-production roles
|
||||
|
||||
|
|
@ -38,7 +39,7 @@ Writes scripts, dialogue, voiceover copy, narration. Use for any video with
|
|||
spoken or written words beyond a tagline.
|
||||
|
||||
- **Toolsets:** kanban, file
|
||||
- **Skills:** `kanban-worker`, `humanizer` (post-process to strip AI-tells)
|
||||
- **Skills:** `humanizer` (post-process to strip AI-tells)
|
||||
- **Outputs:** `script.md`, `narration.md`, `dialogue/scene-NN.md`
|
||||
|
||||
### copywriter
|
||||
|
|
@ -47,7 +48,7 @@ Like `writer` but specifically for marketing copy: taglines, CTAs, voiceover
|
|||
scripts for product videos.
|
||||
|
||||
- **Toolsets:** kanban, file
|
||||
- **Skills:** `kanban-worker`, `humanizer`
|
||||
- **Skills:** `humanizer`
|
||||
- **Outputs:** `copy.md`
|
||||
|
||||
### concept-artist / visual-designer
|
||||
|
|
@ -58,7 +59,7 @@ follow. Often produces still reference frames using image-generation APIs or
|
|||
local skills.
|
||||
|
||||
- **Toolsets:** kanban, terminal, file
|
||||
- **Skills:** `kanban-worker` plus any project-specific design skill —
|
||||
- **Skills:** any project-specific design skill —
|
||||
`claude-design` (UI/web), `sketch` (quick mockup variants),
|
||||
`popular-web-designs` (matching known web aesthetic), `pixel-art` (retro),
|
||||
`ascii-art` (terminal/retro), `excalidraw` (hand-drawn frames),
|
||||
|
|
@ -71,7 +72,7 @@ Maps the brief to a beat-by-beat shot list with timing. Critical for narrative
|
|||
film and music video. Often pairs with a diagramming tool.
|
||||
|
||||
- **Toolsets:** kanban, file
|
||||
- **Skills:** `kanban-worker` plus a diagram skill — `excalidraw` (sketch),
|
||||
- **Skills:** a diagram skill — `excalidraw` (sketch),
|
||||
`architecture-diagram` (technical/system), `concept-diagrams` (educational/
|
||||
scientific)
|
||||
- **Outputs:** `storyboard.md` with one row per scene/shot, optional
|
||||
|
|
@ -83,7 +84,7 @@ Designs the visual language: framing, color, motion, transitions. Reviews
|
|||
generator output for visual consistency. Hands off per-scene `VISUAL_SPEC.md`.
|
||||
|
||||
- **Toolsets:** kanban, terminal, file, video, vision
|
||||
- **Skills:** `kanban-worker` plus the visual skill that matches the project
|
||||
- **Skills:** the visual skill that matches the project
|
||||
(e.g., `ascii-video` for ASCII work, `manim-video` for explainers,
|
||||
`touchdesigner-mcp` for real-time visuals, etc.)
|
||||
- **Outputs:** `scenes/scene-NN/VISUAL_SPEC.md`, review comments on renderer
|
||||
|
|
@ -124,8 +125,9 @@ instead of overloading one. Each loads a different creative skill.
|
|||
| `renderer-video` | (external image-to-video API: Runway / Kling / Luma) | Animating still images in narrative film |
|
||||
| `renderer-motion-graphics` | (external — Remotion CLI) | Motion graphics, kinetic typography, UI animations |
|
||||
|
||||
For external-API renderers, the profile holds the API client logic; only
|
||||
`kanban-worker` is loaded, plus the terminal toolset and the API key.
|
||||
For external-API renderers, the profile holds the API client logic; no extra
|
||||
skill is loaded (kanban guidance is auto-injected into every kanban worker),
|
||||
plus the terminal toolset and the API key.
|
||||
|
||||
### image-generator
|
||||
|
||||
|
|
@ -133,7 +135,7 @@ Specifically for text-to-image generation. Often produces stills that go to
|
|||
`renderer-video` for animation.
|
||||
|
||||
- **Toolsets:** kanban, terminal, file
|
||||
- **Skills:** `kanban-worker`, optionally `comfyui` (drives a local
|
||||
- **Skills:** optionally `comfyui` (drives a local
|
||||
ComfyUI install for image generation)
|
||||
- **External APIs (alternative to local ComfyUI):** FAL, Replicate, OpenAI
|
||||
Images, Midjourney
|
||||
|
|
@ -146,7 +148,7 @@ ComfyUI's image-to-video workflows locally. Almost always follows
|
|||
`image-generator` in narrative film pipelines.
|
||||
|
||||
- **Toolsets:** kanban, terminal, file
|
||||
- **Skills:** `kanban-worker`, optionally `comfyui` (for local image-to-video
|
||||
- **Skills:** optionally `comfyui` (for local image-to-video
|
||||
workflows like AnimateDiff or WAN)
|
||||
- **External APIs:** Runway, Kling, Luma, Pika
|
||||
- **Outputs:** `scenes/scene-NN/clip.mp4`
|
||||
|
|
@ -159,7 +161,7 @@ spectrograms when the editor or renderer needs a visual reference of the
|
|||
audio's energy.
|
||||
|
||||
- **Toolsets:** kanban, terminal, file
|
||||
- **Skills:** `kanban-worker`, `songsee` (audio visualization), plus one of:
|
||||
- **Skills:** `songsee` (audio visualization), plus one of:
|
||||
- `songwriting-and-ai-music` — when commissioning lyrics + Suno prompts
|
||||
- `heartmula` — when generating music with the open-source local model
|
||||
- `spotify` — when sourcing existing tracks
|
||||
|
|
@ -169,11 +171,11 @@ audio's energy.
|
|||
### voice-talent / narrator
|
||||
|
||||
Generates voiceover audio. Calls a TTS API directly; no Hermes skill required
|
||||
beyond `kanban-worker`. The user can also supply pre-recorded VO instead of
|
||||
generation.
|
||||
(kanban guidance is auto-injected into every kanban worker). The user can also
|
||||
supply pre-recorded VO instead of generation.
|
||||
|
||||
- **Toolsets:** kanban, terminal, file
|
||||
- **Skills:** `kanban-worker`
|
||||
- **Skills:** none — kanban guidance is auto-injected into every kanban worker
|
||||
- **External APIs:** ElevenLabs, OpenAI TTS, etc.
|
||||
- **Outputs:** `audio/voiceover/line-NN.mp3`, `audio/voiceover/timeline.mp3`
|
||||
|
||||
|
|
@ -183,7 +185,7 @@ Sound effects and ambient design. Often optional unless the brief calls for
|
|||
sound design specifically.
|
||||
|
||||
- **Toolsets:** kanban, terminal, file
|
||||
- **Skills:** `kanban-worker`, `songsee` for audio-feature visualization when
|
||||
- **Skills:** `songsee` for audio-feature visualization when
|
||||
designing to a track
|
||||
- **Outputs:** `audio/sfx/*.mp3`
|
||||
|
||||
|
|
@ -195,7 +197,7 @@ Assembles the final cut from clips. Uses ffmpeg for stitching, fades,
|
|||
transitions. Reviews each clip for pacing and quality before assembly.
|
||||
|
||||
- **Toolsets:** kanban, terminal, file
|
||||
- **Skills:** `kanban-worker`
|
||||
- **Skills:** none — kanban guidance is auto-injected into every kanban worker
|
||||
- **External tools:** ffmpeg, ffprobe
|
||||
- **Outputs:** `output/final.mp4`, `output/final-noaudio.mp4`
|
||||
|
||||
|
|
@ -206,7 +208,7 @@ brand-consistent output and the editor just stitches, the colorist is overkill.
|
|||
Worth including for narrative film with hero shots.
|
||||
|
||||
- **Toolsets:** kanban, terminal, file
|
||||
- **Skills:** `kanban-worker`
|
||||
- **Skills:** none — kanban guidance is auto-injected into every kanban worker
|
||||
- **Outputs:** `output/final-graded.mp4`
|
||||
|
||||
### audio-mixer
|
||||
|
|
@ -215,7 +217,7 @@ Mixes voiceover + music + SFX into a final audio track. Sets levels, ducks
|
|||
music under VO, normalizes loudness (LUFS).
|
||||
|
||||
- **Toolsets:** kanban, terminal, file
|
||||
- **Skills:** `kanban-worker`
|
||||
- **Skills:** none — kanban guidance is auto-injected into every kanban worker
|
||||
- **External tools:** ffmpeg with `loudnorm` filter, optional `sox`
|
||||
- **Outputs:** `audio/final-mix.mp3`
|
||||
|
||||
|
|
@ -225,7 +227,7 @@ Burns subtitles into the video, generates SRT, handles accessibility. Can also
|
|||
generate captions from audio via Whisper.
|
||||
|
||||
- **Toolsets:** kanban, terminal, file
|
||||
- **Skills:** `kanban-worker`
|
||||
- **Skills:** none — kanban guidance is auto-injected into every kanban worker
|
||||
- **External tools:** Whisper (CLI or API), ffmpeg subtitle filters
|
||||
- **Outputs:** `output/captions.srt`, `output/final-captioned.mp4`
|
||||
|
||||
|
|
@ -235,7 +237,7 @@ Final encode + format variants. Produces deliverables for each platform target
|
|||
(square for IG, vertical for TikTok, full HD for YouTube, etc.).
|
||||
|
||||
- **Toolsets:** kanban, terminal, file
|
||||
- **Skills:** `kanban-worker`
|
||||
- **Skills:** none — kanban guidance is auto-injected into every kanban worker
|
||||
- **Outputs:** `output/final-1080.mp4`, `output/final-9x16.mp4`, etc.
|
||||
|
||||
## QA roles
|
||||
|
|
@ -248,7 +250,7 @@ quality). Distinct from the cinematographer (who reviews visuals during
|
|||
production) and the editor (who reviews for assembly).
|
||||
|
||||
- **Toolsets:** kanban, terminal, file, video, vision
|
||||
- **Skills:** `kanban-worker`
|
||||
- **Skills:** none — kanban guidance is auto-injected into every kanban worker
|
||||
- **Review tools:** `video_analyze` (native clip review via multimodal LLM),
|
||||
`vision_analyze` (frame/thumbnail review), ffprobe
|
||||
- **Outputs:** `review-notes.md`, comments on tasks
|
||||
|
|
@ -260,7 +262,7 @@ when the brand guidelines are detailed and a generic reviewer might miss
|
|||
violations.
|
||||
|
||||
- **Toolsets:** kanban, file
|
||||
- **Skills:** `kanban-worker`
|
||||
- **Skills:** none — kanban guidance is auto-injected into every kanban worker
|
||||
- **Outputs:** comments + `brand-review.md`
|
||||
|
||||
## Composing teams — heuristics
|
||||
|
|
|
|||
|
|
@ -50,18 +50,12 @@ called from the terminal toolset; they don't appear in `always_load`.
|
|||
| `gif-search` | Find existing GIFs | Editor / concept artist sourcing references |
|
||||
| `gifs` | GIF tooling | Masterer producing GIF deliverables |
|
||||
|
||||
### Kanban infrastructure (`hermes-agent/skills/devops/`)
|
||||
|
||||
| Skill | What it does | When to load |
|
||||
|-------|--------------|--------------|
|
||||
| `kanban-orchestrator` | Decomposition playbook + anti-temptation rules for orchestrator profiles | Director only |
|
||||
| `kanban-worker` | Pitfalls, examples, edge cases for kanban workers (deeper than auto-injected guidance) | Any profile — load when handling tricky multi-step workflows |
|
||||
### Kanban infrastructure
|
||||
|
||||
The kanban plugin auto-injects baseline orchestration guidance into every
|
||||
worker's system prompt — the `kanban_create` fan-out pattern, claim/handoff
|
||||
lifecycle, and the "decompose, don't execute" rule for orchestrators.
|
||||
`kanban-orchestrator` and `kanban-worker` are deeper playbooks loaded when a
|
||||
profile needs them.
|
||||
lifecycle, and the "decompose, don't execute" rule for orchestrators. There is
|
||||
no kanban skill to load; the guidance is always present for kanban workers.
|
||||
|
||||
## External tools (called from terminal toolset)
|
||||
|
||||
|
|
@ -102,8 +96,7 @@ toolsets:
|
|||
- terminal
|
||||
- file
|
||||
skills:
|
||||
always_load:
|
||||
- kanban-orchestrator
|
||||
always_load: []
|
||||
```
|
||||
|
||||
The director's terminal access is conventional but the SOUL.md rules forbid
|
||||
|
|
@ -117,7 +110,6 @@ toolsets:
|
|||
- file
|
||||
skills:
|
||||
always_load:
|
||||
- kanban-worker
|
||||
- humanizer # post-process scripts to strip AI-tells
|
||||
```
|
||||
|
||||
|
|
@ -132,7 +124,6 @@ toolsets:
|
|||
- file
|
||||
skills:
|
||||
always_load:
|
||||
- kanban-worker
|
||||
# plus one or more (style-dependent):
|
||||
# - claude-design (UI / web product video)
|
||||
# - sketch (quick mockup variants)
|
||||
|
|
@ -151,7 +142,6 @@ toolsets:
|
|||
- file
|
||||
skills:
|
||||
always_load:
|
||||
- kanban-worker
|
||||
# one of:
|
||||
# - excalidraw (sketch storyboards)
|
||||
# - architecture-diagram (technical/system content)
|
||||
|
|
@ -169,7 +159,6 @@ toolsets:
|
|||
- vision # vision_analyze — review stills / exported frames
|
||||
skills:
|
||||
always_load:
|
||||
- kanban-worker
|
||||
# the visual skill that matches the project, e.g.:
|
||||
# - ascii-video (ASCII projects)
|
||||
# - manim-video (math/explainer)
|
||||
|
|
@ -188,7 +177,6 @@ toolsets:
|
|||
- file
|
||||
skills:
|
||||
always_load:
|
||||
- kanban-worker
|
||||
# ONE skill per renderer variant (or empty for external-API renderers):
|
||||
# - ascii-video (renderer-ascii)
|
||||
# - manim-video (renderer-manim)
|
||||
|
|
@ -202,9 +190,9 @@ skills:
|
|||
```
|
||||
|
||||
For external-API renderers (image-to-video-generator using Runway, voice-talent
|
||||
using ElevenLabs, renderer-motion-graphics using Remotion), `always_load` only
|
||||
contains `kanban-worker` — the role's work is API-driven and the API key +
|
||||
terminal commands suffice.
|
||||
using ElevenLabs, renderer-motion-graphics using Remotion), `always_load` is
|
||||
empty — the role's work is API-driven and the API key +
|
||||
terminal commands suffice (kanban guidance is auto-injected regardless).
|
||||
|
||||
For multi-skill renderer setups (rare — usually one variant per skill is
|
||||
cleaner) use `--skill <name>` on individual `kanban_create` calls to override
|
||||
|
|
@ -219,7 +207,6 @@ toolsets:
|
|||
- file
|
||||
skills:
|
||||
always_load:
|
||||
- kanban-worker
|
||||
# for image-generator that drives ComfyUI locally:
|
||||
# - comfyui
|
||||
env_required:
|
||||
|
|
@ -242,7 +229,6 @@ toolsets:
|
|||
- file
|
||||
skills:
|
||||
always_load:
|
||||
- kanban-worker
|
||||
- songsee # spectrograms / audio analysis
|
||||
# plus (depending on what the project needs):
|
||||
# - songwriting-and-ai-music (commissioning Suno tracks)
|
||||
|
|
@ -260,11 +246,11 @@ toolsets:
|
|||
- video # video_analyze — editor reviews assembled cuts natively
|
||||
- vision # vision_analyze — spot-check frames
|
||||
skills:
|
||||
always_load:
|
||||
- kanban-worker
|
||||
always_load: []
|
||||
```
|
||||
|
||||
These are mostly ffmpeg-driven; no special skill needed beyond `kanban-worker`.
|
||||
These are mostly ffmpeg-driven; no special skill needed (kanban guidance is
|
||||
auto-injected into every kanban worker).
|
||||
For captioner add Whisper invocation patterns to the SOUL.md.
|
||||
|
||||
### reviewer / brand-cop
|
||||
|
|
@ -277,8 +263,7 @@ toolsets:
|
|||
- video # video_analyze — review full clips natively
|
||||
- vision # vision_analyze — review stills / exported frames
|
||||
skills:
|
||||
always_load:
|
||||
- kanban-worker
|
||||
always_load: []
|
||||
```
|
||||
|
||||
## API key requirements
|
||||
|
|
|
|||
|
|
@ -423,8 +423,6 @@ def render_soul_md(team_member: dict, plan: dict) -> str:
|
|||
"- **Decompose, route, comment, approve — that's the whole job.**\n"
|
||||
"- **Read TEAM.md** for the canonical task graph. Do not invent "
|
||||
"new roles unless the brief truly demands it.\n"
|
||||
"- **Load the `kanban-orchestrator` skill** for the deeper "
|
||||
"decomposition playbook beyond the auto-injected baseline.\n"
|
||||
)
|
||||
|
||||
common_commands = (
|
||||
|
|
|
|||
|
|
@ -1,214 +0,0 @@
|
|||
---
|
||||
name: kanban-orchestrator
|
||||
description: Decomposition playbook + anti-temptation rules for an orchestrator profile routing work through Kanban. The "don't do the work yourself" rule and the basic lifecycle are auto-injected into every kanban worker's system prompt; this skill is the deeper playbook when you're specifically playing the orchestrator role.
|
||||
version: 3.0.0
|
||||
platforms: [linux, macos, windows]
|
||||
environments: [kanban]
|
||||
metadata:
|
||||
hermes:
|
||||
tags: [kanban, multi-agent, orchestration, routing]
|
||||
related_skills: [kanban-worker]
|
||||
---
|
||||
|
||||
# Kanban Orchestrator — Decomposition Playbook
|
||||
|
||||
> The **core worker lifecycle** (including the `kanban_create` fan-out pattern and the "decompose, don't execute" rule) is auto-injected into every kanban process via the `KANBAN_GUIDANCE` system-prompt block. This skill is the deeper playbook when you're an orchestrator profile whose whole job is routing.
|
||||
|
||||
## Profiles are user-configured — not a fixed roster
|
||||
|
||||
Hermes setups vary widely. Some users run a single profile that does everything; some run a small fleet (`docker-worker`, `cron-worker`); some run a curated specialist team they've named themselves. There is **no default specialist roster** — the orchestrator skill does not know what profiles exist on this machine.
|
||||
|
||||
Before fanning out, you must ground the decomposition in the profiles that actually exist. The dispatcher silently fails to spawn unknown assignee names — it doesn't autocorrect, doesn't suggest, doesn't fall back. So a card assigned to `researcher` on a setup that only has `docker-worker` just sits in `ready` forever.
|
||||
|
||||
**Step 0: discover available profiles before planning.**
|
||||
|
||||
Use one of these:
|
||||
|
||||
- `hermes profile list` — prints the table of profiles configured on this machine. Run it through your terminal tool if you have one; otherwise ask the user.
|
||||
- `kanban_list(assignee="<some-name>")` — sanity-check a single name. Returns an empty list (rather than an error) for an unknown assignee, so this only confirms a name you're already considering.
|
||||
- **Just ask the user.** "What profiles do you have set up?" is a fine first turn when the goal needs more than one specialist.
|
||||
|
||||
Cache the result in your working memory for the rest of the conversation. Re-asking every turn wastes a tool call.
|
||||
|
||||
## When to use the board (vs. just doing the work)
|
||||
|
||||
Create Kanban tasks when any of these are true:
|
||||
|
||||
1. **Multiple specialists are needed.** Research + analysis + writing is three profiles.
|
||||
2. **The work should survive a crash or restart.** Long-running, recurring, or important.
|
||||
3. **The user might want to interject.** Human-in-the-loop at any step.
|
||||
4. **Multiple subtasks can run in parallel.** Fan-out for speed.
|
||||
5. **Review / iteration is expected.** A reviewer profile loops on drafter output.
|
||||
6. **The audit trail matters.** Board rows persist in SQLite forever.
|
||||
|
||||
If *none* of those apply — it's a small one-shot reasoning task — use `delegate_task` instead or answer the user directly.
|
||||
|
||||
## The anti-temptation rules
|
||||
|
||||
Your job description says "route, don't execute." The rules that enforce that:
|
||||
|
||||
- **Do not execute the work yourself.** Your restricted toolset usually doesn't even include terminal/file/code/web for implementation. If you find yourself "just fixing this quickly" — stop and create a task for the right specialist.
|
||||
- **For any concrete task, create a Kanban task and assign it.** Every single time.
|
||||
- **Split multi-lane requests before creating cards.** A user prompt can contain several independent workstreams. Extract those lanes first, then create one card per lane instead of bundling unrelated work into a single implementer card.
|
||||
- **Run independent lanes in parallel.** If two cards do not need each other's output, leave them unlinked so the dispatcher can fan them out. Link only true data dependencies.
|
||||
- **Never create dependent work as independent ready cards.** If a card must wait for another card, pass `parents=[...]` in the original `kanban_create` call. Do not create it first and link it later, and do not rely on prose like "wait for T1" inside the body.
|
||||
- **If no specialist fits the available profiles, ask the user which profile to create or which existing profile to use.** Do not invent profile names; the dispatcher will silently drop unknown assignees.
|
||||
- **Decompose, route, and summarize — that's the whole job.**
|
||||
|
||||
## Decomposition playbook
|
||||
|
||||
### Step 1 — Understand the goal
|
||||
|
||||
Ask clarifying questions if the goal is ambiguous. Cheap to ask; expensive to spawn the wrong fleet.
|
||||
|
||||
### Step 2 — Sketch the task graph
|
||||
|
||||
Before creating anything, draft the graph out loud (in your response to the user). Treat every concrete workstream as a candidate card:
|
||||
|
||||
1. Extract the lanes from the request.
|
||||
2. Map each lane to one of the profiles you discovered in Step 0. If a lane doesn't fit any existing profile, ask the user which to use or create.
|
||||
3. Decide whether each lane is independent or gated by another lane.
|
||||
4. Create independent lanes as parallel cards with no parent links.
|
||||
5. Create synthesis/review/integration cards with parent links to the lanes they depend on. A child created with unfinished parents starts in `todo`; the dispatcher promotes it to `ready` only after every parent is done.
|
||||
|
||||
Examples of prompts that should fan out (using placeholder profile names — substitute whatever exists on the user's setup):
|
||||
|
||||
- "Build an app" → one card to a design-oriented profile for product/UI direction, one or two cards to engineering profiles for implementation, plus a later integration/review card if the user has a reviewer profile.
|
||||
- "Fix blockers and check model variants" → one implementation card for the blocker fixes plus one discovery/research card for config/source verification. A final reviewer card can depend on both.
|
||||
- "Research docs and implement" → a docs-research card can run in parallel with a codebase-discovery card; implementation waits only if it truly needs those findings.
|
||||
- "Analyze this screenshot and find the related code" → one card to a vision-capable profile for the visual analysis while another searches the codebase.
|
||||
|
||||
Words like "also," "finally," or "and" do not automatically imply a dependency. They often mean "make sure this is covered before reporting back." Only link tasks when one card cannot start until another card's output exists.
|
||||
|
||||
Show the graph to the user before creating cards. Let them correct it — including which actual profile name should own each lane.
|
||||
|
||||
### Step 3 — Create tasks and link
|
||||
|
||||
Use the profile names from Step 0. The example below uses placeholders `<profile-A>`, `<profile-B>`, `<profile-C>` — replace them with what the user actually has.
|
||||
|
||||
```python
|
||||
t1 = kanban_create(
|
||||
title="research: Postgres cost vs current",
|
||||
assignee="<profile-A>", # whichever profile handles research on this setup
|
||||
body="Compare estimated infrastructure costs, migration costs, and ongoing ops costs over a 3-year window. Sources: AWS/GCP pricing, team time estimates, current Postgres bills from peers.",
|
||||
tenant=os.environ.get("HERMES_TENANT"),
|
||||
)["task_id"]
|
||||
|
||||
t2 = kanban_create(
|
||||
title="research: Postgres performance vs current",
|
||||
assignee="<profile-A>", # same profile, run in parallel
|
||||
body="Compare query latency, throughput, and scaling characteristics at our expected data volume (~500GB, 10k QPS peak). Sources: benchmark papers, public case studies, pgbench results if easy.",
|
||||
)["task_id"]
|
||||
|
||||
t3 = kanban_create(
|
||||
title="synthesize migration recommendation",
|
||||
assignee="<profile-B>", # whichever profile does synthesis/analysis
|
||||
body="Read the findings from T1 (cost) and T2 (performance). Produce a 1-page recommendation with explicit trade-offs and a go/no-go call.",
|
||||
parents=[t1, t2],
|
||||
)["task_id"]
|
||||
|
||||
t4 = kanban_create(
|
||||
title="draft decision memo",
|
||||
assignee="<profile-C>", # whichever profile drafts user-facing prose
|
||||
body="Turn the analyst's recommendation into a 2-page memo for the CTO. Match the tone of previous decision memos in the team's knowledge base.",
|
||||
parents=[t3],
|
||||
)["task_id"]
|
||||
```
|
||||
|
||||
`parents=[...]` gates promotion — children stay in `todo` until every parent reaches `done`, then auto-promote to `ready`. No manual coordination needed; the dispatcher and dependency engine handle it.
|
||||
|
||||
If the task graph has dependencies, create the parent cards first, capture their returned ids, and include those ids in the child card's `parents` list during the child `kanban_create` call. Avoid creating all cards in parallel and linking them afterward; that creates a window where the dispatcher can claim a child before its inputs exist.
|
||||
|
||||
### Step 4 — Complete your own task
|
||||
|
||||
If you were spawned as a task yourself (e.g. a planner profile was assigned `T0: "investigate Postgres migration"`), mark it done with a summary of what you created:
|
||||
|
||||
```python
|
||||
kanban_complete(
|
||||
summary="decomposed into T1-T4: 2 research lanes in parallel, 1 synthesis on their outputs, 1 prose draft on the recommendation",
|
||||
metadata={
|
||||
"task_graph": {
|
||||
"T1": {"assignee": "<profile-A>", "parents": []},
|
||||
"T2": {"assignee": "<profile-A>", "parents": []},
|
||||
"T3": {"assignee": "<profile-B>", "parents": ["T1", "T2"]},
|
||||
"T4": {"assignee": "<profile-C>", "parents": ["T3"]},
|
||||
},
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
### Step 5 — Report back to the user
|
||||
|
||||
Tell them what you created in plain prose, naming the actual profiles you used:
|
||||
|
||||
> I've queued 4 tasks:
|
||||
> - **T1** (`<profile-A>`): cost comparison
|
||||
> - **T2** (`<profile-A>`): performance comparison, in parallel with T1
|
||||
> - **T3** (`<profile-B>`): synthesizes T1 + T2 into a recommendation
|
||||
> - **T4** (`<profile-C>`): turns T3 into a CTO memo
|
||||
>
|
||||
> The dispatcher will pick up T1 and T2 now. T3 starts when both finish. You'll get a gateway ping when T4 completes. Use the dashboard or `hermes kanban tail <id>` to follow along.
|
||||
|
||||
## Common patterns
|
||||
|
||||
**Fan-out + fan-in (research → synthesize):** N research-style cards with no parents, one synthesis card with all of them as parents.
|
||||
|
||||
**Parallel implementation + validation:** one implementer card makes the change while one explorer/researcher card verifies config, docs, or source mapping. A reviewer card can depend on both. Do not make the implementer own unrelated verification just because the user mentioned both in one sentence.
|
||||
|
||||
**Pipeline with gates:** `planner → implementer → reviewer`. Each stage's `parents=[previous_task]`. Reviewer blocks or completes; if reviewer blocks, the operator unblocks with feedback and respawns.
|
||||
|
||||
**Same-profile queue:** N tasks, all assigned to the same profile, no dependencies between them. Dispatcher serializes — that profile processes them in priority order, accumulating experience in its own memory.
|
||||
|
||||
**Human-in-the-loop:** Any task can `kanban_block()` to wait for input. Dispatcher respawns after `/unblock`. The comment thread carries the full context.
|
||||
|
||||
## Pitfalls
|
||||
|
||||
**Inventing profile names that don't exist.** The dispatcher silently fails to spawn unknown assignees — the card just sits in `ready` forever. Always assign to a profile from your Step 0 discovery; ask the user if you're unsure.
|
||||
|
||||
**Bundling independent lanes into one card.** If the user asks for two independent outcomes, create two cards. Example: "fix blockers and check model variants" is not one fixer task; create a fixer/engineer card for the fixes and an explorer/researcher card for the variant check, then optionally gate review on both.
|
||||
|
||||
**Over-linking because of wording.** "Finally check X" may still be parallel with implementation if X is static config, docs, or source discovery. Link it after implementation only when the check depends on the implementation result.
|
||||
|
||||
**Forgetting dependency links.** If the task graph says `research -> implement -> review`, do not create all tasks as independent ready cards. Use parent links so implement/review cannot run before their inputs exist.
|
||||
|
||||
**Reassignment vs. new task.** If a reviewer blocks with "needs changes," create a NEW task linked from the reviewer's task — don't re-run the same task with a stern look. The new task is assigned to the original implementer profile.
|
||||
|
||||
**Argument order for links.** `kanban_link(parent_id=..., child_id=...)` — parent first. Mixing them up demotes the wrong task to `todo`.
|
||||
|
||||
**Don't pre-create the whole graph if the shape depends on intermediate findings.** If T3's structure depends on what T1 and T2 find, let T3 exist as a "synthesize findings" task whose own first step is to read parent handoffs and plan the rest. Orchestrators can spawn orchestrators.
|
||||
|
||||
**Tenant inheritance.** If `HERMES_TENANT` is set in your env, pass `tenant=os.environ.get("HERMES_TENANT")` on every `kanban_create` call so child tasks stay in the same namespace.
|
||||
|
||||
## Goal-mode cards (persistent workers)
|
||||
|
||||
By default a dispatched worker gets **one shot** at its card: it does its work, calls `kanban_complete`/`kanban_block`, and exits. For open-ended cards where one turn rarely finishes the job, pass `goal_mode=True` to wrap that worker in a Ralph-style goal loop — the same engine behind the `/goal` slash command:
|
||||
|
||||
```python
|
||||
kanban_create(
|
||||
title="Translate the full docs site to French",
|
||||
body="Acceptance: every page translated, no English left, links intact.",
|
||||
assignee="<translator-profile>",
|
||||
goal_mode=True, # judge re-checks the card after each turn
|
||||
goal_max_turns=15, # optional budget (default 20)
|
||||
)["task_id"]
|
||||
```
|
||||
|
||||
How it behaves:
|
||||
- After each worker turn, an auxiliary judge evaluates the worker's response against the card's **title + body** (treated as the acceptance criteria).
|
||||
- Not done + budget remains → the worker keeps going **in the same session** (full context retained — not a fresh respawn).
|
||||
- Worker calls `kanban_complete`/`kanban_block` itself → loop stops, normal lifecycle.
|
||||
- Budget exhausted without completion → the card is **blocked** for human review (sticky), never a silent exit.
|
||||
|
||||
When to use it: long, multi-step, or "keep going until X is true" cards. When NOT to: cheap one-shot cards (translation of a single string, a quick lookup) — the judge overhead isn't worth it, and the dispatcher's existing retry/circuit-breaker already handles transient worker failures.
|
||||
|
||||
Write the body as **explicit acceptance criteria** — the judge is only as good as the goal text. "Translate the README" is weaker than "Translate every section of the README to French; no English sentences remain."
|
||||
|
||||
## Recovering stuck workers
|
||||
|
||||
When a worker profile keeps crashing, hallucinating, or getting blocked by its own mistakes (usually: wrong model, missing skill, broken credential), the kanban dashboard flags the task with a ⚠ badge and opens a **Recovery** section in the drawer. Three primary actions:
|
||||
|
||||
1. **Reclaim** (or `hermes kanban reclaim <task_id>`) — abort the running worker immediately and reset the task to `ready`. The existing claim TTL is ~15 min; this is the fast path out.
|
||||
2. **Reassign** (or `hermes kanban reassign <task_id> <new-profile> --reclaim`) — switch the task to a different profile (one that exists on this setup) and let the dispatcher pick it up with a fresh worker.
|
||||
3. **Change profile model** — the dashboard prints a copy-paste hint for `hermes -p <profile> model` since profile config lives on disk; edit it in a terminal, then Reclaim to retry with the new model.
|
||||
|
||||
Hallucination warnings appear on tasks where a worker's `kanban_complete(created_cards=[...])` claim included card ids that don't exist or weren't created by the worker's profile (the gate blocks the completion), or where the free-form summary references `t_<hex>` ids that don't resolve (advisory prose scan, non-blocking). Both produce audit events that persist even after recovery actions — the trail stays for debugging.
|
||||
|
|
@ -1,214 +0,0 @@
|
|||
---
|
||||
name: kanban-worker
|
||||
description: Pitfalls, examples, and edge cases for Hermes Kanban workers. The lifecycle itself is auto-injected into every worker's system prompt as KANBAN_GUIDANCE (from agent/prompt_builder.py); this skill is what you load when you want deeper detail on specific scenarios.
|
||||
version: 2.0.0
|
||||
platforms: [linux, macos, windows]
|
||||
environments: [kanban]
|
||||
metadata:
|
||||
hermes:
|
||||
tags: [kanban, multi-agent, collaboration, workflow, pitfalls]
|
||||
related_skills: [kanban-orchestrator]
|
||||
---
|
||||
|
||||
# Kanban Worker — Pitfalls and Examples
|
||||
|
||||
> You're seeing this skill because the Hermes Kanban dispatcher spawned you as a worker with `--skills kanban-worker` — it's loaded automatically for every dispatched worker. The **lifecycle** (6 steps: orient → work → heartbeat → block/complete) also lives in the `KANBAN_GUIDANCE` block that's auto-injected into your system prompt. This skill is the deeper detail: good handoff shapes, retry diagnostics, edge cases.
|
||||
|
||||
## Workspace handling
|
||||
|
||||
Your workspace kind determines how you should behave inside `$HERMES_KANBAN_WORKSPACE`:
|
||||
|
||||
| Kind | What it is | How to work |
|
||||
|---|---|---|
|
||||
| `scratch` | Fresh tmp dir, yours alone | Read/write freely; it gets GC'd when the task is archived. |
|
||||
| `dir:<path>` | Shared persistent directory | Other runs will read what you write. Treat it like long-lived state. Path is guaranteed absolute (the kernel rejects relative paths). |
|
||||
| `worktree` | Git worktree at the resolved path | If `.git` doesn't exist, run `git worktree add <path> ${HERMES_KANBAN_BRANCH:-wt/$HERMES_KANBAN_TASK}` from the main repo first, then cd and work normally. Commit work here. |
|
||||
|
||||
## Tenant isolation
|
||||
|
||||
If `$HERMES_TENANT` is set, the task belongs to a tenant namespace. When reading or writing persistent memory, prefix memory entries with the tenant so context doesn't leak across tenants:
|
||||
|
||||
- Good: `business-a: Acme is our biggest customer`
|
||||
- Bad (leaks): `Acme is our biggest customer`
|
||||
|
||||
## Good summary + metadata shapes
|
||||
|
||||
The `kanban_complete(summary=..., metadata=...)` handoff is how downstream workers read what you did. Patterns that work:
|
||||
|
||||
**Coding task:**
|
||||
```python
|
||||
kanban_complete(
|
||||
summary="shipped rate limiter — token bucket, keys on user_id with IP fallback, 14 tests pass",
|
||||
metadata={
|
||||
"changed_files": ["rate_limiter.py", "tests/test_rate_limiter.py"],
|
||||
"tests_run": 14,
|
||||
"tests_passed": 14,
|
||||
"decisions": ["user_id primary, IP fallback for unauthenticated requests"],
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
**Coding task that needs human review (review-required):**
|
||||
|
||||
For most code-changing tasks, the work isn't truly *done* until a human reviewer has eyes on it. Block instead of complete, with `reason` prefixed `review-required: ` so the dashboard surfaces the row as needing review. Drop the structured metadata (changed files, test counts, diff/PR url) into a comment first, since `kanban_block` only carries the human-readable reason — comments are the durable annotation channel. Reviewer either approves and runs `hermes kanban unblock <id>` (which re-spawns you with the comment thread for any follow-ups) or asks for changes via another comment.
|
||||
|
||||
```python
|
||||
import json
|
||||
|
||||
kanban_comment(
|
||||
body="review-required handoff:\n" + json.dumps({
|
||||
"changed_files": ["rate_limiter.py", "tests/test_rate_limiter.py"],
|
||||
"tests_run": 14,
|
||||
"tests_passed": 14,
|
||||
"diff_path": "/path/to/worktree", # or PR url if pushed
|
||||
"decisions": ["user_id primary, IP fallback for unauthenticated requests"],
|
||||
}, indent=2),
|
||||
)
|
||||
kanban_block(
|
||||
reason="review-required: rate limiter shipped, 14/14 tests pass — needs eyes on the user_id/IP fallback choice before merging",
|
||||
)
|
||||
```
|
||||
|
||||
Use `kanban_complete` only when the task is genuinely terminal — e.g. a one-line typo fix, a docs change with no functional consequences, or a research task where the artifact IS the writeup itself.
|
||||
|
||||
**Research task:**
|
||||
```python
|
||||
kanban_complete(
|
||||
summary="3 competing libraries reviewed; vLLM wins on throughput, SGLang on latency, Tensorrt-LLM on memory efficiency",
|
||||
metadata={
|
||||
"sources_read": 12,
|
||||
"recommendation": "vLLM",
|
||||
"benchmarks": {"vllm": 1.0, "sglang": 0.87, "trtllm": 0.72},
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
**Review task:**
|
||||
```python
|
||||
kanban_complete(
|
||||
summary="reviewed PR #123; 2 blocking issues found (SQL injection in /search, missing CSRF on /settings)",
|
||||
metadata={
|
||||
"pr_number": 123,
|
||||
"findings": [
|
||||
{"severity": "critical", "file": "api/search.py", "line": 42, "issue": "raw SQL concat"},
|
||||
{"severity": "high", "file": "api/settings.py", "issue": "missing CSRF middleware"},
|
||||
],
|
||||
"approved": False,
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
Shape `metadata` so downstream parsers (reviewers, aggregators, schedulers) can use it without re-reading your prose.
|
||||
|
||||
## Shipping deliverables (`artifacts=[...]`)
|
||||
|
||||
If your task produced files a human actually wants — a chart, a PDF, a spreadsheet, a generated image, an archive — pass their **absolute paths** to `kanban_complete(artifacts=[...])`. The gateway notifier uploads each one as a native attachment to whoever subscribed to the task, so the deliverable lands in their chat alongside the completion message instead of being a path they have to go fetch.
|
||||
|
||||
```python
|
||||
kanban_complete(
|
||||
summary="Q3 revenue analysis: 14% QoQ growth, EMEA the laggard. Chart + full PDF attached.",
|
||||
artifacts=["/tmp/q3-revenue.png", "/tmp/q3-report.pdf"],
|
||||
metadata={"rows_analyzed": 48000, "growth_qoq": 0.14},
|
||||
)
|
||||
```
|
||||
|
||||
Images and video embed inline; PDFs, docx, csv/xlsx/json/yaml, pptx, zip/tar/gz, audio, and html upload as files. Rules:
|
||||
|
||||
- **Absolute paths only**, and the file must still exist when you complete — don't point at a scratch file you already deleted.
|
||||
- **Only real deliverables.** Skip intermediate logs, scratch files, and inputs the human already has.
|
||||
- `artifacts` is the **top-level** parameter the notifier reads. Do not bury deliverable paths in `metadata` (e.g. `metadata.codex_lane.artifacts`) and expect them to upload — the notifier only scans the top-level `artifacts` list, with a best-effort fallback over your `summary`/`result` text. Metadata paths are for downstream-worker bookkeeping, not delivery.
|
||||
- A bare string is auto-promoted to a one-element list, and it merges with any pre-existing `metadata.artifacts` without dupes.
|
||||
|
||||
Same primitive works outside kanban: any agent surface delivers a file just by writing its absolute path into the response, and Slack/Discord/Telegram/etc. upload it natively — the `artifacts` param is the structured kanban entry point.
|
||||
|
||||
## Claiming cards you actually created
|
||||
|
||||
If your run produced new kanban tasks (via `kanban_create`), pass the ids in `created_cards` on `kanban_complete`. The kernel verifies each id exists and was created by your profile; any phantom id blocks the completion with an error listing what went wrong, and the rejected attempt is permanently recorded on the task's event log. **Only list ids you captured from a successful `kanban_create` return value — never invent ids from prose, never paste ids from earlier runs, never claim cards another worker created.**
|
||||
|
||||
```python
|
||||
# GOOD — capture return values, then claim them.
|
||||
c1 = kanban_create(title="remediate SQL injection", assignee="security-worker")
|
||||
c2 = kanban_create(title="fix CSRF middleware", assignee="web-worker")
|
||||
|
||||
kanban_complete(
|
||||
summary="Review done; spawned remediations for both findings.",
|
||||
metadata={"pr_number": 123, "approved": False},
|
||||
created_cards=[c1["task_id"], c2["task_id"]],
|
||||
)
|
||||
```
|
||||
|
||||
```python
|
||||
# BAD — claiming ids you don't have captured return values for.
|
||||
kanban_complete(
|
||||
summary="Created remediation cards t_a1b2c3d4, t_deadbeef", # hallucinated
|
||||
created_cards=["t_a1b2c3d4", "t_deadbeef"], # → gate rejects
|
||||
)
|
||||
```
|
||||
|
||||
If a `kanban_create` call fails (exception, tool_error), the card was NOT created — do not include a phantom id for it. Retry the create, or omit the id and mention the failure in your summary. The prose-scan pass also catches `t_<hex>` references in your free-form summary that don't resolve; these don't block the completion but show up as advisory warnings on the task in the dashboard.
|
||||
|
||||
## Block reasons that get answered fast
|
||||
|
||||
Bad: `"stuck"` — the human has no context.
|
||||
|
||||
Good: one sentence naming the specific decision you need. Leave longer context as a comment instead.
|
||||
|
||||
```python
|
||||
kanban_comment(
|
||||
task_id=os.environ["HERMES_KANBAN_TASK"],
|
||||
body="Full context: I have user IPs from Cloudflare headers but some users are behind NATs with thousands of peers. Keying on IP alone causes false positives.",
|
||||
)
|
||||
kanban_block(reason="Rate limit key choice: IP (simple, NAT-unsafe) or user_id (requires auth, skips anonymous endpoints)?")
|
||||
```
|
||||
|
||||
The block message is what appears in the dashboard / gateway notifier. The comment is the deeper context a human reads when they open the task.
|
||||
|
||||
## Heartbeats worth sending
|
||||
|
||||
Good heartbeats name progress: `"epoch 12/50, loss 0.31"`, `"scanned 1.2M/2.4M rows"`, `"uploaded 47/120 videos"`.
|
||||
|
||||
Bad heartbeats: `"still working"`, empty notes, sub-second intervals. Every few minutes max; skip entirely for tasks under ~2 minutes.
|
||||
|
||||
## Retry scenarios
|
||||
|
||||
If you open the task and `kanban_show` returns `runs: [...]` with one or more closed runs, you're a retry. The prior runs' `outcome` / `summary` / `error` tell you what didn't work. Don't repeat that path. Typical retry diagnostics:
|
||||
|
||||
- `outcome: "timed_out"` — the previous attempt hit `max_runtime_seconds`. You may need to chunk the work or shorten it.
|
||||
- `outcome: "crashed"` — OOM or segfault. Reduce memory footprint.
|
||||
- `outcome: "spawn_failed"` + `error: "..."` — usually a profile config issue (missing credential, bad PATH). Ask the human via `kanban_block` instead of retrying blindly.
|
||||
- `outcome: "reclaimed"` + `summary: "task archived..."` — operator archived the task out from under the previous run; you probably shouldn't be running at all, check status carefully.
|
||||
- `outcome: "blocked"` — a previous attempt blocked; the unblock comment should be in the thread by now.
|
||||
|
||||
## Notification routing
|
||||
|
||||
You can configure the gateway to receive cross-profile Kanban task notifications by adding `notification_sources` to `~/.hermes/config.yaml`.
|
||||
- `notification_sources: ['*']` accepts subscriptions from all profiles.
|
||||
- `notification_sources: ['default', 'zilor-ppt']` or `"default,zilor-ppt"` restricts subscriptions to specified profiles.
|
||||
- Omitting the key keeps the default behavior (profile isolation).
|
||||
|
||||
## Do NOT
|
||||
|
||||
- Call `delegate_task` as a substitute for `kanban_create`. `delegate_task` is for short reasoning subtasks inside YOUR run; `kanban_create` is for cross-agent handoffs that outlive one API loop.
|
||||
- Call `clarify` to ask the human a question. You are running headless — there is no live user to answer. The call will time out (default ~120s) and the task will sit silently in `running` with no signal that it needs input. Use `kanban_comment` (context) + `kanban_block(reason=...)` (decision needed) instead — the task surfaces on the board as blocked, the operator sees it, unblocks with their answer in a comment, and you respawn with the thread.
|
||||
- Modify files outside `$HERMES_KANBAN_WORKSPACE` unless the task body says to.
|
||||
- Create follow-up tasks assigned to yourself — assign to the right specialist.
|
||||
- Complete a task you didn't actually finish. Block it instead.
|
||||
|
||||
## Pitfalls
|
||||
|
||||
**Task state can change between dispatch and your startup.** Between when the dispatcher claimed and when your process actually booted, the task may have been blocked, reassigned, or archived. Always `kanban_show` first. If it reports `blocked` or `archived`, stop — you shouldn't be running.
|
||||
|
||||
**Workspace may have stale artifacts.** Especially `dir:` and `worktree` workspaces can have files from previous runs. Read the comment thread — it usually explains why you're running again and what state the workspace is in.
|
||||
|
||||
**Don't rely on the CLI when the guidance is available.** The `kanban_*` tools work across all terminal backends (Docker, Modal, SSH). `hermes kanban <verb>` from your terminal tool will fail in containerized backends because the CLI isn't installed there. When in doubt, use the tool.
|
||||
|
||||
## CLI fallback (for scripting)
|
||||
|
||||
Every tool has a CLI equivalent for human operators and scripts:
|
||||
- `kanban_show` ↔ `hermes kanban show <id> --json`
|
||||
- `kanban_complete` ↔ `hermes kanban complete <id> --summary "..." --metadata '{...}'`
|
||||
- `kanban_block` ↔ `hermes kanban block <id> "reason"`
|
||||
- `kanban_create` ↔ `hermes kanban create "title" --assignee <profile> [--parent <id>]`
|
||||
- etc.
|
||||
|
||||
Use the tools from inside an agent; the CLI exists for the human at the terminal.
|
||||
|
|
@ -2703,20 +2703,17 @@ def test_build_worker_context_caps_huge_summary(kanban_home):
|
|||
conn.close()
|
||||
|
||||
|
||||
def test_default_spawn_auto_loads_kanban_worker_skill(kanban_home, monkeypatch):
|
||||
"""The dispatcher's _default_spawn must include --skills kanban-worker
|
||||
in its argv so every worker loads the skill automatically, even if
|
||||
the profile hasn't wired it into its default skills config.
|
||||
def test_default_spawn_does_not_auto_load_any_skill(kanban_home, monkeypatch):
|
||||
"""The dispatcher no longer auto-loads a bundled kanban skill.
|
||||
|
||||
The kanban lifecycle (formerly the kanban-worker/kanban-orchestrator
|
||||
skills) is now injected into every worker's system prompt via
|
||||
KANBAN_GUIDANCE, so _default_spawn must NOT append a `--skills` flag
|
||||
when the task carries no per-task skills.
|
||||
|
||||
We intercept Popen to capture the argv without actually spawning a
|
||||
hermes subprocess (which would hang trying to call an LLM).
|
||||
"""
|
||||
# Pretend the bundled kanban-worker skill resolves for this isolated
|
||||
# HERMES_HOME — the fixture creates an empty tmpdir without the
|
||||
# devops/kanban-worker tree, and _default_spawn gates the --skills
|
||||
# flag on actual resolvability.
|
||||
monkeypatch.setattr(kb, "_kanban_worker_skill_available", lambda _h: True)
|
||||
|
||||
captured = {}
|
||||
|
||||
class FakeProc:
|
||||
|
|
@ -2742,10 +2739,8 @@ def test_default_spawn_auto_loads_kanban_worker_skill(kanban_home, monkeypatch):
|
|||
conn.close()
|
||||
|
||||
cmd = captured["cmd"]
|
||||
assert "--skills" in cmd, f"spawn argv missing --skills: {cmd}"
|
||||
idx = cmd.index("--skills")
|
||||
assert cmd[idx + 1] == "kanban-worker", (
|
||||
f"expected 'kanban-worker', got {cmd[idx + 1]!r}"
|
||||
assert "--skills" not in cmd, (
|
||||
f"spawn argv should not auto-load any skill: {cmd}"
|
||||
)
|
||||
assert "--accept-hooks" in cmd, f"spawn argv missing --accept-hooks: {cmd}"
|
||||
assert cmd.index("--accept-hooks") < cmd.index("chat"), (
|
||||
|
|
@ -2985,8 +2980,7 @@ def test_create_task_skills_lists_all_toolset_typos(kanban_home):
|
|||
|
||||
def test_default_spawn_appends_per_task_skills(kanban_home, monkeypatch):
|
||||
"""Dispatcher argv must carry one `--skills X` pair per task skill,
|
||||
in addition to the built-in kanban-worker."""
|
||||
monkeypatch.setattr(kb, "_kanban_worker_skill_available", lambda _h: True)
|
||||
in declared order. No skill is auto-loaded anymore."""
|
||||
captured = {}
|
||||
|
||||
class FakeProc:
|
||||
|
|
@ -3019,10 +3013,8 @@ def test_default_spawn_appends_per_task_skills(kanban_home, monkeypatch):
|
|||
for i, tok in enumerate(cmd):
|
||||
if tok == "--skills" and i + 1 < len(cmd):
|
||||
skill_names.append(cmd[i + 1])
|
||||
# kanban-worker first (built-in), then per-task extras in order.
|
||||
assert skill_names[0] == "kanban-worker", skill_names
|
||||
assert "translation" in skill_names
|
||||
assert "github-code-review" in skill_names
|
||||
# Only the per-task skills, in declared order — nothing auto-loaded.
|
||||
assert skill_names == ["translation", "github-code-review"], skill_names
|
||||
# --skills must appear BEFORE the `chat` subcommand so argparse
|
||||
# attaches them to the top-level parser, not the subcommand.
|
||||
chat_idx = cmd.index("chat")
|
||||
|
|
@ -3034,9 +3026,9 @@ def test_default_spawn_appends_per_task_skills(kanban_home, monkeypatch):
|
|||
)
|
||||
|
||||
|
||||
def test_default_spawn_dedupes_kanban_worker_from_task_skills(kanban_home, monkeypatch):
|
||||
"""If a task explicitly lists 'kanban-worker', we don't double-pass it."""
|
||||
monkeypatch.setattr(kb, "_kanban_worker_skill_available", lambda _h: True)
|
||||
def test_default_spawn_passes_task_skills_verbatim(kanban_home, monkeypatch):
|
||||
"""Per-task skills are passed through verbatim — there is no built-in
|
||||
kanban skill to dedupe against anymore."""
|
||||
captured = {}
|
||||
|
||||
class FakeProc:
|
||||
|
|
@ -3052,7 +3044,7 @@ def test_default_spawn_dedupes_kanban_worker_from_task_skills(kanban_home, monke
|
|||
try:
|
||||
tid = kb.create_task(
|
||||
conn, title="dup", assignee="x",
|
||||
skills=["kanban-worker", "translation"],
|
||||
skills=["translation", "github-code-review"],
|
||||
)
|
||||
task = kb.get_task(conn, tid)
|
||||
workspace = kb.resolve_workspace(task)
|
||||
|
|
@ -3061,12 +3053,14 @@ def test_default_spawn_dedupes_kanban_worker_from_task_skills(kanban_home, monke
|
|||
conn.close()
|
||||
|
||||
cmd = captured["cmd"]
|
||||
worker_pairs = [
|
||||
i for i, tok in enumerate(cmd)
|
||||
if tok == "--skills" and i + 1 < len(cmd) and cmd[i + 1] == "kanban-worker"
|
||||
skill_names = [
|
||||
cmd[i + 1]
|
||||
for i, tok in enumerate(cmd)
|
||||
if tok == "--skills" and i + 1 < len(cmd)
|
||||
]
|
||||
assert len(worker_pairs) == 1, (
|
||||
f"kanban-worker appeared {len(worker_pairs)} times in argv: {cmd}"
|
||||
# Exactly the task's skills, once each, in order — no auto-loaded extras.
|
||||
assert skill_names == ["translation", "github-code-review"], (
|
||||
f"unexpected --skills in argv: {cmd}"
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -132,8 +132,6 @@ def test_spawn_sets_goal_env_only_when_enabled(kanban_home, monkeypatch):
|
|||
return _FakeProc()
|
||||
|
||||
monkeypatch.setattr("subprocess.Popen", _fake_popen)
|
||||
# Avoid the kanban-worker skill probe touching the real skills dir.
|
||||
monkeypatch.setattr(kb, "_kanban_worker_skill_available", lambda home: False)
|
||||
|
||||
with kb.connect() as conn:
|
||||
tid = kb.create_task(
|
||||
|
|
@ -162,7 +160,6 @@ def test_spawn_no_goal_env_for_plain_task(kanban_home, monkeypatch):
|
|||
return _FakeProc()
|
||||
|
||||
monkeypatch.setattr("subprocess.Popen", _fake_popen)
|
||||
monkeypatch.setattr(kb, "_kanban_worker_skill_available", lambda home: False)
|
||||
|
||||
with kb.connect() as conn:
|
||||
tid = kb.create_task(conn, title="plain", assignee="default")
|
||||
|
|
|
|||
|
|
@ -1224,8 +1224,16 @@ def test_kanban_guidance_in_worker_prompt(monkeypatch, tmp_path):
|
|||
|
||||
|
||||
def test_kanban_guidance_prompt_size_bounded(monkeypatch, tmp_path):
|
||||
"""Sanity: the guidance block is under 4 KB so it doesn't blow
|
||||
up the cached prompt."""
|
||||
"""Sanity: the guidance block stays lean so it doesn't blow up the
|
||||
cached prompt.
|
||||
|
||||
The ceiling guards against unbounded growth, not against any growth.
|
||||
The block absorbed the load-bearing worker/orchestrator reference
|
||||
details (workspace kinds, deliverable artifacts, created-card claims,
|
||||
profile discovery) when the standalone kanban-worker / kanban-orchestrator
|
||||
skills were removed and folded into this always-injected guidance, so the
|
||||
ceiling is sized to fit that content with a little headroom.
|
||||
"""
|
||||
monkeypatch.setenv("HERMES_KANBAN_TASK", "t_fake")
|
||||
home = tmp_path / ".hermes"
|
||||
home.mkdir()
|
||||
|
|
@ -1234,7 +1242,7 @@ def test_kanban_guidance_prompt_size_bounded(monkeypatch, tmp_path):
|
|||
monkeypatch.setattr(_P, "home", lambda: tmp_path)
|
||||
|
||||
from agent.prompt_builder import KANBAN_GUIDANCE
|
||||
assert 1_500 < len(KANBAN_GUIDANCE) < 4_096, (
|
||||
assert 1_500 < len(KANBAN_GUIDANCE) < 5_500, (
|
||||
f"KANBAN_GUIDANCE is {len(KANBAN_GUIDANCE)} chars — too short (missing?) or too long"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -1382,8 +1382,8 @@ KANBAN_CREATE_SCHEMA = {
|
|||
"items": {"type": "string"},
|
||||
"description": (
|
||||
"Skill names to force-load into the dispatched "
|
||||
"worker (in addition to the built-in kanban-worker "
|
||||
"skill). Use this to pin a task to a specialist "
|
||||
"worker. The kanban lifecycle is already injected "
|
||||
"automatically; use this to pin a task to a specialist "
|
||||
"context — e.g. ['translation'] for a translation "
|
||||
"task, ['github-code-review'] for a reviewer task. "
|
||||
"The names must match skills installed on the "
|
||||
|
|
|
|||
|
|
@ -62,8 +62,7 @@ If a skill is missing from this list but present in the repo, the catalog is reg
|
|||
|
||||
| Skill | Description | Path |
|
||||
|-------|-------------|------|
|
||||
| [`kanban-orchestrator`](/docs/user-guide/skills/bundled/devops/devops-kanban-orchestrator) | Decomposition playbook + anti-temptation rules for an orchestrator profile routing work through Kanban. The "don't do the work yourself" rule and the basic lifecycle are auto-injected into every kanban worker's system prompt; this skill... | `devops/kanban-orchestrator` |
|
||||
| [`kanban-worker`](/docs/user-guide/skills/bundled/devops/devops-kanban-worker) | Pitfalls, examples, and edge cases for Hermes Kanban workers. The lifecycle itself is auto-injected into every worker's system prompt as KANBAN_GUIDANCE (from agent/prompt_builder.py); this skill is what you load when you want deeper det... | `devops/kanban-worker` |
|
||||
|
||||
|
||||
## dogfood
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ This page is the contract. It exists for two audiences:
|
|||
- **Operators** picking which lanes to wire into a board (which profiles to create, which assignees to use).
|
||||
- **Plugin / integration authors** wanting to add a new lane shape (a CLI worker that wraps Codex / Claude Code / OpenCode, a containerised review worker, a non-Hermes service that pulls tasks via the API).
|
||||
|
||||
If you're writing the worker code itself — the agent that runs *inside* a lane — the [`kanban-worker`](https://github.com/NousResearch/hermes-agent/blob/main/skills/devops/kanban-worker/SKILL.md) skill is the deeper procedural detail.
|
||||
If you're writing the worker code itself — the agent that runs *inside* a lane — the kanban lifecycle and reference details are injected into the worker's system prompt automatically (the `KANBAN_GUIDANCE` block in [`agent/prompt_builder.py`](https://github.com/NousResearch/hermes-agent/blob/main/agent/prompt_builder.py)).
|
||||
|
||||
## The hierarchy
|
||||
|
||||
|
|
@ -64,7 +64,7 @@ For most code-changing tasks, the work isn't truly *done* the moment the worker
|
|||
- **Drop structured metadata into a `kanban_comment` first** since `kanban_block` only carries the human-readable `reason`. Comments are the durable annotation channel — every audit-relevant field (changed_files, tests_run, diff_path or PR url, decisions) belongs there.
|
||||
- **Reviewer either approves and unblocks**, which respawns the worker with the comment thread for follow-ups; or asks for changes via another comment, which the next worker run sees as part of `kanban_show`'s context.
|
||||
|
||||
The [`kanban-worker`](https://github.com/NousResearch/hermes-agent/blob/main/skills/devops/kanban-worker/SKILL.md) skill has worked examples for both `kanban_complete` (truly terminal tasks — typo fixes, docs changes, research writeups) and the `review-required` block pattern.
|
||||
The injected `KANBAN_GUIDANCE` covers both `kanban_complete` (truly terminal tasks — typo fixes, docs changes, research writeups) and the `review-required` block pattern.
|
||||
|
||||
## Logs and audit trail
|
||||
|
||||
|
|
@ -80,9 +80,9 @@ The dashboard renders run history with summaries, metadata blocks, and exit-stat
|
|||
|
||||
### Hermes profile lane (default)
|
||||
|
||||
The shape every kanban worker takes today: the assignee is a profile name, the dispatcher spawns `hermes -p <profile>`, the worker auto-loads the [`kanban-worker`](https://github.com/NousResearch/hermes-agent/blob/main/skills/devops/kanban-worker/SKILL.md) skill plus the `KANBAN_GUIDANCE` system-prompt block, and uses the `kanban_*` tools to terminate the run. No setup beyond defining the profile.
|
||||
The shape every kanban worker takes today: the assignee is a profile name, the dispatcher spawns `hermes -p <profile>`, the worker gets the `KANBAN_GUIDANCE` system-prompt block injected automatically, and uses the `kanban_*` tools to terminate the run. No setup beyond defining the profile.
|
||||
|
||||
When you create profiles for your fleet, choose names that match the *role* you want the orchestrator to route to. The orchestrator (when there is one) discovers your profile names via `hermes profile list` — there's no fixed roster the system assumes (see the [`kanban-orchestrator`](https://github.com/NousResearch/hermes-agent/blob/main/skills/devops/kanban-orchestrator/SKILL.md) skill for the orchestrator side of the contract).
|
||||
When you create profiles for your fleet, choose names that match the *role* you want the orchestrator to route to. The orchestrator (when there is one) discovers your profile names via `hermes profile list` — there's no fixed roster the system assumes (the orchestrator side of the contract is part of the injected `KANBAN_GUIDANCE`).
|
||||
|
||||
### Orchestrator profile lane
|
||||
|
||||
|
|
@ -110,5 +110,4 @@ So lane authors don't have to reimplement these:
|
|||
|
||||
- [Kanban overview](./kanban) — the user-facing intro.
|
||||
- [Kanban tutorial](./kanban-tutorial) — walkthrough with the dashboard open.
|
||||
- [`kanban-worker`](https://github.com/NousResearch/hermes-agent/blob/main/skills/devops/kanban-worker/SKILL.md) — the skill the worker process loads.
|
||||
- [`kanban-orchestrator`](https://github.com/NousResearch/hermes-agent/blob/main/skills/devops/kanban-orchestrator/SKILL.md) — the orchestrator side.
|
||||
- [`KANBAN_GUIDANCE`](https://github.com/NousResearch/hermes-agent/blob/main/agent/prompt_builder.py) — the worker + orchestrator lifecycle injected into every kanban worker's system prompt.
|
||||
|
|
|
|||
|
|
@ -310,7 +310,7 @@ kanban_create(
|
|||
kanban_complete(summary="decomposed into 2 research tasks + 1 writer; linked dependencies")
|
||||
```
|
||||
|
||||
The "(Orchestrators)" tools — `kanban_list`, `kanban_create`, `kanban_link`, `kanban_unblock`, and `kanban_comment` on foreign tasks — are available through the same toolset; the convention (enforced by the `kanban-orchestrator` skill) is that worker profiles don't fan out or route unrelated work, and orchestrator profiles don't execute implementation work. Dispatcher-spawned workers are still task-scoped for destructive lifecycle operations and cannot mutate unrelated tasks.
|
||||
The "(Orchestrators)" tools — `kanban_list`, `kanban_create`, `kanban_link`, `kanban_unblock`, and `kanban_comment` on foreign tasks — are available through the same toolset; the convention (encoded in the auto-injected kanban guidance) is that worker profiles don't fan out or route unrelated work, and orchestrator profiles don't execute implementation work. Dispatcher-spawned workers are still task-scoped for destructive lifecycle operations and cannot mutate unrelated tasks.
|
||||
|
||||
### Why tools instead of shelling to `hermes kanban`
|
||||
|
||||
|
|
@ -322,7 +322,7 @@ Three reasons:
|
|||
|
||||
**Zero schema footprint on normal sessions.** A regular `hermes chat` session has zero `kanban_*` tools in its schema unless the active profile explicitly enables the `kanban` toolset for orchestrator work. Dispatcher-spawned task workers get task-scoped tools because `HERMES_KANBAN_TASK` is set; orchestrator profiles get the broader routing surface through config. No tool bloat for users who never touch kanban.
|
||||
|
||||
The `kanban-worker` and `kanban-orchestrator` skills teach the model which tool to call when and in what order.
|
||||
The auto-injected kanban guidance teaches the model which tool to call when and in what order.
|
||||
|
||||
### Recommended handoff evidence
|
||||
|
||||
|
|
@ -358,9 +358,9 @@ Keep secrets, raw logs, tokens, OAuth material, and unrelated transcripts out of
|
|||
tests, say so explicitly in `summary` and use `metadata` for the evidence that
|
||||
does exist, such as source URLs, issue ids, or manual review steps.
|
||||
|
||||
### The worker skill
|
||||
### The worker lifecycle
|
||||
|
||||
Any profile that should be able to work kanban tasks must load the `kanban-worker` skill. It teaches the worker the full lifecycle in **tool calls**, not CLI commands:
|
||||
Every profile that works kanban tasks automatically gets the worker lifecycle — it's injected into the worker's system prompt at spawn (the `KANBAN_GUIDANCE` block), so there is **nothing to install or configure**. It teaches the worker the full lifecycle in **tool calls**, not CLI commands:
|
||||
|
||||
1. On spawn, call `kanban_show()` to read title + body + parent handoffs + prior attempts + full comment thread.
|
||||
2. `cd $HERMES_KANBAN_WORKSPACE` (via the terminal tool) and do the work there.
|
||||
|
|
@ -374,22 +374,7 @@ protocol. If the worker process exits with status 0 while the task is still
|
|||
of respawning it into the same loop. This usually means the model wrote a
|
||||
plain-text answer and exited without using the Kanban tool surface.
|
||||
|
||||
`kanban-worker` is a bundled skill, synced into every profile during install and
|
||||
update — there is no separate Skills Hub install step. Verify it is present in
|
||||
whichever profile you use for kanban workers (`researcher`, `writer`, `ops`,
|
||||
etc.):
|
||||
|
||||
```bash
|
||||
hermes -p <your-worker-profile> skills list | grep kanban-worker
|
||||
```
|
||||
|
||||
If the bundled copy is missing, restore it for that profile:
|
||||
|
||||
```bash
|
||||
hermes -p <your-worker-profile> skills reset kanban-worker --restore
|
||||
```
|
||||
|
||||
The dispatcher also auto-passes `--skills kanban-worker` when spawning every worker, so the worker always has the pattern library available even if a profile's default skills config doesn't include it.
|
||||
The lifecycle plus the load-bearing reference details (workspace kinds, deliverable `artifacts`, claiming created cards) ship in that system-prompt block, so every worker has them regardless of which profile it runs under — no per-profile skill setup required.
|
||||
|
||||
### Pinning extra skills to a specific task
|
||||
|
||||
|
|
@ -426,7 +411,7 @@ hermes kanban create "audit auth flow" \
|
|||
|
||||
**From the dashboard**, type the skills comma-separated into the **skills** field of the inline create form.
|
||||
|
||||
These skills are **additive** to the built-in `kanban-worker` — the dispatcher emits one `--skills <name>` flag for each (and for the built-in), so the worker spawns with all of them loaded. The skill names must match skills that are actually installed on the assignee's profile (run `hermes skills list` to see what's available); there's no runtime install.
|
||||
The dispatcher emits one `--skills <name>` flag per skill listed, so the worker spawns with all of them loaded on top of the auto-injected kanban guidance. The skill names must match skills that are actually installed on the assignee's profile (run `hermes skills list` to see what's available); there's no runtime install.
|
||||
|
||||
### Goal-mode cards (`--goal`)
|
||||
|
||||
|
|
@ -442,9 +427,9 @@ hermes kanban create "Translate the docs site to French" \
|
|||
|
||||
Use it for open-ended, multi-step, or "keep going until X is true" cards. Skip it for cheap one-shot work — the per-turn judge overhead isn't worth it, and the dispatcher's existing retry/circuit-breaker already handles transient worker failures. The judge is only as good as your goal text, so write the body as **explicit acceptance criteria**.
|
||||
|
||||
### The orchestrator skill
|
||||
### How the orchestrator behaves
|
||||
|
||||
A **well-behaved orchestrator does not do the work itself.** It decomposes the user's goal into tasks, links them, assigns each to one of the profiles you've set up, and steps back. The `kanban-orchestrator` skill encodes this as tool-call patterns: anti-temptation rules, a Step-0 profile-discovery prompt (the dispatcher silently fails on unknown assignee names, so the orchestrator must ground every card in profiles that actually exist on your machine), and a decomposition playbook keyed on `kanban_create` / `kanban_link` / `kanban_comment`.
|
||||
A **well-behaved orchestrator does not do the work itself.** It decomposes the user's goal into tasks, links them, assigns each to one of the profiles you've set up, and steps back. The orchestrator guidance — anti-temptation rules, a Step-0 profile-discovery prompt (the dispatcher silently fails on unknown assignee names, so the orchestrator must ground every card in profiles that actually exist on your machine), and a decomposition playbook keyed on `kanban_create` / `kanban_link` / `kanban_comment` — is injected into the worker's system prompt automatically; there is nothing to install.
|
||||
|
||||
A canonical orchestrator turn (two parallel researchers handing off to a writer):
|
||||
|
||||
|
|
@ -465,19 +450,7 @@ kanban_complete(
|
|||
)
|
||||
```
|
||||
|
||||
`kanban-orchestrator` is a bundled skill. It is synced into each profile during
|
||||
install and update, so there is no separate Skills Hub install step. Verify it is
|
||||
present in your orchestrator profile:
|
||||
|
||||
```bash
|
||||
hermes -p orchestrator skills list | grep kanban-orchestrator
|
||||
```
|
||||
|
||||
If the bundled copy is missing, restore it for that profile:
|
||||
|
||||
```bash
|
||||
hermes -p orchestrator skills reset kanban-orchestrator --restore
|
||||
```
|
||||
The orchestrator guidance ships in the worker's system prompt automatically — there is nothing to install or sync per profile.
|
||||
|
||||
For best results, pair it with a profile whose toolsets are restricted to board operations (`kanban`, `gateway`, `memory`) so the orchestrator literally cannot execute implementation tasks even if it tries.
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ Use when a Hermes Kanban worker wants to run Codex CLI as an isolated implementa
|
|||
| Author | Hermes Agent |
|
||||
| License | MIT |
|
||||
| Tags | `kanban`, `codex`, `worktrees`, `autonomous-agents`, `prediction-market-bot` |
|
||||
| Related skills | [`kanban-worker`](/docs/user-guide/skills/bundled/devops/devops-kanban-worker), [`codex`](/docs/user-guide/skills/bundled/autonomous-ai-agents/autonomous-ai-agents-codex), [`hermes-agent`](/docs/user-guide/skills/bundled/autonomous-ai-agents/autonomous-ai-agents-hermes-agent) |
|
||||
| Related skills | [`codex`](/docs/user-guide/skills/bundled/autonomous-ai-agents/autonomous-ai-agents-codex), [`hermes-agent`](/docs/user-guide/skills/bundled/autonomous-ai-agents/autonomous-ai-agents-hermes-agent) |
|
||||
|
||||
## Reference: full SKILL.md
|
||||
|
||||
|
|
|
|||
|
|
@ -1,231 +0,0 @@
|
|||
---
|
||||
title: "Kanban Orchestrator"
|
||||
sidebar_label: "Kanban Orchestrator"
|
||||
description: "Decomposition playbook + anti-temptation rules for an orchestrator profile routing work through Kanban"
|
||||
---
|
||||
|
||||
{/* This page is auto-generated from the skill's SKILL.md by website/scripts/generate-skill-docs.py. Edit the source SKILL.md, not this page. */}
|
||||
|
||||
# Kanban Orchestrator
|
||||
|
||||
Decomposition playbook + anti-temptation rules for an orchestrator profile routing work through Kanban. The "don't do the work yourself" rule and the basic lifecycle are auto-injected into every kanban worker's system prompt; this skill is the deeper playbook when you're specifically playing the orchestrator role.
|
||||
|
||||
## Skill metadata
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| Source | Bundled (installed by default) |
|
||||
| Path | `skills/devops/kanban-orchestrator` |
|
||||
| Version | `3.0.0` |
|
||||
| Platforms | linux, macos, windows |
|
||||
| Tags | `kanban`, `multi-agent`, `orchestration`, `routing` |
|
||||
| Related skills | [`kanban-worker`](/docs/user-guide/skills/bundled/devops/devops-kanban-worker) |
|
||||
|
||||
## Reference: full SKILL.md
|
||||
|
||||
:::info
|
||||
The following is the complete skill definition that Hermes loads when this skill is triggered. This is what the agent sees as instructions when the skill is active.
|
||||
:::
|
||||
|
||||
# Kanban Orchestrator — Decomposition Playbook
|
||||
|
||||
> The **core worker lifecycle** (including the `kanban_create` fan-out pattern and the "decompose, don't execute" rule) is auto-injected into every kanban process via the `KANBAN_GUIDANCE` system-prompt block. This skill is the deeper playbook when you're an orchestrator profile whose whole job is routing.
|
||||
|
||||
## Profiles are user-configured — not a fixed roster
|
||||
|
||||
Hermes setups vary widely. Some users run a single profile that does everything; some run a small fleet (`docker-worker`, `cron-worker`); some run a curated specialist team they've named themselves. There is **no default specialist roster** — the orchestrator skill does not know what profiles exist on this machine.
|
||||
|
||||
Before fanning out, you must ground the decomposition in the profiles that actually exist. The dispatcher silently fails to spawn unknown assignee names — it doesn't autocorrect, doesn't suggest, doesn't fall back. So a card assigned to `researcher` on a setup that only has `docker-worker` just sits in `ready` forever.
|
||||
|
||||
**Step 0: discover available profiles before planning.**
|
||||
|
||||
Use one of these:
|
||||
|
||||
- `hermes profile list` — prints the table of profiles configured on this machine. Run it through your terminal tool if you have one; otherwise ask the user.
|
||||
- `kanban_list(assignee="<some-name>")` — sanity-check a single name. Returns an empty list (rather than an error) for an unknown assignee, so this only confirms a name you're already considering.
|
||||
- **Just ask the user.** "What profiles do you have set up?" is a fine first turn when the goal needs more than one specialist.
|
||||
|
||||
Cache the result in your working memory for the rest of the conversation. Re-asking every turn wastes a tool call.
|
||||
|
||||
## When to use the board (vs. just doing the work)
|
||||
|
||||
Create Kanban tasks when any of these are true:
|
||||
|
||||
1. **Multiple specialists are needed.** Research + analysis + writing is three profiles.
|
||||
2. **The work should survive a crash or restart.** Long-running, recurring, or important.
|
||||
3. **The user might want to interject.** Human-in-the-loop at any step.
|
||||
4. **Multiple subtasks can run in parallel.** Fan-out for speed.
|
||||
5. **Review / iteration is expected.** A reviewer profile loops on drafter output.
|
||||
6. **The audit trail matters.** Board rows persist in SQLite forever.
|
||||
|
||||
If *none* of those apply — it's a small one-shot reasoning task — use `delegate_task` instead or answer the user directly.
|
||||
|
||||
## The anti-temptation rules
|
||||
|
||||
Your job description says "route, don't execute." The rules that enforce that:
|
||||
|
||||
- **Do not execute the work yourself.** Your restricted toolset usually doesn't even include terminal/file/code/web for implementation. If you find yourself "just fixing this quickly" — stop and create a task for the right specialist.
|
||||
- **For any concrete task, create a Kanban task and assign it.** Every single time.
|
||||
- **Split multi-lane requests before creating cards.** A user prompt can contain several independent workstreams. Extract those lanes first, then create one card per lane instead of bundling unrelated work into a single implementer card.
|
||||
- **Run independent lanes in parallel.** If two cards do not need each other's output, leave them unlinked so the dispatcher can fan them out. Link only true data dependencies.
|
||||
- **Never create dependent work as independent ready cards.** If a card must wait for another card, pass `parents=[...]` in the original `kanban_create` call. Do not create it first and link it later, and do not rely on prose like "wait for T1" inside the body.
|
||||
- **If no specialist fits the available profiles, ask the user which profile to create or which existing profile to use.** Do not invent profile names; the dispatcher will silently drop unknown assignees.
|
||||
- **Decompose, route, and summarize — that's the whole job.**
|
||||
|
||||
## Decomposition playbook
|
||||
|
||||
### Step 1 — Understand the goal
|
||||
|
||||
Ask clarifying questions if the goal is ambiguous. Cheap to ask; expensive to spawn the wrong fleet.
|
||||
|
||||
### Step 2 — Sketch the task graph
|
||||
|
||||
Before creating anything, draft the graph out loud (in your response to the user). Treat every concrete workstream as a candidate card:
|
||||
|
||||
1. Extract the lanes from the request.
|
||||
2. Map each lane to one of the profiles you discovered in Step 0. If a lane doesn't fit any existing profile, ask the user which to use or create.
|
||||
3. Decide whether each lane is independent or gated by another lane.
|
||||
4. Create independent lanes as parallel cards with no parent links.
|
||||
5. Create synthesis/review/integration cards with parent links to the lanes they depend on. A child created with unfinished parents starts in `todo`; the dispatcher promotes it to `ready` only after every parent is done.
|
||||
|
||||
Examples of prompts that should fan out (using placeholder profile names — substitute whatever exists on the user's setup):
|
||||
|
||||
- "Build an app" → one card to a design-oriented profile for product/UI direction, one or two cards to engineering profiles for implementation, plus a later integration/review card if the user has a reviewer profile.
|
||||
- "Fix blockers and check model variants" → one implementation card for the blocker fixes plus one discovery/research card for config/source verification. A final reviewer card can depend on both.
|
||||
- "Research docs and implement" → a docs-research card can run in parallel with a codebase-discovery card; implementation waits only if it truly needs those findings.
|
||||
- "Analyze this screenshot and find the related code" → one card to a vision-capable profile for the visual analysis while another searches the codebase.
|
||||
|
||||
Words like "also," "finally," or "and" do not automatically imply a dependency. They often mean "make sure this is covered before reporting back." Only link tasks when one card cannot start until another card's output exists.
|
||||
|
||||
Show the graph to the user before creating cards. Let them correct it — including which actual profile name should own each lane.
|
||||
|
||||
### Step 3 — Create tasks and link
|
||||
|
||||
Use the profile names from Step 0. The example below uses placeholders `<profile-A>`, `<profile-B>`, `<profile-C>` — replace them with what the user actually has.
|
||||
|
||||
```python
|
||||
t1 = kanban_create(
|
||||
title="research: Postgres cost vs current",
|
||||
assignee="<profile-A>", # whichever profile handles research on this setup
|
||||
body="Compare estimated infrastructure costs, migration costs, and ongoing ops costs over a 3-year window. Sources: AWS/GCP pricing, team time estimates, current Postgres bills from peers.",
|
||||
tenant=os.environ.get("HERMES_TENANT"),
|
||||
)["task_id"]
|
||||
|
||||
t2 = kanban_create(
|
||||
title="research: Postgres performance vs current",
|
||||
assignee="<profile-A>", # same profile, run in parallel
|
||||
body="Compare query latency, throughput, and scaling characteristics at our expected data volume (~500GB, 10k QPS peak). Sources: benchmark papers, public case studies, pgbench results if easy.",
|
||||
)["task_id"]
|
||||
|
||||
t3 = kanban_create(
|
||||
title="synthesize migration recommendation",
|
||||
assignee="<profile-B>", # whichever profile does synthesis/analysis
|
||||
body="Read the findings from T1 (cost) and T2 (performance). Produce a 1-page recommendation with explicit trade-offs and a go/no-go call.",
|
||||
parents=[t1, t2],
|
||||
)["task_id"]
|
||||
|
||||
t4 = kanban_create(
|
||||
title="draft decision memo",
|
||||
assignee="<profile-C>", # whichever profile drafts user-facing prose
|
||||
body="Turn the analyst's recommendation into a 2-page memo for the CTO. Match the tone of previous decision memos in the team's knowledge base.",
|
||||
parents=[t3],
|
||||
)["task_id"]
|
||||
```
|
||||
|
||||
`parents=[...]` gates promotion — children stay in `todo` until every parent reaches `done`, then auto-promote to `ready`. No manual coordination needed; the dispatcher and dependency engine handle it.
|
||||
|
||||
If the task graph has dependencies, create the parent cards first, capture their returned ids, and include those ids in the child card's `parents` list during the child `kanban_create` call. Avoid creating all cards in parallel and linking them afterward; that creates a window where the dispatcher can claim a child before its inputs exist.
|
||||
|
||||
### Step 4 — Complete your own task
|
||||
|
||||
If you were spawned as a task yourself (e.g. a planner profile was assigned `T0: "investigate Postgres migration"`), mark it done with a summary of what you created:
|
||||
|
||||
```python
|
||||
kanban_complete(
|
||||
summary="decomposed into T1-T4: 2 research lanes in parallel, 1 synthesis on their outputs, 1 prose draft on the recommendation",
|
||||
metadata={
|
||||
"task_graph": {
|
||||
"T1": {"assignee": "<profile-A>", "parents": []},
|
||||
"T2": {"assignee": "<profile-A>", "parents": []},
|
||||
"T3": {"assignee": "<profile-B>", "parents": ["T1", "T2"]},
|
||||
"T4": {"assignee": "<profile-C>", "parents": ["T3"]},
|
||||
},
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
### Step 5 — Report back to the user
|
||||
|
||||
Tell them what you created in plain prose, naming the actual profiles you used:
|
||||
|
||||
> I've queued 4 tasks:
|
||||
> - **T1** (`<profile-A>`): cost comparison
|
||||
> - **T2** (`<profile-A>`): performance comparison, in parallel with T1
|
||||
> - **T3** (`<profile-B>`): synthesizes T1 + T2 into a recommendation
|
||||
> - **T4** (`<profile-C>`): turns T3 into a CTO memo
|
||||
>
|
||||
> The dispatcher will pick up T1 and T2 now. T3 starts when both finish. You'll get a gateway ping when T4 completes. Use the dashboard or `hermes kanban tail <id>` to follow along.
|
||||
|
||||
## Common patterns
|
||||
|
||||
**Fan-out + fan-in (research → synthesize):** N research-style cards with no parents, one synthesis card with all of them as parents.
|
||||
|
||||
**Parallel implementation + validation:** one implementer card makes the change while one explorer/researcher card verifies config, docs, or source mapping. A reviewer card can depend on both. Do not make the implementer own unrelated verification just because the user mentioned both in one sentence.
|
||||
|
||||
**Pipeline with gates:** `planner → implementer → reviewer`. Each stage's `parents=[previous_task]`. Reviewer blocks or completes; if reviewer blocks, the operator unblocks with feedback and respawns.
|
||||
|
||||
**Same-profile queue:** N tasks, all assigned to the same profile, no dependencies between them. Dispatcher serializes — that profile processes them in priority order, accumulating experience in its own memory.
|
||||
|
||||
**Human-in-the-loop:** Any task can `kanban_block()` to wait for input. Dispatcher respawns after `/unblock`. The comment thread carries the full context.
|
||||
|
||||
## Pitfalls
|
||||
|
||||
**Inventing profile names that don't exist.** The dispatcher silently fails to spawn unknown assignees — the card just sits in `ready` forever. Always assign to a profile from your Step 0 discovery; ask the user if you're unsure.
|
||||
|
||||
**Bundling independent lanes into one card.** If the user asks for two independent outcomes, create two cards. Example: "fix blockers and check model variants" is not one fixer task; create a fixer/engineer card for the fixes and an explorer/researcher card for the variant check, then optionally gate review on both.
|
||||
|
||||
**Over-linking because of wording.** "Finally check X" may still be parallel with implementation if X is static config, docs, or source discovery. Link it after implementation only when the check depends on the implementation result.
|
||||
|
||||
**Forgetting dependency links.** If the task graph says `research -> implement -> review`, do not create all tasks as independent ready cards. Use parent links so implement/review cannot run before their inputs exist.
|
||||
|
||||
**Reassignment vs. new task.** If a reviewer blocks with "needs changes," create a NEW task linked from the reviewer's task — don't re-run the same task with a stern look. The new task is assigned to the original implementer profile.
|
||||
|
||||
**Argument order for links.** `kanban_link(parent_id=..., child_id=...)` — parent first. Mixing them up demotes the wrong task to `todo`.
|
||||
|
||||
**Don't pre-create the whole graph if the shape depends on intermediate findings.** If T3's structure depends on what T1 and T2 find, let T3 exist as a "synthesize findings" task whose own first step is to read parent handoffs and plan the rest. Orchestrators can spawn orchestrators.
|
||||
|
||||
**Tenant inheritance.** If `HERMES_TENANT` is set in your env, pass `tenant=os.environ.get("HERMES_TENANT")` on every `kanban_create` call so child tasks stay in the same namespace.
|
||||
|
||||
## Goal-mode cards (persistent workers)
|
||||
|
||||
By default a dispatched worker gets **one shot** at its card: it does its work, calls `kanban_complete`/`kanban_block`, and exits. For open-ended cards where one turn rarely finishes the job, pass `goal_mode=True` to wrap that worker in a Ralph-style goal loop — the same engine behind the `/goal` slash command:
|
||||
|
||||
```python
|
||||
kanban_create(
|
||||
title="Translate the full docs site to French",
|
||||
body="Acceptance: every page translated, no English left, links intact.",
|
||||
assignee="<translator-profile>",
|
||||
goal_mode=True, # judge re-checks the card after each turn
|
||||
goal_max_turns=15, # optional budget (default 20)
|
||||
)["task_id"]
|
||||
```
|
||||
|
||||
How it behaves:
|
||||
- After each worker turn, an auxiliary judge evaluates the worker's response against the card's **title + body** (treated as the acceptance criteria).
|
||||
- Not done + budget remains → the worker keeps going **in the same session** (full context retained — not a fresh respawn).
|
||||
- Worker calls `kanban_complete`/`kanban_block` itself → loop stops, normal lifecycle.
|
||||
- Budget exhausted without completion → the card is **blocked** for human review (sticky), never a silent exit.
|
||||
|
||||
When to use it: long, multi-step, or "keep going until X is true" cards. When NOT to: cheap one-shot cards (translation of a single string, a quick lookup) — the judge overhead isn't worth it, and the dispatcher's existing retry/circuit-breaker already handles transient worker failures.
|
||||
|
||||
Write the body as **explicit acceptance criteria** — the judge is only as good as the goal text. "Translate the README" is weaker than "Translate every section of the README to French; no English sentences remain."
|
||||
|
||||
## Recovering stuck workers
|
||||
|
||||
When a worker profile keeps crashing, hallucinating, or getting blocked by its own mistakes (usually: wrong model, missing skill, broken credential), the kanban dashboard flags the task with a ⚠ badge and opens a **Recovery** section in the drawer. Three primary actions:
|
||||
|
||||
1. **Reclaim** (or `hermes kanban reclaim <task_id>`) — abort the running worker immediately and reset the task to `ready`. The existing claim TTL is ~15 min; this is the fast path out.
|
||||
2. **Reassign** (or `hermes kanban reassign <task_id> <new-profile> --reclaim`) — switch the task to a different profile (one that exists on this setup) and let the dispatcher pick it up with a fresh worker.
|
||||
3. **Change profile model** — the dashboard prints a copy-paste hint for `hermes -p <profile> model` since profile config lives on disk; edit it in a terminal, then Reclaim to retry with the new model.
|
||||
|
||||
Hallucination warnings appear on tasks where a worker's `kanban_complete(created_cards=[...])` claim included card ids that don't exist or weren't created by the worker's profile (the gate blocks the completion), or where the free-form summary references `t_<hex>` ids that don't resolve (advisory prose scan, non-blocking). Both produce audit events that persist even after recovery actions — the trail stays for debugging.
|
||||
|
|
@ -1,210 +0,0 @@
|
|||
---
|
||||
title: "Kanban Worker — Pitfalls, examples, and edge cases for Hermes Kanban workers"
|
||||
sidebar_label: "Kanban Worker"
|
||||
description: "Pitfalls, examples, and edge cases for Hermes Kanban workers"
|
||||
---
|
||||
|
||||
{/* This page is auto-generated from the skill's SKILL.md by website/scripts/generate-skill-docs.py. Edit the source SKILL.md, not this page. */}
|
||||
|
||||
# Kanban Worker
|
||||
|
||||
Pitfalls, examples, and edge cases for Hermes Kanban workers. The lifecycle itself is auto-injected into every worker's system prompt as KANBAN_GUIDANCE (from agent/prompt_builder.py); this skill is what you load when you want deeper detail on specific scenarios.
|
||||
|
||||
## Skill metadata
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| Source | Bundled (installed by default) |
|
||||
| Path | `skills/devops/kanban-worker` |
|
||||
| Version | `2.0.0` |
|
||||
| Platforms | linux, macos, windows |
|
||||
| Tags | `kanban`, `multi-agent`, `collaboration`, `workflow`, `pitfalls` |
|
||||
| Related skills | [`kanban-orchestrator`](/docs/user-guide/skills/bundled/devops/devops-kanban-orchestrator) |
|
||||
|
||||
## Reference: full SKILL.md
|
||||
|
||||
:::info
|
||||
The following is the complete skill definition that Hermes loads when this skill is triggered. This is what the agent sees as instructions when the skill is active.
|
||||
:::
|
||||
|
||||
# Kanban Worker — Pitfalls and Examples
|
||||
|
||||
> You're seeing this skill because the Hermes Kanban dispatcher spawned you as a worker with `--skills kanban-worker` — it's loaded automatically for every dispatched worker. The **lifecycle** (6 steps: orient → work → heartbeat → block/complete) also lives in the `KANBAN_GUIDANCE` block that's auto-injected into your system prompt. This skill is the deeper detail: good handoff shapes, retry diagnostics, edge cases.
|
||||
|
||||
## Workspace handling
|
||||
|
||||
Your workspace kind determines how you should behave inside `$HERMES_KANBAN_WORKSPACE`:
|
||||
|
||||
| Kind | What it is | How to work |
|
||||
|---|---|---|
|
||||
| `scratch` | Fresh tmp dir, yours alone | Read/write freely; it gets GC'd when the task is archived. |
|
||||
| `dir:<path>` | Shared persistent directory | Other runs will read what you write. Treat it like long-lived state. Path is guaranteed absolute (the kernel rejects relative paths). |
|
||||
| `worktree` | Git worktree at the resolved path | If `.git` doesn't exist, run `git worktree add <path> ${HERMES_KANBAN_BRANCH:-wt/$HERMES_KANBAN_TASK}` from the main repo first, then cd and work normally. Commit work here. |
|
||||
|
||||
## Tenant isolation
|
||||
|
||||
If `$HERMES_TENANT` is set, the task belongs to a tenant namespace. When reading or writing persistent memory, prefix memory entries with the tenant so context doesn't leak across tenants:
|
||||
|
||||
- Good: `business-a: Acme is our biggest customer`
|
||||
- Bad (leaks): `Acme is our biggest customer`
|
||||
|
||||
## Good summary + metadata shapes
|
||||
|
||||
The `kanban_complete(summary=..., metadata=...)` handoff is how downstream workers read what you did. Patterns that work:
|
||||
|
||||
**Coding task:**
|
||||
```python
|
||||
kanban_complete(
|
||||
summary="shipped rate limiter — token bucket, keys on user_id with IP fallback, 14 tests pass",
|
||||
metadata={
|
||||
"changed_files": ["rate_limiter.py", "tests/test_rate_limiter.py"],
|
||||
"tests_run": 14,
|
||||
"tests_passed": 14,
|
||||
"decisions": ["user_id primary, IP fallback for unauthenticated requests"],
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
**Coding task that needs human review (review-required):**
|
||||
|
||||
For most code-changing tasks, the work isn't truly *done* until a human reviewer has eyes on it. Block instead of complete, with `reason` prefixed `review-required: ` so the dashboard surfaces the row as needing review. Drop the structured metadata (changed files, test counts, diff/PR url) into a comment first, since `kanban_block` only carries the human-readable reason — comments are the durable annotation channel. Reviewer either approves and runs `hermes kanban unblock <id>` (which re-spawns you with the comment thread for any follow-ups) or asks for changes via another comment.
|
||||
|
||||
```python
|
||||
import json
|
||||
|
||||
kanban_comment(
|
||||
body="review-required handoff:\n" + json.dumps({
|
||||
"changed_files": ["rate_limiter.py", "tests/test_rate_limiter.py"],
|
||||
"tests_run": 14,
|
||||
"tests_passed": 14,
|
||||
"diff_path": "/path/to/worktree", # or PR url if pushed
|
||||
"decisions": ["user_id primary, IP fallback for unauthenticated requests"],
|
||||
}, indent=2),
|
||||
)
|
||||
kanban_block(
|
||||
reason="review-required: rate limiter shipped, 14/14 tests pass — needs eyes on the user_id/IP fallback choice before merging",
|
||||
)
|
||||
```
|
||||
|
||||
Use `kanban_complete` only when the task is genuinely terminal — e.g. a one-line typo fix, a docs change with no functional consequences, or a research task where the artifact IS the writeup itself.
|
||||
|
||||
**Research task:**
|
||||
```python
|
||||
kanban_complete(
|
||||
summary="3 competing libraries reviewed; vLLM wins on throughput, SGLang on latency, Tensorrt-LLM on memory efficiency",
|
||||
metadata={
|
||||
"sources_read": 12,
|
||||
"recommendation": "vLLM",
|
||||
"benchmarks": {"vllm": 1.0, "sglang": 0.87, "trtllm": 0.72},
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
**Review task:**
|
||||
```python
|
||||
kanban_complete(
|
||||
summary="reviewed PR #123; 2 blocking issues found (SQL injection in /search, missing CSRF on /settings)",
|
||||
metadata={
|
||||
"pr_number": 123,
|
||||
"findings": [
|
||||
{"severity": "critical", "file": "api/search.py", "line": 42, "issue": "raw SQL concat"},
|
||||
{"severity": "high", "file": "api/settings.py", "issue": "missing CSRF middleware"},
|
||||
],
|
||||
"approved": False,
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
Shape `metadata` so downstream parsers (reviewers, aggregators, schedulers) can use it without re-reading your prose.
|
||||
|
||||
## Claiming cards you actually created
|
||||
|
||||
If your run produced new kanban tasks (via `kanban_create`), pass the ids in `created_cards` on `kanban_complete`. The kernel verifies each id exists and was created by your profile; any phantom id blocks the completion with an error listing what went wrong, and the rejected attempt is permanently recorded on the task's event log. **Only list ids you captured from a successful `kanban_create` return value — never invent ids from prose, never paste ids from earlier runs, never claim cards another worker created.**
|
||||
|
||||
```python
|
||||
# GOOD — capture return values, then claim them.
|
||||
c1 = kanban_create(title="remediate SQL injection", assignee="security-worker")
|
||||
c2 = kanban_create(title="fix CSRF middleware", assignee="web-worker")
|
||||
|
||||
kanban_complete(
|
||||
summary="Review done; spawned remediations for both findings.",
|
||||
metadata={"pr_number": 123, "approved": False},
|
||||
created_cards=[c1["task_id"], c2["task_id"]],
|
||||
)
|
||||
```
|
||||
|
||||
```python
|
||||
# BAD — claiming ids you don't have captured return values for.
|
||||
kanban_complete(
|
||||
summary="Created remediation cards t_a1b2c3d4, t_deadbeef", # hallucinated
|
||||
created_cards=["t_a1b2c3d4", "t_deadbeef"], # → gate rejects
|
||||
)
|
||||
```
|
||||
|
||||
If a `kanban_create` call fails (exception, tool_error), the card was NOT created — do not include a phantom id for it. Retry the create, or omit the id and mention the failure in your summary. The prose-scan pass also catches `t_<hex>` references in your free-form summary that don't resolve; these don't block the completion but show up as advisory warnings on the task in the dashboard.
|
||||
|
||||
## Block reasons that get answered fast
|
||||
|
||||
Bad: `"stuck"` — the human has no context.
|
||||
|
||||
Good: one sentence naming the specific decision you need. Leave longer context as a comment instead.
|
||||
|
||||
```python
|
||||
kanban_comment(
|
||||
task_id=os.environ["HERMES_KANBAN_TASK"],
|
||||
body="Full context: I have user IPs from Cloudflare headers but some users are behind NATs with thousands of peers. Keying on IP alone causes false positives.",
|
||||
)
|
||||
kanban_block(reason="Rate limit key choice: IP (simple, NAT-unsafe) or user_id (requires auth, skips anonymous endpoints)?")
|
||||
```
|
||||
|
||||
The block message is what appears in the dashboard / gateway notifier. The comment is the deeper context a human reads when they open the task.
|
||||
|
||||
## Heartbeats worth sending
|
||||
|
||||
Good heartbeats name progress: `"epoch 12/50, loss 0.31"`, `"scanned 1.2M/2.4M rows"`, `"uploaded 47/120 videos"`.
|
||||
|
||||
Bad heartbeats: `"still working"`, empty notes, sub-second intervals. Every few minutes max; skip entirely for tasks under ~2 minutes.
|
||||
|
||||
## Retry scenarios
|
||||
|
||||
If you open the task and `kanban_show` returns `runs: [...]` with one or more closed runs, you're a retry. The prior runs' `outcome` / `summary` / `error` tell you what didn't work. Don't repeat that path. Typical retry diagnostics:
|
||||
|
||||
- `outcome: "timed_out"` — the previous attempt hit `max_runtime_seconds`. You may need to chunk the work or shorten it.
|
||||
- `outcome: "crashed"` — OOM or segfault. Reduce memory footprint.
|
||||
- `outcome: "spawn_failed"` + `error: "..."` — usually a profile config issue (missing credential, bad PATH). Ask the human via `kanban_block` instead of retrying blindly.
|
||||
- `outcome: "reclaimed"` + `summary: "task archived..."` — operator archived the task out from under the previous run; you probably shouldn't be running at all, check status carefully.
|
||||
- `outcome: "blocked"` — a previous attempt blocked; the unblock comment should be in the thread by now.
|
||||
|
||||
## Notification routing
|
||||
|
||||
You can configure the gateway to receive cross-profile Kanban task notifications by adding `notification_sources` to `~/.hermes/config.yaml`.
|
||||
- `notification_sources: ['*']` accepts subscriptions from all profiles.
|
||||
- `notification_sources: ['default', 'zilor-ppt']` or `"default,zilor-ppt"` restricts subscriptions to specified profiles.
|
||||
- Omitting the key keeps the default behavior (profile isolation).
|
||||
|
||||
## Do NOT
|
||||
|
||||
- Call `delegate_task` as a substitute for `kanban_create`. `delegate_task` is for short reasoning subtasks inside YOUR run; `kanban_create` is for cross-agent handoffs that outlive one API loop.
|
||||
- Call `clarify` to ask the human a question. You are running headless — there is no live user to answer. The call will time out (default ~120s) and the task will sit silently in `running` with no signal that it needs input. Use `kanban_comment` (context) + `kanban_block(reason=...)` (decision needed) instead — the task surfaces on the board as blocked, the operator sees it, unblocks with their answer in a comment, and you respawn with the thread.
|
||||
- Modify files outside `$HERMES_KANBAN_WORKSPACE` unless the task body says to.
|
||||
- Create follow-up tasks assigned to yourself — assign to the right specialist.
|
||||
- Complete a task you didn't actually finish. Block it instead.
|
||||
|
||||
## Pitfalls
|
||||
|
||||
**Task state can change between dispatch and your startup.** Between when the dispatcher claimed and when your process actually booted, the task may have been blocked, reassigned, or archived. Always `kanban_show` first. If it reports `blocked` or `archived`, stop — you shouldn't be running.
|
||||
|
||||
**Workspace may have stale artifacts.** Especially `dir:` and `worktree` workspaces can have files from previous runs. Read the comment thread — it usually explains why you're running again and what state the workspace is in.
|
||||
|
||||
**Don't rely on the CLI when the guidance is available.** The `kanban_*` tools work across all terminal backends (Docker, Modal, SSH). `hermes kanban <verb>` from your terminal tool will fail in containerized backends because the CLI isn't installed there. When in doubt, use the tool.
|
||||
|
||||
## CLI fallback (for scripting)
|
||||
|
||||
Every tool has a CLI equivalent for human operators and scripts:
|
||||
- `kanban_show` ↔ `hermes kanban show <id> --json`
|
||||
- `kanban_complete` ↔ `hermes kanban complete <id> --summary "..." --metadata '{...}'`
|
||||
- `kanban_block` ↔ `hermes kanban block <id> "reason"`
|
||||
- `kanban_create` ↔ `hermes kanban create "title" --assignee <profile> [--parent <id>]`
|
||||
- etc.
|
||||
|
||||
Use the tools from inside an agent; the CLI exists for the human at the terminal.
|
||||
|
|
@ -21,7 +21,7 @@ Plan, set up, and monitor a multi-agent video production pipeline backed by Herm
|
|||
| License | MIT |
|
||||
| Platforms | linux, macos, windows |
|
||||
| Tags | `video`, `kanban`, `multi-agent`, `orchestration`, `production-pipeline` |
|
||||
| Related skills | [`kanban-orchestrator`](/docs/user-guide/skills/bundled/devops/devops-kanban-orchestrator), [`kanban-worker`](/docs/user-guide/skills/bundled/devops/devops-kanban-worker), [`ascii-video`](/docs/user-guide/skills/bundled/creative/creative-ascii-video), [`manim-video`](/docs/user-guide/skills/bundled/creative/creative-manim-video), [`p5js`](/docs/user-guide/skills/bundled/creative/creative-p5js), [`comfyui`](/docs/user-guide/skills/bundled/creative/creative-comfyui), [`touchdesigner-mcp`](/docs/user-guide/skills/bundled/creative/creative-touchdesigner-mcp), [`blender-mcp`](/docs/user-guide/skills/optional/creative/creative-blender-mcp), [`pixel-art`](/docs/user-guide/skills/optional/creative/creative-pixel-art), [`ascii-art`](/docs/user-guide/skills/bundled/creative/creative-ascii-art), [`songwriting-and-ai-music`](/docs/user-guide/skills/bundled/creative/creative-songwriting-and-ai-music), [`heartmula`](/docs/user-guide/skills/bundled/media/media-heartmula), [`songsee`](/docs/user-guide/skills/bundled/media/media-songsee), `spotify`, [`youtube-content`](/docs/user-guide/skills/bundled/media/media-youtube-content), [`claude-design`](/docs/user-guide/skills/bundled/creative/creative-claude-design), [`excalidraw`](/docs/user-guide/skills/bundled/creative/creative-excalidraw), [`architecture-diagram`](/docs/user-guide/skills/bundled/creative/creative-architecture-diagram), [`concept-diagrams`](/docs/user-guide/skills/optional/creative/creative-concept-diagrams), [`baoyu-comic`](/docs/user-guide/skills/optional/creative/creative-baoyu-comic), [`baoyu-infographic`](/docs/user-guide/skills/bundled/creative/creative-baoyu-infographic), [`humanizer`](/docs/user-guide/skills/bundled/creative/creative-humanizer), [`gif-search`](/docs/user-guide/skills/bundled/media/media-gif-search), [`meme-generation`](/docs/user-guide/skills/optional/creative/creative-meme-generation) |
|
||||
| Related skills | [`ascii-video`](/docs/user-guide/skills/bundled/creative/creative-ascii-video), [`manim-video`](/docs/user-guide/skills/bundled/creative/creative-manim-video), [`p5js`](/docs/user-guide/skills/bundled/creative/creative-p5js), [`comfyui`](/docs/user-guide/skills/bundled/creative/creative-comfyui), [`touchdesigner-mcp`](/docs/user-guide/skills/bundled/creative/creative-touchdesigner-mcp), [`blender-mcp`](/docs/user-guide/skills/optional/creative/creative-blender-mcp), [`pixel-art`](/docs/user-guide/skills/optional/creative/creative-pixel-art), [`ascii-art`](/docs/user-guide/skills/bundled/creative/creative-ascii-art), [`songwriting-and-ai-music`](/docs/user-guide/skills/bundled/creative/creative-songwriting-and-ai-music), [`heartmula`](/docs/user-guide/skills/bundled/media/media-heartmula), [`songsee`](/docs/user-guide/skills/bundled/media/media-songsee), `spotify`, [`youtube-content`](/docs/user-guide/skills/bundled/media/media-youtube-content), [`claude-design`](/docs/user-guide/skills/bundled/creative/creative-claude-design), [`excalidraw`](/docs/user-guide/skills/bundled/creative/creative-excalidraw), [`architecture-diagram`](/docs/user-guide/skills/bundled/creative/creative-architecture-diagram), [`concept-diagrams`](/docs/user-guide/skills/optional/creative/creative-concept-diagrams), [`baoyu-comic`](/docs/user-guide/skills/optional/creative/creative-baoyu-comic), [`baoyu-infographic`](/docs/user-guide/skills/bundled/creative/creative-baoyu-infographic), [`humanizer`](/docs/user-guide/skills/bundled/creative/creative-humanizer), [`gif-search`](/docs/user-guide/skills/bundled/media/media-gif-search), [`meme-generation`](/docs/user-guide/skills/optional/creative/creative-meme-generation) |
|
||||
|
||||
## Reference: full SKILL.md
|
||||
|
||||
|
|
@ -187,7 +187,7 @@ task graphs. See **[references/examples.md](https://github.com/NousResearch/herm
|
|||
file` toolset, the director's `SOUL.md` rules forbid it from executing
|
||||
work itself. It decomposes and routes only — every concrete task becomes
|
||||
a `hermes kanban create` call to a specialist profile. The
|
||||
`kanban-orchestrator` skill spells this out further.
|
||||
auto-injected kanban orchestration guidance spells this out further.
|
||||
|
||||
7. **Don't over-decompose.** A 30-second product video does NOT need 20 tasks.
|
||||
Aim for the smallest task graph that still parallelizes well and exposes the
|
||||
|
|
|
|||
|
|
@ -62,8 +62,7 @@ Hermes 在执行 `hermes update` 时也会同步内置技能,但同步清单
|
|||
|
||||
| 技能 | 描述 | 路径 |
|
||||
|-------|-------------|------|
|
||||
| [`kanban-orchestrator`](/user-guide/skills/bundled/devops/devops-kanban-orchestrator) | 面向编排器(orchestrator)配置文件的分解策略与反诱惑规则,用于通过 Kanban 路由工作。"不要自己做工作"规则和基本生命周期会自动注入每个 Kanban worker 的系统 prompt;如需更深入的细节,请加载此技能。 | `devops/kanban-orchestrator` |
|
||||
| [`kanban-worker`](/user-guide/skills/bundled/devops/devops-kanban-worker) | Hermes Kanban worker 的陷阱、示例和边界情况。生命周期本身会作为 `KANBAN_GUIDANCE` 自动注入每个 worker 的系统 prompt(来自 `agent/prompt_builder.py`);当需要更深入细节时加载此技能。 | `devops/kanban-worker` |
|
||||
|
||||
|
||||
## dogfood
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
- **运维人员**:选择将哪些通道接入看板(创建哪些 profile,使用哪些 assignee)。
|
||||
- **插件/集成作者**:希望添加新的通道形态(封装 Codex / Claude Code / OpenCode 的 CLI worker、容器化审查 worker、通过 API 拉取任务的非 Hermes 服务)。
|
||||
|
||||
如果你编写的是 worker 代码本身——即运行在通道*内部*的 agent——请参阅 [`kanban-worker`](https://github.com/NousResearch/hermes-agent/blob/main/skills/devops/kanban-worker/SKILL.md) skill,其中包含更深入的操作细节。
|
||||
如果你编写的是 worker 代码本身——即运行在通道*内部*的 agent——kanban 生命周期与参考细节会自动注入到 worker 的系统提示中([`agent/prompt_builder.py`](https://github.com/NousResearch/hermes-agent/blob/main/agent/prompt_builder.py) 中的 `KANBAN_GUIDANCE` 块)。
|
||||
|
||||
## 层级结构
|
||||
|
||||
|
|
@ -64,7 +64,7 @@ kanban 内核强制要求每次运行恰好由其中一项终止。既未调用
|
|||
- **先将结构化元数据写入 `kanban_comment`**,因为 `kanban_block` 只携带人类可读的 `reason`。Comment 是持久的注解通道——所有与审计相关的字段(changed_files、tests_run、diff_path 或 PR url、决策记录)都应放在这里。
|
||||
- **Reviewer 批准并解除阻塞**,这将重新生成 worker 并附带 comment 线程用于后续跟进;或通过另一条 comment 要求修改,下一次 worker 运行时将通过 `kanban_show` 的上下文看到这些内容。
|
||||
|
||||
[`kanban-worker`](https://github.com/NousResearch/hermes-agent/blob/main/skills/devops/kanban-worker/SKILL.md) skill 中有 `kanban_complete`(真正终态的任务——拼写修复、文档变更、研究报告)和 `review-required` block 模式的完整示例。
|
||||
自动注入的 `KANBAN_GUIDANCE` 同时涵盖 `kanban_complete`(真正终态的任务——拼写修复、文档变更、研究报告)和 `review-required` block 模式。
|
||||
|
||||
## 日志与审计追踪
|
||||
|
||||
|
|
@ -80,9 +80,9 @@ kanban 内核强制要求每次运行恰好由其中一项终止。既未调用
|
|||
|
||||
### Hermes profile 通道(默认)
|
||||
|
||||
当前所有 kanban worker 采用的形态:assignee 是 profile 名称,调度器生成 `hermes -p <profile>`,worker 自动加载 [`kanban-worker`](https://github.com/NousResearch/hermes-agent/blob/main/skills/devops/kanban-worker/SKILL.md) skill 以及 `KANBAN_GUIDANCE` 系统提示块,并使用 `kanban_*` 工具终止运行。除定义 profile 外无需任何额外配置。
|
||||
当前所有 kanban worker 采用的形态:assignee 是 profile 名称,调度器生成 `hermes -p <profile>`,worker 会自动获得注入的 `KANBAN_GUIDANCE` 系统提示块,并使用 `kanban_*` 工具终止运行。除定义 profile 外无需任何额外配置。
|
||||
|
||||
为你的 fleet 创建 profile 时,选择与你希望 orchestrator 路由到的*角色*相匹配的名称。orchestrator(如果存在)通过 `hermes profile list` 发现你的 profile 名称——系统不假设固定的名单(orchestrator 侧的契约请参阅 [`kanban-orchestrator`](https://github.com/NousResearch/hermes-agent/blob/main/skills/devops/kanban-orchestrator/SKILL.md) skill)。
|
||||
为你的 fleet 创建 profile 时,选择与你希望 orchestrator 路由到的*角色*相匹配的名称。orchestrator(如果存在)通过 `hermes profile list` 发现你的 profile 名称——系统不假设固定的名单(orchestrator 侧的契约也是注入的 `KANBAN_GUIDANCE` 的一部分)。
|
||||
|
||||
### Orchestrator profile 通道
|
||||
|
||||
|
|
@ -110,5 +110,4 @@ profile 通道的特化形态:orchestrator 是一个 Hermes profile,其工
|
|||
|
||||
- [Kanban 概览](./kanban) — 面向用户的介绍。
|
||||
- [Kanban 教程](./kanban-tutorial) — 开启仪表板的完整演练。
|
||||
- [`kanban-worker`](https://github.com/NousResearch/hermes-agent/blob/main/skills/devops/kanban-worker/SKILL.md) — worker 进程加载的 skill。
|
||||
- [`kanban-orchestrator`](https://github.com/NousResearch/hermes-agent/blob/main/skills/devops/kanban-orchestrator/SKILL.md) — orchestrator 侧。
|
||||
- [`KANBAN_GUIDANCE`](https://github.com/NousResearch/hermes-agent/blob/main/agent/prompt_builder.py) — 注入到每个 kanban worker 系统提示中的 worker + orchestrator 生命周期。
|
||||
|
|
@ -240,7 +240,7 @@ kanban_create(
|
|||
kanban_complete(summary="decomposed into 2 research tasks + 1 writer; linked dependencies")
|
||||
```
|
||||
|
||||
"(编排器)"工具 —— `kanban_list`、`kanban_create`、`kanban_link`、`kanban_unblock`,以及对外部任务的 `kanban_comment` —— 通过同一工具集提供;约定(由 `kanban-orchestrator` skill 强制执行)是 worker 配置文件不进行扇出或路由无关工作,编排器配置文件不执行实现工作。调度器启动的 worker 仍然针对破坏性生命周期操作限定在任务范围内,无法修改无关任务。
|
||||
"(编排器)"工具 —— `kanban_list`、`kanban_create`、`kanban_link`、`kanban_unblock`,以及对外部任务的 `kanban_comment` —— 通过同一工具集提供;约定(编码在自动注入的 kanban 指引中)是 worker 配置文件不进行扇出或路由无关工作,编排器配置文件不执行实现工作。调度器启动的 worker 仍然针对破坏性生命周期操作限定在任务范围内,无法修改无关任务。
|
||||
|
||||
### 为什么使用工具而不是 shell 执行 `hermes kanban`
|
||||
|
||||
|
|
@ -252,7 +252,7 @@ kanban_complete(summary="decomposed into 2 research tasks + 1 writer; linked dep
|
|||
|
||||
**对普通会话零 schema 占用。** 普通的 `hermes chat` 会话在其 schema 中没有任何 `kanban_*` 工具,除非活动配置文件为编排器工作显式启用了 `kanban` 工具集。调度器启动的任务 worker 因为设置了 `HERMES_KANBAN_TASK` 而获得任务范围的工具;编排器配置文件通过配置获得更广泛的路由界面。对于从不使用 kanban 的用户,没有工具膨胀。
|
||||
|
||||
`kanban-worker` 和 `kanban-orchestrator` skill 教导模型何时调用哪个工具以及调用顺序。
|
||||
自动注入的 kanban 指引教导模型何时调用哪个工具以及调用顺序。
|
||||
|
||||
### 推荐的交接证据
|
||||
|
||||
|
|
@ -280,9 +280,9 @@ kanban_complete(summary="decomposed into 2 research tasks + 1 writer; linked dep
|
|||
|
||||
不要将密钥、原始日志、token(令牌)、OAuth 材料和无关记录放入 `metadata`。改为存储指针和摘要。如果任务没有文件或测试,在 `summary` 中明确说明,并在 `metadata` 中放置确实存在的证据,例如来源 URL、issue id 或手动审查步骤。
|
||||
|
||||
### Worker skill
|
||||
### Worker 生命周期
|
||||
|
||||
任何应该能够处理 kanban 任务的配置文件都必须加载 `kanban-worker` skill。它通过**工具调用**(而非 CLI 命令)教导 worker 完整的生命周期:
|
||||
任何处理 kanban 任务的配置文件都会**自动**获得 worker 生命周期 —— 它在启动时被注入到 worker 的系统 prompt 中(`KANBAN_GUIDANCE` 块),因此**无需安装或配置任何东西**。它通过**工具调用**(而非 CLI 命令)教导 worker 完整的生命周期:
|
||||
|
||||
1. 启动时,调用 `kanban_show()` 读取标题 + 正文 + 父级交接 + 先前尝试 + 完整评论线程。
|
||||
2. 通过终端工具执行 `cd $HERMES_KANBAN_WORKSPACE`,在那里完成工作。
|
||||
|
|
@ -291,20 +291,6 @@ kanban_complete(summary="decomposed into 2 research tasks + 1 writer; linked dep
|
|||
|
||||
最终的 `kanban_complete` / `kanban_block` 调用是 worker 协议的一部分。如果 worker 进程以状态 0 退出而任务仍处于 `running` 状态,调度器将其视为协议违规,发出 `protocol_violation` 事件,并在下一个 tick 自动阻塞任务而不是重新启动它进入同一循环。这通常意味着模型写了一个纯文本答案并退出,而没有使用 Kanban 工具界面。
|
||||
|
||||
`kanban-worker` 是一个内置 skill,在安装和更新期间同步到每个配置文件 —— 无需单独的 Skills Hub 安装步骤。验证它是否存在于你用于 kanban worker 的配置文件中(`researcher`、`writer`、`ops` 等):
|
||||
|
||||
```bash
|
||||
hermes -p <your-worker-profile> skills list | grep kanban-worker
|
||||
```
|
||||
|
||||
如果内置副本丢失,为该配置文件恢复它:
|
||||
|
||||
```bash
|
||||
hermes -p <your-worker-profile> skills reset kanban-worker --restore
|
||||
```
|
||||
|
||||
调度器在启动每个 worker 时也会自动传递 `--skills kanban-worker`,因此即使配置文件的默认 skills 配置不包含它,worker 也始终拥有该模式库。
|
||||
|
||||
### 为特定任务固定额外 skill
|
||||
|
||||
有时单个任务需要受让人配置文件默认不携带的专业上下文 —— 需要 `translation` skill 的翻译任务、需要 `github-code-review` 的审查任务、需要 `security-pr-audit` 的安全审计。与其每次都编辑受让人的配置文件,不如直接将 skill 附加到任务上。
|
||||
|
|
@ -340,11 +326,11 @@ hermes kanban create "audit auth flow" \
|
|||
|
||||
**从仪表盘**,在内联创建表单的 **skills** 字段中以逗号分隔输入 skill 名称。
|
||||
|
||||
这些 skill 是对内置 `kanban-worker` 的**补充** —— 调度器为每个 skill(以及内置的)发出一个 `--skills <name>` 标志,因此 worker 启动时加载了所有这些 skill。skill 名称必须与受让人配置文件上实际安装的 skill 匹配(运行 `hermes skills list` 查看可用内容);没有运行时安装。
|
||||
调度器为列出的每个 skill 发出一个 `--skills <name>` 标志,因此 worker 在自动注入的 kanban 指引之上加载了所有这些 skill。skill 名称必须与受让人配置文件上实际安装的 skill 匹配(运行 `hermes skills list` 查看可用内容);没有运行时安装。
|
||||
|
||||
### 编排器 skill
|
||||
### 编排器的行为方式
|
||||
|
||||
**行为良好的编排器不会自己做工作。** 它将用户的目标分解为任务,链接它们,将每个任务分配给你设置的配置文件之一,然后退后。`kanban-orchestrator` skill 将此编码为工具调用模式:反诱惑规则、Step-0 配置文件发现提示(调度器在未知受让人名称上静默失败,因此编排器必须将每张卡片落地到你机器上实际存在的配置文件),以及以 `kanban_create` / `kanban_link` / `kanban_comment` 为核心的分解手册。
|
||||
**行为良好的编排器不会自己做工作。** 它将用户的目标分解为任务,链接它们,将每个任务分配给你设置的配置文件之一,然后退后。编排器指引 —— 反诱惑规则、Step-0 配置文件发现提示(调度器在未知受让人名称上静默失败,因此编排器必须将每张卡片落地到你机器上实际存在的配置文件),以及以 `kanban_create` / `kanban_link` / `kanban_comment` 为核心的分解手册 —— 会自动注入到 worker 的系统 prompt 中;无需安装任何东西。
|
||||
|
||||
典型的编排器轮次(两个并行研究员交接给一个写作者):
|
||||
|
||||
|
|
@ -365,17 +351,7 @@ kanban_complete(
|
|||
)
|
||||
```
|
||||
|
||||
`kanban-orchestrator` 是一个内置 skill。它在安装和更新期间同步到每个配置文件,因此无需单独的 Skills Hub 安装步骤。验证它是否存在于你的编排器配置文件中:
|
||||
|
||||
```bash
|
||||
hermes -p orchestrator skills list | grep kanban-orchestrator
|
||||
```
|
||||
|
||||
如果内置副本丢失,为该配置文件恢复它:
|
||||
|
||||
```bash
|
||||
hermes -p orchestrator skills reset kanban-orchestrator --restore
|
||||
```
|
||||
编排器指引随 worker 的系统 prompt 自动提供 —— 无需按配置文件安装或同步任何东西。
|
||||
|
||||
为获得最佳效果,将其与工具集限制为看板操作(`kanban`、`gateway`、`memory`)的配置文件配对,这样编排器即使尝试也无法执行实现任务。
|
||||
|
||||
|
|
|
|||
|
|
@ -1,207 +0,0 @@
|
|||
---
|
||||
title: "Kanban Orchestrator"
|
||||
sidebar_label: "Kanban Orchestrator"
|
||||
description: "用于通过 Kanban 路由工作的编排器 profile 的任务分解手册及反诱惑规则"
|
||||
---
|
||||
|
||||
{/* This page is auto-generated from the skill's SKILL.md by website/scripts/generate-skill-docs.py. Edit the source SKILL.md, not this page. */}
|
||||
|
||||
# Kanban Orchestrator
|
||||
|
||||
用于通过 Kanban 路由工作的编排器 profile 的任务分解手册及反诱惑规则。"不要自己执行工作"规则和基本生命周期会自动注入每个 kanban worker 的系统 prompt(提示词)中;本 skill 是当你专门扮演编排器角色时使用的更深层手册。
|
||||
|
||||
## Skill 元数据
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| 来源 | 内置(默认安装) |
|
||||
| 路径 | `skills/devops/kanban-orchestrator` |
|
||||
| 版本 | `3.0.0` |
|
||||
| 平台 | linux, macos, windows |
|
||||
| 标签 | `kanban`, `multi-agent`, `orchestration`, `routing` |
|
||||
| 相关 skill | [`kanban-worker`](/user-guide/skills/bundled/devops/devops-kanban-worker) |
|
||||
|
||||
## 参考:完整 SKILL.md
|
||||
|
||||
:::info
|
||||
以下是 Hermes 在触发此 skill 时加载的完整 skill 定义。这是 skill 激活时 agent 所看到的指令内容。
|
||||
:::
|
||||
|
||||
# Kanban Orchestrator — 任务分解手册
|
||||
|
||||
> **核心 worker 生命周期**(包括 `kanban_create` 扇出模式和"分解而非执行"规则)通过 `KANBAN_GUIDANCE` 系统 prompt 块自动注入每个 kanban 进程。本 skill 是当你作为编排器 profile、整个职责就是路由时使用的更深层手册。
|
||||
|
||||
## Profile 由用户配置——不是固定名单
|
||||
|
||||
Hermes 的配置因人而异。有些用户运行单个 profile 处理所有事务;有些运行小型集群(`docker-worker`、`cron-worker`);有些运行自己命名的精选专家团队。**没有默认的专家名单**——编排器 skill 不知道此机器上存在哪些 profile。
|
||||
|
||||
在扇出之前,你必须基于实际存在的 profile 来制定分解方案。调度器会静默地忽略无法识别的 assignee 名称——它不会自动纠正、不会建议、也不会回退。因此,在只有 `docker-worker` 的配置上,分配给 `researcher` 的卡片会永远停留在 `ready` 状态。
|
||||
|
||||
**第 0 步:在规划前发现可用的 profile。**
|
||||
|
||||
使用以下方法之一:
|
||||
|
||||
- `hermes profile list` — 打印此机器上已配置的 profile 表。如果有终端工具,通过终端工具运行;否则询问用户。
|
||||
- `kanban_list(assignee="<some-name>")` — 验证单个名称。对于未知 assignee 返回空列表(而非报错),因此只能确认你已在考虑的名称。
|
||||
- **直接询问用户。** 当目标需要多个专家时,"你配置了哪些 profile?"是一个合理的开场问题。
|
||||
|
||||
将结果缓存在工作记忆中供本次对话使用。每轮都重新询问会浪费工具调用。
|
||||
|
||||
## 何时使用看板(vs. 直接执行工作)
|
||||
|
||||
当以下任一条件成立时,创建 Kanban 任务:
|
||||
|
||||
1. **需要多个专家。** 研究 + 分析 + 写作需要三个 profile。
|
||||
2. **工作应在崩溃或重启后继续存在。** 长期运行、周期性或重要的任务。
|
||||
3. **用户可能需要介入。** 任意步骤需要人工参与。
|
||||
4. **多个子任务可以并行运行。** 扇出以提高速度。
|
||||
5. **预期需要审查/迭代。** 审查者 profile 循环处理起草者的输出。
|
||||
6. **审计追踪很重要。** 看板行永久保存在 SQLite 中。
|
||||
|
||||
如果*以上均不适用*——这是一个小型一次性推理任务——改用 `delegate_task` 或直接回答用户。
|
||||
|
||||
## 反诱惑规则
|
||||
|
||||
你的职责描述是"路由,不执行"。执行该规则的约束:
|
||||
|
||||
- **不要自己执行工作。** 你受限的工具集通常甚至不包含用于实现的终端/文件/代码/网络工具。如果你发现自己在"快速修复这个"——停下来,为合适的专家创建任务。
|
||||
- **对于任何具体任务,创建 Kanban 任务并分配它。** 每一次都如此。
|
||||
- **在创建卡片之前拆分多通道请求。** 用户的一个 prompt 可能包含多个独立的工作流。先提取这些通道,然后每个通道创建一张卡片,而不是将不相关的工作打包到单个实现者卡片中。
|
||||
- **并行运行独立通道。** 如果两张卡片不需要彼此的输出,不要链接它们,让调度器可以扇出处理。只链接真正的数据依赖。
|
||||
- **永远不要将依赖工作创建为独立的 ready 卡片。** 如果一张卡片必须等待另一张卡片,在原始 `kanban_create` 调用中传入 `parents=[...]`。不要先创建再链接,也不要依赖卡片正文中的"等待 T1"之类的描述。
|
||||
- **如果没有专家适合现有 profile,询问用户应创建哪个 profile 或使用哪个现有 profile。** 不要凭空发明 profile 名称;调度器会静默丢弃未知 assignee。
|
||||
- **分解、路由、汇总——这就是全部工作。**
|
||||
|
||||
## 任务分解手册
|
||||
|
||||
### 第 1 步——理解目标
|
||||
|
||||
如果目标不明确,提出澄清性问题。询问的成本很低;派出错误的团队代价高昂。
|
||||
|
||||
### 第 2 步——草拟任务图
|
||||
|
||||
在创建任何内容之前,在回复用户时大声(在响应中)草拟任务图。将每个具体工作流视为候选卡片:
|
||||
|
||||
1. 从请求中提取通道。
|
||||
2. 将每个通道映射到第 0 步中发现的某个 profile。如果某个通道不适合任何现有 profile,询问用户使用或创建哪个。
|
||||
3. 决定每个通道是独立的还是受另一个通道门控的。
|
||||
4. 将独立通道创建为无父链接的并行卡片。
|
||||
5. 将综合/审查/集成卡片创建时带上其所依赖通道的父链接。使用未完成父任务创建的子任务从 `todo` 开始;调度器仅在每个父任务完成后才将其提升为 `ready`。
|
||||
|
||||
应该扇出的 prompt 示例(使用占位符 profile 名称——替换为用户配置中实际存在的名称):
|
||||
|
||||
- "构建一个应用" → 一张卡片给面向设计的 profile 负责产品/UI 方向,一两张卡片给工程 profile 负责实现,如果用户有审查者 profile,再加一张后续的集成/审查卡片。
|
||||
- "修复阻塞项并检查模型变体" → 一张实现卡片用于修复阻塞项,加一张发现/研究卡片用于配置/源码验证。最终的审查者卡片可以依赖两者。
|
||||
- "研究文档并实现" → 文档研究卡片可以与代码库发现卡片并行运行;只有当实现真正需要这些发现时才等待。
|
||||
- "分析这张截图并找到相关代码" → 一张卡片给具备视觉能力的 profile 进行视觉分析,同时另一张卡片搜索代码库。
|
||||
|
||||
"也"、"最后"或"和"等词语不自动意味着依赖关系。它们通常意味着"确保在汇报前涵盖这一点"。只有当一张卡片在另一张卡片的输出存在之前无法开始时,才链接任务。
|
||||
|
||||
在创建卡片之前将任务图展示给用户。让他们纠正——包括哪个实际 profile 名称应该负责每个通道。
|
||||
|
||||
### 第 3 步——创建任务并链接
|
||||
|
||||
使用第 0 步中的 profile 名称。以下示例使用占位符 `<profile-A>`、`<profile-B>`、`<profile-C>`——替换为用户实际拥有的名称。
|
||||
|
||||
```python
|
||||
t1 = kanban_create(
|
||||
title="research: Postgres cost vs current",
|
||||
assignee="<profile-A>", # whichever profile handles research on this setup
|
||||
body="Compare estimated infrastructure costs, migration costs, and ongoing ops costs over a 3-year window. Sources: AWS/GCP pricing, team time estimates, current Postgres bills from peers.",
|
||||
tenant=os.environ.get("HERMES_TENANT"),
|
||||
)["task_id"]
|
||||
|
||||
t2 = kanban_create(
|
||||
title="research: Postgres performance vs current",
|
||||
assignee="<profile-A>", # same profile, run in parallel
|
||||
body="Compare query latency, throughput, and scaling characteristics at our expected data volume (~500GB, 10k QPS peak). Sources: benchmark papers, public case studies, pgbench results if easy.",
|
||||
)["task_id"]
|
||||
|
||||
t3 = kanban_create(
|
||||
title="synthesize migration recommendation",
|
||||
assignee="<profile-B>", # whichever profile does synthesis/analysis
|
||||
body="Read the findings from T1 (cost) and T2 (performance). Produce a 1-page recommendation with explicit trade-offs and a go/no-go call.",
|
||||
parents=[t1, t2],
|
||||
)["task_id"]
|
||||
|
||||
t4 = kanban_create(
|
||||
title="draft decision memo",
|
||||
assignee="<profile-C>", # whichever profile drafts user-facing prose
|
||||
body="Turn the analyst's recommendation into a 2-page memo for the CTO. Match the tone of previous decision memos in the team's knowledge base.",
|
||||
parents=[t3],
|
||||
)["task_id"]
|
||||
```
|
||||
|
||||
`parents=[...]` 门控提升——子任务保持在 `todo` 状态,直到每个父任务达到 `done`,然后自动提升为 `ready`。无需手动协调;调度器和依赖引擎会处理这一切。
|
||||
|
||||
如果任务图有依赖关系,先创建父卡片,捕获其返回的 id,并在子卡片的 `kanban_create` 调用中将这些 id 包含在 `parents` 列表中。避免并行创建所有卡片后再链接;这会产生一个时间窗口,调度器可能在子任务的输入存在之前就认领它。
|
||||
|
||||
### 第 4 步——完成你自己的任务
|
||||
|
||||
如果你是作为任务被派生的(例如,规划者 profile 被分配了 `T0: "调查 Postgres 迁移"`),用你创建内容的摘要标记它为完成:
|
||||
|
||||
```python
|
||||
kanban_complete(
|
||||
summary="decomposed into T1-T4: 2 research lanes in parallel, 1 synthesis on their outputs, 1 prose draft on the recommendation",
|
||||
metadata={
|
||||
"task_graph": {
|
||||
"T1": {"assignee": "<profile-A>", "parents": []},
|
||||
"T2": {"assignee": "<profile-A>", "parents": []},
|
||||
"T3": {"assignee": "<profile-B>", "parents": ["T1", "T2"]},
|
||||
"T4": {"assignee": "<profile-C>", "parents": ["T3"]},
|
||||
},
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
### 第 5 步——向用户汇报
|
||||
|
||||
用简明的文字告诉他们你创建了什么,并说明你使用的实际 profile 名称:
|
||||
|
||||
> 我已排队 4 个任务:
|
||||
> - **T1**(`<profile-A>`):成本对比
|
||||
> - **T2**(`<profile-A>`):性能对比,与 T1 并行
|
||||
> - **T3**(`<profile-B>`):综合 T1 + T2 生成建议
|
||||
> - **T4**(`<profile-C>`):将 T3 转化为 CTO 备忘录
|
||||
>
|
||||
> 调度器现在将认领 T1 和 T2。T3 在两者完成后启动。T4 完成时你会收到 gateway 通知。使用仪表板或 `hermes kanban tail <id>` 跟踪进度。
|
||||
|
||||
## 常见模式
|
||||
|
||||
**扇出 + 扇入(研究 → 综合):** N 张无父链接的研究类卡片,一张以所有研究卡片为父的综合卡片。
|
||||
|
||||
**并行实现 + 验证:** 一张实现者卡片进行变更,同时一张探索/研究卡片验证配置、文档或源码映射。审查者卡片可以依赖两者。不要因为用户在一句话中同时提到了两者,就让实现者承担不相关的验证工作。
|
||||
|
||||
**带门控的流水线:** `planner → implementer → reviewer`。每个阶段的 `parents=[previous_task]`。审查者阻塞或完成;如果审查者阻塞,操作员带着反馈解除阻塞并重新派发。
|
||||
|
||||
**同 profile 队列:** N 个任务,全部分配给同一个 profile,彼此之间无依赖。调度器串行处理——该 profile 按优先级顺序处理它们,在自己的记忆中积累经验。
|
||||
|
||||
**人工参与循环:** 任何任务都可以调用 `kanban_block()` 等待输入。调度器在 `/unblock` 后重新派发。评论线程携带完整上下文。
|
||||
|
||||
## 常见陷阱
|
||||
|
||||
**发明不存在的 profile 名称。** 调度器会静默地忽略无法识别的 assignee——卡片会永远停留在 `ready` 状态。始终从第 0 步发现的 profile 中分配;如果不确定,询问用户。
|
||||
|
||||
**将独立通道打包到一张卡片中。** 如果用户要求两个独立的结果,创建两张卡片。示例:"修复阻塞项并检查模型变体"不是一个修复任务;为修复创建一张修复/工程卡片,为变体检查创建一张探索/研究卡片,然后可选地将审查门控在两者之上。
|
||||
|
||||
**因措辞而过度链接。** "最后检查 X"如果 X 是静态配置、文档或源码发现,仍然可以与实现并行。只有当检查依赖于实现结果时,才将其链接在实现之后。
|
||||
|
||||
**忘记依赖链接。** 如果任务图说 `research -> implement -> review`,不要将所有任务创建为独立的 ready 卡片。使用父链接,确保 implement/review 在其输入存在之前无法运行。
|
||||
|
||||
**重新分配 vs. 新任务。** 如果审查者以"需要修改"阻塞,创建一个从审查者任务链接的**新**任务——不要用严厉的眼神重新运行同一个任务。新任务分配给原始实现者 profile。
|
||||
|
||||
**链接的参数顺序。** `kanban_link(parent_id=..., child_id=...)` — 父任务在前。混淆顺序会将错误的任务降级为 `todo`。
|
||||
|
||||
**如果形状取决于中间发现,不要预先创建整个任务图。** 如果 T3 的结构取决于 T1 和 T2 的发现,让 T3 作为一个"综合发现"任务存在,其第一步是读取父任务的交接内容并规划其余部分。编排器可以派生编排器。
|
||||
|
||||
**Tenant 继承。** 如果你的环境中设置了 `HERMES_TENANT`,在每次 `kanban_create` 调用中传入 `tenant=os.environ.get("HERMES_TENANT")`,以确保子任务保持在同一命名空间中。
|
||||
|
||||
## 恢复卡住的 worker
|
||||
|
||||
当一个 worker profile 持续崩溃、产生幻觉或被自身错误阻塞时(通常是:错误的模型、缺少 skill、凭据损坏),kanban 仪表板会在任务上标记 ⚠ 徽章,并在抽屉中打开**恢复**部分。三个主要操作:
|
||||
|
||||
1. **Reclaim**(或 `hermes kanban reclaim <task_id>`)——立即中止正在运行的 worker 并将任务重置为 `ready`。现有认领 TTL 约为 15 分钟;这是最快的解决路径。
|
||||
2. **Reassign**(或 `hermes kanban reassign <task_id> <new-profile> --reclaim`)——将任务切换到不同的 profile(此配置上存在的 profile)并让调度器用新 worker 认领它。
|
||||
3. **更改 profile 模型**——仪表板会打印 `hermes -p <profile> model` 的复制粘贴提示,因为 profile 配置存储在磁盘上;在终端中编辑它,然后 Reclaim 以使用新模型重试。
|
||||
|
||||
当 worker 的 `kanban_complete(created_cards=[...])` 声明包含不存在或非该 worker profile 创建的卡片 id 时(门控会阻止完成),或者自由格式摘要引用了无法解析的 `t_<hex>` id 时(建议性文本扫描,非阻塞),会出现幻觉警告。两者都会产生审计事件,即使在恢复操作后也会持久保存——追踪记录保留用于调试。
|
||||
|
|
@ -1,202 +0,0 @@
|
|||
---
|
||||
title: "Kanban Worker — Hermes Kanban worker 的陷阱、示例与边界情况"
|
||||
sidebar_label: "Kanban Worker"
|
||||
description: "Hermes Kanban worker 的陷阱、示例与边界情况"
|
||||
---
|
||||
|
||||
{/* This page is auto-generated from the skill's SKILL.md by website/scripts/generate-skill-docs.py. Edit the source SKILL.md, not this page. */}
|
||||
|
||||
# Kanban Worker
|
||||
|
||||
Hermes Kanban worker 的陷阱、示例与边界情况。生命周期本身会自动注入到每个 worker 的系统 prompt(提示词)中,作为 `KANBAN_GUIDANCE`(来自 `agent/prompt_builder.py`);当你需要深入了解特定场景时,加载此 skill 即可。
|
||||
|
||||
## Skill 元数据
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| 来源 | 内置(默认安装) |
|
||||
| 路径 | `skills/devops/kanban-worker` |
|
||||
| 版本 | `2.0.0` |
|
||||
| 平台 | linux, macos, windows |
|
||||
| 标签 | `kanban`, `multi-agent`, `collaboration`, `workflow`, `pitfalls` |
|
||||
| 相关 skill | [`kanban-orchestrator`](/user-guide/skills/bundled/devops/devops-kanban-orchestrator) |
|
||||
|
||||
## 参考:完整 SKILL.md
|
||||
|
||||
:::info
|
||||
以下是 Hermes 在触发此 skill 时加载的完整 skill 定义。这是 skill 激活时 agent 所看到的指令内容。
|
||||
:::
|
||||
|
||||
# Kanban Worker — 陷阱与示例
|
||||
|
||||
> 你看到此 skill,是因为 Hermes Kanban 调度器以 `--skills kanban-worker` 参数将你作为 worker 派生——它会为每个被派发的 worker 自动加载。**生命周期**(6 个步骤:orient → work → heartbeat → block/complete)也存在于自动注入到你系统 prompt 中的 `KANBAN_GUIDANCE` 块里。此 skill 是更深层的细节:良好的交接形式、重试诊断、边界情况。
|
||||
|
||||
## 工作区处理
|
||||
|
||||
你的工作区类型决定了你在 `$HERMES_KANBAN_WORKSPACE` 内部的行为方式:
|
||||
|
||||
| 类型 | 含义 | 操作方式 |
|
||||
|---|---|---|
|
||||
| `scratch` | 全新的临时目录,仅供你使用 | 自由读写;任务归档后会被 GC 回收。 |
|
||||
| `dir:<path>` | 共享的持久化目录 | 其他运行实例会读取你写入的内容。将其视为长期状态。路径保证为绝对路径(内核拒绝相对路径)。 |
|
||||
| `worktree` | 位于已解析路径的 Git worktree | 若 `.git` 不存在,先从主仓库执行 `git worktree add <path> <branch>`,然后 cd 进去正常工作。在此提交工作。 |
|
||||
|
||||
## 租户隔离
|
||||
|
||||
若 `$HERMES_TENANT` 已设置,则该任务属于某个租户命名空间。在读写持久化内存时,请为内存条目添加租户前缀,以防上下文跨租户泄漏:
|
||||
|
||||
- 正确:`business-a: Acme is our biggest customer`
|
||||
- 错误(会泄漏):`Acme is our biggest customer`
|
||||
|
||||
## 良好的 summary + metadata 形式
|
||||
|
||||
`kanban_complete(summary=..., metadata=...)` 的交接方式是下游 worker 读取你工作成果的途径。以下是有效的模式:
|
||||
|
||||
**编码任务:**
|
||||
```python
|
||||
kanban_complete(
|
||||
summary="shipped rate limiter — token bucket, keys on user_id with IP fallback, 14 tests pass",
|
||||
metadata={
|
||||
"changed_files": ["rate_limiter.py", "tests/test_rate_limiter.py"],
|
||||
"tests_run": 14,
|
||||
"tests_passed": 14,
|
||||
"decisions": ["user_id primary, IP fallback for unauthenticated requests"],
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
**需要人工审查的编码任务(review-required):**
|
||||
|
||||
对于大多数涉及代码变更的任务,在人工审查者过目之前,工作并未真正*完成*。应使用 block 而非 complete,并在 `reason` 前加 `review-required: ` 前缀,以便仪表板将该行标记为待审查。先将结构化元数据(变更文件、测试计数、diff/PR url)写入 comment,因为 `kanban_block` 只携带人类可读的原因——comment 是持久化注释的渠道。审查者可执行 `hermes kanban unblock <id>` 批准(这会携带 comment 线程重新派生你以处理后续事项),或通过另一条 comment 要求修改。
|
||||
|
||||
```python
|
||||
import json
|
||||
|
||||
kanban_comment(
|
||||
body="review-required handoff:\n" + json.dumps({
|
||||
"changed_files": ["rate_limiter.py", "tests/test_rate_limiter.py"],
|
||||
"tests_run": 14,
|
||||
"tests_passed": 14,
|
||||
"diff_path": "/path/to/worktree", # or PR url if pushed
|
||||
"decisions": ["user_id primary, IP fallback for unauthenticated requests"],
|
||||
}, indent=2),
|
||||
)
|
||||
kanban_block(
|
||||
reason="review-required: rate limiter shipped, 14/14 tests pass — needs eyes on the user_id/IP fallback choice before merging",
|
||||
)
|
||||
```
|
||||
|
||||
仅在任务真正终结时使用 `kanban_complete`——例如单行拼写修复、无功能影响的文档变更,或产出物本身即为成果的研究任务。
|
||||
|
||||
**研究任务:**
|
||||
```python
|
||||
kanban_complete(
|
||||
summary="3 competing libraries reviewed; vLLM wins on throughput, SGLang on latency, Tensorrt-LLM on memory efficiency",
|
||||
metadata={
|
||||
"sources_read": 12,
|
||||
"recommendation": "vLLM",
|
||||
"benchmarks": {"vllm": 1.0, "sglang": 0.87, "trtllm": 0.72},
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
**审查任务:**
|
||||
```python
|
||||
kanban_complete(
|
||||
summary="reviewed PR #123; 2 blocking issues found (SQL injection in /search, missing CSRF on /settings)",
|
||||
metadata={
|
||||
"pr_number": 123,
|
||||
"findings": [
|
||||
{"severity": "critical", "file": "api/search.py", "line": 42, "issue": "raw SQL concat"},
|
||||
{"severity": "high", "file": "api/settings.py", "issue": "missing CSRF middleware"},
|
||||
],
|
||||
"approved": False,
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
请将 `metadata` 的结构设计为下游解析器(审查者、聚合器、调度器)无需重新阅读你的文字描述即可直接使用。
|
||||
|
||||
## 认领你实际创建的卡片
|
||||
|
||||
若你的运行产生了新的 kanban 任务(通过 `kanban_create`),请在 `kanban_complete` 的 `created_cards` 中传入这些 id。内核会验证每个 id 是否存在且由你的 profile 创建;任何幻构的 id 都会导致完成操作被阻断,并附带错误列表说明问题所在,且被拒绝的尝试会永久记录在任务的事件日志中。**只列出你从成功的 `kanban_create` 返回值中捕获的 id——绝不凭空捏造 id,绝不粘贴来自早期运行的 id,绝不认领其他 worker 创建的卡片。**
|
||||
|
||||
```python
|
||||
# 正确 — 捕获返回值,然后认领。
|
||||
c1 = kanban_create(title="remediate SQL injection", assignee="security-worker")
|
||||
c2 = kanban_create(title="fix CSRF middleware", assignee="web-worker")
|
||||
|
||||
kanban_complete(
|
||||
summary="Review done; spawned remediations for both findings.",
|
||||
metadata={"pr_number": 123, "approved": False},
|
||||
created_cards=[c1["task_id"], c2["task_id"]],
|
||||
)
|
||||
```
|
||||
|
||||
```python
|
||||
# 错误 — 认领没有捕获返回值的 id。
|
||||
kanban_complete(
|
||||
summary="Created remediation cards t_a1b2c3d4, t_deadbeef", # 幻构
|
||||
created_cards=["t_a1b2c3d4", "t_deadbeef"], # → 门控拒绝
|
||||
)
|
||||
```
|
||||
|
||||
若 `kanban_create` 调用失败(异常、tool_error),则卡片未被创建——不要为其包含幻构 id。重试创建,或省略该 id 并在 summary 中说明失败情况。散文扫描阶段也会捕获你自由格式 summary 中无法解析的 `t_<hex>` 引用;这些不会阻断完成操作,但会在仪表板的任务上显示为建议性警告。
|
||||
|
||||
## 能快速得到回应的 block 原因
|
||||
|
||||
差:`"stuck"` — 人类没有任何上下文。
|
||||
|
||||
好:一句话说明你需要的具体决策。将更长的上下文作为 comment 留下。
|
||||
|
||||
```python
|
||||
kanban_comment(
|
||||
task_id=os.environ["HERMES_KANBAN_TASK"],
|
||||
body="Full context: I have user IPs from Cloudflare headers but some users are behind NATs with thousands of peers. Keying on IP alone causes false positives.",
|
||||
)
|
||||
kanban_block(reason="Rate limit key choice: IP (simple, NAT-unsafe) or user_id (requires auth, skips anonymous endpoints)?")
|
||||
```
|
||||
|
||||
block 消息是仪表板/gateway 通知器中显示的内容。comment 是人类打开任务时阅读的深层上下文。
|
||||
|
||||
## 值得发送的 heartbeat
|
||||
|
||||
好的 heartbeat 应说明进度:`"epoch 12/50, loss 0.31"`、`"scanned 1.2M/2.4M rows"`、`"uploaded 47/120 videos"`。
|
||||
|
||||
差的 heartbeat:`"still working"`、空 notes、亚秒级间隔。最多每隔几分钟发送一次;对于约 2 分钟以内的任务可完全跳过。
|
||||
|
||||
## 重试场景
|
||||
|
||||
若你打开任务后 `kanban_show` 返回的 `runs: [...]` 中包含一个或多个已关闭的运行,说明你是一次重试。先前运行的 `outcome` / `summary` / `error` 会告诉你哪里出了问题。不要重复那条路径。典型的重试诊断:
|
||||
|
||||
- `outcome: "timed_out"` — 上次尝试达到了 `max_runtime_seconds`。你可能需要将工作分块或缩短。
|
||||
- `outcome: "crashed"` — OOM 或段错误。减少内存占用。
|
||||
- `outcome: "spawn_failed"` + `error: "..."` — 通常是 profile 配置问题(缺少凭证、错误的 PATH)。通过 `kanban_block` 询问人类,而不是盲目重试。
|
||||
- `outcome: "reclaimed"` + `summary: "task archived..."` — 操作员在上次运行期间将任务归档;你可能根本不应该在运行,请仔细检查状态。
|
||||
- `outcome: "blocked"` — 上次尝试被阻断;解除阻断的 comment 现在应该已在线程中。
|
||||
|
||||
## 禁止事项
|
||||
|
||||
- 不要用 `delegate_task` 替代 `kanban_create`。`delegate_task` 用于你的运行内部的短期推理子任务;`kanban_create` 用于跨 agent 的、超出单次 API 循环的交接。
|
||||
- 不要修改 `$HERMES_KANBAN_WORKSPACE` 之外的文件,除非任务正文明确要求。
|
||||
- 不要创建分配给自己的后续任务——分配给合适的专家。
|
||||
- 不要完成一个你实际上没有完成的任务。改为 block 它。
|
||||
|
||||
## 陷阱
|
||||
|
||||
**任务状态可能在调度与启动之间发生变化。** 从调度器认领任务到你的进程实际启动之间,任务可能已被 block、重新分配或归档。始终先执行 `kanban_show`。若其报告 `blocked` 或 `archived`,请停止——你不应该在运行。
|
||||
|
||||
**工作区可能存在过期产物。** 尤其是 `dir:` 和 `worktree` 工作区可能包含来自先前运行的文件。阅读 comment 线程——它通常会解释你为何再次运行以及工作区处于何种状态。
|
||||
|
||||
**当指导已可用时,不要依赖 CLI。** `kanban_*` 工具可在所有终端后端(Docker、Modal、SSH)上工作。从你的终端工具执行 `hermes kanban <verb>` 在容器化后端中会失败,因为 CLI 未安装在那里。如有疑问,使用工具。
|
||||
|
||||
## CLI 回退(用于脚本)
|
||||
|
||||
每个工具都有对应的 CLI 等价命令,供人工操作员和脚本使用:
|
||||
- `kanban_show` ↔ `hermes kanban show <id> --json`
|
||||
- `kanban_complete` ↔ `hermes kanban complete <id> --summary "..." --metadata '{...}'`
|
||||
- `kanban_block` ↔ `hermes kanban block <id> "reason"`
|
||||
- `kanban_create` ↔ `hermes kanban create "title" --assignee <profile> [--parent <id>]`
|
||||
- 等等。
|
||||
|
||||
在 agent 内部使用工具;CLI 供终端前的人类使用。
|
||||
|
|
@ -21,7 +21,7 @@ description: "规划、搭建并监控由 Hermes Kanban 支撑的多智能体视
|
|||
| 许可证 | MIT |
|
||||
| 平台 | linux, macos, windows |
|
||||
| 标签 | `video`, `kanban`, `multi-agent`, `orchestration`, `production-pipeline` |
|
||||
| 相关技能 | [`kanban-orchestrator`](/user-guide/skills/bundled/devops/devops-kanban-orchestrator)、[`kanban-worker`](/user-guide/skills/bundled/devops/devops-kanban-worker)、[`ascii-video`](/user-guide/skills/bundled/creative/creative-ascii-video)、[`manim-video`](/user-guide/skills/bundled/creative/creative-manim-video)、[`p5js`](/user-guide/skills/bundled/creative/creative-p5js)、[`comfyui`](/user-guide/skills/bundled/creative/creative-comfyui)、[`touchdesigner-mcp`](/user-guide/skills/bundled/creative/creative-touchdesigner-mcp)、[`blender-mcp`](/user-guide/skills/optional/creative/creative-blender-mcp)、[`pixel-art`](/user-guide/skills/bundled/creative/creative-pixel-art)、[`ascii-art`](/user-guide/skills/bundled/creative/creative-ascii-art)、[`songwriting-and-ai-music`](/user-guide/skills/bundled/creative/creative-songwriting-and-ai-music)、[`heartmula`](/user-guide/skills/bundled/media/media-heartmula)、[`songsee`](/user-guide/skills/bundled/media/media-songsee)、[`spotify`](/user-guide/skills/bundled/media/media-spotify)、[`youtube-content`](/user-guide/skills/bundled/media/media-youtube-content)、[`claude-design`](/user-guide/skills/bundled/creative/creative-claude-design)、[`excalidraw`](/user-guide/skills/bundled/creative/creative-excalidraw)、[`architecture-diagram`](/user-guide/skills/bundled/creative/creative-architecture-diagram)、[`concept-diagrams`](/user-guide/skills/optional/creative/creative-concept-diagrams)、[`baoyu-comic`](/user-guide/skills/bundled/creative/creative-baoyu-comic)、[`baoyu-infographic`](/user-guide/skills/bundled/creative/creative-baoyu-infographic)、[`humanizer`](/user-guide/skills/bundled/creative/creative-humanizer)、[`gif-search`](/user-guide/skills/bundled/media/media-gif-search)、[`meme-generation`](/user-guide/skills/optional/creative/creative-meme-generation) |
|
||||
| 相关技能 | [`ascii-video`](/user-guide/skills/bundled/creative/creative-ascii-video)、[`manim-video`](/user-guide/skills/bundled/creative/creative-manim-video)、[`p5js`](/user-guide/skills/bundled/creative/creative-p5js)、[`comfyui`](/user-guide/skills/bundled/creative/creative-comfyui)、[`touchdesigner-mcp`](/user-guide/skills/bundled/creative/creative-touchdesigner-mcp)、[`blender-mcp`](/user-guide/skills/optional/creative/creative-blender-mcp)、[`pixel-art`](/user-guide/skills/bundled/creative/creative-pixel-art)、[`ascii-art`](/user-guide/skills/bundled/creative/creative-ascii-art)、[`songwriting-and-ai-music`](/user-guide/skills/bundled/creative/creative-songwriting-and-ai-music)、[`heartmula`](/user-guide/skills/bundled/media/media-heartmula)、[`songsee`](/user-guide/skills/bundled/media/media-songsee)、[`spotify`](/user-guide/skills/bundled/media/media-spotify)、[`youtube-content`](/user-guide/skills/bundled/media/media-youtube-content)、[`claude-design`](/user-guide/skills/bundled/creative/creative-claude-design)、[`excalidraw`](/user-guide/skills/bundled/creative/creative-excalidraw)、[`architecture-diagram`](/user-guide/skills/bundled/creative/creative-architecture-diagram)、[`concept-diagrams`](/user-guide/skills/optional/creative/creative-concept-diagrams)、[`baoyu-comic`](/user-guide/skills/bundled/creative/creative-baoyu-comic)、[`baoyu-infographic`](/user-guide/skills/bundled/creative/creative-baoyu-infographic)、[`humanizer`](/user-guide/skills/bundled/creative/creative-humanizer)、[`gif-search`](/user-guide/skills/bundled/media/media-gif-search)、[`meme-generation`](/user-guide/skills/optional/creative/creative-meme-generation) |
|
||||
|
||||
## 参考:完整 SKILL.md
|
||||
|
||||
|
|
@ -146,7 +146,7 @@ director profile 从此接管,通过 kanban 工具集将工作分解并路由
|
|||
|
||||
5. **尊重现有技能。** 当某个场景适合现有技能时,相关渲染器应通过任务上的 `--skill <name>` 或 profile 中的 `always_load` 加载该技能。不要重新推导技能已提供的内容。
|
||||
|
||||
6. **director 绝不执行。** 即使拥有完整的 `kanban + terminal + file` 工具集,director 的 `SOUL.md` 规则也禁止其自行执行工作。它只负责分解和路由——每个具体任务都变成对专业 profile 的 `hermes kanban create` 调用。`kanban-orchestrator` 技能对此有进一步说明。
|
||||
6. **director 绝不执行。** 即使拥有完整的 `kanban + terminal + file` 工具集,director 的 `SOUL.md` 规则也禁止其自行执行工作。它只负责分解和路由——每个具体任务都变成对专业 profile 的 `hermes kanban create` 调用。自动注入的 kanban 编排指引对此有进一步说明。
|
||||
|
||||
7. **不要过度分解。** 一个 30 秒的产品视频**不需要** 20 个任务。目标是最小任务图,同时仍能良好并行化并暴露正确的人工审核节点。
|
||||
|
||||
|
|
|
|||
|
|
@ -188,16 +188,6 @@ const sidebars: SidebarsConfig = {
|
|||
'user-guide/skills/bundled/data-science/data-science-jupyter-live-kernel',
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'category',
|
||||
label: 'devops',
|
||||
key: 'skills-bundled-devops',
|
||||
collapsed: true,
|
||||
items: [
|
||||
'user-guide/skills/bundled/devops/devops-kanban-orchestrator',
|
||||
'user-guide/skills/bundled/devops/devops-kanban-worker',
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'category',
|
||||
label: 'dogfood',
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue