# Portable content format for AI-assistance items and bundles
**Status**: Proposed (2026-05-01)
## Context
AI-assistance content — rules, skills, agents — targets multiple clients
(Claude Code, Copilot, opencode), each with its own on-disk conventions.
Maintaining parallel content per client does not scale.
The Agent Skills open standard ([agentskills.io](https://agentskills.io))
provides a stable base for skills, but does not cover always-on rules or
agent personas. We need one format that:
- handles all three item kinds with a shared base schema,
- supports bundle distribution without duplicating item content,
- allows minor per-client deviations without forking entire files,
- is self-describing so it can evolve without silently breaking older
consumers.
This ADR is one of four child ADRs of [ADR-0001](./0001-multi-kind-compiler-architecture.md).
It owns the on-disk SSOT contract; [ADR-0003](./0003-generation-pipeline.md)
owns how that SSOT becomes per-client output.
The authoritative specification lives at [`docs/format-spec.md`](../format-spec.md).
This ADR records the design decisions; the spec records the contract.
## Decision
### Three item kinds, one base schema
Rules (`RULE.md`), skills (`SKILL.md`), and agents (`AGENT.md`) share a
common frontmatter shape: `schema`, `name`, `description`, `license`,
`metadata`. Kind-specific fields are minimal:
- **Rules** add optional `scope.paths` for path-scoping.
- **Skills** add nothing — they follow the Agent Skills standard as-is.
- **Agents** add `mode`, `model`, `tools`, `preload-skills`.
### Vocabulary
The words **rules, skills, agents** stay throughout the schema, CLI, and
docs. Client-specific words (Copilot's "instructions", Claude's "subagent")
appear only in generated output and per-client documentation, never in
upskill's own surface.
### Directory-per-item layout
> **Superseded by [ADR-0006](./0006-flat-item-layout.md)** for the
> kind-subdir aspect. The decision that each item is its own directory
> stands; the requirement that items live under `rules/`, `skills/`,
> `agents/` subdirectories is dropped. Kind is determined by the
> entrypoint filename alone.
A directory per item — for all three kinds — enables ancillary resources
(templates, scripts, reference files) without future migration. Per
ADR-0006, item directories live directly under `<item-root>`:
```text
<item-root>/
├── <name-a>/RULE.md
├── <name-b>/SKILL.md
└── <name-c>/AGENT.md
```
An item directory MAY contain more than one entrypoint when the
entrypoints share a name; this expresses a tightly-coupled set of
kinds for one capability. See ADR-0006 for the full layout contract
and rationale.
The `.agents/` path is **not** a source-registry convention. It is
purely the consumer-side generated-output path for the opencode
canonical-store pattern (per [ADR-0003](./0003-generation-pipeline.md)).
Source registries SHOULD avoid `.agents/` as their root to prevent
confusion with the consumer-side meaning.
### `metadata` block for governance
A nested `metadata:` field carries versioning (`version`, quoted semver),
ownership (`author`), audience targeting (`audience`), and arbitrary
unknown keys. Implementations preserve unknown keys through round-trips so
external governance systems can layer on top without forking the schema.
### Client-specific passthrough blocks
Top-level `claude:`, `copilot:`, `opencode:` blocks carry fields that have
no neutral equivalent (Copilot's `excludeAgent`, opencode's `temperature`).
The generator merges each block only into the matching client's output;
non-matching blocks are stripped.
### Conditional content directives
`<!-- @client:X -->` / `<!-- @endclient -->` HTML comment blocks enable
minor per-client body variations without separate files. Comma-separated
client lists. Negation via `!` (`!opencode`). No nesting. Known client
identifiers: `claude`, `copilot`, `opencode`.
### Per-client override files
When directives become unwieldy, authors provide complete body overrides:
`RULE.claude.md`, `SKILL.copilot.md`, `AGENT.opencode.md`. Override files
contain body only; frontmatter always comes from the canonical entrypoint.
### Bundle as pure manifest
A bundle is a single `<name>.bundle.md` file listing items by their `name`
slug. Items live in separate item-source repos; bundles reference them,
never contain them. Bundles compose cleanly (installing multiple bundles
produces their union) and avoid content duplication.
### Schema versioning
Every entrypoint carries `schema: 1`. Implementations reject unknown
future versions with a clear upgrade message. Minor additions (new
optional fields) do not bump the schema; breaking changes (removed
fields, renamed fields, changed semantics, new required fields) do.
### Body content conventions
For portability across clients:
- **No H1 in body.** The generator produces an H1 from `name` for clients
that require one.
- **Describe capability, not specific tools.** "Search the codebase" — not
"use the `Read` tool."
- **MCP tools in `ServerName:tool_name` form.** Portable across clients;
client-specific internal forms (`mcp__server__tool`) stay out of SSOT.
- **No client-specific constructs.** No `$ARGUMENTS`, no `` !`cmd` ``,
no `@path` imports, no `#tool:` references.
- **File references via standard markdown links** with relative paths.
- **Fenced code blocks require language hints.** Use `text` when no
specific language applies.
## Consequences
**Positive.** Authors maintain content once; tooling does the per-client
mapping. Open-standard interop: agentskills.io-compatible tools consume
our skill output unchanged. Schema versioning means we can evolve without
silently breaking older toolchains.
**Negative.** Schema evolution requires version-aware handling. Adding a
required field is a breaking change forcing a schema bump. The spec is a
living document; keeping it and the implementation in sync requires
discipline.
## Alternatives considered
**(a) Custom proprietary schema unrelated to Agent Skills.** Rejected:
fragments the ecosystem; the open standard already covers skills well and
the `metadata` block provides sufficient extension surface for org-specific
concerns.
**(b) File-per-client with no SSOT.** Rejected: drift between client files
is inevitable at scale.
**(c) Inline content in bundles.** Rejected: forces bundles to grow with
every item change. Pure-manifest bundles compose cleanly and decouple item
versioning from bundle versioning.
## References
- Authoritative spec: [`docs/format-spec.md`](../format-spec.md)
- Parent ADR: [ADR-0001](./0001-multi-kind-compiler-architecture.md)
- Sibling ADRs: [ADR-0003](./0003-generation-pipeline.md),
[ADR-0004](./0004-cli-surface.md),
[ADR-0005](./0005-skills-sh-ecosystem-interop.md)
- Agent Skills open standard: <https://agentskills.io>