# Debug: `gfa classify` Falls Back to Rule-Based Despite AWS Credentials

**Date:** 2026-02-26
**Reported symptom:** `gfa classify -c gitflow-all-engineers.yaml -w 5` prints
"No API key configured for LLM classification" and uses rule-based classification
even though `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` are set in the shell and
`~/Duetto/repos/.env.local` exists.

---

## Root Cause

**The installed binary (`gfa`) is running a stale version (v3.13.14) of the
`classification/batch_classifier.py` that predates Bedrock support.**

The dev source tree (`src/`) is at v3.13.15 and contains the Bedrock-aware
`BatchCommitClassifier`. The installed `~/.local/share/uv/tools/gitflow-analytics`
tool is still at v3.13.14 — one release behind — and was never re-installed after
the Bedrock refactor landed.

---

## Exact Code Path

### Installed v3.13.14 — `batch_classifier.py` (lines 69–103)

```python
# Installed version: batch_classifier.py (v3.13.14)
if isinstance(llm_config, dict):
    llm_config_obj = LLMConfig(
        api_key=llm_config.get("api_key", ""),   # <-- Only reads api_key
        model=llm_config.get("model", "..."),
        # NO provider, aws_region, aws_profile, bedrock_model_id fields!
        ...
    )

# The check that fires the fallback:
if not llm_config_obj.api_key:                  # api_key is empty string
    logger.warning(
        "No API key configured for LLM classification. "  # <-- THIS IS THE MESSAGE
        "Will fall back to rule-based classification."
    )
    self.llm_enabled = False
```

The installed `LLMConfig` dataclass also has **no `provider` field** — it is
OpenRouter-only. The config dict coming from `pipeline_classify.py` contains
`provider: "bedrock"`, `aws_region: "us-east-1"`, and `bedrock_model_id`, but
the installed `BatchCommitClassifier` silently ignores all of those keys and
only checks `api_key`.

### Dev source v3.13.15 — corrected `batch_classifier.py` (lines 72–110)

```python
# Current dev source: batch_classifier.py (v3.13.15)
if isinstance(llm_config, dict):
    llm_config_obj = LLMConfig(
        provider=llm_config.get("provider", "auto"),      # Reads provider
        api_key=llm_config.get("api_key", ""),
        aws_region=llm_config.get("aws_region"),          # Reads AWS fields
        aws_profile=llm_config.get("aws_profile"),
        bedrock_model_id=llm_config.get("bedrock_model_id", "..."),
        ...
    )

# Corrected check:
classifier_has_provider = self.llm_classifier.classifier is not None
if not classifier_has_provider and not llm_config_obj.api_key:
    logger.warning("No LLM provider configured ...")
    self.llm_enabled = False
else:
    self.llm_enabled = True   # Bedrock path sets classifier, so this wins
```

---

## Why The Env/Credential Check Is Not The Issue

Several factors were investigated and ruled out:

1. **`_detect_aws_credentials()` works correctly.** It reads
   `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` directly from `os.environ`.
   These env vars survive `load_dotenv(override=True)` because `load_dotenv`
   only overwrites variables that are *present in the dotenv file* — absent
   keys are left untouched.

2. **`~/Duetto/repos/.env.local` only contains `GITHUB_TOKEN`.** It has no
   AWS keys to load or clobber.

3. **The env file search path is correct.** `ConfigLoader._find_env_files`
   looks in the config file's parent directory first (`~/Duetto/repos/`), which
   is exactly where `.env.local` lives. Loading it correctly adds `GITHUB_TOKEN`
   without disturbing AWS vars.

4. **The YAML config is correctly written** (`provider: bedrock`,
   `bedrock_model_id: "anthropic.claude-3-haiku-20240307-v1:0"`,
   `aws_region: "us-east-1"`, `enabled: true`). The config loader passes all
   these fields to `BatchCommitClassifier` in the `llm_config` dict.

The failure is entirely in the installed binary consuming a pre-Bedrock version
of `batch_classifier.py`.

---

## Fix

Re-install the tool from the current dev source:

```bash
# From the gitflow-analytics repo root
uv tool install --force -e .
# or
uv tool upgrade gitflow-analytics
```

Verify the new version:

```bash
gfa --version   # should show 3.13.15 or higher
```

Then re-run:

```bash
gfa classify -c ~/Duetto/repos/gitflow-all-engineers.yaml -w 5 --log INFO
```

Expected log output (v3.13.15):

```
LLM provider auto-detected: AWS Bedrock (credentials found)
BedrockClassifier initialised: model=anthropic.claude-3-haiku-20240307-v1:0 region=us-east-1 profile=default
LLM Classifier initialised: provider=bedrock model=anthropic.claude-3-haiku-20240307-v1:0
```

---

## Files Involved

| File | Installed path | Source path |
|------|---------------|-------------|
| `batch_classifier.py` | `~/.local/share/uv/tools/gitflow-analytics/lib/python3.12/site-packages/gitflow_analytics/classification/batch_classifier.py` | `src/gitflow_analytics/classification/batch_classifier.py` |
| `llm_commit_classifier.py` | `.../qualitative/classifiers/llm_commit_classifier.py` | `src/gitflow_analytics/qualitative/classifiers/llm_commit_classifier.py` |

The installed `batch_classifier.py` is the v3.13.14 version where:
- `LLMConfig` in `llm_commit_classifier.py` had no `provider`/`aws_region`/`bedrock_model_id` fields
- The Bedrock-aware check `classifier_has_provider = self.llm_classifier.classifier is not None` did not exist
- The only guard was `if not llm_config_obj.api_key` (line 92 in installed file)

---

## Secondary Observation (Non-Blocking)

In the current dev source `pipeline_classify.py` (line 104), the `"enabled"` key
is passed in the `llm_config` dict but `BatchCommitClassifier.__init__` never
reads it. If `enabled: false` is set in YAML the classifier will still be
initialised and attempt LLM calls. This is a latent bug but does not affect the
reported issue.
