CreaRack-SL

Sync Cascade: ediciones puntuales en lugar de body completo (P5 · s83)

Sync Cascade: ediciones puntuales en lugar de body completo (P5 · s83)

Tercera palanca del plan de optimización de coste del Bibliotecario (sesión s83, PR #84, 2026-05-25). Cambia la estrategia de wiki_propose_mirror_update: en vez de pedirle a Haiku que devuelva el documento espejo completo, ahora devuelve ediciones puntuales {old_str, new_str} que se aplican mediante search/replace seguro.

Problema que resuelve

wikiProposeMirrorUpdate usaba max_tokens: 24000 y pedía a Haiku el body completo del espejo. Dos consecuencias graves:

  1. Coste desproporcionado: el output de tokens es el componente más caro de Haiku. El pico del 24-05-2026 ($4,03/día, de los cuales $2,96 solo en output) fue causado por el refactor de onboardings, que disparó cascadas a 3 documentos de ~42 KB cada uno, regenerados enteros.
  2. Riesgo destructivo (footgun s80): el flujo de PR #66/#67 (+20 / -600 LOC) vivía aquí. Se parcheó subiendo límites, pero la raíz estructural permanecía: “devuélveme el documento entero”. Un modelo truncado o confundido podía borrar secciones enteras sin aviso.

Solución implementada

Nuevo protocolo con Haiku

El system prompt cambia de “devuelve proposed_body completo” a “devuelve edits: [{old_str, new_str}]”:

"edits": [
  { "old_str": "<fragmento EXACTO del espejo>", "new_str": "<texto de reemplazo>" }
]

Reglas impuestas a Haiku en el prompt:

  • old_str debe ser copia exacta y literal del espejo (mismos espacios, acentos, mayúsculas).
  • Para añadir contenido nuevo: usar una línea ancla existente como old_str y repetirla al inicio de new_str.
  • Solo devolver fragmentos que cambian, nunca el documento completo.
  • max_tokens bajado de 24 000 → 4 000 (solo fragmentos, no documentos enteros).

Función applyMirrorEdits

Nueva función de aplicación segura (ver entity--biblioteca--function--apply-mirror-edits):

applyMirrorEdits(body: string, edits: MirrorEdit[])
  → { result: string; applied: number; failed: Array<{excerpt, reason}> }

Comportamiento ante casos problemáticos:

CasoAcciónRazón
old_str vacíoOmitirempty_old_str
old_str === new_strOmitirnoop
old_str no encontradoOmitirnot_found
old_str aparece >1 vezOmitirambiguous
Aplicación correctaAplicar—

Lógica de seguridad post-apply

Tras aplicar ediciones, wikiProposeMirrorUpdate aplica salvaguardas adicionales:

  1. 0 ediciones aplicadas → should_propose=false con mensaje [safety]. Mejor revisión manual que aplicar a ciegas.
  2. should_propose=true con edits=[] → should_propose=false con mensaje [safety].
  3. Red heredada de s80 (aún activa): si el resultado encoge >50% del mirror_body → should_propose=false.

Respuesta enriquecida

El caller (sync_cascade.mjs) sigue recibiendo proposed_body construido. Se añaden dos campos de observabilidad:

  • edits_applied: número de ediciones aplicadas con éxito.
  • edits_failed: lista de {excerpt, reason} para trazabilidad.

Validación

  • Smoke test de applyMirrorEdits: 5/5 casos
    • Reemplazo normal ✅
    • Ancla inexistente (no-destructiva) ✅
    • Ancla ambigua omitida ✅
    • Inserción vía ancla ✅
    • Preserva el resto del documento ✅
  • tsc --noEmit sin errores en wiki.ts ✅

Impacto esperado en coste

MétricaAntesDespués
max_tokens Haiku24 0004 000
Output típicoBody completo (~14K tokens en onboardings grandes)Solo fragmentos (~500–1500 tokens)
Riesgo PR destructivoPresente (body completo regenerado)Eliminado por construcción

Véase también

  • [[entity—biblioteca—function—apply-mirror-edits]]