Grounds on (exists today):
src/gaia/skills/ — the shipped runtime: format.py (parser/writer/validator), manager.py (SkillManager, discovery + precedence), loader.py (<skill>/<tool> registration), permissions.py (grammar + connector bridge), cli.py (gaia skill), sets.py (the skills: / skill_sets: grammar), migrate.py (OpenClaw/Hermes → GAIA, #692) · src/gaia/agents/base/agent.py (Agent.load_skill, unload_skill, SKILL_DIRS, skill_manager, loaded_skills, SKILL_MANIFEST, load_skill_set, select_skill_set) · src/gaia/agents/base/tools.py (@tool, _TOOL_REGISTRY) · src/gaia/agents/registry.py (KNOWN_TOOLS, namespaced_agent_id) · src/gaia/connectors/providers/base.py (ConnectorRequirement) · src/gaia/ui/routers/agents.py (/api/agents, the panel to mirror)Also exists (hub side): workers/agent-hub/src/ — the skill catalog lane, the skills/ R2 namespace, POST /publish/skill validating this grammar server-side, and the pre-publish audit gate (#2467).Still proposed (not written yet): the /api/skills router and Agent UI panel, gaia skill update, and the permission sandbox for local-capability domains (#1019). Every such symbol is marked PROPOSED where it appears.Shipped since: the marketplace verbs gaia skill search|install|remove|publish plus keygen/trust, install-time tier-ceiling enforcement, and the declarative skills: block (#2467); plus gaia skill migrate (#692).Component: the
SKILL.md field grammar — the on-disk contract for GAIA’s skill ecosystem (issue #691).Module: gaia.skills — shipped (#888). The parser, validator, discovery, tool loader, permission grammar, and gaia skill list|info|create|import|export all exist. The contract is grounded on the live tool registry in gaia.agents.base.tools.Status: Format = decided. Runtime = Phase 1 + the marketplace shipped (loader, CLI, publish/install, signature verification, install-time tier ceilings, declarative skills: / skill_sets:); the local-capability sandbox and the REST/UI surface remain proposed. The companion Agent Skills spec owns the integration surface (discovery, scoping, progressive disclosure); this document is authoritative for the field grammar.Target consumers: #887 (skill auto-synthesis), #553 (self-improving agent), #1451 (tool-loader Part 3), #647 (skill marketplace), #648 (OEM bundling), #462 (Agent Manifest).Why this exists
A GAIA agent’s capabilities are compiled in. Tools arrive through hardcoded mixins (RAGToolsMixin, BrowserToolsMixin, … catalogued in KNOWN_TOOLS,
registry.py:38)
wired into the agent class and shipped inside the wheel. A user who wants a new
capability has no path short of forking the agent. Skills make a capability
portable: a folder with a SKILL.md an agent loads only when relevant.
The Agent Skills spec answers why skills, and how an agent
composes them. This document answers a narrower, higher-stakes question: what
exactly goes in the frontmatter. That matters because the frontmatter is a
contract, not a private detail — several landed and in-flight features read or
emit these fields:
- #887 (skill auto-synthesis) and
#553 (self-improving agent) will
emit
SKILL.mdfiles via skill extraction. The field names they write must be the field names this spec defines. - #1451 (tool-loader Part 3)
reads the
tools_requiredfield to union a recipe’s tools into per-turn tool selection — a hard cross-spec dependency already written down intool-loader.mdx. - #647 (marketplace) and #648 (OEM bundling) need a stable definition of “a skill” and its trust tiers to publish and pre-bundle.
Prior art (credit before contribution)
GAIA does not invent a skill format. It adopts one and extends the frontmatter. The base and the compatible formats:
GAIA’s contribution is the
metadata.gaia namespace: typed @tool
declarations, a <domain>:<level> permission grammar bridged to the connector
grant model, three install-time security tiers, and the tools_required recipe
contract — all expressed inside the standard’s metadata map, so a GAIA skill
remains a valid standard skill.
Decided design
The schema is the standard’s required base plus a single GAIA-owned namespace. A standard runtime reads the base and ignoresmetadata.gaia; GAIA reads both.
Adopted base (Agent Skills standard)
The metadata.gaia namespace
Everything GAIA-specific is nested here. Omit the whole block and the skill is a
valid instruction-only skill (see Off-states).
tools vs tools_required — never conflate them
These are two different fields with two different consumers, and collapsing them
breaks the recipe contract:
The field name
tools_required is locked as the cross-spec contract with
#887/#1451. A skill may have either, both, or neither.
Field reference
Manifest-side grammar: skills, skill_sets, default_skill_set
The fields above describe one skill. An agent declares which skills it
composes in its gaia-agent.yaml, using three top-level keys
(#2466):
Each entry in
skills or a set’s list is either a skill name or a mapping:
Set names use the same slug shape as skill names, capped at 32 chars — a
lowercase alphanumeric start and end with internal hyphens:
skills: list;
that is a contradiction (an always-on skill loads for every set) and fails to
parse.
Every malformed shape fails at manifest-parse time with a message naming the
fix — a list that repeats a name, an empty set, a default_skill_set naming an
undeclared set, an unrecognized key in a reference mapping, a non-boolean
required. There is no partial acceptance: an agent either declares a coherent
set of skills or does not load.
version: is enforced at load. The range is matched against the installed
skill’s frontmatter version, and a mismatch is fail-loud: a required entry
raises and the agent does not launch, an optional one is skipped with the reason
logged. A skill with no version in its frontmatter cannot satisfy a pin —
otherwise a local edit would silently shadow a pinned hub install. A range that
names no version (>=v2, 1.2.x) is rejected at manifest-parse time with the
shapes above, rather than treated as “any” — so the verdict never depends on
which skills happen to be installed on the machine doing the parsing.What #2467 still owns is resolution
across installable versions — choosing which version to fetch from the
marketplace. Checking the one version actually installed happens today.gaia/skills/sets.py,
and consumed by both the manifest validator and the runtime, so the two cannot
drift.
Naming
Skill names follow the Agent Skills rule:^[a-z0-9]+(-[a-z0-9]+)*$, ≤64 chars,
and must equal the skill’s directory name (so web-research/SKILL.md has
name: web-research). No leading, trailing, or consecutive hyphens. Names are
unique within a discovery root; precedence between roots is defined in
Agent Skills → Discovery.
Permission model
Permissions follow<domain>:<level> with an optional :scope qualifier. There
are no implicit grants — a skill declares every domain it touches.
Scopes narrow a grant:
filesystem:read:~/.gaia/data/**,
network:read:*.brave.com, shell:execute:git,npm. An unscoped grant covers
the whole domain — except mcp:connect, where a scope is mandatory (below).
Grammar violations — an empty entry, a missing level, an unknown domain, or a
level the domain does not define — fail loudly at parse time.
Bridge to the connector grant model — no parallel ledger
Agent Skills already states that a skill carrying permissions is granted to an agent reusing the connectors per-agent grant model. This spec makes the mapping concrete rather than inventing a second grant system:network:*andmcp:connectresolve, at scope-time, to aConnectorRequirement(connector_id, scopes, reason)(connectors/providers/base.py:23) — the same frozen primitive agents already declare viaREQUIRED_CONNECTORS(agent.py:297). The skill’spermissionsbecome the agent’s connector requirements when the skill is loaded — appended to a per-instance copy, never to the ClassVar, so one agent’s skill cannot leak requirements into a sibling.filesystem:*,shell:*,database:*,desktop:*,env:*are new local-capability domains with no connector equivalent. Until the skill sandbox lands (PROPOSED — see Phased build) a skill declaring one is refused outright — at every security tier,verifiedincluded. A tier is a provenance claim, not a sandbox.
namespaced_agent_id keying
(builtin: / custom:sha256: / installed:,
registry.py:392)
that already isolates agent grants today.
Two decisions the runtime made where this spec was ambiguous
The bridge above did not say what happens when a permission’s scope has no counterpart in the connector catalog. The implementation (permissions.py)
settled both cases, and these are now part of the contract:
mcp:connectmust name a catalog connector. A baremcp:connectis a validation error, not a wildcard — the error lists the available connector ids and points atgaia connectors list. So ismcp:connect:<unknown-id>. An unscoped MCP grant would mean “connect to any MCP server”, which is exactly the ambient authority the permission model exists to prevent.- An unmatched
network:*scope resolves to a reservednetworkpseudo-connector-id. Nothing in the catalog represents raw outbound HTTP, sonetwork:read:*.brave.combecomesConnectorRequirement(connector_id="network", scopes=("read:*.brave.com",), …). If the scope does name a real catalog connector, the requirement targets that connector instead.
Security tiers
Three tiers set the install-time permission ceiling and grant behavior.
Promotion path:
experimental → community → verified. A publisher signs to
reach community; an AMD audit request reaches verified. Re-audit on every
version bump.
A claim is not a tier. gaia skill install computes
min(claimed tier, tier the signature earned) and enforces the ceiling against
that, so an unsigned skill can never install as verified no matter what its front
matter says. A key’s trust-store role decides what it attests: publisher →
community, amd → verified. The trust store ships empty and no AMD key is
bundled, so out of the box every skill installs experimental — the honest floor
rather than a placeholder that makes the top tier look anchored. See
gaia skill install.
The dangerous set the spec names — shell:execute, desktop:control,
database:write — is retained as authoritative, but all three are local-capability
domains, so in v1 they are refused before any tier check: the prompt is
currently reachable only for network:write.
Tiers are NOT the governance decision system. The governance layer’s
DecisionType = Literal["ALLOW", "REVIEW", "BLOCK"]
(governance/schemas.py:17)
is a run-time, per-action verdict. Security tiers are an install-time trust
ladder that sets the ceiling a skill’s permissions may reach. They are
complementary, not the same axis: a verified skill still has each action
adjudicated at run time by governance.The install-time ceiling now ships (#2467):
signatures are verified, the claimed tier is collapsed to the attested one, and a
permission above the resulting ceiling refuses the install. What remains deferred to
Phase 2 is the run-time sandbox for local-capability domains —
which is why those domains are refused outright rather than granted at any tier.Pre-publish security audit
Every skill passes an automated audit before it can enter the hub catalog (#2468). Skills are contributed by pull request — there is no self-serve publish path (the hub’s publish endpoint is gated on a maintainer-held token secret), so the PR check is the gate:BLOCK fails it, REVIEW holds the PR for maintainer
sign-off, ALLOW passes. Submit under skills/community/<name>/; see
Publishing to the Hub → Publishing a skill.
Authors run the same engine locally, so a clean local audit is the whole
requirement:
0 allow, 5 review, 6 block, 4
the skill could not be parsed. A skill the audit cannot read is never passed —
there is nothing to audit and nothing to publish.
Four analyzers run over the directory. Nothing imports the skill, runs its code,
or touches the network, so auditing a hostile skill is itself safe — this is a
static gate, and the runtime sandbox remains Phase 2.
Tier-scaled rigor. The same findings produce different verdicts depending on
the tier a skill claims, which is what makes the tiers above mean something at
publish time:
A skill cannot be stamped a tier whose gate it did not clear. The report lists
the tiers its findings actually cleared, and the hub compares the claimed tier
against that list — so an unearned claim is refused, not recorded.
verified
never appears in that list, which is what stops a publisher self-stamping the top
tier; the AMD audit is what adds it.
A new version re-earns its verdict. The report is bound to the skill name, the
version, and a digest of the audited bytes, so a report cannot be replayed onto a
different publish. CI re-runs the full audit on every PR touching a skill, with no
“already audited” short-circuit.
Disclosure. Findings name file:line and the fix, but the offending source
text is withheld from every report unless a caller passes --show-snippets
locally. In CI the per-finding detail goes to the repo’s private Security → Code
scanning tab as SARIF; the PR comment carries verdicts, tiers, and counts only.
REVIEW currently refuses the publish rather than admitting it — the quarantine
lane and its maintainer sign-off workflow are
#2675.
Tool registration
A skill’stools map onto the existing decorator and registry. On load, the
runtime imports the skill’s tools.py and registers each @tool under a
<skill-name>/<tool-name> namespace into _TOOL_REGISTRY to prevent collisions
when an agent composes multiple skills — the same namespacing
Agent Skills describes. The skill’s
tools.py uses the standard @tool decorator unchanged:
Every failure restores
_TOOL_REGISTRY (and sys.modules) byte-for-byte — there
is no partial load, as Agent Skills
requires. Shipping a tools.py while declaring no tools is not an error: the
module is simply not imported, and the skill loads instruction-only.
CLI
Shipped verbs (gaia/skills/cli.py) —
authoring and local management. Full flag reference in
CLI Reference → Skills.
0 ok, 2 usage, 3 not found,
4 invalid (including a discovery root containing a malformed skill), and — for
audit — 5 review, 6 block. REVIEW and BLOCK are separate codes so CI can
hold a skill for sign-off without reporting it as rejected.
Marketplace verbs (#2467 /
#647) — these talk to the Agent Hub’s
skills lane:
install accepts a SemVer range and takes the highest published version
satisfying it — the same rule agent dependencies: use. Installs are recorded in
~/.gaia/skills/skill-lock.json with the requested range, the resolved version,
the artifact SHA-256, the signing key, and the tier actually installed at.
Still PROPOSED — update (marketplace):
The proposed verbs are absent, not stubbed.
gaia skill update is not a
command that prints “not implemented” — it does not parse, so a user cannot
discover a verb that cannot work. #691’s
acceptance asked for list/install/remove: all three now ship.What the hub enforces at publish
The Agent Hub Worker servesPOST /publish/skill and a skill catalog lane
(#2467), and gaia skill publish is
the client that calls it.
That endpoint re-validates this grammar server-side before a skill may enter the
catalog. It is a gatekeeper, not a replacement for the loader’s validation above:
a skill can be locally valid and still be refused publication. Rejections name
the offending field.
nameanddescriptionare required;namematches^[a-z0-9]+(-[a-z0-9]+)*$and is ≤64 chars,descriptionis ≤1024.versionis required to publish (optional in the format itself, where a skill may be unversioned) and must be SemVer.0.0.0is refused — the unversioned sentinel can never become an immutable release.metadata.gaia.security_tiermust be one ofverified/community/experimental, and the tier is gated by the pre-publish security audit (#2468) —communityandverifiedcannot publish without a cleared report, and a skill cannot claim a tier whose gate it did not clear.permissions,tools,tools_required, andrequirementsmust match the shapes above; the catalog entry carries them so an installer can show the permission preview before anything is written to disk.
Declarative consumption — the skills: block
An agent names the skills it composes in its gaia-agent.yaml, and any Agent
subclass resolves that block at construction — so a custom agent under
~/.gaia/agents/<id>/ consumes an installed hub skill with no code change:
gaia skill install puts the skill in a discovery root
and this reads what is there; an agent never pulls code from the network at
startup. Ordering mirrors agent dependencies:: a skill providing a tool another
declares in tools_required loads first, declaration order breaks ties, and a
cycle raises rather than picking a winner. A missing required: true skill raises
with the gaia skill install command that fixes it; a missing optional one is
logged and skipped.
The skill_sets: grammar is #2466. It
expands a set into SkillRequirement values and resolves them through the same
path, so there is one resolver and one place that enforces conflicts.
Agent UI dashboard support
Acceptance #4 requires a skills management panel. The proposed/api/skills
router mirrors the existing /api/agents router
(ui/routers/agents.py:
list :130, detail :174, export :184, import :222) — the same
list/detail/install/remove shape, so the panel reuses the agents-panel
conventions rather than inventing new ones.
/api/skills router today — the dashboard is greenfield. The data
it needs now exists (SkillManager.list_skills(), and gaia skill list --json /
gaia skill info --json already emit the list and detail shapes), so the router is
a thin wrapper rather than new machinery. Until it ships the panel is absent, not
broken (see Off-states).
Cross-format compatibility & migration
Three formats, one namespace pattern. GAIA reads the standard base directly and treats Hermes/OpenClaw as migration sources, not runtime targets.
A single shipped tool —
gaia skill migrate --from {hermes,openclaw,auto}
(migrate.py,
#692) — reads the foreign namespace and
writes a metadata.gaia block. Every migrated skill lands experimental,
re-earning trust via the same reset_security_tier() gaia skill import uses.
Migration emits a report of fields needing manual review, and the output is
round-tripped through parse_skill() before install, so a migration can never
emit a SKILL.md the parser rejects.
Field-by-field: OpenClaw → GAIA
Fields GAIA models fully are consumed — they move intometadata.gaia and
leave the vendor block. Anything partially or un-modeled is preserved under
metadata.<vendor> and named in the report, which is what makes the
vendor-namespace pattern lossless in practice rather than in principle.
requires.env is a requirement, not a permission. It declares which
variables must exist; it grants no access, so it maps to the advisory
requirements.env_vars rather than the env:read domain. Treating it as a
permission would make nearly every real OpenClaw skill unmigratable for a field
GAIA already has a home for.Field-by-field: Hermes → GAIA
The shapes published skills actually use
Validated against 26 real published ClawHub skills (each commit-pinned, with its source URL, license, and digest recorded intests/fixtures/openclaw_skills/PROVENANCE.md).
The documented schema is not what the registry contains, so the migrator reads
all of these:
Of the 26, 13 migrate cleanly and 13 are refused — 10 for a local capability
(they shell out) and 3 for having no frontmatter. That ratio is the honest shape
of the corpus, not a shortfall: a skill whose whole purpose is running
git or
tmux cannot be bridged until the sandbox lands.
Relationship to neighbors
- Agent Skills — owns the architecture and integration surface (discovery roots, progressive disclosure, scoping, the skill/tool/agent distinction). This doc owns the field grammar. Where they overlap, that spec is canonical for the compatibility surface and this one for the schema; the two are reconciled (see Correcting the prior spec).
- Agent Manifest (#462) — a
gaia-agent.yamlreferences skills by name + version range;SKILL.mdis the skill-level complement. The keys are specified in manifest-side grammar above; how a set is selected per launch is in Agent Skills → Skill sets. - Agent bundles — skill packaging relates to the existing agent bundle format
(
.zip+bundle.json,BUNDLE_FORMAT_VERSION = 1,export_import.py:40). - Builder template — scaffolds
agent.pytoday (builder/template.py); emittingSKILL.mdis a future extension, not current behavior. - OEM bundling (#648) — needs a
stable definition of “a skill” to pre-configure hardware SKUs. A SKU pre-bundles
skills as
verified-tier entries in a discovery root; the directory anatomy and tier defaults this doc fixes are that definition.
Example skills
Six worked examples. The three for existing agents (acceptance #6) declare only tool names that exist in the current registry. All parse as YAML; tool skills round-trip against theirtools.py.
1. Minimal instruction-only skill (bare standard)
1. Minimal instruction-only skill (bare standard)
Proves a bare Agent Skills
SKILL.md loads with no GAIA fields — defaulting to
experimental, no tools, no permissions.2. Tool skill in the new shape (web-research)
2. Tool skill in the new shape (web-research)
The restructured schema: standard base +
metadata.gaia.3. Recipe skill with tools_required (the #887/#1451 contract)
3. Recipe skill with tools_required (the #887/#1451 contract)
A distilled procedure that consumes existing registry tools. Each
tools_required name resolves against a real registry tool (query_documents
from RAGToolsMixin, read_file from FileIOToolsMixin; remember from the
tool-loader CORE set).4a. rag-search (from RAGToolsMixin)
4a. rag-search (from RAGToolsMixin)
Acceptance #6 — a skill mapping the
rag mixin’s real tools.4b. web-research (from BrowserToolsMixin)
4b. web-research (from BrowserToolsMixin)
Acceptance #6 — the
browser mixin’s real tools (search_web, fetch_page,
download_file).4c. file-operations (from FileIOToolsMixin)
4c. file-operations (from FileIOToolsMixin)
Acceptance #6 — the
file_io mixin’s real tools.5. Hermes skill + its metadata.gaia migration
5. Hermes skill + its metadata.gaia migration
A Hermes skill (left) and what
gaia skill migrate --from hermes produces
(right) — the modeled metadata.hermes fields become metadata.gaia, and the
result lands experimental. Unmodeled ones (category) stay under
metadata.hermes; experimental being the omitted default, the emitted file
need not restate it (gaia skill info always shows the effective tier).6. OpenClaw skill + its migration
6. OpenClaw skill + its migration
An OpenClaw skill and the mapping
migrate computes for it.
metadata.openclaw requirements map to metadata.gaia fields and trust resets
to experimental.Off-states (safe floors)
Every degraded condition lands on a conservative, well-defined floor — nothing breaks because a GAIA feature is absent.
Future-gated phases are additive with precedence. The
tools_required
signal that #1451 consumes returns [] cleanly until #887 lands
(tool-loader.mdx),
so a recipe skill authored today is valid but inert until its consumer ships —
it never blocks or errors.
Correcting the prior spec
This document revises an earlier draft ofskill-format.mdx. The most
important correction:
The base standard is Agent Skills (agentskills.io),
not OpenClaw. The prior draft framed “OpenClaw / ClawHub” as the external
standard to be compatible with. In fact agentskills.io (as used by Claude Code)
is the base GAIA adopts; OpenClaw is just one of several compatible
third-party formats, alongside Hermes. This reconciles skill-format.mdx
with Agent Skills, which had already flagged
“OpenClaw/ClawHub” as a placeholder from an earlier draft.
The second structural correction: GAIA-specific fields move from the top level
into metadata.gaia. The prior draft put permissions / security_tier /
requirements / tools at the top level, which collides with the standard’s
namespace and breaks lossless cross-runtime parsing. Nesting them under
metadata.gaia mirrors the convergent prior art (Hermes metadata.hermes,
OpenClaw metadata.openclaw).
KPIs
A format contract is judged by conformance and losslessness, not runtime speed. The bar:Phased build
The format (Phase 0) and the runtime core (Phase 1) have shipped; the remaining phases are additive. Each phase holds the prior floor until it lands.Phase 0 — Format contract (SHIPPED)
Success criteria:- Schema decided: adopted base +
metadata.gaia;toolsvstools_requiredseparated;compatibility/allowed-toolsexcluded. - Agent Skills reconciled — mapping table nests
metadata.gaia.*, the placeholder Note becomes a Division-of-authority Note, andallowed-tools/compatibilityare marked excluded. - All example skills parse; the three acceptance-#6 skills use only real registry tool names.
- Traceability covers 100% of #691 acceptance criteria.
Phase 1 — Loader, validator, discovery, CLI core (SHIPPED, #888)
What landed:gaia.skills.format—SKILL.mdparser, writer, and validator (Skill,SkillTool,SkillRequirements,GaiaMetadata,parse_skill,parse_skill_file,parse_skill_metadata,validate_skill). Round-trip is identity: unknown top-level keys, othermetadata.<vendor>namespaces, and unknownmetadata.gaiakeys are all preserved, so passing a foreign skill through GAIA loses nothing.compatibility/allowed-tools/disallowed-toolsparse and are ignored, exactly as specified above.gaia.skills.manager—SkillManagerover three roots (agent-bundled→user→claude-import), progressive disclosure (discover= frontmatter,load= + body,resource_path= on-demand files, traversal-checked), auditable shadowing (shadowed()), surfaced parse failures (discovery_errors), and optional hot-reload viastart_watching().gaia.skills.loader— importstools.pyand registers each@toolas<skill>/<tool>in_TOOL_REGISTRY, cross-checking the manifest against the real signatures. No partial load: any failure restores the registry byte-for-byte.gaia.skills.permissions— the<domain>:<level>[:scope]grammar, the connector bridge fornetwork/mcp, and the loud refusal of local-capability domains.gaia.skills.sets(#2466) — the declarativeskills:/skill_sets:/default_skill_set:manifest grammar (see Manifest-side grammar), parsed once and shared bygaia.hub.manifestand the baseAgent, plus per-launch selection (SKILL_MANIFEST,select_skill_set,load_skill_set). An undeclared set name raisesSkillSetErrorrather than falling back. The email agent is the reference consumer.Agent.load_skill/unload_skill/SKILL_DIRS/skill_manager/loaded_skills— plusget_skills_system_prompt(), auto-discovered by_get_mixin_prompts(), which returns""when no skill is loaded so every existing agent’s prompt stays byte-identical.gaia skill list|info|create|import|export.
- A tool skill loads and its tools register and call under
<skill>/<tool>. - A bare standard skill loads instruction-only at
security_tier: experimental. - Validation fails loudly on a
tools↔tools.pymismatch — missing tool, undeclared extra, parameter-name mismatch, required/optional flip, or type contradiction — with no partial load.
./.gaia/skills/ root, and network-egress enforcement. (The
install-time half of the tier ceiling has since shipped with the marketplace —
see Phase 4.)
Phase 2 — Permission enforcement + load-time tier ceilings + sandbox (PROPOSED, #1019)
The connector bridge shipped early, in Phase 1:network:* / mcp:connect
already resolve to ConnectorRequirement at load-time. The install-time tier
ceiling shipped with the marketplace (Phase 4). What remains is the sandbox that
makes local-capability domains loadable at all, the load-time ceiling — which
covers a skill that never passed through install, e.g. one from gaia skill create/import or copied into a root by hand — and network-egress policy over
the declared network requirements.
Success criteria:
- A hand-placed skill requesting a permission above its tier ceiling fails to load (installing one already fails).
filesystem/shell/database/desktop/envroute to the sandbox and load instead of being refused.- A declared
network:<level>:<scope>actually constrains egress.
Phase 3 — Agent UI dashboard (PROPOSED; #701)
/api/skills mirroring /api/agents, plus the skills management panel.
gaia skill migrate --from {hermes,openclaw,auto} was slated for this phase and
shipped ahead of it (#692) — it needs
no UI surface, so it did not wait for one. Visual import remains part of this
phase: it is the same migrator behind a panel that does not exist yet.
Success criteria:
- The panel lists installed skills with tier + version and supports install/remove (acceptance #4).
- The panel offers OpenClaw import over
gaia.skills.migrate, reusing the shipped mapper rather than a second implementation.
Phase 4 — Marketplace (SHIPPED, #2467) + consumer wiring (PROPOSED; #887 / #1451)
Shipped: the hub’s skills lane (POST /publish/skill, the skills/ R2
namespace, GET /index.json filtered by type: "skill"), gaia skill search|install|remove|publish, Ed25519 bundle signing with a trust store,
install-time tier ceilings, skill-lock.json, and the declarative skills:
block. The pre-publish security audit gate is
#2468 — publish calls it and fails
loudly when its engine is absent rather than publishing un-audited.
Still proposed: gaia skill update; #887 emits conformant SKILL.md; #1451 unions
tools_required.
Success criteria:
- A published skill installs by
name@<range>, lock-tracked, and loads into an agent — including a non-bundled one. (Met.) - An unsigned skill cannot install as
verified;experimentalneeds--allow-experimental; the per-tier permission ceiling is honored. (Met.) - #887-synthesized files validate against this schema unchanged.
- #1451 unions a matched skill’s
tools_requiredahead of semantic results (the contract intool-loader.mdx).
Open questions
The five design forks (base standard,metadata.gaia nesting, top-level
version, migration-only v1, excluding compatibility/allowed-tools) are
locked by the maintainer and are recorded as settled above. What remains are
implementation-level questions for whoever builds the runtime:
-
tools_requiredvalidation timing. Fail-vs-skip is not open — GAIA’s fail-loudly policy settles it: a name unknown to the framework is a manifest defect and fails loudly; a name valid but inactive in the current agent’s registry is scoping (omitted + logged, not an error). The genuine open nuance is when the unknown-name check runs — at install against a static tool catalog, or deferred — since the active registry is assembled dynamically from mixins and other skills, so the full tool universe may not be known at install time. Affects #1451 robustness. -
Skill→skill dependencies. The prior draft allowed a skill to depend on
other skills (
requirements.skills). Keep it undermetadata.gaia.requirements, or defer dependency resolution to the marketplace (#647)? -
/api/skillsinstall sources. Registry-only, or also local path / URL? The latter widens the install-time security surface. -
Body-injection scanning.Resolved by #2468: every skill’s body is scanned, and the tier decides the consequence. The “required forcommunity+, advisory forexperimental” split was the right instinct, but it does not need to be its own policy — body findings feed the same tier gate as code findings, so the split falls out for free: atexperimentala body finding is surfaced and does not gate, atcommunityit forcesREVIEW, atverifiedamediumis already enough to hold it. See Pre-publish security audit for the scan itself. Two decisions inside it are worth recording, because both trade precision against evasion:- Fenced and quoted text is downgraded one step, never ignored. A skill that documents injection patterns so the model can refuse them is the good case, so blocking it would punish the careful author. But fenced text still reaches the model — models do not reliably treat it as inert data — so a code block buys a severity step, not a pass. Otherwise “put the directive in a fence” would be the standing bypass.
- Prohibitions are not directives. “NEVER dump the environment” is the security instruction, not a request for one, so a prohibition immediately before a match suppresses it. Rules whose own pattern opens with a negation (concealment’s “do not tell the user”) are unaffected, because the check looks only at text before the match.
-
Settled with Phase 1: this document stays indocs/spec/relocation.docs/plans/. Agent Skills is the spec surface — it already lives indocs/spec/and satisfies #691’s “documented indocs/spec/” acceptance. Moving this doc too would split the field grammar from the architecture across two directories for no reader benefit, and would break every inbound/plans/skill-formatlink, including the ones the runtime’s own error messages emit (FORMAT_DOCS_URLinskills/errors.pypoints athttps://amd-gaia.ai/docs/plans/skill-format). Revisit only if the two documents ever merge.
Current state of the code
The format is grounded on real primitives, and the Phase 1 runtime now exists. Only the sandbox/run-time-enforcement and REST/UI layers are greenfield; the marketplace shipped in #2467.Dependencies
Blocking: none. This is a foundational format contract; its design inputs are external standards plus the in-repo drafts. Design inputs: Agent Skills (base), Hermes (metadata.hermes), OpenClaw (metadata.openclaw), Claude Code (reference
impl); in-repo Agent Skills (architecture / compatibility
surface) and tool-loader.mdx Part 3 (the tools_required
contract).
Downstream consumers. The features whose contract this format must satisfy. A
closed tracking issue means that design is settled and already references this
format — not that the dependency is gone.