agent-config-fanout
Installation
SKILL.md
Agent Config Fan-out
Route here when the job is to propagate one hand-edited master config into the native config formats of multiple agent CLIs (Claude, Codex, Kimi, Hermes, Copilot, future tools), or to build that propagation engine for a new domain.
Operating Principles
- One hand-edited source of truth. The master file (e.g.
hooks.master.json) is the only artifact edited by hand. Every per-target config and machine map is generated. - Ambiguity is detected, resolved once, and remembered. Divergent mappings across targets become lock-file entries (
hooks.mappings.lock.json). Re-syncs apply them automatically. - Generated output is deterministic and idempotent. A
--checkgate must return zero changed bytes when the master is unchanged. - Consumers fall back to an embedded default. A generated map going missing must not break the consumer; generated values merge over a small embedded fallback.
- Publishers stay normalized. In the Bloodbank reference, every agent CLI invokes one canonical entrypoint (
~/.agents/hooks/bloodbank/publish.py --client <agent> --hook <event>). Per-client code belongs behind adapters; legacy per-clientpublish.pyfiles are wrappers, not new implementation homes. checkgates CI;sync/applywrites;--resolverecords decisions. Never hand-edit a generated file.- Destination topology is a security boundary. In the skill fan-out engine every destination is validated before any clone, cache write, or link change, and re-validated at each mutation — one unsafe or broken symlink must produce zero mutation. Packs tighten this further: sealed payloads are checksum-verified and may contain no symlinks at all. See references/skill-packs.md → Security invariants.
- A pack is one declaration, not a hand-expanded list.
packs[]names a versioned directory of skills; its members are resolved, verified, and fanned out as a unit. Redundancy pruning runs BEFORE override: askills[]entry that a pack declared in the same manifest already provides is dropped and the pack member wins — it is not an override. Only entries that SURVIVE pruning override a pack member of the same name. ("An explicitskills[]entry always wins" was the pre-revision rule and a shipped bug.) See references/skill-packs.md → Precedence.