Customization
Everything you'd want to tune about a Foundry agent or workflow — persona, menus, templates, output paths, review layers, activation behavior — is data in TOML files, not prose buried in skill bodies. You override that data in your project; you never edit installed skill files.
Two surfaces, one mechanism:
- Per-skill overrides — three layers, resolved by
.foundry/scripts/resolve-customization.mjs. - Central config — four layers, resolved by
.foundry/scripts/resolve-config.mjs(identity, artifact paths, the agent roster).
The three-layer per-skill model
Every customizable skill ships a customize.toml declaring its surface and defaults. At activation, the skill resolves that surface through three layers:
<skill-dir>/customize.toml ← 1. skill defaults (installer-owned, overwritten on update)
.foundry/custom/<skill>.toml ← 2. team overrides (committed to your repo)
.foundry/custom/<skill>.user.toml ← 3. personal overrides (gitignored)Later layers win. The installer never touches anything under .foundry/custom/ — your overrides survive every update.
Agent-skills expose an [agent] table (persona, menu, facts); workflow skills expose a [workflow] table (templates, paths, gates, review layers, facts). Same merge rules for both.
The sparse-override rule
Override files contain only the fields you're changing — never a copy of the whole customize.toml. A sparse file is what makes updates safe: when a skill ships new defaults, everything you didn't override picks them up automatically.
# .foundry/custom/e11-agent-pm.toml — this is a COMPLETE, valid team override
[agent]
persistent_facts = ["All PRDs must name a rollout owner."]Merge semantics
| Field shape | Rule | Examples |
|---|---|---|
| Scalars (strings, booleans, numbers) | Override wins | icon, role, prd_template, on_complete, e11_platform_capability_map |
| Tables | Deep-merge (key by key, recursively) | [agent], [workflow], [agents.<code>] in central config |
Arrays of tables whose entries all carry code or id | Replace-or-append by key — matching key replaces that entry, new key appends | [[agent.menu]] (keyed by code), [[workflow.review_layers]], [[workflow.extra_query_keys]] (keyed by id) |
| All other arrays | Append (base, then team, then user, in order) | persistent_facts, activation_steps_prepend/_append, principles |
| Removal | No removal mechanism. Neutralize instead: replace a keyed entry with a disabled version (e.g. empty instruction), or override a scalar to false/"" | disable a review layer, turn off a gate |
Two consequences worth internalizing:
principlesandpersistent_factsappend — you extend the shipped persona, you don't replace it. (In older installs'customize.yaml, agentprinciplesreplaced. The upgrade flags this.)- Keyed arrays are surgical — overriding
[[agent.menu]]withcode = "PR"changes only that menu item; a new code adds an item.
How resolution runs
Skills resolve their surface at activation:
node {project-root}/.foundry/scripts/resolve-customization.mjs --skill <skill-dir> --key workflow--skill is the installed skill directory (the skill name derives from its basename); optional dotted --key narrows the output (agent, workflow.review_layers, workflow.on_complete); result is JSON on stdout. Missing override files are skipped silently; a malformed override warns on stderr and resolution proceeds without it; a missing base customize.toml is an error.
Which layer won? --explain. Both resolvers answer provenance questions directly:
node .foundry/scripts/resolve-customization.mjs --skill <skill-dir> --explain [--key workflow.review_layers]
node .foundry/scripts/resolve-config.mjs --project-root . --explain [--key modules.foundry]Output lists every resolved key with the layers that define it (defined_in), the merge rule that applied (override / append / keyed(code) / keyed(id)), and — for scalars — the winner layer whose file set the final value. When an agent behaves unexpectedly, this is the first debugging move (or just ask e11-customize "why does dev-flow behave like X?" — its explain path runs this for you).
Safe fallback (documented in every skill): if the script can't run and no override files exist, the skill reads its own customize.toml directly — nothing to merge, nothing to get wrong. If overrides do exist, the skill stops and asks for a working resolver (Node ≥ 20) instead of approximating the merge by hand — a stopped activation is recoverable, a silently mis-merged one is not. (e11-dev-auto, which runs unattended, ends blocked with condition customization resolver unavailable instead of asking.)
e11-customize — the guided way
You don't have to hand-write TOML. The e11-customize skill ([CF] from any menu) authors overrides conversationally:
- Classify intent — directed ("make the PM terse"), exploratory ("what can I customize?"), audit ("what have we overridden?"), or cross-cutting.
- Discover — runs its
list-customizable-skills.mjshelper to enumerate every installed skill with a customization surface, grouped agent/workflow, with existing-override status. - Pick the right surface — agent-level for persona/org-wide behavior, workflow-level for template swaps, paths, and step-specific behavior. If your intent isn't on the exposed surface, it says so plainly rather than inventing fields.
- Compose sparsely against the target's
customize.tomlfields. - Place it — team (
<skill>.toml, committed: policies, org conventions) or user (<skill>.user.toml, gitignored: personal tone, shortcuts). - Show the full TOML (or a diff), wait for your yes, write, verify by running the resolver and showing you the merged result. It never silently overwrites.
Central config (four layers)
Identity and project-level settings live in central config, resolved lowest-to-highest:
.foundry/config.toml ← installer-generated, team scope (module prompt answers)
.foundry/config.user.toml ← installer-generated, user scope (your name, language, skill level; gitignored)
.foundry/custom/config.toml ← hand-authored team overrides
.foundry/custom/config.user.toml ← hand-authored personal overridesnode {project-root}/.foundry/scripts/resolve-config.mjs --project-root {project-root} --key core --key modules.foundrySame merge rules. The first two files are regenerated on install (don't edit them — your prompt answers are preserved); the custom/ pair is yours.
What lives here:
- Identity —
user_name,communication_language,document_output_language,user_skill_level(user scope). - Paths —
output_folder,planning_artifacts,implementation_artifacts,project_knowledge,project_name. - The agent roster — one
[agents.<code>]table per agent (name, title, icon, team, description), written frommodule.yaml. Roster-aware skills (e11-multi-perspective,e11-refine,e11-retrospective) resolve their cast from here — add a custom persona to the roundtable by adding an[agents.<code>]table in.foundry/custom/config.toml. - The platform master switch —
e11_platform = falseunder[modules.foundry]in.foundry/custom/config.tomldisables everye11_platform_*gate at once (capability map, SDK composition, stamps, alignment check, pre-loads) — the one-line off switch for non-platform repos. Individual gates can still be toggled per skill.
The runtime module config .foundry/foundry/config.yaml is still written for skills that read it as a fallback; the TOML mirrors it.
Worked examples
1. Add a menu item to an agent
Give the PM a shortcut that runs the readiness check and then multi-perspective on the result. Keyed array — new code appends:
# .foundry/custom/e11-agent-pm.toml
[[agent.menu]]
code = "RR"
label = "Readiness roundtable"
skill = "e11-check-implementation-readiness"
note = "After the report writes, invoke skill:e11-multi-perspective on it."Verify: node .foundry/scripts/resolve-customization.mjs --skill .claude/skills/e11-agent-pm --key agent.menu — the merged menu shows the shipped items plus RR.
2. Disable the alloy review layer
e11-code-review ships five [[workflow.review_layers]] entries: blind-hunter, edge-case-hunter, verification-gap, acceptance-auditor, and alloy-auditor. There's no removal — an empty instruction disables a layer. For a repo with no @engineering11/* surface:
# .foundry/custom/e11-code-review.toml
[[workflow.review_layers]]
id = "alloy-auditor"
name = "Alloy Citation Auditor (disabled — non-platform repo)"
instruction = ""The id matches, so this entry replaces the shipped one. The same pattern works for e11-dev-auto's and e11-dev-flow's layer lists — each skill's layers are its own.
3. Change the PRD template
e11-prd exposes template scalars (prd_template, validation_checklist_template, …). Copy the shipped template out, point the override at your copy:
# .foundry/custom/e11-prd.toml
[workflow]
prd_template = "{project-root}/.foundry/custom/e11-prd-acme-template.md"Scalar — override wins; the skill renders your structure from the next run. (e11-customize automates the copy-then-point flow.) Output location works the same way: prd_output_path = "{project-root}/docs/prds".
4. Add a corpus to the knowledge layer
e11-knowledge exposes corpus_root (scalar) and extra_query_keys (keyed array, checked before the built-in key tables). Add a team runbook corpus and remap the corpus for a monorepo with hoisted node_modules:
# .foundry/custom/e11-knowledge.toml
[workflow]
corpus_root = "{project-root}/../../node_modules/@engineering11/alloy"
[[workflow.extra_query_keys]]
id = "runbook"
path_template = "runbooks/<name>.md"
root = "{project-root}/docs/ops"Now skill:e11-knowledge runbook:rotate-keys resolves docs/ops/runbooks/rotate-keys.md, and every built-in sdk:/pattern:/… key reads from the remapped root. See Knowledge layer.
Gotchas
- Wrong layer file name — the override must be named for the skill directory:
e11-agent-pm.toml, notpm.toml. Check Troubleshooting when an override doesn't land. - Wrong merge mode assumptions — putting a "replacement" list in
persistent_factsappends it after the shipped facts; a keyed-array entry without itsid/codeappends instead of replacing. - Commit team overrides.
.foundry/custom/*.toml(non-.user) is meant to be in git — it's your team's policy layer. The installer's.gitignoreentries already exclude the.user.tomlfiles.
Source
Resolver: src/scripts/resolve-customization.mjs · central config: src/scripts/resolve-config.mjs · guided skill: src/foundry/tasks/e11-customize/SKILL.md · roster + prompts: src/foundry/module.yaml
See also: Upgrading Foundry · Agents quick-reference · Troubleshooting.