Volver a la wiki

applyMirrorEdits — función de aplicación segura de ediciones en espejos

applyMirrorEdits — función de aplicación segura de ediciones en espejos

Función interna de functions/api/mcp/handlers/wiki.ts introducida en PR #84 (s83, 2026-05-25) como parte del plan de optimización de coste del Bibliotecario. Implementa un search/replace seguro sobre el body del documento espejo: aplica ediciones puntuales {old_str, new_str} enviadas por Haiku, descartando silenciosamente aquellas que sean ambiguas, inexistentes o no-operativas.

Firma

interface MirrorEdit {
  old_str: string;
  new_str: string;
}

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

Comportamiento

Itera la lista de ediciones en orden. Para cada MirrorEdit:

  1. Guarda de old_str vacío → failed con reason: 'empty_old_str'.
  2. Guarda de noop (old_str === new_str) → failed con reason: 'noop'.
  3. Búsqueda: result.indexOf(oldStr).
    • No encontrado → failed con reason: 'not_found'.
  4. Guarda de ambigüedad: si old_str aparece más de una vez → failed con reason: 'ambiguous'.
  5. Aplicación: result = result.slice(0, idx) + newStr + result.slice(idx + oldStr.length). Incrementa applied.

La función opera sobre el body completo del espejo (no el body truncado que se envía al prompt), garantizando que el contenido fuera del contexto del prompt no se pierda.

Propiedades de seguridad

Casos de test validados (5/5)

#EscenarioResultado esperado
1Reemplazo normal en texto únicoapplied=1, texto sustituido
2Ancla inexistentefailed=[{reason:'not_found'}], body sin cambios
3Ancla ambigua (aparece 2+ veces)failed=[{reason:'ambiguous'}], body sin cambios
4Inserción vía ancla (ancla repetida al inicio de new_str)applied=1, contenido nuevo insertado tras ancla
5Múltiples ediciones: preserva secciones no tocadasSolo las secciones objetivo cambian

Contexto de uso

Llamada exclusivamente desde wikiProposeMirrorUpdate tras recibir la respuesta de Haiku:

if (shouldPropose && rawEdits.length > 0) {
  const { result, applied, failed } = applyMirrorEdits(mirrorBody, rawEdits);
  // si applied === 0 → shouldPropose = false (safety)
  // si applied > 0  → proposedBody = result
}

Los campos edits_applied y edits_failed se incluyen en el JSON de respuesta del handler para observabilidad del caller (sync_cascade.mjs).

Ubicación en el código

functions/api/mcp/handlers/wiki.ts
  └── applyMirrorEdits()         ← esta función (línea ~2234)
  └── wikiProposeMirrorUpdate()  ← caller principal
  └── wikiPropagateMirrorChanges() ← orquestador de cascada

Véase también

Subir