# NousResearch/hermes-agent · Kanban skill validation, availability, and packaging fixes

## Open now

- 6 issues, 4 PRs open on GitHub.
- #34273: feat(swarm): support custom verifier/synthesizer body + skills
- #35202: bug(kanban): failure_limit circuit breaker bypassed when worker crashes before first heartbeat
- #44072: kanban create: --skill flag should validate against installed skill registry at dispatch time
- #44675: Kanban: worker crashes silently when --skill references non-existent skill in worker profile
- #54088: Kanban worker should not crash when an optional preloaded skill cannot be resolved
- #59764: Two dispatch-contract mismatches: (a) goal_mode tasks reject \`kanban_block\` with \`kind\` unset / \`capability\` / \`transient\`, but the tool schema documents all four kinds and says "Omit only if none apply"; (b) card \`skills\` that a worker profile lacks are skipped with only a log-file warning ("Unknown skill(s)"), silently losing the context the card was authored with
- PR #35212: fix(kanban): count stale-claim reclaims toward circuit breaker
- PR #44686: fix(kanban): surface worker crash cause from log in detect_crashed_workers
- PR #54100: fix(kanban): warn and continue when optional skill is unavailable (#54088)
- PR #112608: feat(swarm): per-swarm verifier + synthesizer body and skills overrides (#34273)

These items address bugs and features around skill resolution in the kanban system: validating skill names at creation and dispatch time, excluding archived skills from availability checks, shipping bundled skills in the package, and related swarm-level skill override support.

## TL;DR

- 35 member(s): 6 open issue(s), 5 closed issue(s), 4 open PR(s), 5 merged PR(s), 15 PR(s) closed without merging.
- A fix was merged to main in PR #23273. Verify it addresses your case.
- **Recommended:** read PR #23273 (and any competing PRs below) before filing. Confirm the merged change covers your symptom.

## Maintainer resolution verdict

- Internal-tracker verdict over this cluster's issue members: `partially_resolved` · deciding PR(s): `pr-23273,pr-55585`. This is distinct from the fix status above. Weigh it yourself.

## How this cluster was grouped

- Link evidence (`STRUCTURAL_PARTIAL`): some members are connected by structural evidence (shared changed files, or a closing/superseded reference); the rest are joined by an automated relevance judgement over their text.
- Members outside that structural core may belong to a different underlying problem.
- Pairwise link density `0.05`: the share of member pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36. `1.00` means every pair is linked.

## Open related issues

- #34273: feat(swarm): support custom verifier/synthesizer body + skills
- #35202: bug(kanban): failure_limit circuit breaker bypassed when worker crashes before first heartbeat
- #44072: kanban create: --skill flag should validate against installed skill registry at dispatch time
- #44675: Kanban: worker crashes silently when --skill references non-existent skill in worker profile
- #54088: Kanban worker should not crash when an optional preloaded skill cannot be resolved
- #59764: Two dispatch-contract mismatches: (a) goal_mode tasks reject \`kanban_block\` with \`kind\` unset / \`capability\` / \`transient\`, but the tool schema documents all four kinds and says "Omit only if none apply"; (b) card \`skills\` that a worker profile lacks are skipped with only a log-file warning ("Unknown skill(s)"), silently losing the context the card was authored with

## Closed / duplicate issues

- #22921: \[Bug\]: kanban_create accepts toolset names in skills field, causing immediate worker crash
- #23725: Full kanban suite not installed with Hermes 0.130
- #27136: fix(cli): unknown skills should warn instead of crash to prevent Kanban worker auto-blocking
- #29415: Bug: kanban_swarm.py references missing skill avoid-ai-writing, synthesizer crashes in retry loop (v0.14.0)
- #41311: \[Bug\]: Kanban dispatcher injects \`--skills kanban-worker\` for an archived skill, crashing workers (availability check rglob-matches what the loader rejects)

## Prior & competing PRs

- **#23273: fix(kanban): reject toolset names in task skills field (salvage #22933)**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`; implication `superseded`
  - files: `hermes_cli/kanban_db.py`, `tests/hermes_cli/test_kanban_core_functionality.py`, `tests/plugins/test_kanban_dashboard_plugin.py`, `tools/kanban_tools.py`
- **#28368: fix(kanban): auto-install bundled skills (e.g. kanban-worker) on kanban init (#23884)**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`; implication `superseded`
  - files: `hermes_cli/kanban.py`
- **#28421: fix(packaging): ship bundled skills in wheel (#23738)**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`; implication `superseded`
  - files: `hermes_constants.py`, `setup.py`, `tools/skills_sync.py`
  - fix hunks:
  - `hermes_constants.py`

    ```diff
    @@ -5,6 +5,7 @@
     """
     
     import os
    +import sysconfig
     from contextvars import ContextVar, Token
     from pathlib import Path
     
    ```

    ```diff
    @@ -139,6 +140,23 @@ def get_default_hermes_root() -> Path:
         return env_path
     
     
    +def _get_packaged_data_dir(name: str) -> Path | None:
    +    """Return an installed data-files directory if one exists.
    +
    +    Used to discover bundled skills/optional-skills when Hermes is installed
    +    from a wheel that emitted them via setuptools data_files.
    +    """
    +    candidates = []
    +    for scheme in ("data", "purelib", "platlib"):
    +        raw = sysconfig.get_path(scheme)
    +        if raw:
    +            candidates.append(Path(raw) / name)
    +    for candidate in candidates:
    +        if candidate.exists():
    +            return candidate
    +    return None
    +
    +
     def get_optional_skills_dir(default: Path | None = None) -> Path:
         """Return the optional-skills directory, honoring package-manager wrappers.
     
    ```

    ```diff
    @@ -148,11 +166,34 @@ def get_optional_skills_dir(default: Path | None = None) -> Path:
         override = os.getenv("HERMES_OPTIONAL_SKILLS", "").strip()
         if override:
             return Path(override)
    +    packaged = _get_packaged_data_dir("optional-skills")
    +    if packaged is not None:
    +        return packaged
         if default is not None:
             return default
         return get_hermes_home() / "optional-skills"
     
     
    +def get_bundled_skills_dir(default: Path | None = None) -> Path:
    +    """Return the bundled skills directory for source and packaged installs.
    +
    +    Resolution order:
    +        1. ``HERMES_BUNDLED_SKILLS`` env var (Nix wrapper / explicit override)
    +        2. Wheel-installed ``<sysconfig data>/skills`` (pip install path)
    +        3. Caller-supplied ``default`` (typically the source-checkout path)
    +        4. ``<HERMES_HOME>/skills`` last-resort
    +    """
    +    override = os.getenv("HERMES_BUNDLED_SKILLS", "").strip()
    +    if override:
    +        return Path(override)
    +    packaged = _get_packaged_data_dir("skills")
    +    if packaged is not None:
    +        return packaged
    +    if default is not None:
    +        return default
    +    return get_hermes_home() / "skills"
    +
    +
     def get_hermes_dir(new_subpath: str, old_name: str) -> Path:
         """Resolve a Hermes subdirectory with backward compatibility.
     
    ```
  - `setup.py`

    ```diff
    @@ -0,0 +1,28 @@
    +from __future__ import annotations
    +
    +from collections import defaultdict
    +from pathlib import Path
    +
    +from setuptools import setup
    +
    +
    +REPO_ROOT = Path(__file__).parent.resolve()
    +
    +
    +def _data_file_tree(root_name: str) -> list[tuple[str, list[str]]]:
    +    root = REPO_ROOT / root_name
    +    grouped: defaultdict[str, list[str]] = defaultdict(list)
    +    for path in sorted(root.rglob("*")):
    +        if not path.is_file():
    +            continue
    +        rel_path = path.relative_to(REPO_ROOT)
    +        grouped[str(rel_path.parent)].append(str(rel_path))
    +    return sorted(grouped.items())
    +
    +
    +setup(
    +    data_files=[
    +        *_data_file_tree("skills"),
    +        *_data_file_tree("optional-skills"),
    +    ]
    +)
    ```
  - `tools/skills_sync.py`

    ```diff
    @@ -26,7 +26,7 @@
     import os
     import shutil
     from pathlib import Path
    -from hermes_constants import get_hermes_home
    +from hermes_constants import get_bundled_skills_dir, get_hermes_home
     from typing import Dict, List, Tuple
     from utils import atomic_replace
     
    ```

    ```diff
    @@ -42,12 +42,10 @@ def _get_bundled_dir() -> Path:
         """Locate the bundled skills/ directory.
     
         Checks HERMES_BUNDLED_SKILLS env var first (set by Nix wrapper),
    -    then falls back to the relative path from this source file.
    +    then a wheel-installed data dir, then falls back to the relative
    +    path from this source file.
         """
    -    env_override = os.getenv("HERMES_BUNDLED_SKILLS")
    -    if env_override:
    -        return Path(env_override)
    -    return Path(__file__).parent.parent / "skills"
    +    return get_bundled_skills_dir(Path(__file__).parent.parent / "skills")
     
     
     def _read_manifest() -> Dict[str, str]:
    ```
- **#29485: fix(kanban): remove missing avoid-ai-writing skill from swarm synthesizer**. closed without merge · fix tier `suggested`
  - maintainer direction: category `implemented_elsewhere`; implication `superseded`; refs `69b74c15a324fcac460b5a143e5662036dae6387`, `#29415`
  - _suggested-tier fix detail withheld; re-request with `?tier=all` to include it._
- **#34381: feat(swarm): per-swarm verifier + synthesizer body and skills overrides (#34273)**. closed without merge · fix tier `suggested`
  - maintainer direction: category `superseded_by_pr`; implication `superseded`; refs `#46477`, `#46477`
- **#41401: fix(kanban): exclude .archive/ from worker skill availability check**. closed without merge · fix tier `suggested`
  - maintainer direction: category `superseded_by_pr`; implication `superseded`; refs `#41325`, `#41325`, `#41325`
  - _suggested-tier fix detail withheld; re-request with `?tier=all` to include it._
- **#50473: refactor(kanban): fold worker/orchestrator skills into injected guidance**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`; implication `superseded`
  - files: `agent/prompt_builder.py`, `agent/skill_utils.py`, `hermes_cli/kanban.py`, `hermes_cli/kanban_db.py`, `hermes_cli/kanban_swarm.py`, `optional-skills/creative/kanban-video-orchestrator/SKILL.md`, `optional-skills/creative/kanban-video-orchestrator/assets/setup.sh.tmpl`, `optional-skills/creative/kanban-video-orchestrator/references/examples.md`, `optional-skills/creative/kanban-video-orchestrator/references/kanban-setup.md`, `optional-skills/creative/kanban-video-orchestrator/references/role-archetypes.md`, `optional-skills/creative/kanban-video-orchestrator/references/tool-matrix.md`, `optional-skills/creative/kanban-video-orchestrator/scripts/bootstrap_pipeline.py`, `skills/devops/kanban-orchestrator/SKILL.md`, `skills/devops/kanban-worker/SKILL.md`, `tests/hermes_cli/test_kanban_core_functionality.py`, `tests/hermes_cli/test_kanban_goal_mode.py`, `tests/tools/test_kanban_tools.py`, `tools/kanban_tools.py`, `website/docs/reference/skills-catalog.md`, `website/docs/user-guide/features/kanban-worker-lanes.md` (+12 more)
  - fix hunks:
  - `agent/prompt_builder.py`

    ```diff
    @@ -238,6 +238,23 @@ def _strip_yaml_frontmatter(content: str) -> str:
         "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 "
    ```
  - `agent/skill_utils.py`

    ```diff
    @@ -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.
    ```
  - `hermes_cli/kanban.py`

    ```diff
    @@ -26,7 +26,7 @@
     
     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
     
     
     # ---------------------------------------------------------------------------
    ```

    ```diff
    @@ -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",
    ```

    ```diff
    @@ -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
    ```

    ```diff
    @@ -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)
    ```
  - `hermes_cli/kanban_db.py`

    ```diff
    @@ -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.
    ```

    ```diff
    @@ -1045,8 +1044,7 @@ class Event:
         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
    ```

    ```diff
    @@ -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:
    ```

    ```diff
    @@ -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)
    ```

    ```diff
    @@ -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
    ```

    ```diff
    @@ -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:
    ```

    ```diff
    @@ -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],
    ```

    ```diff
    @@ -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])
    ```

    ```diff
    @@ -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
    ```
  - `hermes_cli/kanban_swarm.py`

    ```diff
    @@ -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
    ```
  - `optional-skills/creative/kanban-video-orchestrator/SKILL.md`

    ```diff
    @@ -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
    ```

    ```diff
    @@ -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
    ```
  - `optional-skills/creative/kanban-video-orchestrator/assets/setup.sh.tmpl`

    ```diff
    @@ -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
    ```
  - `optional-skills/creative/kanban-video-orchestrator/references/examples.md`

    ```diff
    @@ -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
    ```
  - `optional-skills/creative/kanban-video-orchestrator/references/kanban-setup.md`

    ```diff
    @@ -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]
    ```

    ```diff
    @@ -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
     
    ```
  - `optional-skills/creative/kanban-video-orchestrator/references/role-archetypes.md`

    ```diff
    @@ -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
     
    ```

    ```diff
    @@ -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
    ```

    ```diff
    @@ -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
    ```

    ```diff
    @@ -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),
    ```

    ```diff
    @@ -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
    ```

    ```diff
    @@ -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
    ```

    ```diff
    @@ -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
     
    ```

    ```diff
    @@ -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
    ```

    ```diff
    @@ -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`
    ```

    ```diff
    @@ -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
    ```

    ```diff
    @@ -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`
     
    ```

    ```diff
    @@ -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`
     
    ```

    ```diff
    @@ -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`
     
    ```

    ```diff
    @@ -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
    ```

    ```diff
    @@ -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`
     
    ```

    ```diff
    @@ -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`
     
    ```

    ```diff
    @@ -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
    ```

    ```diff
    @@ -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
    ```

    ```diff
    @@ -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
    ```
  - `optional-skills/creative/kanban-video-orchestrator/references/tool-matrix.md`

    ```diff
    @@ -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)
     
    ```

    ```diff
    @@ -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
    ```

    ```diff
    @@ -117,7 +110,6 @@ toolsets:
       - file
     skills:
       always_load:
    -    - kanban-worker
         - humanizer            # post-process scripts to strip AI-tells
     ```
     
    ```

    ```diff
    @@ -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)
    ```

    ```diff
    @@ -151,7 +142,6 @@ toolsets:
       - file
     skills:
       always_load:
    -    - kanban-worker
         # one of:
         # - excalidraw              (sketch storyboards)
         # - architecture-diagram    (technical/system content)
    ```

    ```diff
    @@ -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)
    ```

    ```diff
    @@ -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)
    ```

    ```diff
    @@ -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
    ```

    ```diff
    @@ -219,7 +207,6 @@ toolsets:
       - file
     skills:
       always_load:
    -    - kanban-worker
         # for image-generator that drives ComfyUI locally:
         # - comfyui
     env_required:
    ```

    ```diff
    @@ -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)
    ```

    ```diff
    @@ -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
    ```

    ```diff
    @@ -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
    ```
  - `optional-skills/creative/kanban-video-orchestrator/scripts/bootstrap_pipeline.py`

    ```diff
    @@ -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 = (
    ```
  - `/dev/null`

    ```diff
    @@ -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.
    ```
  - `/dev/null`

    ```diff
    @@ -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.
    ```
  - `tests/hermes_cli/test_kanban_core_functionality.py`

    ```diff
    @@ -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:
    ```

    ```diff
    @@ -2742,10 +2739,8 @@ def fake_popen(cmd, **kwargs):
             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"), (
    ```

    ```diff
    @@ -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:
    ```

    ```diff
    @@ -3019,10 +3013,8 @@ def fake_popen(cmd, **kwargs):
         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")
    ```

    ```diff
    @@ -3034,9 +3026,9 @@ def fake_popen(cmd, **kwargs):
         )
     
     
    -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:
    ```

    ```diff
    @@ -3052,7 +3044,7 @@ def fake_popen(cmd, **kwargs):
         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)
    ```

    ```diff
    @@ -3061,12 +3053,14 @@ def fake_popen(cmd, **kwargs):
             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}"
         )
     
     
    ```
  - `tests/hermes_cli/test_kanban_goal_mode.py`

    ```diff
    @@ -132,8 +132,6 @@ def _fake_popen(cmd, **kwargs):
             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(
    ```

    ```diff
    @@ -162,7 +160,6 @@ def _fake_popen(cmd, **kwargs):
             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")
    ```
  - `tests/tools/test_kanban_tools.py`

    ```diff
    @@ -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()
    ```

    ```diff
    @@ -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"
         )
     
    ```
  - `tools/kanban_tools.py`

    ```diff
    @@ -1382,8 +1382,8 @@ def _board_schema_prop() -> dict[str, str]:
                     "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 "
    ```
  - `website/docs/reference/skills-catalog.md`

    ```diff
    @@ -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
     
    ```
  - `website/docs/user-guide/features/kanban-worker-lanes.md`

    ```diff
    @@ -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
     
    ```

    ```diff
    @@ -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
     
    ```

    ```diff
    @@ -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
     
    ```

    ```diff
    @@ -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.
    ```
  - `website/docs/user-guide/features/kanban.md`

    ```diff
    @@ -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`
     
    ```

    ```diff
    @@ -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
     
    ```

    ```diff
    @@ -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.
    ```

    ```diff
    @@ -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
     
    ```

    ```diff
    @@ -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`)
     
    ```

    ```diff
    @@ -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):
     
    ```

    ```diff
    @@ -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.
     
    ```
  - `website/docs/user-guide/skills/bundled/autonomous-ai-agents/autonomous-ai-agents-kanban-codex-lane.md`

    ```diff
    @@ -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
     
    ```
  - `/dev/null`

    ```diff
    @@ -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.
    ```
  - `/dev/null`

    ```diff
    @@ -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.
    ```
  - `website/docs/user-guide/skills/optional/creative/creative-kanban-video-orchestrator.md`

    ```diff
    @@ -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
     
    ```

    ```diff
    @@ -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
    ```
  - `website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/reference/skills-catalog.md`

    ```diff
    @@ -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
     
    ```
  - `website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/user-guide/features/kanban-worker-lanes.md`

    ```diff
    @@ -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` 块）。
     
     ## 层级结构
     
    ```

    ```diff
    @@ -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 模式。
     
     ## 日志与审计追踪
     
    ```

    ```diff
    @@ -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 通道
     
    ```

    ```diff
    @@ -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 侧。
    \ No newline at end of file
    +- [`KANBAN_GUIDANCE`](https://github.com/NousResearch/hermes-agent/blob/main/agent/prompt_builder.py) — 注入到每个 kanban worker 系统提示中的 worker + orchestrator 生命周期。
    \ No newline at end of file
    ```
  - `website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/user-guide/features/kanban.md`

    ```diff
    @@ -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`
     
    ```

    ```diff
    @@ -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 指引教导模型何时调用哪个工具以及调用顺序。
     
     ### 推荐的交接证据
     
    ```

    ```diff
    @@ -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`，在那里完成工作。
    ```

    ```diff
    @@ -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 附加到任务上。
    ```

    ```diff
    @@ -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 中；无需安装任何东西。
     
     典型的编排器轮次（两个并行研究员交接给一个写作者）：
     
    ```

    ```diff
    @@ -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`）的配置文件配对，这样编排器即使尝试也无法执行实现任务。
     
    ```
  - `/dev/null`

    ```diff
    @@ -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 时（建议性文本扫描，非阻塞），会出现幻觉警告。两者都会产生审计事件，即使在恢复操作后也会持久保存——追踪记录保留用于调试。
    \ No newline at end of file
    ```
  - `/dev/null`

    ```diff
    @@ -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 供终端前的人类使用。
    \ No newline at end of file
    ```
  - `website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/user-guide/skills/optional/creative/creative-kanban-video-orchestrator.md`

    ```diff
    @@ -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
     
    ```

    ```diff
    @@ -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 个任务。目标是最小任务图，同时仍能良好并行化并暴露正确的人工审核节点。
     
    ```
  - `website/sidebars.ts`

    ```diff
    @@ -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',
    ```
- **#55585: fix(kanban): unknown skill warns instead of crashing the worker**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`; implication `superseded`
  - files: `cli.py`, `scripts/release.py`, `tui_gateway/server.py`
  - fix hunks:
  - `cli.py`

    ```diff
    @@ -15359,7 +15359,21 @@ def main(
             )
             if missing_skills:
                 missing_display = ", ".join(missing_skills)
    -            raise ValueError(f"Unknown skill(s): {missing_display}")
    +            # If at least one skill loaded, degrade gracefully: skip the
    +            # unknown ones and continue. A typo'd skill name should not crash
    +            # the worker (which auto-blocks the Kanban task after retries).
    +            # Only when EVERY requested skill is missing do we hard-fail, so a
    +            # fully-misconfigured worker fails loudly instead of running blind.
    +            if loaded_skills:
    +                logger.warning(
    +                    "Unknown skill(s) requested, skipping: %s. "
    +                    "Continuing with: %s. "
    +                    "List available skills with `hermes skills list`.",
    +                    missing_display,
    +                    ", ".join(loaded_skills),
    +                )
    +            else:
    +                raise ValueError(f"Unknown skill(s): {missing_display}")
             if skills_prompt:
                 cli.system_prompt = "\n\n".join(
                     part for part in (cli.system_prompt, skills_prompt) if part
    ```
  - `scripts/release.py`

    ```diff
    @@ -45,6 +45,7 @@
     
     # Auto-extracted from noreply emails + manual overrides
     AUTHOR_MAP = {
    +    "[redacted:email]": "jimmyjohansson84",  # PR #27123 salvage (Kanban unknown-skill warn-instead-of-crash; #27136)
         "[redacted:email]": "theAgenticBuilder",  # PR #14180 salvage (route delegate_task progress lines through _safe_print so ACP stdio JSON-RPC frames stay clean)
         "[redacted:email]": "huangxudong663-sys",  # PR #15157 salvage (isinstance(dict) guard on tool-call model_extra; NVIDIA NIM non-dict crash)
         "[redacted:email]": "Nickperillo",  # PR #8008 salvage (Discord channel-name matching + flush pending sends on shutdown)
    ```
  - `tui_gateway/server.py`

    ```diff
    @@ -4212,12 +4212,25 @@ def _make_agent(
         if startup_skills:
             from agent.skill_commands import build_preloaded_skills_prompt
     
    -        skills_prompt, _loaded_skills, missing_skills = build_preloaded_skills_prompt(
    +        skills_prompt, loaded_skills, missing_skills = build_preloaded_skills_prompt(
                 startup_skills,
                 task_id=session_id or key,
             )
             if missing_skills:
    -            raise ValueError(f"Unknown skill(s): {', '.join(missing_skills)}")
    +            missing_display = ", ".join(missing_skills)
    +            # Degrade gracefully when some skills loaded; only hard-fail when
    +            # every requested skill is missing. Mirrors cli.py — a typo'd skill
    +            # name should not crash the worker and auto-block the Kanban task.
    +            if loaded_skills:
    +                logger.warning(
    +                    "Unknown skill(s) requested, skipping: %s. "
    +                    "Continuing with: %s. "
    +                    "List available skills with `hermes skills list`.",
    +                    missing_display,
    +                    ", ".join(loaded_skills),
    +                )
    +            else:
    +                raise ValueError(f"Unknown skill(s): {missing_display}")
             if skills_prompt:
                 system_prompt = "\n\n".join(
                     part for part in (system_prompt, skills_prompt) if part
    ```
- **#22933: fix(kanban): reject toolset names in task skills**. closed without merge
  - maintainer direction: category `merged_via`; implication `superseded`; refs `#23273`
- **#22974: fix(kanban): validate task skills and surface stuck task diagnostics**. closed without merge
  - maintainer direction: category `implemented_elsewhere`; implication `superseded`; refs `1f5983c4c`, `#23183`, `#22974`, `#23154`, `#23183`, `#23183`, `#23183`, `#23273`, `#23183`
- **#23738: fix(packaging): ship bundled skills in wheel**. closed without merge
  - maintainer direction: category `merged_via`; implication `superseded`; refs `#28421`
- **#23884: fix(kanban): auto-install bundled skills (e.g. kanban-worker) on kanban init**. closed without merge
  - maintainer direction: category `merged_via`; implication `superseded`; refs `97ac94fe5642e8516e917e22688142645f6250db`, `#28368`
- **#27123: fix(gemini): add missing role field to systemInstruction for Gemma compatibility**. closed without merge
  - maintainer direction: category `merged_via`; implication `superseded`; refs `018009bc3`, `#55585`, `#27123`
- **#27201: fix(cli): unknown skills should warn instead of crash**. closed without merge
  - maintainer direction: category `merged_via`; implication `superseded`; refs `018009bc382de86f864e3fa8af3982c2f0aa7bfb`
- **#35212: fix(kanban): count stale-claim reclaims toward circuit breaker**. open
  - maintainer direction: category `uncategorized`; implication `needs_llm`
  - files: `hermes_cli/kanban_db.py`, `tests/hermes_cli/test_kanban_db.py`
- **#41325: fix(kanban): exclude .archive/ from worker skill availability check**. closed without merge
  - maintainer direction: category `implemented_elsewhere`; implication `superseded`; refs `84e1d31e5442eeff0bfcf1c2ffab6acf7fe95f45`, `#50473`
- **#44092: feat(kanban): validate --skill names at task creation time**. closed without merge
  - maintainer direction: category `superseded_by_pr`; implication `superseded`; refs `#44101`, `#44101`, `#44101`, `#44101`
- **#44101: fix(kanban): validate --skill against the worker profile's skill registry at create time**. closed without merge
  - maintainer direction: category `superseded_by_pr`; implication `superseded`; refs `#93807`
- **#44686: fix(kanban): surface worker crash cause from log in detect_crashed_workers**. open
  - maintainer direction: category `uncategorized`; implication `needs_llm`; refs `#46985`
  - files: `hermes_cli/kanban_db.py`, `hermes_cli/kanban_db_dispatch.py`, `tests/hermes_cli/test_kanban_crash_log_snippet.py`
- **#45917: fix(kanban): validate per-task --skills against assignee profile at create time**. closed without merge
  - maintainer direction: category `superseded_by_pr`; implication `superseded`; refs `#44101`, `#44072`, `#44101`, `#44101`, `#39179`, `#41813`
- **#46477: feat(swarm): per-swarm verifier + synthesizer body and skills overrides (#34273)**. closed without merge
  - maintainer direction: category `superseded_by_pr`; implication `superseded`; refs `#89763`
- **#54100: fix(kanban): warn and continue when optional skill is unavailable (#54088)**. open
  - maintainer direction: category `uncategorized`; implication `needs_llm`
  - files: `cli.py`, `tests/cli/test_cli_preloaded_skills.py`
- **#89763: feat(swarm): per-swarm verifier + synthesizer body and skills overrides (#34273)**. closed without merge
  - maintainer direction: category `superseded_by_pr`; implication `superseded`; refs `#112608`
- **#112608: feat(swarm): per-swarm verifier + synthesizer body and skills overrides (#34273)**. open
  - maintainer direction: category `duplicate_of`; implication `duplicate`; refs `#53577`, `#34273`, `#53577`
  - files: `hermes_cli/kanban_swarm.py`, `tests/hermes_cli/test_kanban_swarm.py`

## Fix status

- Merged to main in PR #23273 (cited above). No release data is tracked. Verify downstream.
- Fix last verified (merged) `2026-06-30`.
- Subsystem: `agent`
