Skip to content

docs(skills): make storage source, ownership, mutability, and Curator scope one legible contract #110133

Open

Description

@Xipong

Summary

The skills system currently exposes several independent concepts through overlapping labels:

  1. physical/effective location — profile-local, skills.create_dir, skills.external_dirs, trusted project, plugin;
  2. distribution origin / updater — bundled, Hub/URL, external upstream, user-authored, agent-authored;
  3. write policy — foreground skill_manage, background review, Curator lifecycle;
  4. Curator opt-in state — the .usage.json created_by: agent marker;
  5. scope — skill content may be shared while usage/ownership state remains profile-local.

The documentation describes pieces of this model, but not as one contract, and the CLI collapses several categories. An operator cannot reliably answer “which copy is active, who can modify it, and who will update it?” without reading the implementation.

This was encountered while configuring the same agent-authored library across seven profiles while preserving bundled/Hub update ownership. The reproductions below use a fresh synthetic HERMES_HOME against current main (5dea46d13deec9549bdc2ea703ae9201d733c28d); they do not depend on that real installation.

This is not another request for a global skill store. It is a documentation + observability request for the behavior Hermes already exposes.

Where the documentation and surfaces diverge

1. The opening mental model is already false for supported roots

The Skills page says that “All skills live in ~/.hermes/skills/ — the primary directory and source of truth”:

The same page later documents create_dir, external_dirs, and trusted project roots, while profiles use their own HERMES_HOME. “All skills” and a single “source of truth” therefore do not describe the supported resolution model.

2. external_dirs are called both read-only and mutable

The shipped config example says:

External dirs are read-only

The user guide says existing external skills are updated in place and that external dirs are not a write-protection boundary:

Current implementation has a third, contextual rule: external/project roots are read-only to autonomous lifecycle maintenance, while user-directed tool calls may still edit them:

So “read-only” is not a property of the directory alone; it currently depends on the write origin. PR #108040 may change the foreground default, which makes it even more important for the docs and config example to state the same final contract.

3. create_dir is documented as fully integrated, but is not

The guide recommends create_dir for a shared “brain” / fleet-wide volume and says its skills appear in the prompt index, skills_list, skill_view, and slash commands:

On current main, a clean repro produces:

skill_manage create : true
in prompt index     : true
skills_list names   : []
skill_view          : Skill 'create-dir-audit-demo' not found.

The implementation has a canonical get_all_skills_dirs() containing local → create_dir → external:

but the list/view path rebuilds roots as project + local + external and omits create_dir:

This specific functional bug is already tracked by #108157. I am including it here only because the current documentation presents the broken path as the recommended no-hacks solution for shared creation.

4. hermes skills list cannot reveal the effective storage/ownership model

The installed-skills command accepts only all | hub | builtin | local:

and classifies every non-Hub, non-bundled skill as local, regardless of whether the winning file is in an external or project root:

Synthetic current-main output with one local skill, one external skill, and one create_dir skill:

Installed Skills
Name                 Source  Trust
external-audit-demo  local   local
local-audit-demo     local   local

0 hub-installed, 0 builtin, 2 local

The external skill is labeled local; the create_dir skill is absent because of #108157. There is no path or update-owner field with which to disambiguate them.

5. “agent-created” is still used as an operator-facing synonym for Curator-managed

The Curator guide eventually explains the actual contract correctly:

created_by is a policy flag, not a provenance claim

The implementation says the same and shows that curator adopt writes created_by: agent to a user-authored skill:

That policy sidecar always lives under the active profile's get_hermes_home()/skills/.usage.json, not beside a shared create_dir artifact:

Yet the same guide section is titled “What agent-created means”, its sample status reports agent-created=..., and the real CLI still prints that counter:

That forces users to learn that “agent-created” sometimes means authorship and sometimes means “autonomous Curator writes are allowed.” Existing issue #70712 covers the Desktop “learned” badge; this issue asks that the docs and CLI stop overloading the same concept too.

Why this matters

This ambiguity affects safe operations rather than wording alone:

  • an external/upstream-owned skill can be mistaken for a local owned copy;
  • a user can assume “read-only” protects foreground writes when it currently does not;
  • a user can assume an adopted skill was authored by the agent;
  • a recommended create_dir shared setup advertises skills the agent cannot subsequently load;
  • across profiles, shared content and profile-local lifecycle metadata can be mistaken for one shared management state.

The practical workaround is duplicate profile-local copies plus manual reconciliation—the opposite of the “shared brain” mental model suggested by the guide.

Requested outcome

Please define one canonical skills contract and expose it consistently. A matrix in the Skills page would be sufficient documentation-wise, with rows for:

  • profile-local / bundled copy;
  • Hub-installed;
  • skills.create_dir;
  • skills.external_dirs;
  • trusted project skills;
  • plugin-provided skills.

Suggested columns:

  • resolution precedence and effective path;
  • distribution origin / update owner;
  • foreground skill_manage mutability;
  • background-review mutability;
  • Curator lifecycle eligibility (adopt, stale/archive, pin);
  • location/scope of .usage.json state;
  • whether hermes update or Hub update may overwrite the copy;
  • which discovery surfaces include the root.

For operator visibility, hermes skills list (including JSON output if added) should report separate concepts rather than one overloaded Source value—for example location_kind, origin/update_owner, and curator_managed—or at minimum show the resolved path and distinguish external, create_dir, and project from profile-local.

Acceptance criteria

  • skills.md, curator.md, and cli-config.yaml.example describe the same external-dir mutability contract.
  • The opening Skills mental model covers profile-local, create_dir, external, project, and plugin roots instead of saying all skills live in one directory.
  • create_dir is either integrated across every documented discovery surface (tracked by skills.create_dir skills are advertised in the prompt but skill_view / skills_list / slash commands can't find them #108157) or the docs stop claiming that it is.
  • hermes skills list lets an operator identify the winning copy's location/update ownership; external skills are not reported only as local.
  • Curator output distinguishes authorship/provenance from the autonomous-management opt-in; an adopted skill is not described as agent-authored.
  • Multi-profile docs state explicitly whether content, usage metadata, and Curator ownership are profile-local or shared.
  • Contract tests cover every documented root across prompt index, skills_list, skill_view, slash commands, and write/lifecycle guards.

Related, not duplicate

Those issues each address one behavior or UI. This issue is for the missing cross-cutting documentation and operator-facing contract that ties storage, provenance/update ownership, mutability, Curator management, and profile scope together.

Activity

  1. added
    type/docsDocumentation improvements
    P3Low — cosmetic, nice to have
    tool/skillsSkills system (list, view, manage)
    area/profilesMulti-profile isolation, HERMES_HOME scoping
    area/configConfig system, migrations, profiles
    on Sep 13, 2026
  2. kokhlo commented on Sep 13, 2026

    @kokhlo

    Verified the report against current main (5dea46d1) — all five divergence points hold by direct reading, and #110138 already covers the documentation half. Adding review context on what it does and doesn't close, since the acceptance criteria reach beyond the two edited files.

    Confirmed on main:

    • skills.md L11 still opens with "All skills live in ~/.hermes/skills/ — the primary directory and source of truth" while the same page later documents create_dir, external_dirs, and trusted project roots.
    • cli-config.yaml.example L1057 says external dirs are flatly "read-only", but the actual boundary is write-origin, not directory: tools/skill_manager_guards.py L164-181 only refuses autonomous (background review) writes to external/project roots — user-directed skill_manage edits still land in place, as skills.md already says.
    • hermes_cli/skills_hub.py L790 classifies every non-Hub, non-bundled skill as local with no path/origin column, so hermes skills list cannot distinguish an external root or create_dir copy from a profile-local one.

    On #110138 (docs-only: skills.md + curator.md):

    • The new contract section is accurate against the code: get_all_skills_dirs() (agent/skill_utils.py L398-406) resolves local → create_dir → external, trusted project roots sit above that chain (L399-400 docstring, L409-413 comment), and the write-policy split matches _background_review_write_guard.
    • Good catch inside it: the lifecycle numbers it corrects (14d stale / 30d archive) match config_defaults.py — the previous 30d/90d text was stale either way.

    Still open per the acceptance criteria, not covered by that PR:

    1. cli-config.yaml.example L1055-1059 still calls external dirs "read-only" with no write-origin qualifier — AC Terminal tool #1 names this file explicitly.
    2. hermes skills list still reports one overloaded Source value (skills_hub.py L781-805; choices in subcommands/skills.py L70) — AC Fix terminal interactivity #4 needs location_kind/resolved-path level reporting.
    3. hermes curator status still prints agent-created=N for what is a management opt-in (hermes_cli/curator.py L117-119) — AC Update snapshot #5.

    Compose note: #108040 (open) proposes default-denying in-place writes to external dirs. #110138's contract text describes today's behavior ("user-directed ... update ... in place"), which is correct for current main — but if #108040 lands, that sentence and the config example should flip together, so the two PRs share one sentence whichever order they merge in.

    Items 2 and 3 look like small, well-scoped follow-ups to split from the docs PR if maintainers prefer — happy to take either or review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3Low — cosmetic, nice to havearea/configConfig system, migrations, profilesarea/profilesMulti-profile isolation, HERMES_HOME scopingtool/skillsSkills system (list, view, manage)type/docsDocumentation improvements

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions