# NeuroBrix Changelog

---

## 0.1.0 (February 2026)

First public release. Universal Deep Learning Inference Engine with:
- Pip-installable package (`pip install neurobrix`)
- Registry CLI: `neurobrix import`, `neurobrix list`, `neurobrix remove`
- Forge publish pipeline: `python forge/forge.py publish` → neurobrix.es → hub.neurobrix.es
- Multi-GPU strategies (single_gpu, pipeline, tensor_parallel, zero3)
- CompiledSequenceV2 for zero-overhead execution
- AMP DtypeEngine for numerical stability
- Verified models: PixArt-Sigma, Sana 1024/4K, DeepSeek-MoE-16B, Janus-Pro-7B

---

## Development History

### Package Restructure + Registry + Forge Extraction (2026-02-08)

**PACKAGE COMPLETE: NeuroBrix is now a fully separated pip-installable package with zero forge coupling.**

Major architectural milestone: Complete separation of public runtime package (`src/neurobrix/`) from private forge tooling (`forge/`). NeuroTax vendored into forge, eliminating pip install requirement for build operations.

#### Key Achievements (February 2026)

| Achievement | Description |
|-------------|-------------|
| **Package Structure** | `src/neurobrix/` pip-installable package with core/, kernels/, nbx/, config/ |
| **CLI Entry Points** | `neurobrix` command (run, info, inspect, validate, import, list, remove) |
| **Python Module** | `python -m neurobrix` via `__main__.py` for dev workflow |
| **Total Separation** | forge/ has ZERO imports from neurobrix.core, neurobrix.core has ZERO imports from forge/ |
| **NeuroTax Local** | forge/neurotax/ vendored copy, no more pip dependency for forge |
| **Cache Separation** | `~/.neurobrix/cache/` (runtime) vs `.cache/` (forge workspace) |
| **C++ Runtime Removed** | Pure Python runtime only, C++ experimental code deleted |
| **Documentation Audit** | All docs updated: path corrections, CLI updates, system reality |

#### Cache Architecture

```
PUBLIC (neurobrix package):
  ~/.neurobrix/
  ├── store/          ← Downloaded .nbx files
  └── cache/          ← Extracted models (runtime reads from here)
      └── model_name/
          ├── manifest.json
          ├── topology.json
          └── ...

PRIVATE (forge workspace):
  /home/mlops/NeuroBrix_System/.cache/
  ├── graphs/         ← Trace output (forge/tracer/)
  └── models/         ← Build workspace (forge/importer/)
```

#### File Relocations

| Old Path | New Path | Notes |
|----------|----------|-------|
| `core/` | `src/neurobrix/core/` | Public runtime package |
| `kernels/` | `src/neurobrix/kernels/` | Triton kernels |
| `nbx/` | `src/neurobrix/nbx/` | NBX format handling |
| `config/` | `src/neurobrix/config/` | YAML configs (hardware/, vendors/) |
| `importer/` | `forge/importer/` | Private build tooling |
| `trace/` | `forge/tracer/` | Private trace system |
| `vendors/` | `forge/vendors/` | Vendored diffusers |
| `config/families/` | `forge/config/families/` | Family templates |
| `neurobrix.py` | Deleted (shadowed package) | Use `neurobrix` CLI |

#### Import Renames (260+ occurrences)

```python
# OLD:
from core.runtime import RuntimeExecutor
from kernels.adapter import KernelAdapter
from nbx.container import NBXContainer

# NEW:
from neurobrix.core.runtime import RuntimeExecutor
from neurobrix.kernels.adapter import KernelAdapter
from neurobrix.nbx.container import NBXContainer
```

#### CLI Changes

```bash
# OLD (monolithic neurobrix.py):
python neurobrix.py run --model ... --hardware ...
python neurobrix.py trace --family ... --model ...
python neurobrix.py import --snapshot-path ... --family ...

# NEW (separated):
neurobrix run --model ... --hardware ...              # pip-installed CLI
PYTHONPATH=src python -m neurobrix run ...            # dev workflow
python forge/forge.py trace --family ... --model ...  # private forge
python forge/forge.py build --snapshot-path ...       # private forge
```

#### Registry Complete (2026-02-07)

**REGISTRY COMPLETE: End-to-end publish → import → run pipeline via neurobrix.es.**

#### Key Achievements

| Achievement | Description |
|-------------|-------------|
| **neurobrix import** | Download .nbx from registry via signed URLs through hub.neurobrix.es |
| **neurobrix list** | List installed models (registry cache + dev workspace) |
| **neurobrix remove** | Remove models from local cache and/or store |
| **forge publish** | Upload .nbx to MinIO via presigned PUT, register in DB |
| **API Tokens** | `nbx_...` Bearer tokens for CLI auth (Prisma ApiToken model) |
| **hub.neurobrix.es** | Public download domain via Cloudflare -> HAProxy -> MinIO |
| **Dual S3 Client** | Internal for uploads, public for signed download URLs |

#### Registry Architecture

```
forge publish → POST /api/admin/upload → presigned PUT → MinIO (internal 10.0.0.36:9000)
             → POST /api/admin/models → register in PostgreSQL

neurobrix import → GET /api/models/{org}/{name}/download → signed URL (hub.neurobrix.es)
               → download .nbx → extract to ~/.neurobrix/cache/
```

#### New CLI Commands

```bash
neurobrix import sana/1600m-1024    # Download + extract from registry
neurobrix list                       # Show installed models
neurobrix remove 1600m-1024          # Remove from cache
neurobrix remove 1600m-1024 --store  # Also remove .nbx from store
```

#### New Forge Command

```bash
NEUROBRIX_API_TOKEN=nbx_... python forge/forge.py publish model.nbx \
  --org sana --name 1600m-1024 --category IMAGE --tags "diffusion,sana"
```

#### User Cache Structure

```
~/.neurobrix/
├── store/          ← Downloaded .nbx files
└── cache/          ← Extracted models (runtime reads from here)
    └── 1600m-1024/
        ├── manifest.json
        ├── topology.json
        ├── runtime/
        ├── components/
        └── modules/
```

#### Files Modified/Created

| File | Action |
|------|--------|
| `src/neurobrix/cli.py` | Added import/list/remove commands, NEUROBRIX_HOME/STORE_DIR/CACHE_DIR constants, updated find_model() |
| `forge/forge.py` | Added publish command with presigned PUT upload + progress bar |

#### neurobrix.es Changes (10.0.0.39)

| File | Action |
|------|--------|
| `prisma/schema.prisma` | Added ApiToken model |
| `src/lib/api-auth.ts` | Created — Bearer token + NextAuth dual auth |
| `src/lib/storage.ts` | Dual S3 client (internal + public hub.neurobrix.es) |
| `src/app/api/admin/tokens/route.ts` | Created — POST: generate token, GET: list tokens |
| `src/app/api/admin/upload/route.ts` | Updated — Bearer token auth |
| `src/app/api/admin/models/route.ts` | Updated — Bearer token auth, upsert |
| `src/app/api/models/[org]/[model]/download/route.ts` | Updated — signed URLs via hub.neurobrix.es |

#### Security

- Signed URLs (1h expiry) prevent unauthorized downloads
- OPNsense ACL restricts hub.neurobrix.es to `/neurobrix/` path only
- API tokens required for publish (NEUROBRIX_API_TOKEN env var)
- Bearer auth validated against Prisma ApiToken table

#### Performance

| Operation | Speed | Notes |
|-----------|-------|-------|
| forge publish (9.1 GB) | ~10s @ 580 MB/s | Internal network to MinIO |
| neurobrix import (9.1 GB) | ~2m09s @ 70 MB/s | Via Cloudflare/hub.neurobrix.es |

---

## Phase 15-16: Package Restructure + Registry (Consolidated - See Above)

The detailed Phase 15-16 changes have been consolidated into the "Package Restructure + Registry + Forge Extraction (2026-02-08)" section above.

---

## Phase 13: Trace System Separation (2026-01-28)

### v13.0.0 - Trace Module Independence

**ARCHITECTURE SEPARATION: Trace system now fully independent from core runtime.**

Complete extraction of trace system to standalone module with zero dependencies on src/neurobrix/core/.

#### Key Changes

| Change | Description |
|--------|-------------|
| **forge/tracer/ Module** | Extracted from src/neurobrix/core/graph/ to forge/tracer/ |
| **Standalone CLI** | `python forge/forge.py trace` (was: trace_cli.py) |
| **Zero Imports** | forge/tracer/ has ZERO imports from src/neurobrix/core/, nbx/, forge/importer/ |
| **Vendor Setup** | `forge/tracer/vendor_setup.py` replaces `src/neurobrix/core/utils/vendor_loader.py` |

#### File Relocations (Updated Post-Package Restructure)

| Old Path | New Path | Final Path |
|----------|----------|------------|
| `neurobrix.py trace` | `python trace_cli.py` | `python forge/forge.py trace` |
| `neurobrix.py trace-worker` | `python trace_cli.py worker` | `python forge/forge.py trace --worker` |
| `core/graph/` | `trace/` | `forge/tracer/` |
| `core/utils/vendor_loader.py` | `trace/vendor_setup.py` | `forge/tracer/vendor_setup.py` |

#### Architecture Benefits

- **Portability**: trace/ can be used independently
- **Clear Separation**: TRACE → IMPORT → RUNTIME phases
- **Zero Coupling**: No circular dependencies
- **Maintainability**: trace/ is self-contained

#### Documentation Updates

All documentation files updated to reflect new structure:
- `CLAUDE.md` - Updated workflow and file references
- `docs/TECHNICAL_SPEC.md` - Updated architecture diagrams
- `docs/CHANGELOG.md` - Added Phase 13
- `docs/CLASSIC_TRACER_AUDIT.md` - Updated file paths
- `docs/TRACER_ALIGNMENT_PLAN.md` - Updated references
- `docs/trace_graph_audit.md` - Updated file paths

---

## Phase 12: Architecture Cleanup & Code Hygiene (2026-01-24)

### v12.0.0 - Enterprise-Grade Refactoring

**ARCHITECTURE CLEANUP: Zero wrappers, zero deprecated code, direct imports everywhere.**

Complete refactoring of `core/` directory following ZERO principles.

#### Key Changes

| Change | Description |
|--------|-------------|
| **ZERO WRAPPERS** | Removed all wrapper modules, direct imports at usage sites |
| **Deprecated Removal** | Removed all deprecated functions and aliases |
| **Dtype Consolidation** | Single source of truth in `core/dtype/` |
| **Memory Manager** | Centralized in `core/runtime/memory/` |

#### Deleted Wrappers

| File | Reason |
|------|--------|
| `core/runtime/vendor_config.py` | Unnecessary wrapper, use direct `core/config/` imports |
| `core/runtime/family_config.py` | Unnecessary wrapper, use direct `core/config/` imports |

#### Deprecated Functions Removed

| File | Function | Replacement |
|------|----------|-------------|
| `core/hardware/detector.py` | `detect_hardware()` | Use `get_device_info()` with explicit device |
| `core/hardware/detector.py` | `get_optimal_device()` | Use explicit device specification (ZERO AUTO-DETECTION) |

#### Deprecated Aliases Removed

| File | Alias | Replacement |
|------|-------|-------------|
| `core/module/autoregressive/generator.py` | `AutoregressiveScheduler` | Use `AutoregressiveGenerator` |
| `core/module/autoregressive/generator.py` | `VQImageScheduler` | Use `VQImageGenerator` |
| `core/module/autoregressive/generator.py` | `AutoregressiveState` | Use `GenerationState` |

**Note:** Factory registry in `src/neurobrix/core/module/autoregressive/factory.py` maintains backward compatibility for string config values in NBX files.

#### Architecture After Refactoring

```
src/neurobrix/core/
├── config/                    # Configuration (direct access)
│   ├── hardware.py            # load_hardware_profile()
│   └── family.py              # load_family_config()
├── dtype/                     # Dtype handling (SINGLE SOURCE OF TRUTH)
│   ├── __init__.py
│   └── converter.py           # safe_dtype_convert()
├── hardware/                  # Hardware detection (EXPLICIT DEVICE)
│   ├── detector.py            # get_device_info() - explicit device required
│   └── __init__.py
├── module/
│   ├── autoregressive/        # Token generation
│   │   ├── generator.py       # AutoregressiveGenerator, VQImageGenerator
│   │   ├── factory.py         # Factory with registry (config string compat)
│   │   └── samplers.py
│   ├── scheduler/             # Diffusion schedulers
│   └── tokenizer/
├── runtime/
│   ├── executor.py            # RuntimeExecutor
│   ├── graph_executor.py      # GraphExecutor
│   ├── memory/                # Centralized memory management
│   │   └── manager.py         # MemoryManager.unload_weights()
│   └── ...
└── prism/                     # Hardware allocation
```

#### ZERO WRAPPERS Principle

Before:
```python
# Old: Using wrapper
from neurobrix.core.runtime.vendor_config import load_hardware_profile
```

After:
```python
# New: Direct import
from neurobrix.core.config.hardware import load_hardware_profile
```

#### Files Modified

| File | Change |
|------|--------|
| `core/runtime/executor.py` | Direct imports from core.config |
| `kernels/adapter.py` | Direct imports from core.config |
| `neurobrix.py` | Direct imports from core.config |
| `core/flow/iterative_process.py` | Direct imports from core.config |
| `core/hardware/__init__.py` | Removed deprecated exports |
| `core/module/autoregressive/__init__.py` | Removed deprecated alias exports |

---

## Phase 11: Zero-Overhead Execution & Performance Parity (2026-01-24)

### v11.0.0 - CompiledSequence & Debug Control System

**PERFORMANCE BREAKTHROUGH: NeuroBrix now achieves parity with Diffusers (~23s E2E).**

Major optimization eliminating Python overhead in the execution loop.

#### Problem Identified

Benchmark analysis revealed NeuroBrix was 1.75x slower than Diffusers due to:
1. **Python dict lookups**: ~15,000 dict lookups per transformer step
2. **GPU sync overhead**: `tensor.item()` calls causing 6.3s overhead (75% of measured time)
3. **String dispatch**: Op routing via string comparison at runtime

#### Solution: CompiledSequence

Pre-compiles the entire execution DAG into zero-overhead closures:

| Metric | Before | After |
|--------|--------|-------|
| Dict lookups/step | ~15,000 | 0 |
| isinstance() checks | ~5,000 | 0 |
| GPU sync calls | ~100 | 0 (when DEBUG=False) |
| E2E Time (20 steps) | ~40s | ~23s |

#### New Files

| File | Purpose |
|------|---------|
| `src/neurobrix/core/runtime/graph/compiled_sequence.py` | CompiledSequenceV2 with closure-based resolvers |
| `src/neurobrix/core/runtime/debug.py` | NBX_DEBUG environment variable control |

#### CompiledSequence Architecture

```python
# BEFORE (runtime overhead)
for op_uid in execution_order:
    op_data = ops[op_uid]              # dict lookup
    inputs = resolve(op_data)          # isinstance() checks
    result = dispatch(op_type, ...)    # string dispatch

# AFTER (zero overhead)
for op in compiled_ops:
    args = op.args_resolver(arena)     # pre-compiled closure
    arena[op.output_slot] = op.func(*args)  # direct call
```

Key innovations:
- **TensorArena**: List-based storage with `__slots__` (O(1) access)
- **Closure resolvers**: Pre-compiled lambdas capture slot indices
- **Dead Tensor Analysis**: Liveness analysis frees memory during execution

#### Debug Control System

```python
# Environment variable
NBX_DEBUG=1 python neurobrix.py run ...  # Verbose (slower)
NBX_DEBUG=0 python neurobrix.py run ...  # Fast (default)

# In code
from core.runtime.debug import DEBUG, debug_print
if DEBUG:
    print(f"tensor mean: {tensor.mean().item()}")  # Only when DEBUG=True
```

**CRITICAL**: When `DEBUG=False`, no `tensor.item()` calls are made, eliminating GPU sync overhead.

#### Files Modified

| File | Change |
|------|--------|
| `src/neurobrix/core/runtime/graph_executor.py` | Integration with CompiledSequenceV2 |
| `src/neurobrix/core/runtime/flow/iterative_process.py` | DEBUG guards on logging |
| `src/neurobrix/core/runtime/cfg/cfg_executor.py` | DEBUG guards on logging |

#### Benchmark Results (PixArt-Sigma-XL-2-1024-MS, 20 steps, 1024x1024)

| Framework | E2E Time | Ratio |
|-----------|----------|-------|
| Diffusers | 23.15s | 1.00x |
| NeuroBrix (before) | 40.55s | 0.57x |
| NeuroBrix (after) | ~23s | ~1.00x |

---

## Phase 10: High Performance I/O & Prism SmartSolver (2026-01-12)

### v10.1.0 - Parallel I/O Loading System (2026-01-12)

**PERFORMANCE UPGRADE: 2.5-3x faster weight loading across entire system.**

All weight loading now uses parallel I/O with 8 workers for maximum throughput.

#### Key Changes

| Component | Before | After |
|-----------|--------|-------|
| `WeightLoader` | Sequential shard loading | Parallel (8 workers) |
| `FastNBXLoader` | Sequential shard loading | Parallel (8 workers) |
| `TP Sharding` | Sequential file loading | Parallel (8 workers) |

#### New Files

| File | Purpose |
|------|---------|
| `src/neurobrix/core/io/__init__.py` | I/O package exports |
| `src/neurobrix/core/io/smart_loader.py` | SmartLoader, PrefetchQueue, IOConfig |
| `src/neurobrix/config/system.yml` | I/O configuration (num_workers, prefetch) |

#### SmartLoader API

```python
from neurobrix.core.io import SmartLoader, get_io_config

config = get_io_config()  # Loads from system.yml
loader = SmartLoader(config)
weights = loader.load_shards_parallel(shard_paths)
```

#### Configuration (src/neurobrix/config/system.yml)

```yaml
io:
  num_workers: 8              # Parallel loading workers
  use_pinned_memory: true     # Enable pinned memory for DMA
  enable_prefetch: true       # Prefetch next component
  prefetch_queue_size: 2      # Components to prefetch
```

#### Performance Results

| Model | Before | After | Speedup |
|-------|--------|-------|---------|
| PixArt (7 shards) | 42s | 15s | 2.8x |
| Sana (21 GB) | 108s | 42s | 2.5x |

---

### v10.0.0 - Prism SmartSolver with Interconnect-Aware Placement (2026-01-12)

**MULTI-GPU UPGRADE: Topology-aware block placement for optimal data flow.**

SmartSolver now considers NVLink/xGMI topology when allocating blocks across GPUs.

#### Key Changes

| Feature | Description |
|---------|-------------|
| Interconnect-aware spillover | When GPU full, prefer same NVLink group |
| Best-Fit-Decreasing with topology | Capacity + topology hybrid sorting |
| InterconnectTopology class | Full topology graph with bandwidth info |

#### New Strategies

| Strategy | Description |
|----------|-------------|
| `SINGLE_GPU` | All on 1 GPU |
| `COMPONENT_AFFINITY` | 1 component = 1 GPU |
| `FGP_NVLINK` | Fine-grained pipeline with NVLink |
| `FGP_PCIE` | Fine-grained pipeline with PCIe |
| `PP_NVLINK` | Pipeline parallel with NVLink |
| `PP_PCIE` | Pipeline parallel with PCIe |
| `TP_INTENT` | Tensor parallel (placeholder) |
| `ZERO3_OFFLOAD` | CPU offload for large models |

#### InterconnectTopology API

```python
from neurobrix.core.prism.structure import InterconnectTopology

topology.get_bandwidth(0, 1)  # Returns Gbps between GPUs
topology.devices_have_fast_interconnect([0, 1])  # Same NVLink group?
topology.get_fastest_group()  # Best connected GPU group
```

#### Files Modified

| File | Change |
|------|--------|
| `src/neurobrix/core/prism/smart_solver.py` | Added `_get_spillover_device()` |
| `src/neurobrix/core/prism/structure.py` | Enhanced `InterconnectTopology` |
| `src/neurobrix/core/runtime/strategies/tp_sharding.py` | Parallel shard creation |

---

## Phase 9: Architecture Refactoring & Cleanup (2026-01-11)

### v9.0.0 - Root-Level Module Extraction (2026-01-11)

**MAJOR ARCHITECTURE REFACTORING: trace/ and importer/ moved to root level.**

Phase 9 completes the separation of concerns by extracting the trace and import modules from `core/` to root level, establishing a cleaner three-phase architecture: TRACE → IMPORT → RUNTIME.

#### Key Changes

| Change | Description |
|--------|-------------|
| `core/graph/` → `trace/` | Tracer module moved to root level |
| `core/importer/` → `importer/` | Importer module moved to root level |
| Path resolution | Uses `_find_neurobrix_root()` for robust path resolution |
| Scheduler helpers | Created `core/module/scheduler/utils/helpers.py` |

#### Root-Level Structure (Updated Post-Package Restructure)

```
NeuroBrix_System/
├── forge/                    # PRIVATE TOOLING
│   ├── forge.py              # Unified CLI (snap, build, trace, publish)
│   ├── tracer/               # TRACE PHASE
│   │   ├── orchestrator.py   # Multi-component orchestration
│   │   ├── capture.py        # ATenHookManager
│   │   ├── topology.py       # Topology generator
│   │   ├── storage.py        # Graph cache storage
│   │   ├── stimulus/         # Input generation
│   │   └── symbolic/         # Symbolic shapes
│   ├── importer/             # IMPORT PHASE
│   │   ├── builder.py        # NBXBuilder
│   │   ├── analyzer.py       # Model analyzer
│   │   ├── detection/        # Backbone detection
│   │   └── weights/          # Weight handling
│   └── vendors/              # Vendored diffusers
│
└── src/neurobrix/            # PUBLIC PACKAGE (pip install neurobrix)
    ├── cli.py                # CLI entry point
    ├── core/                 # RUNTIME PHASE
    │   ├── runtime/          # Execution
    │   ├── prism/            # Hardware allocation
    │   └── module/           # Schedulers, tokenizers
    ├── kernels/              # Triton kernels
    ├── nbx/                  # NBX format
    └── config/               # YAML configs
```

#### Path Resolution Fix

**Problem:** `GRAPH_CACHE_ROOT` in `importer/builder.py` used incorrect `.parent.parent.parent` path calculation, resolving to `/home/mlops/.cache/graphs` instead of `/home/mlops/NeuroBrix_System/.cache/graphs`.

**Solution:** Introduced `_find_neurobrix_root()` function that searches for `neurobrix.py` marker file in parent directories:

```python
def _find_neurobrix_root() -> Path:
    """Find NeuroBrix root by searching for neurobrix.py in parent dirs."""
    current = Path(__file__).resolve()
    for parent in current.parents:
        if (parent / "neurobrix.py").exists():
            return parent
    # Fallback to cwd
    cwd = Path.cwd()
    if (cwd / "neurobrix.py").exists():
        return cwd
    raise RuntimeError("Cannot find NeuroBrix root (neurobrix.py not found)")

GRAPH_CACHE_ROOT = _find_neurobrix_root() / ".cache" / "graphs"
```

#### Files Modified

| File | Change |
|------|--------|
| `forge/importer/builder.py` | Added `_find_neurobrix_root()`, fixed `GRAPH_CACHE_ROOT` |
| `forge/importer/analyzer.py` | Import `GRAPH_CACHE_ROOT` from builder |
| `forge/tracer/storage.py` | Already used correct approach (verified) |

#### Scheduler Helpers Extraction

Created `src/neurobrix/core/module/scheduler/utils/helpers.py` with shared functions:

```python
def init_step_index(timesteps, timestep, order=None):
    """Shared by Euler, DDIM, DPM++, LCM schedulers."""
    ...

def threshold_sample(sample, config):
    """Shared by DDIM, DPM++, LCM schedulers."""
    ...
```

#### Cache Architecture

```
.cache/
├── graphs/<model>/           # Trace output (from trace/ module)
│   ├── topology.json
│   └── components/<name>/
│       └── graph.json
│
└── models/<model>/           # Runtime cache (from importer/)
    ├── manifest.json
    ├── topology.json         # Copied from graphs/
    ├── runtime/
    │   ├── variables.json
    │   └── defaults.json
    ├── components/<name>/
    │   ├── graph.json
    │   ├── runtime.json
    │   └── weights/
    └── modules/
```

#### Documentation Updates

| File | Version | Changes |
|------|---------|---------|
| `CLAUDE.md` | V11.0 | Updated architecture overview, key files reference |
| `.claude/CONTEXT.md` | V6.0.0 | Updated directory structure, execution pipeline |
| `.claude/RULES.md` | V7.0 | Updated key files reference with root-level modules |
| `docs/CHANGELOG.md` | Phase 9 | Added Phase 9 section |

#### Verification

```bash
# Verify path resolution
python3 -c "
from forge.importer.builder import GRAPH_CACHE_ROOT, _find_neurobrix_root
print(f'NeuroBrix root: {_find_neurobrix_root()}')
print(f'GRAPH_CACHE_ROOT: {GRAPH_CACHE_ROOT}')
print(f'Exists: {GRAPH_CACHE_ROOT.exists()}')
"
# Output:
# NeuroBrix root: /home/mlops/NeuroBrix_System
# GRAPH_CACHE_ROOT: /home/mlops/NeuroBrix_System/.cache/graphs
# Exists: True
```

---

## Phase 8: Sana 4K Support & Documentation (2026-01-09)

### v8.1.0 - Documentation Update V10.0 (2026-01-09)

**COMPREHENSIVE DOCUMENTATION REWRITE**

Complete overhaul of project documentation with focus on ZERO HARDCODE principles and complete system reference.

#### Updated Files

| File | Change | Purpose |
|------|--------|---------|
| `/home/mlops/NeuroBrix_System/CLAUDE.md` | V10.0 rewrite | Compact developer guidelines (~80 lines) |
| `/home/mlops/NeuroBrix_System/docs/TECHNICAL_SPEC.md` | Rewrite | Complete system reference (~1250 lines) |
| `/home/mlops/NeuroBrix_System/docs/CHANGELOG.md` | Phase 8 addition | Historical record |

#### CLAUDE.md V10.0

- Compact format: 670 → 80 lines
- Focus on ZERO principles (ZERO HARDCODE, ZERO FALLBACK, ZERO RENAME)
- Clear workflow: TRACE → IMPORT → GENERATE
- Key file reference table
- Debugging philosophy

#### TECHNICAL_SPEC.md

15 comprehensive sections covering entire system:

1. Philosophy & Vision
2. ZERO Principles
3. Architecture Overview
4. Phase 1: TRACE (torch.compile capture)
5. Phase 2: IMPORT (NBX packaging)
6. Phase 3: PRISM (hardware allocation)
7. Phase 4: RUNTIME (execution)
8. NBX Format Specification
9. TensorDAG (symbolic shapes)
10. Dtype Handling (bf16 fallback)
11. VAE Tiling (4K support)
12. Classifier-Free Guidance
13. Complete File Structure
14. Bug Fix History
15. Verified Models

---

### v8.0.0 - Sana 4K Generation Complete (2026-01-09)

**4K GENERATION WORKING. Sana 4096x4096 verified with VAE tiling.**

Sana 4K generation required multiple architectural fixes across trace, runtime, and VAE tiling systems.

#### Key Achievements

| Achievement | Resolution | Output |
|-------------|------------|--------|
| Sana 4K generation | 4096x4096 | `/tmp/sana_4k_test.png` (24MB) |
| VAE tiling | 32x32 trace + 512px tiles | No OOM on V100 |
| Transformer dynamic resolution | config.sample_size=128 | Correct latent shapes |

#### Transformer Trace Fix

**Problem:** Transformer traced at forced 32x32, wrong for 4K (128x128 latents).

**Root Cause:** `forge/tracer/worker.py` hardcoded `H=W=32` instead of using config.

**Fix:**
```python
# forge/tracer/worker.py - Use config.sample_size for transformers
if hasattr(config, 'sample_size'):
    sample_size = config.sample_size  # 128 for Sana 4K
else:
    sample_size = 32  # fallback for older models
```

#### VAE Trace Fix

**Problem:** DC-AE VAE cannot be traced at 4K (128x128 latent) - creates invalid 5D shapes.

**Root Cause:** DC-AE uses Expand ops with symbolic shapes. At square resolution (H=W), both symbols resolve to same value → wrong dimensions.

**Solution:** VAE stays traced at 32x32, tiling handles 4K at runtime.

#### VAE Tiling Implementation

**Files Created/Modified:**

| File | Purpose |
|------|---------|
| `src/neurobrix/core/runtime/components/vae_tiling.py` | VAE tiling strategy (overlapping tiles) |
| `src/neurobrix/core/runtime/executor.py` | VAE tiling integration |

**Algorithm:**

```
4K latent (1,32,128,128)
    ↓
Split into overlapping 32x32 tiles
    ↓
Each tile: VAE decode → 1024x1024 pixel
    ↓
Blend overlapping regions
    ↓
Final 4096x4096 image
```

#### Bug Fixes

| Bug | File | Fix |
|-----|------|-----|
| `NameError: logger not defined` | `executor.py` | Added `import logging` + logger setup |
| `FileNotFoundError: VAE graph` | `executor.py` | Changed `root_path` → `cache_path` |
| `RuntimeError: reflect padding` | `vae_tiling.py` | Changed to `replicate` mode |
| `RuntimeError: inference_mode tensor modification` | `vae_tiling.py` | Added `.clone()` in blend functions |

#### Padding Mode Fix

```python
# OLD: 'reflect' mode fails when padding > input dimension
tile = torch.nn.functional.pad(tile, (0, pad_w, 0, pad_h), mode='reflect')

# NEW: 'replicate' mode works for any padding size
tile = torch.nn.functional.pad(tile, (0, pad_w, 0, pad_h), mode='replicate')
```

#### Tensor Clone Fix

```python
def _blend_v(self, top, bottom, blend_extent):
    # Clone to allow in-place updates (inference mode tensors are read-only)
    bottom = bottom.clone()
    for y in range(blend_extent):
        alpha = y / blend_extent
        bottom[:, :, y, :] = top[:, :, -blend_extent + y, :] * (1 - alpha) + bottom[:, :, y, :] * alpha
    return bottom
```

#### Verification

```bash
# Sana 4K generation (4096x4096)
neurobrix run \
  --model Sana_1600M_4Kpx_BF16_diffusers \
  --height 4096 --width 4096 \
  --prompt "A photorealistic cat" \
  --steps 20 --hardware v100-32g \
  --output /tmp/sana_4k_test.png

# Output: 4096x4096 PNG, ~24MB
```

#### DtypeAdapter (Pre-existing)

bf16 → fp32 conversion for V100 already implemented in `src/neurobrix/core/runtime/dtype_adapter.py`:

```python
class DtypeAdapter:
    """Handles dtype conversion between model requirements and hardware capabilities."""

    def adapt_inputs(self, inputs: Dict[str, torch.Tensor]) -> Dict[str, torch.Tensor]:
        # bf16 → fp32 on V100 (no bf16 support)
        if self.target_dtype == torch.float32:
            for key, tensor in inputs.items():
                if tensor.dtype == torch.bfloat16:
                    inputs[key] = tensor.to(torch.float32)
        return inputs
```

---

## Phase 7: Runtime Modular Refactoring (2026-01-07)

### v7.1.0 - GraphExecutor Decomposition (2026-01-07)

**GraphExecutor decomposed from 1088 LOC → ~830 LOC with extracted modules**

The monolithic `graph_executor.py` has been decomposed into clean modular architecture, separating tensor resolution from DAG execution.

#### Key Changes

| Before | After | Change |
|--------|-------|--------|
| graph_executor.py: 1088 LOC | graph_executor.py: ~830 LOC | **-24%** |
| 20+ methods in one file | 3 focused modules | Better separation |
| run() method: 216 lines | Split into 5 focused methods | More maintainable |

#### New Module Structure

```
core/runtime/
├── graph_executor.py          # Thin orchestrator (~830 LOC)
├── shape_resolver.py          # EXISTING: V3 symbolic shapes
└── graph/
    ├── __init__.py            # Package exports
    ├── execution_context.py   # Shared state dataclass (~100 LOC)
    └── tensor_resolver.py     # Tensor resolution (~340 LOC)
```

#### New Classes

| Class | Location | Purpose |
|-------|----------|---------|
| `ExecutionContext` | `graph/execution_context.py` | Shared state dataclass for a single run() |
| `TensorResolver` | `graph/tensor_resolver.py` | Resolve tensor IDs to live tensors |

#### Architectural Clarity

- **RuntimeExecutor** (`executor.py`): Orchestrator BETWEEN components (pipeline coordination)
- **GraphExecutor** (`graph_executor.py`): Execution engine WITHIN a single component (DAG execution)

#### Delegation to Existing Infrastructure

GraphExecutor continues to delegate op dispatch to existing kernels/ infrastructure:
- `kernels/adapter.py` → KernelAdapter.launch() for Triton
- `kernels/metadata_ops.py` → execute_metadata_op() for PyTorch native
- `core/runtime/native_dispatcher.py` → NativeATenDispatcher for ATen ops

**NO new OpDispatcher was created - kernels/ already handles dispatch completely.**

#### TensorResolver API

```python
resolver = TensorResolver(ctx)
tensor = resolver.resolve(tensor_id)              # Lookup from store/weights/inputs
tensor = resolver.resolve_normalized(tensor_id)   # + device/contiguity/dtype
args = resolver.resolve_args(op_uid, attrs, ...)  # Resolve positional args
kwargs = resolver.resolve_kwargs(op_uid, attrs, ...)  # Resolve keyword args
inputs = resolver.normalize_inputs(tensors)       # Ensure contiguous + device
```

---

### v7.0.0 - Executor Decomposition (2026-01-07)

**CRITICAL REFACTORING: executor.py decomposed from 3022 LOC → 772 LOC (75% reduction)**

The monolithic `executor.py` (God Object anti-pattern) has been decomposed into clean modular architecture.

#### Key Changes

| Before | After | Reduction |
|--------|-------|-----------|
| executor.py: 3022 LOC | executor.py: 772 LOC | **75%** |

#### New Modular Architecture

```
src/neurobrix/core/runtime/
├── executor.py               # Orchestrator ONLY (772 LOC)
├── flow/                     # Execution flow handlers
│   ├── base.py               # FlowContext, FlowHandler ABC, registry
│   ├── iterative_process.py  # Diffusion denoising loop (~350 LOC)
│   ├── static_graph.py       # Single-pass execution (~70 LOC)
│   ├── forward_pass.py       # Sequential transformer (~200 LOC)
│   └── autoregressive.py     # Token-by-token generation (~400 LOC)
├── resolution/               # Input resolution
│   └── input_resolver.py     # Resolves inputs from connections (~200 LOC)
├── synthesis/                # Input synthesis
│   └── input_synthesizer.py  # Creates synthetic inputs from topology (~350 LOC)
├── extraction/               # Output extraction
│   └── output_extractor.py   # Extracts and routes outputs (~200 LOC)
└── cfg/                      # Classifier-Free Guidance
    └── cfg_executor.py       # CFG batching strategies (~300 LOC)
```

#### New Classes

| Class | Location | Purpose |
|-------|----------|---------|
| `FlowContext` | `flow/base.py` | Immutable context passed to handlers |
| `FlowHandler` | `flow/base.py` | ABC for execution flows |
| `IterativeProcessHandler` | `flow/iterative_process.py` | Diffusion pre_loop → loop → post_loop |
| `StaticGraphHandler` | `flow/static_graph.py` | Single-pass component execution |
| `ForwardPassHandler` | `flow/forward_pass.py` | Sequential transformer execution |
| `AutoregressiveHandler` | `flow/autoregressive.py` | Token-by-token LLM generation |
| `InputResolver` | `resolution/input_resolver.py` | Resolves inputs from topology connections |
| `InputSynthesizer` | `synthesis/input_synthesizer.py` | Creates synthetic inputs (from_dimensions, etc.) |
| `OutputExtractor` | `extraction/output_extractor.py` | Extracts primary output, routes to resolver |
| `CFGExecutor` | `cfg/cfg_executor.py` | Classifier-Free Guidance (batched/sequential) |

#### Factory Pattern

Flow handlers use `@register_flow` decorator and `get_flow_handler()` factory:

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

# Usage
handler = get_flow_handler("iterative_process", ctx)
outputs = handler.execute()
```

#### What Stays in Orchestrator

- Module/executor setup
- Strategy initialization
- Variable resolver setup
- Flow type detection and dispatch
- Component execution (delegates to helpers)
- Weight loading/unloading

#### What Was Extracted

| Extracted To | Methods Moved |
|--------------|---------------|
| `IterativeProcessHandler` | `_execute_iterative_process()`, `_execute_main_loop()` |
| `StaticGraphHandler` | `_execute_static_graph()` |
| `ForwardPassHandler` | `_execute_forward_pass()`, `_preprocess_forward_pass_inputs()` |
| `AutoregressiveHandler` | `_execute_autoregressive_generation()` (390+ LOC) |
| `InputResolver` | `_resolve_component_inputs()`, `_resolve_source_chain()` |
| `InputSynthesizer` | `_synthesize_missing_inputs()`, `_remap_inputs_to_graph()` |
| `OutputExtractor` | `_extract_primary_output()`, `_store_component_outputs()` |
| `CFGExecutor` | `_execute_cfg_negative_encoding()`, `_execute_component_with_cfg()` |

#### Verification

All imports work correctly:
```python
from neurobrix.core.runtime.executor import RuntimeExecutor
from neurobrix.core.runtime.flow import FlowContext, get_flow_handler
from neurobrix.core.runtime.resolution.input_resolver import InputResolver
from neurobrix.core.runtime.synthesis.input_synthesizer import InputSynthesizer
from neurobrix.core.runtime.extraction.output_extractor import OutputExtractor
from neurobrix.core.runtime.cfg.cfg_executor import CFGExecutor
```

---

## Phase 6: Module Architecture Refactoring (2026-01-03)

### v6.0.0 - core/module/ Separation (2026-01-03)

**ARCHITECTURAL REFACTORING: Autoregressive is NOT a scheduler.**

#### Key Changes

| Change | Reason |
|--------|--------|
| `core/scheduler/autoregressive/` → `core/module/autoregressive/` | NOT a scheduler - different paradigm |
| `core/scheduler/` → `core/module/scheduler/` | Diffusion schedulers only |
| `core/tokenizer/` → `core/module/tokenizer/` | Unified tokenizer module |
| `AutoregressiveScheduler` → `AutoregressiveGenerator` | Rename for clarity |
| `VQImageScheduler` → `VQImageGenerator` | Rename for clarity |

#### Fundamental Difference

- **Scheduler**: Drives iterative denoising (20-50 steps, noise → image)
- **Autoregressive**: Token-by-token LLM generation (N tokens, prompt → tokens)

#### New Module Structure
```
src/neurobrix/core/module/
├── scheduler/           # Diffusion ONLY (DDIM, DPM, Euler, Flow)
├── autoregressive/      # Token generation (LLM, VQ image)
└── tokenizer/           # Text tokenization
```

#### Import Updates (src/neurobrix/core/runtime/executor.py)
```python
# OLD:
from neurobrix.core.scheduler.factory import SchedulerFactory
from neurobrix.core.scheduler.autoregressive.scheduler import AutoregressiveScheduler

# NEW:
from neurobrix.core.module.scheduler.factory import SchedulerFactory
from neurobrix.core.module.autoregressive.generator import AutoregressiveGenerator
```

#### Backward Compatibility

Aliases provided for transition:
- `AutoregressiveScheduler = AutoregressiveGenerator`
- `VQImageScheduler = VQImageGenerator`
- `BaseScheduler = Scheduler`

---

## Phase 5: Triton Path Stabilization (2026-01-01 - Present)

### v5.1.0 - Triton Classification & inf/nan Guard Fix (2026-01-01)

**TRITON PATH WORKING. Both PixArt-Alpha and PixArt-Sigma verified with --triton flag.**

#### Key Fixes

| Fix | File | Description |
|-----|------|-------------|
| inf/nan Guard Skip | `graph_executor.py` | Guard now skips METADATA ops (T5 GELU transient inf) |
| Duplicate masked_fill | `classification.py` | Removed duplicate entry |
| Classification Reorg | `classification.py` | Clearer section headers and comments |

#### Classification System (--triton mode)

| Category | Count | Description |
|----------|-------|-------------|
| TRITON | 36 | Custom Triton kernels (relu, sigmoid, pooling, etc.) |
| METADATA (precision) | 26 | PyTorch native - Triton kernels have fp16 drift |
| METADATA (optimized) | ~50 | PyTorch/cuDNN highly optimized (SDPA, conv, norm) |
| METADATA (no compute) | ~50 | View ops, tensor creation, type conversion |

#### Precision Workaround

Matrix ops (mm, bmm, matmul), binary ops (add, sub, mul, div), and unary ops (exp, log, sqrt) are routed to METADATA due to fp16 precision drift that accumulates over 1000s of ops (400x divergence in text encoder).

#### inf/nan Guard Fix

```python
# Only check TRITON ops, not METADATA ops
# METADATA ops may produce transient inf values (e.g., T5 GELU pow(x,3))
if exec_type != OpExecution.METADATA and op_uid in self._op_outputs:
    # ... check for nan/inf ...
```

#### Verification

```bash
# Both work with --triton flag
neurobrix run --model PixArt-Sigma-XL-2-1024-MS --hardware v100-32g --prompt "A cute robot" --steps 5 --triton
neurobrix run --model PixArt-XL-2-1024-MS --hardware v100-32g --prompt "A cute robot" --steps 20 --triton
```

---

## Phase 4: Runtime V0.1 (2025-12-30 - 2025-12-31)

### v5.0.0 - Runtime Complete with Memory Fixes (2025-12-30)

**CRITICAL MEMORY FIXES. GENERATION VERIFIED ON V100 32GB.**

#### Memory Management Fixes

| Fix | File | Impact |
|-----|------|--------|
| `_tensor_store.clear()` in unload_weights | `graph_executor.py` | Memory leak fixed |
| `torch.inference_mode()` wrapper | `graph_executor.py` | Memory doubling prevented |
| Dtype flow from Prism | `weight_loader.py` | fp16 weights on V100 |

#### Dtype Flow Implementation

```
Hardware YAML → Prism._resolve_dtype() → allocation.dtype
    → ExecutorFactory → GraphExecutor(dtype) → WeightLoader.to(dtype)
```

- **ONE NBX file** works on ALL hardware (V100→fp16, A100→bf16)
- **ZERO HARDCODE** for dtype - flows from hardware profile

#### Native Mode (`--triton (legacy: --native)`)

- Complete PyTorch fallback without Triton kernels
- Bypasses `kernels/aten/classification.py` entirely
- Uses `NativeATenDispatcher` for all ops
- Verified working on V100 32GB

#### New Files

| File | Purpose |
|------|---------|
| `forge/tracer/dtype_converter.py` | Graph dtype conversion utility |
| `src/neurobrix/core/runtime/native_dispatcher.py` | PyTorch native ops dispatcher |

#### Files Modified

| File | Changes |
|------|---------|
| `src/neurobrix/core/runtime/graph_executor.py` | inference_mode, _tensor_store.clear() |
| `src/neurobrix/core/runtime/weight_loader.py` | _convert_weights_dtype() method |
| `src/neurobrix/core/runtime/metadata_ops.py` | Added _scalar_tensor handler |
| `src/neurobrix/kernels/classification.py` | Added aten::scalar_tensor |

#### Verification

```bash
# Generation completes on V100 32GB
neurobrix run \
  --model PixArt-Sigma-XL-2-1024-MS \
  --hardware v100-32g \
  --prompt "A sunset" \
  --steps 5
```

---

## Phase 3: Universal Detection (2025-12-03 - Present)

### v4.0.0 - Universal Detection Complete (2025-12-03)

**ZERO HARDCODE. VARIABLE EMBARQUÉE. Tout vient de detection.json.**

#### Core Detection System (`src/neurobrix/core/detection/`)
- `detection_rules.py` - 27 catégories de modèles, détection par clés config
- `weight_analyzer.py` - Analyse dtype dominant depuis les weights
- `input_spec_generator.py` - Génération input_spec depuis config.json
- `component_detector.py` - Orchestrateur de détection universelle

#### NBXContainer Integration (`src/neurobrix/nbx/`)
- `ComponentData` dataclass avec detection.json intégré
- `get_input_spec()`, `get_neural_components()`, `get_dominant_dtype()`
- Chargement automatique detection.json par composant

#### Prism Integration (`src/neurobrix/core/prism/solver.py`)
- `solve_from_container()` - Mémoire calculée depuis detection.json
- `_calculate_component_memory_from_detection()` - ZERO HARDCODE

#### Runtime Integration (`src/neurobrix/core/runtime/executor.py`)
- `create_inputs_from_spec()` - Inputs depuis detection.json
- `get_input_spec()` - Accès direct aux specs
- dtype résolu par Prism (bf16→fp32 sur V100)

#### Tests (64 tests)
- `tests/test_detection.py` - 37 tests détection
- `tests/test_import.py` - 10 tests import
- `tests/test_generate.py` - 17 tests ZERO HARDCODE

---

## Phase 2: The Abraham Alliance (2025-11-26 - 2025-12-02)

### v3.1.0 - Phase 2 Kickoff (2025-11-26)

**Bismillah. On construit l'avenir.**

Phase 2 initiated. Focus: Universal Graph Engine with multi-input/output support.

#### Documentation
- Created `docs/ROADMAP.md` with Phase 2 detailed plan
- Updated `.claude/CONTEXT.md` with phase transition
- Updated `.claude/RULES.md` with Phase 2 concepts
- Created `docs/CHANGELOG.md`

#### Planned Components
- `tools/importer/tracer_v2.py` - torch.fx graph capture
- `core/ops/registry.py` - Universal operations registry
- `tech/engine/graph_engine.py` - New graph executor

---

## Phase 1: Noah's Ark (Complete)

### v3.0.0 - Phase 1 Final (2025-11-26)

**Phase 1 Complete. All systems operational.**

#### Core Achievements
- **CLI** (now `neurobrix` command): snapshot, import, generate, info commands
- **Tracer V1** (now `forge/tracer/`): Atomic layer capture with PyTorch-native args
- **NeuroTax** (now `forge/neurotax/`): Tensor key translation
- **Prism Solver** (`src/neurobrix/core/prism/solver.py`): Weighted multi-GPU sharding
- **Universal Loader** (`src/neurobrix/core/loaders/universal_loader.py`): Manifest-driven weight binding
- **Scheduler Factory** (`src/neurobrix/core/scheduler_factory.py`): Native scheduler instantiation (zero vendor)
- **Pipeline** (`src/neurobrix/pipelines/universal_flow.py`): TYPE-based dispatch, manifest-driven routing

#### Import System
- Import V10.2 (`tools/importer/import_model.py`)
- Tokenizer↔Encoder linking in manifest
- Output role definition (pooled vs sequence)
- Clean harvest (skip tokenizers/schedulers)

#### Key Fixes in Final Sprint
- Removed all `diffusers` imports from runtime
- Manifest-driven tokenizer lookup (no name sniffing)
- Manifest-driven output routing (no "clip" detection)
- PyTorch-native arg names in topology.json
- Prism device placement (no `.to(device)` on Atomic Engines)

#### Imported Models
- `Flex.1-alpha` - 15 transformer blocks (~21GB)
- `PixArt-XL-2-1024-MS` - 2 blocks (~2.4GB)

#### Known Limitations (Addressed in Phase 2)
- Sequential-only execution
- Single input support
- No functional operations (add, matmul, softmax)
- No residual/skip connections

---

### v2.x - Development Versions

*(Internal development versions not tracked)*

---

### v1.0 - Initial Concept

- Basic model loading
- Single-GPU execution
- Manual configuration

---

---

## Guiding Principles

> **"Le Chef écrit la recette. Le Cuisinier la suit aveuglément."**

- **Import** = Intelligence (understands vendor models)
- **Runtime** = Pure execution (reads manifest, executes graph)
- **Manifest** = Single source of truth
