Pattern Materializer
Un Materializer transforme un artefact logique (agent, hook, skill) en fichiers sur disque dans un dossier projet. Le pattern est codifié dans ADR 0003 : le core ne fait jamais d’écriture directe pour ces artefacts ; il passe par un port dédié.
Pourquoi un pattern dédié
Section intitulée « Pourquoi un pattern dédié »Sans Materializer, le core écrirait directement des fichiers .claude/agents/*.md, .claude/hooks/*.json, etc. Cela créerait plusieurs problèmes :
- Couplage : le core connaîtrait le format de fichier d’un agent (Markdown YAML, JSON, etc.) — ce n’est pas son métier.
- Frontières : impossible de mocker l’écriture pour les tests sans patcher
fs. - Évolution : changer le format (par exemple ajouter une signature) demanderait de toucher au core.
Le Materializer isole tout ça derrière un port :
export interface AgentWorkspaceMaterializer { materialize(input: MaterializeAgentInput): Promise<MaterializedAgent>; unmaterialize(input: { projectPath: string; sourceId: string }): Promise<void>; list(projectPath: string): Promise<readonly MaterializedAgent[]>;}Le use-case buildForCatalogue consomme ce port. L’implémentation concrète ClaudeAgentWorkspaceMaterializer connaît, elle, le format Claude Code.
Materializers livrés
Section intitulée « Materializers livrés »| Materializer | Cible | Format de sortie |
|---|---|---|
ClaudeAgentWorkspaceMaterializer | .claude/agents/<slug>.md | Markdown + frontmatter YAML |
HookWorkspaceMaterializer | .claude/hooks/<name>.json | JSON |
SkillWorkspaceMaterializer | .claude/skills/<name>/ | Dossier + manifest |
Tous écrivent dans le dossier projet (<projectPath>/.claude/), jamais ailleurs. L’allowlist Filesystem valide les chemins.
Quand utiliser un nouveau Materializer
Section intitulée « Quand utiliser un nouveau Materializer »- Vous voulez exposer un artefact logique (par exemple “policy de gouvernance”) en tant que fichier consommable par un outil externe (un éditeur, un agent IA, un CI).
- Le format de sortie peut évoluer sans que le core en soit affecté.
- Plusieurs cibles peuvent exister (par exemple un agent pour Claude Code + un agent pour Codex CLI → deux Materializers).
Évitez le pattern Materializer pour des écritures purement internes (sessions chat, mémoire) — celles-ci passent par leur store dédié.
Cycle materialize / unmaterialize
Section intitulée « Cycle materialize / unmaterialize »Install agent (use-case forCatalogue.install) ↓[Lit profil depuis Cortex] ↓[Materializer.materialize → écrit .claude/agents/<slug>.md] ↓[Met à jour <project>/.arka-deck/agents/installed.json] ↓EventBus → 'agent.installed' (async)
Remove agent (forCatalogue.remove) ↓[Materializer.unmaterialize → supprime .claude/agents/<slug>.md] ↓[Met à jour installed.json] ↓EventBus → 'agent.removed' (async)Le suivi installed.json permet une purge sûre : seuls les fichiers tracés sont supprimés, jamais les artefacts de l’utilisateur (autres agents non-arka-deck dans .claude/agents/).
Test d’un Materializer
Section intitulée « Test d’un Materializer »L’implémentation concrète est testable comme tout adapter. Pour les use-cases qui le consomment, on injecte un FakeAgentWorkspaceMaterializer :
const fakeMaterializer: AgentWorkspaceMaterializer = { async materialize(input) { return { /* ... */ }; }, async unmaterialize() {}, async list() { return []; },};
const forCatalogue = buildForCatalogue({ catalogueClient, agentMaterializer: fakeMaterializer, // ...});Voir aussi
Section intitulée « Voir aussi »- ADR : ../../adr/0003-materializer-pattern.md
- Vue d’ensemble : ./overview
- Ports outbound : ./ports-outbound