# Root Cause Analysis: Bedrock LLM Not Being Used in `gfa classify`

**Date**: 2026-02-27
**Symptom**: All 8,784 rows in `qualitative_commits` have `processing_method = 'fallback_only'`
**Command**: `cd ~/Duetto/repos && gfa classify -w 16 -c gitflow-all-engineers.yaml --reclassify`

---

## Root Cause

**`boto3` is not installed in the uv-installed `gfa` environment.**

The `gfa` binary used in production is:
```
/Users/masa/.local/bin/gfa
→ uses Python: /Users/masa/.local/share/uv/tools/gitflow-analytics/bin/python3
```

This is a **separate Python environment** from the local `.venv` in the project directory. The uv-tools environment does **not** have `boto3` installed, so `BedrockClassifier.__init__` raises `ImportError` and the entire LLM path silently fails.

---

## Failure Chain (step by step)

1. `gfa classify` calls `run_classify()` in `pipeline_classify.py`
2. `run_classify()` reads `cfg.analysis.llm_classification` — all settings look correct (`enabled=True`, `provider=bedrock`)
3. `BatchCommitClassifier.__init__()` creates an `LLMCommitClassifier` with `provider='bedrock'`
4. `LLMCommitClassifier._init_bedrock_classifier()` tries to import `boto3` inside `BedrockClassifier.__init__`
5. **`ImportError: boto3 is required for AWS Bedrock support`** is raised
6. `_init_bedrock_classifier()` catches `(ImportError, Exception)` at line 304, logs a WARNING, and sets `self.classifier = None`
7. Back in `BatchCommitClassifier.__init__`: `classifier_has_provider = self.llm_classifier.classifier is not None` → **`False`**
8. `not classifier_has_provider and not llm_config_obj.api_key` → **`True`**
9. `self.llm_enabled = False` is set with warning "No LLM provider configured"
10. Every call to `_classify_commit_batch_with_llm()` hits the `if not self.llm_enabled:` branch (line 329)
11. All commits get stored with `processing_method = 'fallback_only'`, `error = "LLM not configured"`

**Evidence in DB:**
```
processing_method | count
'fallback_only'   | 8784

technical_context breakdown:
{"error": "LLM not configured"}  → 6,248 rows  (new rows, not previously classified)
{"error": null}                  → 2,536 rows  (existing rows updated, technical_context not touched)
```

The `error: null` rows are explained by `_store_commit_classification()` — the **update path** (line 544) sets `processing_method` but never updates `technical_context`, so those rows retain their original `null` error from a previous run.

---

## Why the Local .venv Works Fine

The project's local `.venv` at `/Users/masa/Projects/gitflow-analytics/.venv/` **does** have `boto3` installed (it was installed manually or via dev setup). Running `python -c "import boto3"` in the local venv succeeds. But `gfa` was installed via `uv tool install` which creates a completely isolated environment.

---

## What Does NOT Work in pyproject.toml

`boto3` is not listed as a dependency anywhere in `pyproject.toml`:
- **`dependencies`** (core): no boto3
- **`llm` extra**: only `openai>=1.30.0` and `tiktoken>=0.7.0` — covers OpenRouter only
- **`all` extra**: `gitflow-analytics[github,ml,llm]` — still no boto3
- **No `bedrock` extra exists**

---

## Fix

### Option 1 (Recommended): Add a `bedrock` extra to `pyproject.toml`

```toml
[project.optional-dependencies]
# ... existing extras ...

# AWS Bedrock LLM provider
bedrock = [
    "boto3>=1.34.0",
]

# LLM-powered qualitative analysis (OpenAI / OpenRouter)
llm = [
    "openai>=1.30.0",
    "tiktoken>=0.7.0",
]

# All providers
llm-all = [
    "gitflow-analytics[llm,bedrock]",
]

all = [
    "gitflow-analytics[github,ml,llm,bedrock]",
]
```

Then reinstall:
```bash
uv tool install --force "gitflow-analytics[bedrock]" --from /Users/masa/Projects/gitflow-analytics
# or if installing from PyPI:
uv tool install --force "gitflow-analytics[bedrock]"
```

### Option 2 (Quick Fix): Install boto3 directly in the uv tools env

```bash
/Users/masa/.local/share/uv/tools/gitflow-analytics/bin/pip install boto3>=1.34.0
```

### Option 3: Make boto3 a required (non-optional) dependency

Add `"boto3>=1.34.0"` to the main `dependencies` list in `pyproject.toml`. This is only appropriate if Bedrock is considered a core feature.

---

## Verification After Fix

After installing boto3 in the correct environment:
```bash
# Confirm boto3 is available
/Users/masa/.local/share/uv/tools/gitflow-analytics/bin/python3 -c "import boto3; print('OK:', boto3.__version__)"

# Test a small classify run
cd ~/Duetto/repos && gfa classify -w 1 -c gitflow-all-engineers.yaml --reclassify

# Check DB for LLM usage
sqlite3 /Users/masa/Duetto/repos/.gitflow-cache/gitflow_cache.db \
  "SELECT processing_method, COUNT(*) FROM qualitative_commits GROUP BY processing_method;"
# Should now show 'llm' or 'llm_batch' methods, not just 'fallback_only'
```

---

## Code Improvement Recommendations

### 1. Surface the ImportError more visibly (not silently swallowed)

In `llm_commit_classifier.py` `_init_bedrock_classifier()`:
```python
except ImportError as exc:
    logger.error(  # Change from warning to error
        "boto3 is not installed — AWS Bedrock unavailable. "
        "Install with: pip install boto3  (or: uv tool install gitflow-analytics[bedrock])"
    )
    self.classifier = None
```

### 2. Warn prominently in pipeline_classify.py when enabled=True but llm_enabled=False

In `batch_classifier.py` after setting `self.llm_enabled = False`:
```python
if llm_config_obj.provider == "bedrock":
    logger.error(
        "LLM classification is ENABLED in config (provider=bedrock) but boto3 is not installed. "
        "All commits will use rule-based fallback. "
        "Fix: pip install boto3"
    )
```

### 3. Add a startup check in pipeline_classify.py

Before building `BatchCommitClassifier`, check:
```python
if cfg.analysis.llm_classification.enabled and cfg.analysis.llm_classification.provider == "bedrock":
    try:
        import boto3
    except ImportError:
        _emit("ERROR: LLM classification enabled (bedrock) but boto3 is not installed. "
              "Run: pip install boto3")
```

---

## Summary Table

| Component | File | Line | Issue |
|-----------|------|------|-------|
| Missing boto3 | `pyproject.toml` | optional-deps | `boto3` not in `llm` or `bedrock` extra |
| Silent fail | `llm_commit_classifier.py` | 304 | `ImportError` caught, logs warning, sets `classifier = None` |
| Silent disable | `batch_classifier.py` | 103-108 | `classifier is None` → `llm_enabled = False` with only a warning |
| No startup check | `pipeline_classify.py` | 120-126 | `enabled=True` from config never validated against actual capability |
