Volver a la wiki

Incidente s80 — wikiProposeMirrorUpdate generaba PRs destructivos en docs largos

Incidente s80 — wikiProposeMirrorUpdate generaba PRs destructivos en docs largos

Resumen ejecutivo

El handler MCP wikiProposeMirrorUpdate usaba MAX_BODY=8000 para recortar el mirror_body antes de pasarlo a Haiku 4.5. Para documentos de 20-60KB (onboardings, team-processes), Haiku solo veía los primeros ~8KB, los reescribía, y devolvía ese fragmento como proposed_body completo. El resultado: PRs con diffs del tipo +20/-600 que borraban la mayor parte del mirror al aplicarse.

Detectado en producción mediante E2E real con onboardings. Dos PRs destructivos fueron abiertos y cerrados sin merge antes de identificar la causa raíz.


Cronología

FechaEvento
2026-05-22PR #66 abierto: +16/-602 LOC — cerrado sin merge
2026-05-22PR #67 abierto: +23/-226 LOC — cerrado sin merge
2026-05-22@Esquembri identifica la causa raíz: MAX_BODY=8000 en wikiProposeMirrorUpdate
2026-05-22T13:33:01ZFix mergeado en main (commit@67f7b4a)

Causa raíz

En functions/api/mcp/handlers/wiki.ts, función wikiProposeMirrorUpdate:

// ANTES del fix (incorrecto)
const MAX_BODY = 8000;
const truncatedBody = mirrorBody.slice(0, MAX_BODY); // 8KB de ~50KB
// → Haiku solo veía primeros 8KB y devolvía ese fragmento como proposed_body entero

Haiku 4.5 acepta hasta 200K tokens de contexto, pero el límite arbitrario de 8000 chars (~2KB útiles) era radicalmente bajo para documentos de onboarding y team-processes que típicamente pesan 20-60KB.

El modelo actuaba de buena fe: recibía un fragmento y lo mejoraba. No sabía que debía conservar el resto.


Impacto


Fix aplicado (commit@67f7b4a)

Cuatro cambios en wikiProposeMirrorUpdate:

1. MAX_BODY 8000 → 32000

const MAX_BODY = 32000; // era 8000

Proporcional al contexto real de documentos largos. Haiku 4.5 puede manejar este input con holgura.

2. max_tokens output 4000 → 8000

// Llamada a Haiku: max_tokens subido de 4000 a 8000

Evita truncado en la respuesta cuando el proposed_body es largo.

3. Nuevo ABORT_BODY=50000

const ABORT_BODY = 50000;
if (mirrorBody.length > ABORT_BODY) {
  return JSON.stringify({
    should_propose: false,
    reasoning: `mirror_body excede ${ABORT_BODY} chars (${mirrorBody.length}). Revisa a mano...`,
  });
}

Para docs extremadamente largos: mejor skip con mensaje explícito que riesgo de PR destructivo.

4. Safety check post-Haiku (ratio de reducción sospechosa)

const SUSPICIOUS_REDUCTION_RATIO = 0.5;
if (shouldPropose && proposedBodyRaw.length < mirrorBody.length * SUSPICIOUS_REDUCTION_RATIO) {
  shouldPropose = false;
  reasoning = `[safety] Propose descartado: proposed_body (${proposedBodyRaw.length} chars) < 50% del mirror_body...`;
}

Si Haiku devuelve un proposed_body menor al 50% del mirror_body original, se descarta automáticamente como sospechoso de truncado o reescritura destructiva. El reasoning incluye el contexto original de Haiku para debug.


Lecciones aprendidas

  1. Límites de input a LLMs deben reflejar la distribución real de datos, no estimaciones conservadoras arbitrarias. Un límite de 8KB para documentos de onboarding es estructuralmente incorrecto.
  2. Safety checks post-LLM son necesarios cuando el output puede causar pérdida de datos. No basta con confiar en que el modelo “hará lo correcto” con input incompleto.
  3. Los diffs de PRs deben auditarse antes del merge en pipelines que usan LLMs para proponer cambios. Un PR con ratio -600/+16 debe disparar alarma automática.
  4. Abort explícito > fallback silencioso: para docs >50KB, retornar should_propose: false con mensaje claro es preferible a cualquier intento de propuesta parcial.

Estado

Véase también

Subir