# Audit Report: Entity Alias Dedup

**Verdict: PASS**

**Spec:** `.vibeflow/specs/entity-alias-dedup.md`
**Date:** 2026-03-19
**Tests:** 327 passed, 0 failed (0.24s)

### DoD Checklist

- [x] **1. Schema atualizado** — `ExtractedEntity` em `constants.py:249-254` tem campo `aliases: list[str] = Field(default_factory=list)`. Parsing aceita entities com e sem aliases (backward-compatible via default vazio).

- [x] **2. Prompts atualizados** — `ENTITY_SCAN_PROMPT`, `EXTRACTION_SYSTEM_PROMPT` e `REFLEXION_SYSTEM_PROMPT` em `extract.py` incluem instruções sobre aliases com exemplos concretos (apelido, nome completo, escrita diferente, nome parcial). Campo `aliases` presente na descrição do schema JSON nos prompts.

- [x] **3. Normalização de subjects** — Função `_normalize_extraction_subjects()` em `extract.py` constrói mapa alias→canonical, reescreve `fact.subject` e `relation.source`/`relation.target`, e remove relations identity (`same_as`, `also_known_as`, `nickname_of`, `alias_of`) onde source e target mapeiam pra mesma entity. Chamada ao final de `_single_pass_extract()` e `_multi_pass_extract()`. Validado por testes `test_normalize_subjects_rewrites_alias_to_canonical` e `test_normalize_subjects_removes_same_as_self_relations`.

- [x] **4. Aliases registrados no entity resolution** — `_create_entity()` em `entity_resolution.py` aceita `aliases: list[str] | None = None` e registra cada alias via `_register_alias()`. `resolve_entities()` passa `entity.aliases` ao criar entity nova e popula `alias_cache` in-memory com aliases da extraction. Validado por teste `test_create_entity_registers_aliases`.

- [x] **5. Teste end-to-end com aliases** — `FakeLLMProvider` retorna entities com aliases. Testes cobrem: parsing de aliases (`test_extract_parses_aliases`), default vazio (`test_extract_aliases_default_empty`), normalização de subjects (`test_normalize_subjects_rewrites_alias_to_canonical`), remoção de relations same_as (`test_normalize_subjects_removes_same_as_self_relations`), registro de aliases no entity resolution (`test_create_entity_registers_aliases`).

### Pattern Compliance

- [x] **LLM Interaction** (`patterns/llm-interaction.md`) — Prompts são module-level constants. JSON response format mantido. Markdown fence stripping inalterado. Fail-safe on parse error preservado. Evidência: `extract.py` prompts como constantes no topo do módulo.

- [x] **Pipeline Orchestration** (`patterns/pipeline-orchestration.md`) — Extraction não faz DB writes. Entity resolution faz DB writes via savepoints. Pipeline inalterado (anti-scope respeitado). Evidência: `_normalize_extraction_subjects()` é pure function sem side effects.

- [x] **Testing** (`patterns/testing.md`) — FakeLLMProvider com canned JSON. `_make_extraction_response()` helper reutilizado. Class-based test grouping (TestExtract, TestEntityResolution). Sem DB nos testes. Evidência: `test_write_pipeline.py` novos testes seguem o padrão existente.

### Convention Violations

Nenhuma.

### Budget

Files changed: 4 / ≤ 6 budget
- `src/arandu/constants.py` (modified)
- `src/arandu/write/extract.py` (modified)
- `src/arandu/write/entity_resolution.py` (modified)
- `tests/test_write_pipeline.py` (modified)

### Anti-scope Confirmed

- Entity resolution phases (exact/fuzzy/LLM) — não alterados
- Dedup cross-mensagem — não implementado (feature separada)
- Merge de entities já existentes no banco — não implementado
- Chamada de LLM extra — zero adicionadas
- Bug da tabela `memory_entities` vazia — não tocado
- `facts_deleted`/`facts_unchanged` retornando `int` vs `list` — não tocado
- `FACT_EXTRACTION_PROMPT` / `RELATION_EXTRACTION_PROMPT` — não alterados
- `pipeline.py` — não alterado
