# Spec: Fact-Entity Links — Part 3: Background Jobs + Cleanup + Docs

> Generated via /vibeflow:gen-spec on 2026-03-23
> PRD: `.vibeflow/prds/fact-entity-links.md`
> Budget: ≤ 6 files

## Objective

Background jobs (clustering, importance scoring) usam links pra agrupar e pontuar facts corretamente, e o multi-pass prompt é ajustado pra refletir que duplicação cross-entity não é mais necessária.

## Context

Parts 1-2 criaram o modelo e atualizaram write + read. Mas background jobs ainda operam por `MemoryFact.entity_key` (singular), e o multi-pass prompt ainda pede "primary subject only" como workaround pra duplicação que agora é resolvida pelo link model.

## Definition of Done

1. **[ ] Clustering usa links** — `cluster_user_facts()` agrupa facts usando links em vez de `entity_key`. Um fact linkado a Clara e Vertix aparece no cluster de ambos (ou no cluster mais relevante).
2. **[ ] Importance scoring conta facts via links** — `compute_entity_importance()` conta `fact_density` usando links (não `entity_key`). Entidades que são mencionadas em many facts (via links) têm score maior.
3. **[ ] Multi-pass prompt limpo** — Remover "DEDUPLICATION: Extract each fact from the perspective of its PRIMARY subject only" do `FACT_EXTRACTION_PROMPT`. Com links, o fact natural ("Clara saiu da Vertix", subject Clara) é suficiente. O prompt pode instruir simplesmente: "Each fact should have the most natural subject."
4. **[ ] Docs EN + PT-BR completos** — Seção "Fact-Entity Links" nos docs de write-pipeline e read-pipeline. Explicar: modelo, como links são criados, como retrieval usa links, impacto em background jobs.
5. **[ ] Testes passam** — Existentes + novos cobrindo clustering e importance via links.

## Scope

### 1. `src/arandu/write/extract.py` — Limpar prompt

Remover a regra de DEDUPLICATION cross-entity do `FACT_EXTRACTION_PROMPT` e `EXTRACTION_SYSTEM_PROMPT`. Substituir por:
```
- Each fact should use the most natural subject — the entity the fact is primarily about.
```

### 2. Background jobs: clustering

`cluster_user_facts()` hoje agrupa por `(entity_type, entity_key)`. Com links, pode agrupar facts que mencionam a mesma entidade via links. Mudar o GROUP BY pra usar `MemoryFactEntityLink`.

### 3. Background jobs: importance scoring

`compute_entity_importance()` hoje conta `fact_density` via `entity_key`. Mudar pra contar via links — um fact linkado a 3 entities conta pra todas 3.

### 4. Docs EN + PT-BR

Seção completa explicando Fact-Entity Links em ambos os pipelines.

### 5. Testes

- `test_clustering_uses_links` — fact linkado a 2 entities aparece nos clusters relevantes
- `test_importance_counts_via_links` — entity com links tem fact_density correta

## Anti-scope

- **Modelo `MemoryFactEntityLink`** — já existe (Part 1)
- **Read pipeline retrieval** — já usa links (Part 2)
- **Spreading activation** — já atualizado (Part 2)
- **Reconciliation** — não mudar
- **Entity resolution** — não mudar
- **Consolidation L2/L3** — complexo, deixar pra depois. Usa entity_key por enquanto.
- **Memify** — complexo, deixar pra depois.

## Technical Decisions

### TD-1: Prompt simplification vs removal
**Trade-off:** Remover a regra de dedup pode fazer o LLM voltar a duplicar. Mas com links, duplicatas são harmless (mesmo fact, links diferentes).
**Decisão:** Trocar a regra restritiva por orientação suave: "use the most natural subject". Se o LLM ainda duplicar ocasionalmente, os links + dedup exato cuidam.

### TD-2: Clustering granularity
**Trade-off:** Um fact linkado a 3 entities — pertence a 1 cluster ou 3?
**Decisão:** Pertence ao cluster do primary subject (is_primary=True). Links secundários são pra retrieval, não pra clustering. Mantém clusters limpos.

## Applicable Patterns

- **Pipeline Orchestration** — background jobs como funções async independentes
- **Data Access** — queries via links
- **Error Handling** — fail-safe patterns existentes
- **Testing** — mock-based

## Risks

1. **Prompt change regride precision** — Mitigação: dedup exato + links tornam duplicatas inofensivas no banco. Benchmark pós-change valida.
2. **Clustering muda resultados** — Mitigação: usar is_primary pra clustering, links secundários só pra retrieval.

## Dependencies

- `.vibeflow/specs/fact-entity-links-part-1.md`
- `.vibeflow/specs/fact-entity-links-part-2.md`
