Skip to content

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:

text
<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.

toml
# .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 shapeRuleExamples
Scalars (strings, booleans, numbers)Override winsicon, role, prd_template, on_complete, e11_platform_capability_map
TablesDeep-merge (key by key, recursively)[agent], [workflow], [agents.<code>] in central config
Arrays of tables whose entries all carry code or idReplace-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 arraysAppend (base, then team, then user, in order)persistent_facts, activation_steps_prepend/_append, principles
RemovalNo 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:

  • principles and persistent_facts append — you extend the shipped persona, you don't replace it. (In older installs' customize.yaml, agent principles replaced. The upgrade flags this.)
  • Keyed arrays are surgical — overriding [[agent.menu]] with code = "PR" changes only that menu item; a new code adds an item.

How resolution runs

Skills resolve their surface at activation:

bash
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:

bash
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:

  1. Classify intent — directed ("make the PM terse"), exploratory ("what can I customize?"), audit ("what have we overridden?"), or cross-cutting.
  2. Discover — runs its list-customizable-skills.mjs helper to enumerate every installed skill with a customization surface, grouped agent/workflow, with existing-override status.
  3. 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.
  4. Compose sparsely against the target's customize.toml fields.
  5. Place it — team (<skill>.toml, committed: policies, org conventions) or user (<skill>.user.toml, gitignored: personal tone, shortcuts).
  6. 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:

text
.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 overrides
bash
node {project-root}/.foundry/scripts/resolve-config.mjs --project-root {project-root} --key core --key modules.foundry

Same 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:

  • Identityuser_name, communication_language, document_output_language, user_skill_level (user scope).
  • Pathsoutput_folder, planning_artifacts, implementation_artifacts, project_knowledge, project_name.
  • The agent roster — one [agents.<code>] table per agent (name, title, icon, team, description), written from module.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 switche11_platform = false under [modules.foundry] in .foundry/custom/config.toml disables every e11_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:

toml
# .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:

toml
# .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:

toml
# .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:

toml
# .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, not pm.toml. Check Troubleshooting when an override doesn't land.
  • Wrong merge mode assumptions — putting a "replacement" list in persistent_facts appends it after the shipped facts; a keyed-array entry without its id/code appends 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 .gitignore entries already exclude the .user.toml files.

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.

Proprietary to Engineering11 LLC.