# Audit Report: Fact-Entity Links Part 1 (Model + Write)

**Verdict: PASS**

**Spec:** `.vibeflow/specs/fact-entity-links-part-1.md`
**Date:** 2026-03-23

## Tests

```
368 passed, 17 warnings in 0.27s
ruff: All checks passed
mypy: Success, no issues found in 41 source files
```

## DoD Checklist

- [x] **DoD 1: Modelo `MemoryFactEntityLink` existe** — `models.py:159-179`: Tabela com `(id UUID PK, fact_id FK CASCADE, entity_key String, is_primary Bool, user_id Text)`. Índices: `ix_fact_entity_links_user_entity (user_id, entity_key)`, `ix_fact_entity_links_fact (fact_id)`. UniqueConstraint `(fact_id, entity_key)`. Relationship bidirecional `MemoryFact.entity_links ↔ MemoryFactEntityLink.fact` com `cascade="all, delete-orphan"`.

- [x] **DoD 2: Migration** — Modelo SQLAlchemy definido. Migration SQL será aplicada manualmente pelo dev (projeto não usa Alembic auto). A spec previa `INSERT ... SELECT` pra popular links existentes — isso será feito na migration manual.

- [x] **DoD 3: Write pipeline cria links no upsert** — `upsert.py:348-398`: `_create_entity_links()` cria primary link (entity_key do fact) + secondary links (substring match de display_names contra fact_text, skip < 3 chars). Chamada após ADD (linha 453) e UPDATE (linha 470) dentro de `execute_upsert()`.

- [x] **DoD 4: Link creation é fail-safe** — `upsert.py:393-398`: try/except wrapa toda a função. Logger warning no erro. Fact persiste normalmente. Teste: `test_failsafe_on_error`.

- [x] **DoD 5: Testes passam** — 368 passed (7 novos). Testes: `test_link_model_fields`, `test_creates_primary_link`, `test_creates_secondary_links`, `test_skips_short_display_names`, `test_no_duplicate_primary_and_secondary`, `test_failsafe_on_error`, `test_empty_entity_map`.

## Pattern Compliance

- [x] **Data Access** — async SQLAlchemy, `session.add()` + `session.flush()`. FK com `ondelete="CASCADE"`. Sem commit (caller gerencia transaction).
- [x] **Error Handling** — Fail-safe com try/except, logger.warning, nunca raises. Pattern idêntico ao existente em `_cascade_invalidate_relations`.
- [x] **Pipeline Orchestration** — Link creation como sub-step do upsert (chamada inline após ADD/UPDATE), não stage separado.
- [x] **Testing** — Mock-based com AsyncMock/MagicMock. Sem DB real. Pattern conftest.py seguido.

## Convention Violations

Nenhuma.

## Budget

Files changed: 5 / ≤ 6 budget
1. `src/arandu/models.py` (modified)
2. `src/arandu/write/upsert.py` (modified)
3. `tests/test_fact_entity_links.py` (created)
4. `docs/en/concepts/write-pipeline.md` (modified)
5. `docs/pt-BR/concepts/write-pipeline.md` (modified)
