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 les artefacts agents et hooks dans le dossier Claude Code du projet. 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 | agents/<slug>.md sous <projectPath>/.claude | Markdown + frontmatter YAML |
HookWorkspaceMaterializer | hooks/<name>.json sous <projectPath>/.claude | JSON |
SkillWorkspaceMaterializer | skills/<name> sous <projectPath>/.claude | Dossier + manifest |
Tous écrivent dans le dossier Claude Code du 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 Mission Control”) 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 agents/<slug>/ sous <project>/.claude] ↓[Met à jour <project>/.arka-deck/agents/installed.json] ↓EventBus → 'agent.installed' (async)
Remove agent (forCatalogue.remove) ↓[Materializer.unmaterialize → supprime agents/<slug>/ sous <project>/.claude] ↓[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 sous .claude).
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