# Spec: min_reranker_score + Extraction Context Tuning

> Generated via /vibeflow:gen-spec on 2026-03-24
> Budget: ≤ 6 files

## Objective

Eliminar facts irrelevantes em queries negativas adicionando `min_reranker_score` como filtro pós-reranker, e melhorar a qualidade da extração pra evitar facts ambíguos sem contexto.

## Context

O read pipeline v0.6.34 retorna 15-20 facts irrelevantes em queries negativas (ex: "Qual o time de futebol do Bruno?" retorna "Bruno corre maratona"). O reranker corretamente dá score 0.0 pra esses facts, mas o multiplicative blend mantém eles vivos (`0.80 × 0.30 = 0.24 > min_score 0.15`). O reranker é o único componente que entende semântica profunda — quando ele diz 0.0, o fact deveria ser eliminado.

Separadamente, a extração gera facts ambíguos como "Bruno e Fernanda são do mesmo time" sem contexto (time de trabalho? futebol?). O fact completo deveria ser "Bruno e Fernanda trabalham no mesmo time de dados na Orion Tech".

## Definition of Done

1. **[ ] `min_reranker_score` implementado** — `MemoryConfig` tem campo `min_reranker_score: float = 0.1`. No reranker, facts com `reranker_score < min_reranker_score` são eliminados antes do blend. Facts eliminados recebem `final_score = 0.0`.
2. **[ ] Bypass quando reranker OFF** — Se `enable_reranker=False`, `min_reranker_score` não tem efeito (não há score de reranker pra filtrar).
3. **[ ] Prompt de extração melhorado** — FACT_EXTRACTION_PROMPT e EXTRACTION_SYSTEM_PROMPT instruem: "Each fact must include enough context to be unambiguous. 'Are on the same team' is bad — 'work on the same data team at Orion Tech' is good."
4. **[ ] Testes passam** — Todos os testes existentes + novo teste `test_min_reranker_score_filters_irrelevant`.
5. **[ ] Docs atualizados EN + PT-BR** — `min_reranker_score` documentado em read-pipeline, configuration, e llms-full.txt. CHANGELOG atualizado.

## Scope

### 1. `src/arandu/config.py`
- Adicionar `min_reranker_score: float = 0.1`

### 2. `src/arandu/read/reranker.py`
- Após calcular `reranker_score`, antes do blend: se `reranker_score < config.min_reranker_score`, setar `final_score = 0.0` (será filtrado pelo `min_score` existente no pipeline.py)

### 3. `src/arandu/write/extract.py`
- Adicionar regra de contexto nos prompts FACT_EXTRACTION_PROMPT e EXTRACTION_SYSTEM_PROMPT

### 4. Testes
- `test_min_reranker_score_filters_irrelevant` — fact com reranker < 0.1 é eliminado
- `test_min_reranker_score_bypassed_when_reranker_off` — sem efeito quando reranker desligado

### 5-6. Docs EN + PT-BR
- read-pipeline.md, configuration.md, llms-full.txt, CHANGELOG

## Anti-scope

- **Não mudar o blend multiplicativo** — já funciona pra queries positivas
- **Não mudar min_score default** — 0.15 tá ok
- **Não mudar graph BFS** — agressividade do graph é problema separado
- **Não adicionar graph score decay por hops** — scope separado
- **Não mudar reranker prompt** — já tá bom (acerta 53/54 facts)

## Technical Decisions

### TD-1: Eliminar no reranker, não no pipeline
O filtro `min_reranker_score` roda dentro de `rerank_with_llm()`, não em `pipeline.py`. Motivo: manter a lógica de reranking encapsulada. O pipeline já filtra por `min_score` — facts com `final_score = 0.0` serão naturalmente filtrados.

### TD-2: Default 0.1, não 0.0
Com threshold 0.0, facts com reranker=0.0 passam (comparação `<`, não `<=`). Com 0.1, facts com reranker=0.0 são eliminados mas facts com reranker=0.1+ sobrevivem. O gap 0.0-0.1 é "completamente irrelevante" — seguro eliminar.

### TD-3: Regra de contexto na extração é genérica
A instrução "include enough context to be unambiguous" é agnóstica — não menciona domínios específicos. Cobre "time de trabalho" vs "time de futebol" sem listar exemplos domain-specific.

## Applicable Patterns

- **LLM Interaction** — Prompt changes seguem pattern de instruções genéricas, não domain-specific.
- **Error Handling** — Fail-safe: se `min_reranker_score` parsing falha, usa default 0.1.
- **Pipeline Orchestration** — Filtro é sub-step dentro do reranker, não stage separado.

## Risks

1. **False negatives em queries parcialmente relevantes** — Se o reranker dá 0.05 (quase irrelevante mas não zero), o fact é eliminado. Mitigação: threshold 0.1 é conservador — reranker raramente dá 0.01-0.09.
2. **Reranker errando alto (false positive)** — O caso "time" = 0.7. Mitigação: esse é 1 caso em 54 (1.8%), aceitável. A regra de contexto na extração reduz a probabilidade.
