feat(episode): EXPLORA_entonces-de-dnde-sali-lo-de-basura-la-d_S20260621.R2_XX.ops.2.hot_inf.in.cc.es.000.MGQ_J.PMCFK_E.SGNFD

Skill: NONE | Type: troubleshooting
Summary: EPISODIO 2 — PostgreSQL: Entonces ¿de dónde salió lo de "basura"? — la distinció
This commit is contained in:
Ember 2026-06-21 18:53:52 +00:00
parent 491c648e47
commit 4057702e2b

View file

@ -0,0 +1,48 @@
---
episode_id: "48c81239-bdbe-4735-852e-e5ccb7c84058"
puente_flat: "EXPLORA_entonces-de-dnde-sali-lo-de-basura-la-d_S20260621.R2_XX.ops.2.hot_inf.in.cc.es.000.MGQ_J.PMCFK_E.SGNFD"
session_code: "S20260621.CRONICA_SEMANAL_C15"
acto_dialogico: "informar"
actor_flujo: "multi_actor"
criticidad_aegis: "low"
skill_dominante: "NONE"
fase_proyecto: "operations"
tipo_semantico: "troubleshooting"
summary_one_line: "EPISODIO 2 — PostgreSQL: Entonces ¿de dónde salió lo de "basura"? — la distinción cla"
source_type: "claude_code"
trust_boundary: "internal"
created_at: "2026-06-21T18:53:44.877123+00:00"
relectura_tagged: false
forgejo_commit_sha: "pending"
---
Tienes toda la razón, John — y **mi redacción en el doc fue confusa.** Lo confirmo en vivo con el mismo episodio en los 3 stores:
| Store | `episode_id` | `puente_flat` / `puente_id` |
|---|---|---|
| **PostgreSQL** | `e29a5a0b-…-450755c5` (uuid4) | `DEPURA_la-historia-…-R4_…SGNFD` |
| **Qdrant** | `500ea674-…-291347a` (uuid5) | `DEPURA_la-historia-…-R4_…SGNFD` |
| **Neo4j** | `500ea674-…-291347a` (uuid5) | `DEPURA_la-historia-…-R4_…SGNFD` |
**El `puente_flat`/`puente_id` es BYTE-A-BYTE IDÉNTICO en los 3 → matchea perfecto, exactamente como tú dices.** No da basura.
### Entonces ¿de dónde salió lo de "basura"? — la distinción clave
Cada episodio tiene **DOS identificadores distintos**, y yo los mezclé al explicarte:
1. **`puente_flat` (Qdrant lo llama `puente_id`) — la llave de cruce semántica.** Diseñada a propósito para ser idéntica en los 3 (ADR_S20260514). **Es la que matchea, la que tú tienes en mente. Funciona perfecto.**
2. **`episode_id` — un id técnico INTERNO de cada base, NO la llave de cruce.** Aquí está el detalle: cada base genera su `episode_id` de forma distinta:
- **PostgreSQL** le pone un **uuid4 aleatorio** (es la llave primaria estándar de su tabla — un número al azar).
- **Qdrant y Neo4j** usan un **uuid5 derivado del hash del contenido** (`content_sha256`) — para que el mismo contenido SIEMPRE caiga en el mismo punto (idempotencia / anti-duplicados).
- Como uno es "al azar" y el otro es "calculado del contenido", **nunca coinciden entre sí.**
### La "basura" fue MI error, no del sistema
En mi **primer** reconciliador uní los stores **por `episode_id`** (la llave equivocada) → como el uuid4 de PostgreSQL nunca iguala el uuid5 de Qdrant, salió "todo disjunto, 92 huérfanos" → basura. Cuando lo **corregí a `puente_flat`** (la llave correcta, la que tú dices), **matcheó perfecto: 0 huérfanos en Qdrant.**
**En una frase:** el sistema SÍ tiene un identificador idéntico que une los 3 (`puente_flat`) — tu intuición es correcta. "Da basura" *solo* si uno se equivoca y cruza por `episode_id`, que es un id interno distinto en cada base y nunca fue la llave de cruce. La lección (que ya aprendí): **cruzar siempre por `puente_flat`, nunca por `episode_id`.**
(El único "no-match" legítimo que vimos no es de id sino de **ausencia**: ~15-18 episodios que están en una base y faltan en otra — ese es el drift real y pequeño, no un problema de identificadores.)
### 📍 PARA JOHN — R1
- **Tu intuición:** correcta — `puente_flat` queda idéntico en los 3 y matchea (probado arriba con el mismo episodio).
- **Mi corrección:** la frase del doc "unir por episode_id da basura" es técnicamente cierta pero engañosa; debí decir "**cruzar por `puente_flat` matchea perfecto; solo `episode_id` (id interno) difiere y no sirve de llave**". Si quieres, corrijo esa línea en el `MAPA` para que no confunda a una instancia futura.
- **¿Algo más que aclarar** de la asimetría, o corrijo el doc?