# NeuroBrix Runtime — Analyse Architecturale & Plan d'Optimisation BRIX

**Date:** Fevrier 2026
**Scope:** `src/neurobrix/core/` — Moteur d'inference et d'execution
**Objectif:** Transformer le runtime monolithique en architecture modulaire a briques (BRIX)
**Statut:** Document de reference — Aucun code modifie

---

## Table des Matieres

1. [Etat des Lieux](#1-etat-des-lieux)
2. [Inventaire Complet du Code](#2-inventaire-complet-du-code)
3. [Analyse des Problemes Architecturaux](#3-analyse-des-problemes-architecturaux)
4. [Anatomie des Monolithes](#4-anatomie-des-monolithes)
5. [Analyse du CFG — Double Implementation](#5-analyse-du-cfg--double-implementation)
6. [Analyse de la Tokenisation — Duplication](#6-analyse-de-la-tokenisation--duplication)
7. [Analyse du KV Cache — Brique Excellente](#7-analyse-du-kv-cache--brique-excellente)
8. [Analyse du Scheduler/Sampler — Modele a Suivre](#8-analyse-du-schedulersampler--modele-a-suivre)
9. [Points Forts Actuels](#9-points-forts-actuels)
10. [Vision BRIX — Architecture Cible](#10-vision-brix--architecture-cible)
11. [Plan de Refactoring Detaille](#11-plan-de-refactoring-detaille)
12. [Carte des Dependances Semantiques](#12-carte-des-dependances-semantiques)
13. [Hardcodes & Fuites Semantiques](#13-hardcodes--fuites-semantiques)
14. [Priorites & Sequencement](#14-priorites--sequencement)
15. [Criteres de Validation](#15-criteres-de-validation)

---

## 1. Etat des Lieux

### 1.1 Metriques Globales

| Metrique | Valeur |
|----------|--------|
| Fichiers Python dans `core/` | 95+ |
| Lignes totales `core/` | 35,805 |
| Flow handlers (logique metier) | 2,296 lignes (autoregressive + iterative_process) |
| Briques deja modulaires | 3 (scheduler, sampler, kv_cache) |
| Briques a creer | 2 (TextProcessor, CFGEngine) |
| Briques a refactorer | 2 (FlowEngine, ComponentHandler) |

### 1.2 Architecture Actuelle

```
                    neurobrix run --model X --hardware Y --prompt "..."
                                        |
                                        v
                            ┌──────────────────────┐
                            │   RuntimeExecutor     │  892 lignes
                            │   (orchestrateur)     │  Setup + dispatch
                            └──────────┬───────────┘
                                       |
                        ┌──────────────┼──────────────┐
                        |              |              |
                        v              v              v
              ┌─────────────┐  ┌────────────┐  ┌──────────────┐
              │ iterative_  │  │ autoregres-│  │ static_graph │
              │ process     │  │ sive       │  │ forward_pass │
              │ 807 lignes  │  │ 1489 lignes│  │ (simples)    │
              │ MONOLITHE   │  │ MONOLITHE  │  │ OK           │
              └──────┬──────┘  └──────┬─────┘  └──────────────┘
                     |                |
        ┌────────────┼────────┐      |
        |            |        |      |
        v            v        v      v
   ┌─────────┐ ┌────────┐ ┌────┐ ┌─────────────┐
   │CFGExec. │ │Tokeniz.│ │Sch.│ │GraphExecutor│
   │365 lig. │ │inline  │ │229 │ │1800 lignes  │
   │diffusion│ │duplique│ │OK  │ │performant   │
   │seulement│ │        │ │    │ │             │
   └─────────┘ └────────┘ └────┘ └──────┬──────┘
                                         |
                                    ┌────┼────┐
                                    |         |
                                    v         v
                              ┌──────────┐ ┌──────────────┐
                              │Compiled  │ │KVCacheWrapper│
                              │Sequence  │ │722 lignes    │
                              │1837 lig. │ │excellente    │
                              │zero-over.│ │brique        │
                              └──────────┘ └──────────────┘
```

### 1.3 Le Probleme Central

Les deux flow handlers (`autoregressive.py` et `iterative_process.py`) sont des **monolithes** qui melangent:
- Tokenisation du prompt
- Encodage texte (positif + negatif)
- Logique CFG (batching, formule, masques)
- Initialisation KV cache
- Boucle principale (denoising ou decode)
- Sampling des tokens
- Decodage de la sortie

Chaque nouveau modele ou feature necessite de modifier ces fichiers enormes, avec des branches `if gen_type == "..."` qui s'accumulent.

---

## 2. Inventaire Complet du Code

### 2.1 Fichiers Critiques (Hot Path)

| Fichier | Lignes | Role | Sante |
|---------|--------|------|-------|
| `flow/autoregressive.py` | **1,489** | LLM + VQ image flow | MONOLITHE — 23 branches conditionnelles |
| `runtime/graph_executor.py` | **1,800** | Execution TensorDAG | Performant — pas besoin de toucher |
| `runtime/graph/compiled_sequence.py` | **1,837** | Execution zero-overhead | Excellent — ne pas toucher |
| `runtime/executor.py` | **892** | Orchestrateur principal | OK — quelques hardcodes |
| `flow/iterative_process.py` | **807** | Diffusion flow | Monolithe — 11 branches semantiques |
| `runtime/graph/kv_cache_wrapper.py` | **722** | Cache KV pre-alloue | Excellente brique |
| `runtime/resolution/input_synthesizer.py` | **488** | Synthese inputs manquants | OK — DATA-DRIVEN |
| `runtime/factory.py` | **455** | Factory executors | OK |
| `module/autoregressive/samplers.py` | **406** | Samplers (top-k/p, temp) | Bonne brique |
| `cfg/cfg_executor.py` | **365** | CFG pour diffusion | Bonne base — diffusion seulement |
| `runtime/resolution/output_extractor.py` | **342** | Extraction outputs | OK — quelques fuites |
| `module/autoregressive/generator.py` | **337** | Generateur autoregressif | Bonne brique |
| `components/handlers/transformer_handler.py` | **321** | Handler transformer | OK |
| `runtime/resolution/variable_resolver.py` | **319** | Resolution variables | Excellente brique |

### 2.2 Fichiers Secondaires

| Fichier | Lignes | Role |
|---------|--------|------|
| `module/tokenizer/factory.py` | 274 | TokenizerWrapper |
| `runtime/dtype_adapter.py` | 244 | Adaptation dtype |
| `module/scheduler/factory.py` | 229 | Factory schedulers |
| `components/handlers/text_encoder_handler.py` | 225 | Handler encodeur |
| `module/autoregressive/factory.py` | 202 | Factory generateurs |
| `components/base.py` | 195 | ABC ComponentHandler |
| `runtime/resolution/input_resolver.py` | 190 | Resolution inputs |
| `cfg/strategy.py` | 149 | CFG math pure |
| `flow/base.py` | 141 | ABC FlowHandler + registry |
| `components/handlers/vae_handler.py` | 139 | Handler VAE |
| `runtime/loader.py` | 136 | RuntimePackage |
| `components/registry.py` | 88 | Handler registry |
| `components/handlers/default_handler.py` | 32 | Handler par defaut |

### 2.3 Total par Categorie

| Categorie | Fichiers | Lignes | % du total |
|-----------|----------|--------|------------|
| Flow handlers | 4 | 2,437 | 7% |
| Runtime execution | 5 | 5,784 | 16% |
| Resolution/binding | 4 | 1,339 | 4% |
| Component handlers | 6 | 1,000 | 3% |
| CFG | 3 | 514 | 1% |
| Modules (scheduler/tokenizer/generator) | 10 | 2,000+ | 6% |
| Prism/Strategy | 10 | 3,000+ | 8% |
| Validators | 10 | 3,000+ | 8% |
| Autre (config, io, dtype, etc.) | 40+ | 17,000+ | 47% |

---

## 3. Analyse des Problemes Architecturaux

### Probleme 1 — `autoregressive.py` est un monolithe (1,489 lignes)

**Symptomes:**
- 16 methodes dans une seule classe
- 23 branches `gen_type`/`family` dispersees
- Tokenisation, CFG, KV cache, decode — tout dans un fichier
- Ajouter un nouveau type de generation = toucher partout

**Methodes de `AutoregressiveHandler`:**

| Methode | Lignes | Responsabilite |
|---------|--------|----------------|
| `__init__` | 38-66 | Initialisation + 6 attributs d'etat |
| `execute` | 76-552 | **476 lignes** — orchestration complete |
| `_create_generator` | 554-622 | Factory VQ vs Text generator |
| `_tokenize_prompt` | 624-707 | Tokenisation + chat template |
| `_get_lm_trace_seq_len` | 709-722 | Extraction seq_len du graphe |
| `_get_graph_trace_seq_len` | 724-739 | seq_len du graphe general |
| `_get_graph_input_name` | 741-754 | Nom d'input du graphe |
| `_run_component_with_padding` | 756-792 | Execution avec padding |
| `_run_embed_with_padding` | 794-826 | Embedding avec padding |
| `_decode_output` | 828-894 | Decodage tokens → sortie |
| `_load_lm_head_weight` | 895-929 | Chargement poids lm_head |
| `_compute_logits` | 931-958 | Calcul logits via matmul |
| `_init_graph_lm_executor` | 964-1150 | **186 lignes** — Init GraphExecutor + KV |
| `_graph_lm_prefill` | 1152-1293 | **141 lignes** — Prefill KV cache |
| `_graph_lm_decode_step` | 1295-1441 | **146 lignes** — Decode step |
| `_detect_int_arange_for_rope` | 1443-1476 | Detection position encoding |
| `_unload_graph_lm` | 1478-1489 | Cleanup |

**Branches critiques (`gen_type`/`family`):**

| Ligne | Condition | Impact |
|-------|-----------|--------|
| 152-154 | `gen_type in ("autoregressive_text", "autoregressive_image")` | Path GraphExecutor |
| 184-192 | `"lm_head" in neural_components` | LLM pur (text only) |
| 240-241 | `uses_inputs_embeds and gen_type == "autoregressive_image"` | VQ image embedding |
| 313-319 | `accumulated_embeds is not None` | VQ embedding accumulation |
| 414-441 | `gen_type == "autoregressive_image"` | VQ token offset |
| 487-501 | VQ token offset decision | Text vs image branching |
| 568-589 | Image vs text validation | Generator creation |
| 836-894 | `gen_type == "autoregressive_image"` | Image output decoding |

### Probleme 2 — CFG implemente deux fois

**Implementation 1:** `cfg/cfg_executor.py` (365 lignes)
- Utilisee par `iterative_process.py` uniquement
- Batched mode (batch=2) + Sequential mode (TP)
- Trouve encoder_hidden_states via topology

**Implementation 2:** Inline dans `autoregressive.py`
- Lignes 141-165, 330-365, 429-431
- CFG pour VQ image (Janus)
- Meme formule mais reimplementee localement

**Consequence:** Un bug CFG doit etre corrige a 2 endroits.

### Probleme 3 — Tokenisation dupliquee

- `iterative_process.py:_preprocess_inputs()` (lignes 715-807)
- `autoregressive.py:_tokenize_prompt()` (lignes 624-707)
- Les deux font: charger tokenizer → encoder prompt → gerer padding → gerer attention_mask
- Pas de brique commune

### Probleme 4 — KV Cache init couplee au flow handler

L'initialisation du KV cache est **entierement dans** `autoregressive.py:_init_graph_lm_executor()` (186 lignes):
- Detection SDPA ops dans le graphe
- Configuration `KVCacheConfig` depuis `defaults.json`
- Creation `KVCacheAttentionWrapper`
- Registration des intercepteurs

Ce code devrait etre une brique autonome avec une interface declarative.

### Probleme 5 — Extraction output avec fuites semantiques

`output_extractor.py:extract_hidden_states()` contient:
```python
# Hardcoded key names
for key in ["hidden_states", "last_hidden_state"]:
    ...
# Hardcoded dimension range
HIDDEN_DIM_MIN = 512
HIDDEN_DIM_MAX = 16384
```

Devrait etre resolu par topology declarations.

---

## 4. Anatomie des Monolithes

### 4.1 `autoregressive.py:execute()` — 476 lignes en une methode

```
execute()
├── 1. Lecture topology (generation info, components)      ~30 lig
├── 2. Detection graph LM path                             ~20 lig
├── 3. Creation generateur (VQ ou Text)                    ~15 lig
├── 4. TOKENISATION du prompt                              ~40 lig  ← devrait etre brique
├── 5. Init GraphExecutor + KV cache                       ~30 lig  ← devrait etre brique
├── 6. Chargement lm_head weights                          ~20 lig
├── 7. PREFILL KV cache                                    ~30 lig  ← devrait etre brique
├── 8. BOUCLE DE GENERATION (N tokens)                    ~200 lig
│   ├── a. Decode step (GraphExecutor)                     ~40 lig
│   ├── b. Extract hidden states                           ~20 lig
│   ├── c. Compute logits                                  ~15 lig
│   ├── d. CFG application (VQ image only)                 ~25 lig  ← devrait etre brique
│   ├── e. Sampling via generator                          ~15 lig
│   ├── f. VQ token offset                                 ~30 lig
│   ├── g. Prepare next input (embed + aligner)            ~40 lig
│   └── h. Check stop criteria                             ~15 lig
├── 9. DECODAGE SORTIE                                     ~40 lig  ← devrait etre brique
│   ├── VQ image → gen_vision_model                        ~25 lig
│   └── Text → detokenize                                  ~15 lig
└── 10. Cleanup (unload)                                   ~20 lig
```

### 4.2 `iterative_process.py:execute()` — Structure plus propre

```
execute()
├── 1. _preprocess_inputs()       # Tokenisation        ~90 lig ← devrait etre brique
├── 2. _get_cfg_settings()        # CFG config           ~100 lig
├── 3. _execute_pre_loop()        # Encodeurs texte      ~56 lig
│   └── _execute_negative_encoding()  # CFG negatif      ~110 lig
├── 4. _execute_main_loop()       # Boucle denoising    ~197 lig
│   ├── Scheduler timesteps
│   ├── CFG batching ou guidance embed
│   ├── Execute component
│   └── Scheduler step
├── 5. _execute_post_loop()       # VAE decoder          ~51 lig
└── 6. Return outputs
```

**Observation:** `iterative_process.py` est deja mieux structure avec des methodes privees, mais la tokenisation et le CFG sont toujours inline.

---

## 5. Analyse du CFG — Double Implementation

### 5.1 `cfg/cfg_executor.py` — Brique pour Diffusion

```python
class CFGExecutor:
    """Executes components with Classifier-Free Guidance."""

    def execute_component_with_cfg(
        self, comp_name, current_state, timestep, guidance_scale, encoder_dtype
    ) -> Dict[str, torch.Tensor]:
        """
        Mode BATCHED: [uncond, cond] → single forward → split → apply formula
        Mode SEQUENTIAL: 2 x batch=1 (pour TP strategy)
        """

    def _should_use_sequential_cfg(self, comp_name) -> bool:
        """TP strategy active pour ce composant?"""

    def _execute_sequential_cfg(self, comp_name, ...) -> Dict:
        """2 passes separees pour TP"""

    def _get_encoder_hidden_states_key(self) -> Optional[str]:
        """Trouve le variable key via topology connections"""
```

**Points forts:**
- ZERO HARDCODE — encoder key trouvee via topology
- Supporte batched + sequential
- Gere attention_mask negatif
- Cast dtype pour stabilite numerique

**Limitation:** Ne fonctionne que pour diffusion (attend `current_state`, `timestep`, `encoder_dtype`).

### 5.2 `cfg/strategy.py` — Math Pure

```python
class CFGMode(Enum):
    DISABLED = auto()     # scale <= 1.0
    BATCHED = auto()      # [cond, uncond] ensemble
    SEQUENTIAL = auto()   # cond/uncond separes

class CFGStrategy:
    def prepare_inputs(cond, uncond) -> (batched, orig_batch_size)
    def apply_guidance(cond_output, uncond_output, scale) -> output
    # Formule: output = uncond + scale * (cond - uncond)
```

**Point fort:** 100% math pure, zero couplage. **C'est LE modele a suivre.**

### 5.3 CFG Inline dans `autoregressive.py`

Reimplementation inline pour VQ image (Janus):

```python
# Ligne ~330-365 dans execute():
if cfg_enabled and gen_type == "autoregressive_image":
    # Run with negative embeddings
    logits_uncond = self._compute_logits(hidden_states_uncond)
    logits_cond = self._compute_logits(hidden_states_cond)
    logits = logits_uncond + cfg_scale * (logits_cond - logits_uncond)
```

**Meme formule exacte** que `CFGStrategy.apply_guidance()`, mais reimplementee.

### 5.4 Vision CFG Unifiee

Un `CFGEngine` unique qui:
1. Accepte n'importe quel type de sortie (noise_pred, logits, hidden_states)
2. Gere batched, sequential, et disabled
3. Delegue la preparation des inputs au flow handler
4. Applique la formule universelle

---

## 6. Analyse de la Tokenisation — Duplication

### 6.1 Dans `iterative_process.py` (lignes 715-807)

```python
def _preprocess_inputs(self):
    """Tokenize prompt et prepare inputs."""
    tokenizer = self.ctx.modules.get("tokenizer")
    if tokenizer is None:
        return  # No tokenizer = skip (VAE-only models)

    prompt = self.ctx.variable_resolver.get("global.prompt", "")

    # Encode with tokenizer
    encoded = tokenizer(prompt, max_length=max_seq_len, ...)

    # Store in variable resolver
    self.ctx.variable_resolver.set("global.input_ids", encoded["input_ids"])
    self.ctx.variable_resolver.set("global.attention_mask", encoded["attention_mask"])
```

### 6.2 Dans `autoregressive.py` (lignes 624-707)

```python
def _tokenize_prompt(self, prompt, tokenizer):
    """Tokenize with chat template support."""
    # Chat template handling
    if hasattr(tokenizer, 'chat_template') and tokenizer.chat_template:
        messages = [{"role": "user", "content": prompt}]
        formatted = tokenizer.apply_chat_template(messages, ...)
    else:
        formatted = prompt

    # Encode
    encoded = tokenizer(formatted, max_length=max_length, ...)
    return encoded["input_ids"], encoded["attention_mask"]
```

### 6.3 Differences entre les deux

| Feature | iterative_process | autoregressive |
|---------|-------------------|----------------|
| Chat template | Non | Oui |
| Negative prompt | Oui (CFG) | Non (inline CFG) |
| Max length source | topology shapes | hardcode 512 (!!) |
| Padding side | Oui (left/right) | Non |
| Multi-encoder | Oui (text_encoder_1, _2, _3) | Non (single encoder) |

### 6.4 Vision TextProcessor Unifiee

```
TextProcessorBrick:
  __init__(tokenizer_module, topology_shapes, defaults)

  tokenize(prompt, max_length=None) → {input_ids, attention_mask}
  tokenize_negative(negative_prompt, max_length) → {input_ids, attention_mask}
  apply_chat_template(prompt) → formatted_text
  get_max_sequence_length() → int  # CASCADE: topology > defaults > family

  # Multi-encoder support
  tokenize_for_encoder(prompt, encoder_name) → {input_ids, attention_mask}
```

---

## 7. Analyse du KV Cache — Brique Excellente

### 7.1 Architecture Actuelle

**Fichier:** `runtime/graph/kv_cache_wrapper.py` (722 lignes)

```python
@dataclass
class KVCacheConfig:
    num_layers: int
    num_kv_heads: int
    k_head_dim: int
    v_head_dim: int
    max_cache_len: int
    dtype: torch.dtype

class KVCacheLayer:
    """Single layer's K/V cache with O(1) append."""
    update(k, v) -> (k_with_cache, v_with_cache)
    clear()

class KVCacheAttentionWrapper:
    """Intercepte SDPA ops pour injection KV cache."""
    intercept_attention(q, k, v, ...) -> attn_output
    reset_for_new_sequence()
    set_decode_mode(actual_seq_len)
    get_cache_len() -> int
    get_interceptors() -> Dict[str, Callable]
```

**Points forts:**
- Pre-allocation lazy (alloue au premier forward)
- O(1) indexed writes (pas de concatenation)
- Multi-GPU support (FGP, TP, PP via `device_map`)
- 4 variantes SDPA interceptees (standard, efficient, flash, cudnn)

### 7.2 Probleme d'Integration

L'init du KV cache est **couplee** a `autoregressive.py:_init_graph_lm_executor()`:

```python
# 186 lignes dans autoregressive.py:
def _init_graph_lm_executor(self):
    # 1. Load graph
    graph = load_graph(comp_name, cache_path)

    # 2. Detect SDPA ops
    sdpa_ops = [op for op in graph["ops"] if "attention" in op["op_type"]]

    # 3. Build KV config from defaults
    kv_config = KVCacheConfig(
        num_layers=defaults["lm_config"]["num_layers"],
        num_kv_heads=defaults["lm_config"]["num_kv_heads"],
        ...
    )

    # 4. Create wrapper
    self._kv_wrapper = KVCacheAttentionWrapper(kv_config)

    # 5. Register interceptors on GraphExecutor
    executor.register_interceptors(self._kv_wrapper.get_interceptors())
```

### 7.3 Vision Integration Declarative

Le KV cache devrait s'initialiser depuis le NBX container:

```json
// components/language_model/runtime.json
{
  "kv_cache": {
    "enabled": true,
    "num_layers": 30,
    "num_kv_heads": 32,
    "k_head_dim": 128,
    "v_head_dim": 128,
    "max_cache_len": 704,
    "sdpa_ops": ["aten::scaled_dot_product_attention"]
  }
}
```

Le flow handler ne fait que:
```python
kv_wrapper = KVCacheAttentionWrapper.from_component_config(runtime_json)
executor.register_interceptors(kv_wrapper.get_interceptors())
```

---

## 8. Analyse du Scheduler/Sampler — Modele a Suivre

### 8.1 SchedulerFactory — Pattern Exemplaire

```python
class SchedulerFactory:
    _REGISTRY = {
        "DPMSolverMultistepScheduler": ("diffusion.dpm_solver_pp", "DPMSolverPPScheduler"),
        "DDIMScheduler": ("diffusion.ddim", "DDIMScheduler"),
        "EulerDiscreteScheduler": ("diffusion.euler", "EulerDiscreteScheduler"),
        "FlowMatchEulerDiscreteScheduler": ("flow.flow_euler", "FlowEulerScheduler"),
        "LCMScheduler": ("consistency.lcm", "LCMScheduler"),
        # ... 30+ entrees
    }

    @classmethod
    def create(cls, scheduler_type: str, **kwargs) -> Scheduler:
        if scheduler_type not in cls._REGISTRY:
            raise RuntimeError(f"ZERO FALLBACK: Unknown scheduler '{scheduler_type}'")
        module_path, class_name = cls._REGISTRY[scheduler_type]
        module = importlib.import_module(f".{module_path}", package=__package__)
        return getattr(module, class_name)(**kwargs)
```

**Pourquoi c'est excellent:**
- ZERO FALLBACK sur type inconnu
- Lazy import (import seulement quand necessaire)
- 30+ schedulers supportes sans un seul `if/elif`
- Ajout d'un nouveau scheduler = 1 ligne dans le registre

### 8.2 AutoregressiveFactory — Meme Pattern

```python
class AutoregressiveFactory:
    _REGISTRY = {
        "autoregressive_text": ("generator", "AutoregressiveGenerator"),
        "autoregressive_image": ("generator", "VQImageGenerator"),
    }
    _SAMPLER_REGISTRY = {
        "greedy": ("samplers", "GreedySampler"),
        "temperature": ("samplers", "TemperatureSampler"),
        "top_k": ("samplers", "TopKSampler"),
        "top_p": ("samplers", "TopPSampler"),
        "combined": ("samplers", "CombinedSampler"),
    }
```

### 8.3 Hierarchie des Samplers — Clean

```python
SamplerBase(ABC)
├── GreedySampler          # argmax
├── TemperatureSampler     # scale + multinomial
├── TopKSampler            # top-k filter + sample
├── TopPSampler            # nucleus sampling
└── CombinedSampler        # temperature + top_k + top_p chain
```

**Lecon:** Chaque brick devrait suivre ce pattern — registre + lazy import + ZERO FALLBACK.

---

## 9. Points Forts Actuels

### 9.1 Ce Qui Fonctionne et Ne Doit PAS Etre Touche

| Composant | Lignes | Raison de ne pas toucher |
|-----------|--------|--------------------------|
| `compiled_sequence.py` | 1,837 | Zero-overhead execution — ~5ns/op, parfait |
| `graph_executor.py` | 1,800 | Execution TensorDAG mature et optimisee |
| `kv_cache_wrapper.py` | 722 | Brique excellente, juste ameliorer l'integration |
| `variable_resolver.py` | 319 | System de pointeurs 100% DATA-DRIVEN |
| `scheduler/` (tout le module) | 1,000+ | Pattern registry exemplaire |
| `autoregressive/samplers.py` | 406 | Hierarchie propre |
| `autoregressive/generator.py` | 337 | Generation step clean |

### 9.2 Architecture DATA-DRIVEN — 95% Accomplie

Le systeme de resolution est **quasi-parfait**:

```
Variable Contract (variables.json)
    ↓ pointeurs
    "axis_0": "runtime.batch_size"
    "axis_1": "component.transformer.attributes.state_channels"
    ↓ resolution
    VariableResolver._resolve_pointer()
    ↓
    Tensor [batch_size, channels, H, W]
```

**Cascade des valeurs par defaut:**
```
Priorite 1: CLI arg (--height 512)
    ↓ si absent
Priorite 2: defaults.json (model-specifique)
    ↓ si absent
Priorite 3: family config (image.yml → height: 1024)
    ↓ si absent
ZERO FALLBACK: CRASH
```

### 9.3 FlowHandler Registry — Solide

```python
FLOW_REGISTRY: Dict[str, type] = {}

@register_flow("iterative_process")
class IterativeProcessHandler(FlowHandler): ...

@register_flow("autoregressive_generation")
class AutoregressiveHandler(FlowHandler): ...

# Dispatch:
handler = get_flow_handler(flow_type, ctx)
result = handler.execute()
```

### 9.4 ComponentHandler — Bonne Base

```python
class ComponentHandler(ABC):
    can_handle(class_name, component_type) -> bool
    set_runtime_resolution(height, width)
    transform_inputs(inputs, phase) -> inputs
    prepare_weights(weights, height, width) -> weights
    transform_outputs(outputs, phase) -> outputs
    finalize_embeddings(hidden_state, attention_mask, config) -> dict

# Handlers existants:
TextEncoderComponentHandler   # 225 lig
TransformerComponentHandler   # 321 lig
VAEComponentHandler           # 139 lig
DefaultComponentHandler       # 32 lig
```

---

## 10. Vision BRIX — Architecture Cible

### 10.1 Philosophie BRIX

```
BRIX = Module autonome avec:
  1. Interface claire (ABC ou Protocol)
  2. Factory avec registre (ZERO FALLBACK)
  3. Configuration declarative (depuis NBX container)
  4. Zero dependance vers d'autres briques
  5. Testable en isolation
```

### 10.2 Les 5 Briques Cibles

```
┌─────────────────────────────────────────────────────────────────┐
│                     ARCHITECTURE BRIX CIBLE                      │
│                                                                  │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────────────┐  │
│  │ TextProcessor│  │  CFGEngine   │  │    StateCache        │  │
│  │   (NOUVEAU)  │  │ (REFACTOR)   │  │  (AMELIORER INTEG.)  │  │
│  │              │  │              │  │                      │  │
│  │ - tokenize   │  │ - batched    │  │  - KV cache          │  │
│  │ - encode     │  │ - sequential │  │  - activation cache  │  │
│  │ - negative   │  │ - disabled   │  │  - lazy alloc        │  │
│  │ - chat tpl   │  │ - any output │  │  - multi-GPU         │  │
│  │ - multi-enc  │  │ - universal  │  │  - declarative init  │  │
│  └──────────────┘  └──────────────┘  └──────────────────────┘  │
│                                                                  │
│  ┌──────────────┐  ┌───────────────────────────────────────┐   │
│  │  Scheduler   │  │           FlowEngine                   │   │
│  │  / Sampler   │  │          (REFACTOR)                    │   │
│  │  (EXISTE)    │  │                                        │   │
│  │              │  │  iterative_process:                     │   │
│  │ - DPM++     │  │    pre_loop → loop → post_loop          │   │
│  │ - Euler     │  │    ~200 lig (vs 807 actuel)             │   │
│  │ - DDIM      │  │                                         │   │
│  │ - FlowEuler │  │  autoregressive:                        │   │
│  │ - LCM       │  │    tokenize → prefill → decode → output │   │
│  │ - VQ gen    │  │    ~300 lig (vs 1489 actuel)            │   │
│  │ - Samplers  │  │                                         │   │
│  └──────────────┘  └───────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────┘
```

### 10.3 Brique 1 — TextProcessor (NOUVEAU)

**Fichier cible:** `core/module/text/processor.py`

**Interface:**
```python
class TextProcessor:
    """
    Brique universelle de traitement texte.
    Gere tokenisation, chat template, padding, multi-encodeur.
    """
    def __init__(self, tokenizer: TokenizerWrapper, config: Dict):
        """
        Args:
            tokenizer: TokenizerWrapper from factory
            config: {max_length, padding_side, chat_template_enabled, ...}
        """

    def tokenize(self, text: str) -> TokenizedOutput:
        """Tokenise avec chat template si disponible."""

    def tokenize_negative(self, text: str) -> TokenizedOutput:
        """Tokenise prompt negatif (meme longueur que positif)."""

    def get_max_sequence_length(self) -> int:
        """CASCADE: topology > defaults > family config."""

    def tokenize_for_encoder(self, text: str, encoder_name: str) -> TokenizedOutput:
        """Multi-encodeur: tokenise pour un encodeur specifique."""

@dataclass
class TokenizedOutput:
    input_ids: torch.Tensor
    attention_mask: torch.Tensor
    padding_side: str
    original_length: int
```

**Fonctions a migrer:**
- `iterative_process.py:_preprocess_inputs()` (lignes 715-807)
- `autoregressive.py:_tokenize_prompt()` (lignes 624-707)
- `executor.py:_detect_tokenizer_max_length()` (lignes 371-425)
- Logic padding side de `input_synthesizer.py` (lignes 406-457)

**Impact:** Supprime ~200 lignes des flow handlers.

### 10.4 Brique 2 — CFGEngine (REFACTOR)

**Fichier cible:** `core/cfg/engine.py` (remplace `cfg_executor.py`)

**Interface:**
```python
class CFGEngine:
    """
    Moteur CFG universel — fonctionne pour diffusion ET autoregressif.
    """
    def __init__(self, config: CFGConfig):
        """
        Args:
            config: {guidance_scale, mode, encoder_key, negative_key}
        """

    def is_enabled(self) -> bool:
        """CFG actif si scale > 1.0."""

    def prepare_batch(
        self,
        positive: Dict[str, torch.Tensor],
        negative: Dict[str, torch.Tensor]
    ) -> Dict[str, torch.Tensor]:
        """
        Prepare inputs pour CFG (batched ou sequential).
        UNIVERSEL: fonctionne pour noise_pred, logits, hidden_states.
        """

    def apply(
        self,
        cond_output: torch.Tensor,
        uncond_output: torch.Tensor
    ) -> torch.Tensor:
        """
        Applique formule CFG: uncond + scale * (cond - uncond).
        """

    def split_batch(
        self,
        batched_output: torch.Tensor
    ) -> Tuple[torch.Tensor, torch.Tensor]:
        """Split batched output en [uncond, cond]."""

    @classmethod
    def from_topology(cls, topology: Dict, defaults: Dict) -> "CFGEngine":
        """
        Factory DATA-DRIVEN depuis topology + defaults.
        CASCADE: topology.extracted_values > defaults > family config.
        """

@dataclass
class CFGConfig:
    guidance_scale: float = 7.5
    mode: CFGMode = CFGMode.BATCHED
    do_classifier_free_guidance: bool = True
```

**Fonctions a migrer:**
- `cfg/cfg_executor.py` entier (365 lignes) → generaliser
- `cfg/strategy.py` entier (149 lignes) → integrer
- CFG inline de `autoregressive.py` (~60 lignes) → supprimer
- CFG settings de `iterative_process.py:_get_cfg_settings()` (~100 lignes)

**Impact:** Supprime ~300 lignes des flow handlers + unifie.

### 10.5 Brique 3 — StateCache (AMELIORER INTEGRATION)

**Fichier actuel:** `runtime/graph/kv_cache_wrapper.py` (722 lignes) — excellent, ne pas toucher le code.

**Changement:** Ajouter une factory declarative.

```python
class StateCacheFactory:
    """
    Factory pour creer et configurer le cache d'etat.
    Lit la configuration depuis le NBX container.
    """
    @classmethod
    def from_component(cls, comp_name: str, cache_path: Path) -> Optional[KVCacheAttentionWrapper]:
        """
        Cree KVCacheWrapper depuis runtime.json du composant.
        Retourne None si pas de KV cache declare.
        """
        runtime_json = load_json(cache_path / comp_name / "runtime.json")
        kv_config = runtime_json.get("kv_cache")
        if kv_config is None:
            return None

        return KVCacheAttentionWrapper(KVCacheConfig(
            num_layers=kv_config["num_layers"],
            num_kv_heads=kv_config["num_kv_heads"],
            ...
        ))
```

**Fonctions a migrer:**
- `autoregressive.py:_init_graph_lm_executor()` detection SDPA (lignes 1050-1100)
- `autoregressive.py:_init_graph_lm_executor()` KV config creation (lignes 1100-1150)

**Impact:** Supprime ~100 lignes de `autoregressive.py`.

### 10.6 Brique 4 — Scheduler/Sampler (NE PAS TOUCHER)

Deja en place et fonctionnel:

```
core/module/scheduler/
├── factory.py          # SchedulerFactory avec registre 30+ entries
├── base.py             # Scheduler ABC
├── config.py           # SchedulerConfigError
├── diffusion/          # DPM++, DDIM, Euler, EulerAncestral
├── flow/               # FlowEuler (Flux, SD3)
├── consistency/        # LCM
└── utils/              # Noise schedules, timestep utils

core/module/autoregressive/
├── factory.py          # AutoregressiveFactory
├── generator.py        # AutoregressiveGenerator, VQImageGenerator
└── samplers.py         # Greedy, Temperature, TopK, TopP, Combined
```

**Verdict:** Brique modele. Les autres briques doivent imiter ce pattern.

### 10.7 Brique 5 — FlowEngine (REFACTOR)

Les flow handlers deviennent des **orchestrateurs fins** qui appellent les briques:

**`iterative_process.py` CIBLE (~200 lignes au lieu de 807):**

```python
@register_flow("iterative_process")
class IterativeProcessHandler(FlowHandler):
    def execute(self):
        # 1. Tokenisation via TextProcessor brick
        text_processor = self.ctx.bricks.text_processor
        tokens = text_processor.tokenize(prompt)
        self.ctx.variable_resolver.set("global.input_ids", tokens.input_ids)

        # 2. CFG setup via CFGEngine brick
        cfg = self.ctx.bricks.cfg_engine
        if cfg.is_enabled():
            neg_tokens = text_processor.tokenize_negative(negative_prompt)

        # 3. Pre-loop: execute encodeurs
        for comp in pre_loop_components:
            self._execute_component(comp, "pre_loop")
            if cfg.is_enabled():
                self._execute_negative_encoding(comp)

        # 4. Main loop: scheduler-driven denoising
        scheduler = self.ctx.modules["scheduler"]
        for t in scheduler.timesteps:
            if cfg.is_enabled():
                output = cfg.execute_with_guidance(
                    comp_name, current_state, t
                )
            else:
                output = self._execute_component(loop_comp, "loop", t)
            current_state = scheduler.step(output, t, current_state)

        # 5. Post-loop: VAE decoder
        for comp in post_loop_components:
            self._execute_component(comp, "post_loop")

        return self.ctx.variable_resolver.resolve_all()
```

**`autoregressive.py` CIBLE (~300 lignes au lieu de 1489):**

```python
@register_flow("autoregressive_generation")
class AutoregressiveHandler(FlowHandler):
    def execute(self):
        # 1. Tokenisation via TextProcessor brick
        tokens = self.ctx.bricks.text_processor.tokenize(prompt)

        # 2. Init KV cache via StateCache brick
        kv_wrapper = StateCacheFactory.from_component(lm_comp, cache_path)

        # 3. Create generator via factory
        generator = AutoregressiveFactory.create(gen_type, config)

        # 4. Prefill
        hidden = self._prefill(tokens, kv_wrapper)

        # 5. Decode loop
        for step_idx in generator:
            logits = self._decode_step(kv_wrapper)

            # CFG via CFGEngine brick (VQ image only)
            if cfg.is_enabled():
                logits = cfg.apply(logits_cond, logits_uncond)

            next_token, is_done = generator.step(logits, step_idx)
            if is_done:
                break

        # 6. Decode output
        return self._decode_output(generator.get_generated_tokens())
```

**Impact:** Reduction de ~1,800 lignes combinee.

---

## 11. Plan de Refactoring Detaille

### Phase 1 — TextProcessor Brick (Priorite HAUTE)

| Etape | Action | Fichiers |
|-------|--------|----------|
| 1.1 | Creer `core/module/text/__init__.py` | NOUVEAU |
| 1.2 | Creer `core/module/text/processor.py` avec `TextProcessor` + `TokenizedOutput` | NOUVEAU |
| 1.3 | Migrer `_tokenize_prompt()` de `autoregressive.py` | autoregressive.py |
| 1.4 | Migrer `_preprocess_inputs()` de `iterative_process.py` | iterative_process.py |
| 1.5 | Migrer `_detect_tokenizer_max_length()` de `executor.py` | executor.py |
| 1.6 | Ajouter support multi-encodeur (text_encoder_1, _2, _3) | processor.py |
| 1.7 | Ajouter support padding side (left/right) | processor.py |
| 1.8 | Tests: tokenisation PixArt, Sana, Janus, DeepSeek | tests/ |

**Metriques attendues:**
- ~200 lignes supprimees des flow handlers
- ~250 lignes dans le nouveau module TextProcessor
- 0 duplication tokenisation

### Phase 2 — CFGEngine Brick (Priorite HAUTE)

| Etape | Action | Fichiers |
|-------|--------|----------|
| 2.1 | Creer `core/cfg/engine.py` avec `CFGEngine` + `CFGConfig` | NOUVEAU |
| 2.2 | Integrer logique de `cfg_executor.py` (generaliser) | engine.py |
| 2.3 | Integrer `strategy.py` dans `engine.py` | engine.py |
| 2.4 | Ajouter `from_topology()` factory | engine.py |
| 2.5 | Remplacer CFG inline dans `autoregressive.py` | autoregressive.py |
| 2.6 | Remplacer `_get_cfg_settings()` dans `iterative_process.py` | iterative_process.py |
| 2.7 | Supprimer `cfg_executor.py` et `strategy.py` (code migre) | SUPPRIMER |
| 2.8 | Tests: CFG batched/sequential/disabled pour diffusion + VQ | tests/ |

**Metriques attendues:**
- ~300 lignes supprimees des flow handlers
- ~300 lignes dans le nouveau CFGEngine (vs 514 actuelles dans 2 fichiers)
- 1 seule implementation CFG (vs 2 actuellement)

### Phase 3 — StateCache Factory (Priorite MOYENNE)

| Etape | Action | Fichiers |
|-------|--------|----------|
| 3.1 | Creer `core/module/cache/__init__.py` | NOUVEAU |
| 3.2 | Creer `core/module/cache/factory.py` avec `StateCacheFactory` | NOUVEAU |
| 3.3 | Ajouter `kv_cache` section dans `runtime.json` (forge build) | forge/importer/builder.py |
| 3.4 | Migrer KV cache init de `autoregressive.py:_init_graph_lm_executor()` | autoregressive.py |
| 3.5 | Tests: KV cache init declarative pour Janus, DeepSeek | tests/ |

**Metriques attendues:**
- ~100 lignes supprimees de `autoregressive.py`
- ~80 lignes dans StateCacheFactory
- Init KV cache declarative depuis NBX container

### Phase 4 — FlowEngine Simplification (Priorite MOYENNE)

| Etape | Action | Fichiers |
|-------|--------|----------|
| 4.1 | Refactorer `iterative_process.py:execute()` pour utiliser briques | iterative_process.py |
| 4.2 | Refactorer `autoregressive.py:execute()` pour utiliser briques | autoregressive.py |
| 4.3 | Creer `BrickContext` dans `FlowContext` pour acceder aux briques | base.py |
| 4.4 | Mettre a jour `RuntimeExecutor._create_flow_handler()` | executor.py |
| 4.5 | Tests de regression: PixArt, Sana, Janus, DeepSeek | tests/ |

**Metriques attendues:**
- `autoregressive.py`: 1489 → ~400 lignes
- `iterative_process.py`: 807 → ~250 lignes
- Reduction totale: ~1,600 lignes

### Phase 5 — Cleanup & Hardcode Elimination (Priorite BASSE)

| Etape | Action | Fichiers |
|-------|--------|----------|
| 5.1 | Supprimer hardcode `max_length=512` | autoregressive.py |
| 5.2 | Supprimer hardcode `or 4096` hidden_size | autoregressive.py |
| 5.3 | Supprimer hardcode `vae_scale = 8` | executor.py, graph_executor.py |
| 5.4 | Supprimer fuites semantiques dans `output_extractor.py` | output_extractor.py |
| 5.5 | Migration `_replace_with_state_variable` vers declarations | executor.py |

---

## 12. Carte des Dependances Semantiques

### 12.1 Dependances Actuelles (Problematiques)

```
autoregressive.py ──────► tokenizer/factory.py     (tokenisation inline)
                  ──────► cfg/                       (CFG inline, pas via brique)
                  ──────► kv_cache_wrapper.py        (init inline, 186 lignes)
                  ──────► autoregressive/generator.py (factory call)
                  ──────► graph_executor.py           (direct usage)
                  ──────► output_extractor.py         (direct usage)
                  ──────► variable_resolver.py        (direct usage)

iterative_process.py ──► tokenizer/factory.py      (tokenisation inline)
                     ──► cfg/cfg_executor.py        (via injection)
                     ──► scheduler/factory.py       (via modules)
                     ──► graph_executor.py          (via executors)
                     ──► output_extractor.py        (via injection)
                     ──► variable_resolver.py       (via ctx)
```

### 12.2 Dependances Cibles (BRIX)

```
autoregressive.py ──────► bricks.text_processor     (brique TextProcessor)
                  ──────► bricks.cfg_engine          (brique CFGEngine)
                  ──────► bricks.state_cache         (brique StateCacheFactory)
                  ──────► autoregressive/generator.py (inchange)
                  ──────► graph_executor.py           (inchange)

iterative_process.py ──► bricks.text_processor      (brique TextProcessor)
                     ──► bricks.cfg_engine           (brique CFGEngine)
                     ──► scheduler/factory.py        (inchange)
                     ──► graph_executor.py           (inchange)

FlowContext.bricks ──► TextProcessor instance
                   ──► CFGEngine instance
                   ──► StateCacheFactory ref
```

### 12.3 Flux de Donnees Cible

```
NBX Container
    │
    ├── manifest.json ──────► family, gen_type
    ├── topology.json ──────► connections, synthesis, shapes
    ├── runtime/defaults.json ──► guidance_scale, max_tokens, temperature
    ├── modules/tokenizer/ ────► TextProcessor
    └── components/*/runtime.json ──► KV cache config
    │
    v
RuntimeExecutor
    │
    ├── Initialise TextProcessor (depuis tokenizer module + defaults)
    ├── Initialise CFGEngine (depuis topology + defaults)
    ├── Initialise StateCacheFactory (ref)
    │
    v
FlowContext.bricks = BrickContext(
    text_processor=TextProcessor(...),
    cfg_engine=CFGEngine(...),
    state_cache_factory=StateCacheFactory
)
    │
    v
FlowHandler.execute() ──► Appelle briques via ctx.bricks
```

---

## 13. Hardcodes & Fuites Semantiques

### 13.1 Hardcodes a Eliminer

| Fichier | Ligne | Code | Correction |
|---------|-------|------|------------|
| `autoregressive.py` | 696 | `max_length=512` | TextProcessor.get_max_sequence_length() |
| `autoregressive.py` | 1219 | `or 4096` (hidden_size fallback) | ZERO FALLBACK: crash si absent |
| `autoregressive.py` | 1406 | `or 4096` (hidden_size decode) | ZERO FALLBACK: crash si absent |
| `executor.py` | ~775 | `vae_scale = 8` fallback | Lire depuis component config |
| `graph_executor.py` | ~244, ~395 | `vae_scale = 8` | Lire depuis component config |
| `tokenizer/factory.py` | 29 | `max_length=512` default | TextProcessor resolution cascade |

### 13.2 Fuites Semantiques (3 Documentees)

| Fichier | Ligne | Fuite | Severite | Mitigation |
|---------|-------|-------|----------|------------|
| `output_extractor.py` | ~229 | `["hidden_states", "last_hidden_state"]` noms hardcodes | MOYENNE | Topology declarations preferees |
| `output_extractor.py` | ~235 | `HIDDEN_DIM_MIN=512, MAX=16384` | MOYENNE | Utiliser topology interface outputs |
| `input_synthesizer.py` | ~451 | Pattern Sana `[0] + [-n+1:]` | BASSE | Conditionnel sur flag topology |
| `input_synthesizer.py` | ~99 | Batch = dimension 0 | BASSE | Convention universelle PyTorch |

### 13.3 Branches Conditionnelles a Reduire

**`autoregressive.py` — 23 branches:**
- 8 branches `gen_type == "autoregressive_image"` → deplacer dans VQ-specific handler
- 6 branches `gen_type == "autoregressive_text"` → deplacer dans LLM-specific handler
- 5 branches detection GraphExecutor path → simplifier avec factory
- 4 branches KV cache → deplacer dans StateCache brick

**`iterative_process.py` — 11 branches:**
- 4 branches CFG mode → deplacer dans CFGEngine
- 3 branches packing (Flux) → deplacer dans ComponentHandler
- 2 branches tokenisation → deplacer dans TextProcessor
- 2 branches post-loop → garder (necessaires)

---

## 14. Priorites & Sequencement

### 14.1 Ordre Recommande

```
Phase 1: TextProcessor (2-3 jours)
    ↓ supprime duplication tokenisation
Phase 2: CFGEngine (2-3 jours)
    ↓ unifie CFG + supprime inline
Phase 3: StateCache Factory (1-2 jours)
    ↓ declaratif KV cache init
Phase 4: FlowEngine Simplification (3-4 jours)
    ↓ orchestrateurs fins
Phase 5: Cleanup Hardcodes (1-2 jours)
    ↓ elimination residus
```

### 14.2 Risques

| Phase | Risque | Mitigation |
|-------|--------|------------|
| 1 | Regression tokenisation multi-encodeur | Test PixArt (1 enc), SDXL (2 enc), Sana (1 enc) |
| 2 | CFG batched vs sequential regression | Test PixArt (batched), TP mode (sequential) |
| 3 | KV cache init timing | Test Janus prefill + decode sequence |
| 4 | Flow handler simplification casse le pipeline | Test end-to-end chaque modele |
| 5 | Hardcode removal crash | ZERO FALLBACK garanti |

### 14.3 Tests de Regression Obligatoires

Apres **chaque phase**, executer:

```bash
# Image diffusion (iterative_process)
PYTHONPATH=src python -m neurobrix run \
  --model PixArt-Sigma-XL-2-1024-MS --hardware v100-32g \
  --prompt "A sunset" --steps 10

# Image VQ (autoregressive_image)
PYTHONPATH=src python -m neurobrix run \
  --model Janus-Pro-7B --hardware c4140-4xv100-custom-nvlink \
  --prompt "A cat" --steps 576

# LLM (autoregressive_text)
PYTHONPATH=src python -m neurobrix run \
  --model deepseek-moe-16b-chat --hardware c4140-4xv100-custom-nvlink \
  --prompt "Hello, who are you?"
```

---

## 15. Criteres de Validation

### 15.1 Metriques Quantitatives

| Metrique | Avant | Apres | Cible |
|----------|-------|-------|-------|
| Lignes `autoregressive.py` | 1,489 | ~400 | -73% |
| Lignes `iterative_process.py` | 807 | ~250 | -69% |
| Branches conditionnelles totales | 34 | ~10 | -71% |
| Implementations CFG | 2 | 1 | -50% |
| Implementations tokenisation | 2 | 1 | -50% |
| Briques modulaires | 3 | 5 | +67% |
| Tests de regression | 0 | 3+ | ∞ |

### 15.2 Criteres Qualitatifs

- [ ] Chaque brique est testable en isolation
- [ ] Ajout d'un nouveau scheduler = 1 ligne dans registre
- [ ] Ajout d'un nouveau type CFG = 1 nouvelle classe dans CFGEngine
- [ ] Ajout d'un nouveau tokenizer = 0 changement dans flow handlers
- [ ] ZERO FALLBACK sur toute configuration manquante
- [ ] ZERO HARDCODE dans les flow handlers
- [ ] ZERO SEMANTIC dans les briques (sauf ComponentHandlers)

### 15.3 Definition of Done

Une brique est **DONE** quand:
1. Interface ABC/Protocol definie et documentee
2. Factory avec registre ZERO FALLBACK implementee
3. Configuration 100% declarative depuis NBX container
4. Tests unitaires couvrant tous les modes
5. Flow handlers utilisant la brique au lieu de code inline
6. Code inline original supprime
7. Tests de regression end-to-end passent

---

## Annexe A — Fichiers a Creer

```
src/neurobrix/core/
├── module/
│   ├── text/                        # NOUVEAU — Phase 1
│   │   ├── __init__.py
│   │   └── processor.py            # TextProcessor + TokenizedOutput
│   │
│   └── cache/                       # NOUVEAU — Phase 3
│       ├── __init__.py
│       └── factory.py               # StateCacheFactory
│
└── cfg/
    └── engine.py                    # NOUVEAU — Phase 2 (remplace cfg_executor + strategy)
```

## Annexe B — Fichiers a Modifier

| Fichier | Phase | Changement |
|---------|-------|------------|
| `flow/autoregressive.py` | 1,2,3,4 | Delegation vers briques, reduction 1489→400 |
| `flow/iterative_process.py` | 1,2,4 | Delegation vers briques, reduction 807→250 |
| `runtime/executor.py` | 1,2,3 | Init briques dans FlowContext |
| `flow/base.py` | 4 | Ajouter `BrickContext` dans `FlowContext` |
| `cfg/cfg_executor.py` | 2 | SUPPRIMER (migre vers engine.py) |
| `cfg/strategy.py` | 2 | SUPPRIMER (integre dans engine.py) |

## Annexe C — Fichiers a NE PAS Toucher

| Fichier | Raison |
|---------|--------|
| `runtime/graph_executor.py` | Moteur d'execution mature |
| `runtime/graph/compiled_sequence.py` | Zero-overhead, parfait |
| `runtime/graph/kv_cache_wrapper.py` | Brique excellente |
| `runtime/resolution/variable_resolver.py` | 100% DATA-DRIVEN |
| `module/scheduler/*` | Modele a suivre |
| `module/autoregressive/samplers.py` | Hierarchie propre |
| `module/autoregressive/generator.py` | Brique fonctionnelle |

---

*Ce document sert de reference pour le projet de refactoring BRIX. Aucun code n'a ete modifie. Toute implementation doit suivre le sequencement Phase 1→5 et passer les tests de regression obligatoires apres chaque phase.*
