SEXTANTEcursos técnicos de IA
métodoromper-y-arreglar
presupuestoatención
Entrar
N2 · Memoria/L3

El store externo

Objetivo de maestría

montarás memoria de largo plazo en Magallanes con el Store de LangGraph —namespaces, put/get/search, semantic search donde aporte— con políticas explícitas de escritura y recuperación, y una lista negra razonada de qué NO guardar. Importa porque sin política, un store es un vertedero que crece, encarece y contamina cada encargo siguiente.


3.1El problema

Magallanes ya retoma su encargo. Lo arreglaste en L2: las notas viven fuera de la ventana, sobreviven a la compaction, y al reabrir el run el agente lee el plan intacto en lugar de relanzarlo a ciegas. Dentro del encargo, la amnesia está resuelta.

Hoy llega un encargo nuevo, sobre un tema vecino. Las mismas fuentes del corpus rondan otra vez. Magallanes abre doc-014, llama a leer, la tool devuelve el contenido, el modelo lo clasifica como ruido — y no sabe que ya hizo eso hace siete días. Vuelve a buscar, vuelve a leer, vuelve a descartar lo ya descartado.

Las notas no lo salvan aquí. Las notas son del encargo: viven en el thread y mueren con él. Lo que el agente registró sobre el corpus —este doc_id es fiable, ese otro es ruido— no pertenece a ningún encargo. Pertenece a todos los futuros. Y ahora no se guarda en ninguna parte.

Recuerda L1: hay dos scopes de memoria. Las notas usan el primero —thread-scoped—. Necesitas el segundo: cross-thread, memoria que un encargo deja y el siguiente recoge. La pieza que lo implementa en LangGraph es el Store.


3.2Qué vas a poder hacer

Al terminar serás capaz de:

  • Montar un Store de LangGraph en Magallanes: inyectarlo en el grafo, escribir con put bajo un namespace, recuperar con get y search.
  • Decidir cuándo activar semantic search y cuándo basta una clave exacta, justificándolo con el tamaño del corpus.
  • Escribir la política write/select del agente: qué persiste, cuándo, en qué namespace, hot path o background.
  • Razonar una lista negra de qué NO guardar y detectar entradas que la violan.

Necesitas saber antes:

  • De L1 (/cursos/context-engineering/memoria/la-memoria-es-arquitectura): la distinción short-term (checkpointer, thread-scoped) vs long-term (Store, cross-thread), y los tipos CoALA. Lo recuperamos en 3.3.
  • De L2 (/cursos/context-engineering/memoria/note-taking-estructurado): qué estructura tienen las notas del encargo y por qué sobreviven a la compaction.
  • De N0·L1 (/cursos/context-engineering/diagnostico/el-mito-de-la-ventana-infinita): que cada token en la ventana paga renta de atención. Lo necesitas para entender por qué un store sin política sale caro.

3.3Recupera

Antes de seguir, responde de memoria. Esto reactiva lo anterior y engancha lo nuevo.

  1. Checkpointer vs Store: ¿cuál es thread-scoped y cuál cross-thread? ¿Qué continuidad resuelve cada uno?
  2. En L2, tus notas tenían estructura (estado, decisiones, pendientes). ¿Por qué sobreviven a una compaction, si el historial sí se comprime?
  3. En el mapa de memoria de L1, "fuentes fiables por tema" lo clasificaste como un tipo CoALA concreto. ¿Cuál era, y por qué no era episodic?
Comprueba tu respuesta
  1. El checkpointer es thread-scoped: repone el estado de UN hilo y resuelve la continuidad de sesión. El Store es cross-thread: vive fuera de cualquier hilo y resuelve la continuidad de la relación entre encargos. · fuente: corpus C.1 ("el checkpointer resuelve la continuidad de sesión; el Store, la continuidad de la relación").
  2. Porque viven fuera de la ventana de contexto. La compaction comprime el historial dentro del hilo; las notas están persistidas aparte y se vuelven a leer con leer_notas. Lo que la compaction descarta, la nota ya lo guardó. · fuente: corpus C.2 + B.4.
  3. Semantic — conocimiento sobre el mundo ("knowledge about the world and itself"). "Este doc es fiable" es un hecho estable del corpus, no la traza de una interacción. Sería episodic si guardaras cómo transcurrió un encargo concreto (un few-shot de una sesión exitosa), no el hecho destilado. · fuente: corpus C.1 (CoALA).

3.4El concepto

Empatía antes de entrar: esta es la sección más densa del nivel. Trae namespaces, dos políticas y semantic search, todo nuevo. La estrategia: lee el concepto entero una vez sin pararte, corre el worked example de 3.5, y vuelve aquí. Las políticas se entienden desde el código, no al revés.

Qué es el Store

Aquí está la pieza. El Store de LangGraph es el almacén de memoria long-term del agente: cross-thread, persistente, organizado por namespaces. Analogía: es la wiki de un equipo frente al acta de una reunión. El acta —las notas, el thread— registra una sesión y se archiva. La wiki acumula lo que el equipo sabe; la consulta cualquiera, en cualquier momento. El límite de la analogía: la wiki no se consulta sola. En el agente, cuándo se lee el Store es parte del diseño —la política de select—, no un hábito que aparece solo.

El Store se inyecta en el grafo igual que el checkpointer, en la compilación (corpus F.1):

python
1from langgraph.store.memory import InMemoryStore
2
3store = InMemoryStore()
4graph = builder.compile(checkpointer=checkpointer, store=store)

InMemoryStore para iterar en local; PostgresStore (existe, ✅CoVe en el corpus F.1) cuando el store debe sobrevivir al proceso. Esa migración la harás en L4, cuando el lab mate a Magallanes; aquí basta el de memoria.

El Store organiza la memoria por namespaces: tuplas jerárquicas que actúan como rutas de carpetas. Un namespace no es una cadena; es una tuple[str, ...], p. ej. ("magallanes", "fuentes", "rutas-comerciales"). La jerarquía importa: te deja guardar por tema y recuperar por prefijo.

La API tiene tres operaciones que usarás (firmas verificadas, corpus F.1 y api-nivel2 §F.1-ext):

python
1# put — almacena o actualiza una entrada bajo (namespace, key)
2store.put(
3    ("magallanes", "fuentes", "rutas-comerciales"),   # namespace: tupla jerárquica
4    "doc-014",                                          # key: clave exacta
5    {"veredicto": "ruido", "resumen": "fuente sin datos primarios"},  # value: dict
6)
7
8# get — recupera por clave exacta; devuelve un Item o None
9item = store.get(("magallanes", "fuentes", "rutas-comerciales"), "doc-014")
10
11# search — busca dentro de un namespace; devuelve list[SearchItem]
12resultados = store.search(
13    ("magallanes", "fuentes"),    # prefijo de namespace
14    query="rutas comerciales del siglo XVI",
15    limit=5,
16)

Tres detalles que evitan errores comunes:

  • get necesita la clave exacta. Si no sabes el doc_id de antemano, get no te sirve; ahí entra search. Es común intentar get con una descripción del contenido — devuelve None, porque get no busca, recupera.
  • search recorre por prefijo de namespace. ("magallanes", "fuentes") alcanza todo lo que cuelga de ahí, incluidos los sub-namespaces por tema. Un prefijo más corto, más amplitud.
  • put con la misma (namespace, key) sobrescribe. No apila versiones. Si quieres histórico, la clave debe variar; si quieres estado actual, repite la clave.

Semantic search: opt-in, no por defecto

Por defecto, search con query=... no hace búsqueda semántica: el Store no tiene cómo. Semantic search —recuperar por significado en vez de por coincidencia exacta— se activa explícitamente al construir el Store, pasándole una configuración de índice (corpus F.1, api-nivel2 §F.1-ext):

python
1from langgraph.store.memory import InMemoryStore
2from langchain.embeddings import init_embeddings
3
4store = InMemoryStore(
5    index={
6        "dims": 1536,                                          # dimensiones del vector
7        "embed": init_embeddings("openai:text-embedding-3-small"),
8        "fields": ["$"],                                       # "$" = indexar el value entero
9    }
10)

Con index configurado, store.search(ns, query="...") embebe la query, la compara contra los vectores guardados y ordena por similitud. Cada SearchItem trae un score. Sin index, query no activa nada semántico — recuperas por filtro y namespace. Sin query, search devuelve todas las entradas del namespace prefix ordenadas por clave, hasta limit.

La decisión no es "siempre actívalo". Semantic search añade un coste por escritura (calcular el embedding) y una dependencia (el modelo de embeddings). Si tu corpus tiene 30 fuentes con doc_id conocidos, un get por clave o un search por namespace ya resuelve. Semantic search gana cuando recuperas por un tema que no conoces de antemano. El namespace debe tener demasiadas entradas para listarlas a mano. El tamaño del corpus decide; lo verás en la práctica.

Las dos políticas: write y select

El Store es infraestructura. Lo que lo convierte en memoria útil —o en vertedero— son dos políticas, las mismas que la rúbrica C2 te exigirá por escrito. Vienen del marco de Lance Martin (corpus B.2): la palanca write ("saving it outside the context window to help an agent perform a task") y la palanca select ("pulling it into the context window").

Write — qué persistir y cuándo. No todo merece entrar al Store. Merecen: hechos estables verificados, fuentes evaluadas con su veredicto, preferencias del usuario sobre el formato. El cuándo tiene dos modos (corpus C.1):

  • Hot path — el agente escribe en el turno, con una tool de memoria disponible al instante. Sirve cuando debe poder anotar algo en cuanto lo descubre. LangGraph ofrece las tools de LangMem para esto (corpus F.2): create_manage_memory_tool(namespace) y create_search_memory_tool(namespace). Envuelven el put/search del Store como tools. El coste: latencia en el turno y carga de decisión al agente.
  • Background — un nodo escribe tras el run, fuera del camino crítico. Sirve cuando el destilado puede esperar y no quieres latencia ni cargar al agente con la decisión. El coste: hay que decidir la frecuencia, y la escritura no está disponible durante el turno.

Select — qué recuperar y cuándo. Al abrir un encargo, Magallanes carga lo que el Store sabe del tema: un get por el namespace de preferencias y un search semántico por el tema, con limit corto. El limit no es un adorno: cada entrada recuperada entra a la ventana y paga renta de atención (eco de N0·L1). Recuperar 50 "por si acaso" reintroduce el problema que el Store venía a resolver.

Qué NO guardar

Aquí está el principio que vertebra el nivel: un store sin política de escritura es un vertedero que envenena. El coste de guardar de más no es el disco — el disco es barato. El coste es que search te devolverá esa basura como contexto, y la pagarás en la ventana, encargo tras encargo.

La lista negra, razonada:

  • Datos sin verificar. Si guardas un "hecho" que no comprobaste, lo servirás como cierto. Peor: si un atacante consigue que lo guardes, actuarás sobre él semanas después. Esa es la cara oscura que L4 abre — memory poisoning (corpus C.7).
  • Documentos completos. Re-recuperables con leer(doc_id) cuando los necesites. Guarda el veredicto, no el documento. Un veredicto son tres líneas; un documento, miles de tokens que search reinyectará.
  • Resultados intermedios. Un cálculo a medias, una búsqueda parcial — son del turno, no de la relación. Mueren con el encargo.
  • Cualquier cosa atada al thread. El estado del grafo, el plan, los pendientes: eso es del checkpointer y de las notas. En el Store es ruido cross-encargo.

Contraejemplo, el más tentador: persistir cada tool result "por si acaso". Parece prudente. Es el vertedero. Cada leer() que guardes vuelve como resultado de search y compite por la atención del modelo. La prudencia aquí es escribir menos, con veredicto, no más.


3.5Míralo funcionar

Vamos a montar la memoria semántica de fuentes de Magallanes. Tiene dos piezas: un nodo background que, al cerrar el informe, destila qué aprendió de cada fuente y lo guarda; y un select que, al abrir el siguiente encargo, precarga esos veredictos.

Antes de leerlo línea a línea, lee el bloque entero una vez de corrido. Lo difícil no es ninguna línea suelta; es ver el ciclo. El run termina → un nodo destila y hace put por fuente → el siguiente encargo hace search y arranca con esos veredictos en contexto. Primero capta la forma.

El write: un nodo background que destila fuentes

python
1# memoria_fuentes.py — write en background; usa el Store inyectado en el grafo
2from langgraph.store.base import BaseStore
3
4def destilar_fuentes(state: dict, *, store: BaseStore) -> dict:
5    # Se ejecuta tras cerrar el informe. NO está en el hot path del agente.
6    # state["fuentes_evaluadas"]: lista de {doc_id, tema, veredicto, resumen} del run.
7    for f in state["fuentes_evaluadas"]:
8        store.put(
9            ("magallanes", "fuentes", f["tema"]),     # namespace por tema
10            f["doc_id"],                              # key = el doc_id
11            {                                          # value: el veredicto, NO el documento
12                "veredicto": f["veredicto"],          # "fiable" | "ruido"
13                "resumen": f["resumen"],              # una línea, no el texto completo
14            },
15        )
16    return state

Tres cosas de este nodo:

  • El namespace lleva el tema: ("magallanes", "fuentes", tema). Así el select por tema recupera solo lo relevante, y search por el prefijo ("magallanes", "fuentes") alcanza todo si hace falta.
  • El value es el veredicto, no el documento. {veredicto, resumen} son pocas líneas. El documento entero se queda en el corpus, re-recuperable con leer(). Esto es la lista negra aplicada.
  • Va en background, tras el informe. El agente no paga latencia por esto durante el turno, y no carga con la decisión de cuándo escribir mientras investiga.

Falta enchufarlo al grafo. El nodo se coloca como último paso del run, después de escribir_informe. El grafo lo ejecuta fuera del camino crítico del agente:

python
1# En el grafo de Magallanes: el nodo background va al final del run
2builder.add_node("destilar_fuentes", destilar_fuentes)
3builder.add_edge("escribir_informe", "destilar_fuentes")
4builder.add_edge("destilar_fuentes", END)

LangGraph inyecta el store a destilar_fuentes porque el nodo lo declara como parámetro keyword-only tipado BaseStore, y el grafo se compiló con store=store.

El select: precargar al abrir un encargo nuevo

python
1# select.py — al abrir el encargo, traer lo que ya sabes del tema
2def precargar_memoria(encargo: str, store: BaseStore) -> list:
3    # search semántico por el tema del encargo; limit corto a propósito.
4    conocidas = store.search(
5        ("magallanes", "fuentes"),    # prefijo: todas las fuentes, cualquier tema
6        query=encargo,                # el tema del encargo nuevo
7        limit=5,                      # pocas, no 50 — cada una paga renta de atención
8    )
9    # Cada r es un SearchItem: r.value tiene {veredicto, resumen}; r.score, la similitud.
10    return [
11        {"doc_id": r.key, **r.value}
12        for r in conocidas
13    ]

El select hace search por el prefijo de namespace de fuentes, con la query igual al tema del encargo. Devuelve las cinco fuentes más cercanas con su veredicto. Magallanes arranca el encargo con «doc-014 es ruido» ya en contexto, junto a «doc-027 es fiable para rutas» — sin re-leer nada. El limit=5 es la decisión deliberada: pocas líneas de alto valor, no un volcado.

Self-explanation

Antes de leer la respuesta, intenta tú: ¿por qué el write de fuentes va en background y no en hot path? ¿Y por qué limit=5 y no 50?

Razónalo y comprueba

Background, no hot path: porque el destilado de fuentes puede esperar al final del run. No hay nada que Magallanes necesite anotar al instante mientras investiga — los veredictos se consolidan mejor con el informe ya cerrado. Ponerlo en hot path añadiría latencia a cada turno y obligaría al agente a decidir, en medio de la investigación, qué guardar. El coste no compensa el beneficio. · fuente: corpus C.1 (hot path vs background).

Lo contrario también vale como regla: si el agente debe poder anotar algo en el momento en que lo descubre —y perderlo si espera—, ahí el hot path con create_manage_memory_tool sí compensa (corpus F.2).

limit=5, no 50: porque cada entrada recuperada entra a la ventana de contexto y paga renta de atención (N0·L1). El Store existe para ahorrar contexto, no para reinyectarlo. Un limit=50 traería casi todo el store a la ventana en cada encargo — el vertedero por la puerta de atrás. Cinco veredictos de alto valor baten a cincuenta de valor mezclado.

Si pensaste "background ahorra tokens", matiza: el modo de escritura no cambia cuántos tokens guardas; cambia cuándo pagas latencia y quién decide. El ahorro de contexto lo da el limit del select y la regla de guardar veredictos, no documentos.


3.6Hazlo tú

Andamiaje decreciente: añades un segundo namespace con el primero como plantilla, escribes la política completa, y decides sin guía si un caso amerita semantic search.

Ejercicio 1 — un segundo namespace: preferencias

Magallanes guarda fuentes. Falta lo procedural: las preferencias de formato del informe (longitud de sección, estilo de citación). Usa el nodo de fuentes como plantilla.

  1. Define el namespace ("magallanes", "preferencias") —plano, sin tema, porque hay una sola preferencia por agente—.
  2. Escribe la preferencia con put bajo una clave fija (p. ej. "formato_informe"), de modo que un put nuevo sobrescriba el anterior.
  3. En el select, recupéralo con get (clave exacta), no con search. Justifica en una frase por qué get y no search aquí.

Ejercicio 2 — la política write/select completa, en tabla

Con menos guía: documenta la política de memoria de Magallanes en una tabla con estas columnas — qué, cuándo, namespace, hot/background, qué recupera el select y cuándo. Cubre al menos fuentes y preferencias. Después, defiéndela en cinco líneas: por cada decisión hot/background, el coste que evitas.

Ejercicio 3 — semantic search: ¿sí o no? (sin andamiaje)

Decide si "fuentes fiables" amerita semantic search o si basta get/search por namespace. No hay respuesta única; la justificación es lo que se evalúa. Apóyala en el tamaño del corpus del lab y en cómo recuperas: ¿conoces el doc_id de antemano, o buscas por un tema que aún no tiene clave?

Elaborative interrogation — antes de decidir, predice: si el corpus tuviera 30 documentos con doc_id conocidos, ¿qué te daría semantic search que no te dé un get por clave?

Comprueba tu predicción

Con 30 doc_id conocidos y recuperación por clave, semantic search te da poco. Añade el coste de embeber cada escritura y una dependencia del modelo de embeddings. A cambio, una capacidad —buscar por significado— que no usas si ya tienes la clave. Ahí, get por doc_id o search por namespace basta.

Semantic search empieza a ganar cuando recuperas por un tema que no conoces de antemano ("¿qué sé de rutas comerciales?") y el namespace tiene demasiadas entradas para listarlas y filtrarlas a mano. La query semántica encuentra lo cercano sin que tú sepas las claves. · fuente: corpus F.1 (semantic search opt-in, desactivado por defecto).

Feedback: si dijiste "actívalo siempre, por si acaso", revisa la lista negra de 3.4. "Por si acaso" es el razonamiento que llena el vertedero. Cada capacidad que añades tiene un coste; se activa cuando el patrón de recuperación lo pide, no por defecto.


3.7Comprueba

Sin pistas. Gate de maestría: auditar un store contaminado contra la política.

Un compañero te pasa este volcado del Store de su Magallanes. Diez entradas reales de su lab. Cuatro violan la política de write. Encuéntralas, di qué regla rompe cada una, y escribe la regla de write que la habría evitado.

python
1# Volcado del Store (namespace, key, value)
21.  ("mag", "fuentes", "rutas"),     "doc-014",  {"veredicto": "ruido", "resumen": "sin datos primarios"}
32.  ("mag", "fuentes", "rutas"),     "doc-027",  {"veredicto": "fiable", "resumen": "fuente primaria, 1519"}
43.  ("mag", "fuentes", "rutas"),     "doc-031",  {"texto_completo": "<8.400 palabras del documento entero>"}
54.  ("mag", "estado", "encargo-7"),  "plan",     {"pendientes": ["subtema 3", "subtema 5"]}
65.  ("mag", "preferencias"),         "formato",  {"secciones": "cortas", "citas": "doc_id inline"}
76.  ("mag", "fuentes", "tripulacion"),"doc-052", {"veredicto": "fiable", "resumen": "rumor de un foro, sin verificar"}
87.  ("mag", "fuentes", "rutas"),     "doc-027",  {"veredicto": "fiable", "resumen": "fuente primaria, 1519"}
98.  ("mag", "scratch", "calc"),      "tmp",      {"suma_parcial": 4213}
109.  ("mag", "fuentes", "financiacion"),"doc-061",{"veredicto": "ruido", "resumen": "duplica a doc-014"}
1110. ("mag", "preferencias"),         "formato",  {"secciones": "cortas", "citas": "doc_id inline"}
  1. Marca las cuatro entradas que violan la política.
  2. Nombra la regla rota en cada una.
  3. Escribe la regla de write que la habría evitado.
Criterio de corrección + feedback

Las cuatro violaciones:

  • Entrada 3 — documento completo. Guarda texto_completo (8.400 palabras) en vez del veredicto. Re-recuperable con leer("doc-031"). Regla: guarda el veredicto y un resumen de una línea, nunca el documento; el texto se re-recupera con leer().
  • Entrada 4 — atada al thread. El plan y los pendientes de encargo-7 son del checkpointer y las notas, no del Store cross-encargo. Regla: nada que dependa de un thread entra al Store; el estado del run vive en el checkpointer/notas.
  • Entrada 6 — sin verificar. "Rumor de un foro, sin verificar" marcado como fiable. Lo servirás como cierto en futuros encargos. Regla: solo entran hechos verificados; un veredicto "fiable" exige que la fuente se haya comprobado, no leído.
  • Entrada 8 — resultado intermedio. suma_parcial es scratch del turno, no memoria de la relación. Regla: los resultados intermedios mueren con el encargo; no se persisten.

Caso límite (no cuenta como violación de write, pero anótalo): las entradas 7 y 10 duplican la 2 y la 5 con la misma (namespace, key). Como put sobrescribe, no apilan basura — el store queda en un estado correcto. Si tu auditoría las marcó como "vertedero por duplicación", matiza: misma clave = sobrescritura, no acumulación. Lo que sí delata es un write que repite sin necesidad; barato, pero revisable.

Si tu nodo background se ejecuta dos veces por el mismo run (por un retry o un error), obtienes este patrón. No es basura, es idempotencia — lo correcto. La regla de diseño: haz que put sea siempre idempotente sobre la misma clave para tolerar ejecuciones repetidas.

Nota sobre el criterio: el spec del nivel listaba "duplicado" como categoría de violación, pero put con misma clave es idempotente — no acumula basura. Un alumno que las marque como violación por "exceso de writes" no está incorrecto en espíritu, pero sí en la mecánica del API. Esto se puede aceptar con matiz.

Feedback formativo: si encontraste la 3 y la 4 (documento completo y estado del thread), dominas lo esencial — son las dos que convierten un store en vertedero y las que más caro pagarás en search. Si te faltó la 6 (sin verificar), repasa la lista negra de 3.4: es la entrada más peligrosa, porque no abulta — abre la puerta al poisoning de L4. Gate: necesitas las entradas 3 y 6 identificadas, con su regla, para superar este punto.


3.8Conecta

Ya tienes las tres piezas de la arquitectura de memoria. El checkpointer repone el hilo. Las notas retoman el encargo. El Store conserva lo aprendido entre encargos. Cada una en su scope, cada una con su política.

La rúbrica C2 evalúa exactamente esta lección en su dimensión 2: qué/cuándo escribe el agente —hot path vs background, justificado— y qué/cuándo recupera, con namespacing correcto. La tabla de política del ejercicio 2 es el borrador de ese entregable.

Pero las tres piezas aún no han pasado su prueba. En L4 el lab activa la avería del nivel: mata el proceso de Magallanes a mitad de encargo. Ahí descubrirás cuál de las tres sobrevive con InMemorySaver y cuál no —y por qué el lab migra a Postgres—. La arquitectura que montaste existe precisamente para ese momento.

Y la lista negra de "qué NO guardar" tiene una cara que aún no viste. Hoy la justificaste por coste: el vertedero paga renta en la ventana. Hay otra razón, más oscura. ¿Qué pasa cuando alguien consigue que guardes basura — un dato falso plantado en un README que Magallanes lee, destila y guarda como fiable? Ese es el memory poisoning de L4 (corpus C.7): tu Store es ahora superficie de ataque. La proveniencia que añadirás allí empieza por una regla que ya conoces: solo entra lo verificado.

Cierra el arco de 3.1: Magallanes re-descubría el corpus en cada encargo vecino. Ahora abre el encargo nuevo con un search de cinco líneas y arranca sabiendo qué fuentes son ruido. Lo registrado ya no muere con el thread.


3.9Reflexiona

Tómate un minuto. Responder esto por escrito consolida mejor que releer.

  • ¿Qué aprendiste? En una frase: ¿por qué guardar el veredicto en vez del documento es lo que separa un Store de un vertedero?
  • ¿Qué sigue sin estar claro? ¿Tienes clara la diferencia entre hot path y background, y cuándo elegirías cada uno? Si no, vuelve a 3.4, política write, y a la self-explanation de 3.5.
  • ¿Qué harías distinto? La próxima vez que sientas la tentación de persistir algo "por si acaso", ¿qué dos preguntas te harás antes de hacer el put?

Esto requiere práctica. La intuición de "qué merece cruzar el thread" llega escribiendo políticas y auditando stores, no leyendo sobre ellos. En L4 someterás esta arquitectura al kill, y en L6 la entregarás entera como checkpoint C2.