Skip to main content

Knowledge skills: authoring & governance

Skills are versioned, prompt-layer domain knowledge (M11-7): each one is a vendored SKILL.md file composed into an agent's system prompt in deterministic order, so the cached prompt prefix stays byte-stable. Skills are knowledge only - they shape what an agent produces and execute nothing. The code lives in apps/server/src/skills/ (model, parser, registry), and the vendored skills live beside it, one directory per skill: <name>/SKILL.md.

Skills are treated as code: reviewed in a PR, version-pinned, loaded from disk only (never over the network). Since M22-1 every skill also carries governance metadata that distinguishes and audits proprietary vs third-party knowledge. Governance metadata never reaches a prompt - Skill.render() output is byte-identical whatever the provenance fields say, so adopting or re-labelling a skill never invalidates the prompt cache.

Authoring contract

A skill is a directory under apps/server/src/skills/ whose name equals the skill's name field, containing a single SKILL.md. The file opens with front-matter in the shared grammar (frontmatter.ts): key: value scalars and flat [a, b] lists between --- delimiters - deliberately not full YAML, so the runtime stays offline and dependency-free. Unknown fields, duplicate fields, and malformed lines are rejected at load time, naming the offending file.

FieldRequiredFormMeaning
namealwaysscalarSkill id; must equal the directory name.
versionalwaysscalarSkill version, bumped on any content change.
descriptionalwaysscalarOne-line summary, rendered into the prompt block.
originalwaysproprietary | third-partyWho authored the knowledge (governance; never rendered).
licensethird-party onlySPDX id (e.g. MIT, Apache-2.0)License the upstream content is used under. Forbidden on proprietary skills.
upstreamthird-party only<https-url>@<pinned-ref>Where the content came from, pinned to an immutable ref (tag or commit SHA). Forbidden on proprietary skills.
vettedoptionalscalarFree-text vetting note: who reviewed the content and when.
applies_tooptionallistCollaborator roles the skill is written for (advisory).
requiresoptionallistCapabilities the skill assumes; an agent lacking one gets the skill rejected (and logged), never silently composed.

The markdown body after the front-matter is the knowledge itself. Only name, version, description, and the body are rendered into the prompt block.

Governance contract (proprietary vs third-party)

  • origin: proprietary - authored in-house for Bloom. Declaring a license or an upstream is an error: a proprietary skill carrying external-provenance fields is most likely a mislabelled third-party skill, and the loader refuses the ambiguity.
  • origin: third-party - adopted or adapted from external content. It must declare:
    • license: the SPDX license identifier of the upstream content, so redistribution obligations are auditable from the front-matter alone;
    • upstream: the source URL and a pinned ref (@<tag-or-sha>), so the exact upstream revision the vendored copy derives from can always be diffed. A bare URL is not a pin and is rejected.
  • vetted (either origin, recommended for third-party): a short note recording the human review, e.g. vetted: reviewed by A-G-D 2026-08-09.

SkillRegistry.load() rejects any violation at startup with an error naming the offending file - the same fail-fast strictness as the rest of the front-matter contract. There is no "unknown" origin and no default: every skill states its provenance explicitly.

Examples

A proprietary skill:

---
name: writing-clear-acceptance-criteria
version: 1.0.0
description: How to phrase acceptance criteria that are specific, testable, and unambiguous
origin: proprietary
---

# Writing clear acceptance criteria
...

A third-party skill:

---
name: conventional-commit-messages
version: 1.0.0
description: How to write Conventional Commit messages
origin: third-party
license: MIT
upstream: https://github.com/example/commit-guide@v2.3.0
vetted: reviewed by A-G-D 2026-08-09
---

# Writing Conventional Commit messages
...

Importing a third-party skill (M22-2)

Third-party skills enter the tree only through the dev/CI-time importer - never at runtime, and never by hand-copying content:

make skills-import URL=https://github.com/example/commit-guide REF=v2.3.0
# optional: FILE=<path-in-repo> (default SKILL.md), LICENSE=<spdx-id> and VERSION=<x.y.z>
# (when upstream front-matter declares none - the agentskills.io spec makes both optional),
# SOURCE=<mirror> (fetch location when it differs from the canonical URL)

The importer (tsx src/skills/importer.ts --url ... --ref ... from apps/server, skills/importer.ts) fetches the file from the upstream git repository pinned to REF (tag, branch, or sha - always resolved to the full commit sha), rewrites the front-matter with origin: third-party, the license SPDX id, and upstream: <URL>@<resolved-sha>, validates the result through the same parser the runtime uses, and writes apps/server/src/skills/<name>/SKILL.md. A malformed external skill (missing fields, unsafe name, governance fields smuggled in upstream content) is rejected and nothing is written. Re-importing an already-vendored skill is refused unless the incoming version is a strict bump, so a re-pin is always an explicit, reviewable version change.

The import lands as an ordinary PR, and a human completes the third-party skill vetting checklist (license compatibility, content safety - no secret exfiltration / destructive / prompt-injection instructions - and a soak on a non-production deployment) in that PR before it merges. The runtime SkillRegistry performs no network I/O whatsoever (pinned by test): it only ever reads the vendored files this pipeline produced.

Loading, binding, and composition

SkillRegistry.load() reads every <name>/SKILL.md at startup and fails fast on any malformed skill, duplicate name, or directory/name mismatch. An agent's skills are its code-declared baseline plus the add-only BLOOM_AGENT_SKILLS additions; a skill whose requires capability was not granted is rejected and logged. Composition into the system prompt is deterministic (sorted by name), keeping the cached prompt prefix byte-stable across calls. See architecture.md for where skills sit in the design stage.

The delegated engineer (M22-5)

The Claude Code engineer is not an in-process agent - it runs as a separate process on the runner VM - so its skills ride the implementation brief instead of a system prompt. Selection goes through the same registry path (engine/skills.ts): a code-declared baseline (implementation-craft, verification-before-handoff), add-only additions from BLOOM_RUNNER_ENGINEER_SKILLS on the runner, and the capability gate - the engineer grants repo-workspace (a writable checkout plus a shell), which no in-process one-shot agent has, so engineer skills can never leak into those prompts. The registry is vendored inside the bloom package the runner ships, so composition stays offline there too.

Because the payload leaves Bloom's process, it is composed with compose_delegated_skill_block(): the same deterministic, sorted shape as in-process composition, but each skill is preceded by an explicit provenance tag (<!-- provenance: skill=<name>; version=<v>; origin=<origin>[; license=...; upstream=...] -->), so what knowledge was injected - and where it came from - is auditable from the delegated run's transcript itself.