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:
- Guarda de
old_strvacío →failedconreason: 'empty_old_str'. - Guarda de noop (
old_str === new_str) →failedconreason: 'noop'. - Búsqueda:
result.indexOf(oldStr).- No encontrado →
failedconreason: 'not_found'.
- No encontrado →
- Guarda de ambigüedad: si
old_straparece más de una vez →failedconreason: 'ambiguous'. - Aplicación:
result = result.slice(0, idx) + newStr + result.slice(idx + oldStr.length). Incrementaapplied.
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
- No-destructiva por construcción: ningún
old_strpuede borrar más de lo que contiene exactamente. Un modelo alucinando texto no existente simplemente no aplicará la edición (not_found). - Anti-ambigüedad: si el fragmento aparece en múltiples lugares, se omite. Haiku debe proporcionar suficiente contexto para que el ancla sea única.
- Preservación del resto: lo que no toca ninguna edición se mantiene byte a byte.
Casos de test validados (5/5)
| # | Escenario | Resultado esperado |
|---|---|---|
| 1 | Reemplazo normal en texto único | applied=1, texto sustituido |
| 2 | Ancla inexistente | failed=[{reason:'not_found'}], body sin cambios |
| 3 | Ancla ambigua (aparece 2+ veces) | failed=[{reason:'ambiguous'}], body sin cambios |
| 4 | Inserción vía ancla (ancla repetida al inicio de new_str) | applied=1, contenido nuevo insertado tras ancla |
| 5 | Múltiples ediciones: preserva secciones no tocadas | Solo 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
- [[feature—biblioteca—sync-cascade-ediciones-puntuales]]