Layer Precedence
When compiling resources, xcaffold resolves configuration through a layered hierarchy. Each layer narrows or overrides the previous.
The Resolution Hierarchy
From lowest to highest priority:
- Global config (
~/.xcaffold/global.xcaf) — project-wide defaults inherited by all resources viaextends: global. - Resource definition — the base
.xcaffile for the resource (xcaf/agents/<id>.xcaf,xcaf/skills/<id>.xcaf, etc.). - Blueprint targets — when
--blueprintis active and the named blueprint declares atargets:list, that list controls which providers are compiled. - Project targets (
project.xcaf) — the project-wide compilation target list underproject.targets. - Override files (
<resource>.<provider>.xcaf) — provider-specific field values that replace or clear base values for one provider only. --targetflag — CLI imperative override; highest priority.
Target Resolution
xcaffold apply determines which providers to compile for with this
four-tier precedence (first match wins):
--target flag (if set by the caller)
└─ blueprint.targets (if --blueprint is active and blueprint has targets)
└─ project.targets (from project.xcaf)
└─ error: "no compilation targets configured"Blueprints with their own targets: list compile for exactly those
providers — they do not fall through to project targets. A blueprint
without targets: also does not fall through to project targets; it
errors, requiring you to add targets to the blueprint or pass --target.
When no --target flag is set and no targets are configured anywhere,
xcaffold apply exits with an error rather than defaulting to any
provider.
Override Merge Rules
When an override file exists for a resource (e.g., developer.cursor.xcaf
for a developer agent), the compiler merges it with the base resource
before passing to the renderer:
| Field Type | Merge Behavior |
|---|---|
Scalar (name, model, description) | Override value replaces base |
Boolean (readonly, background) | Override value replaces base |
List (tools, skills, rules, allowed-tools) | See ClearableList semantics below |
Map (mcp-servers, hooks) | Deep merge — override keys win on conflict |
ClearableList merge rules (list fields only):
| Override Value | Result |
|---|---|
| Absent in override | Inherit base value |
[] or ~ | Clear — base value is removed |
[a, b] | Replace — override values used, base discarded |
See Field Model for the full ClearableList
specification.
Examples
Single-Provider Project
kind: project
version: "1.0"
name: my-project
targets: [claude]All resources compile for Claude only. Running xcaffold apply without
--target uses this list.
Multi-Provider Project
kind: project
version: "1.0"
name: my-project
targets: [claude, gemini]Each resource compiles twice — once per target. Override files
(agent.gemini.xcaf) customize fields per provider without duplicating
the base definition.
Blueprint with Its Own Targets
kind: project
version: "1.0"
name: my-project
targets: [claude]
blueprints:
mobile:
name: mobile
targets: [cursor, copilot]
agents: [mobile-dev]Running xcaffold apply --blueprint mobile compiles mobile-dev for
cursor and copilot only, regardless of the project-level targets: [claude]. The blueprint targets take precedence.
Provider-Specific Override
kind: agent
version: "1.0"
name: reviewer
model: sonnet
tools: [Read, Grep, Glob]
kind: agent
version: "1.0"
name: reviewer
tools: [] # cleared — gemini reviewer gets no tools listWhen compiling for gemini, the tools field is cleared. When compiling
for claude, the base [Read, Grep, Glob] is used.