MCP Tool: wiki_lint_bulk
Supercontexto Fase 5 — Bibliotecario-Lint
Tool MCP que itera todas las páginas wiki con status: active y genera un reporte de tres categorías de problemas: páginas stale, orphans y broken sources. Diseñado para ejecutarse desde cron o manualmente con 0 llamadas LLM.
Firma
wiki_lint_bulk(args: {
stale_days?: number; // default 60
limit?: number; // default 200
apply_stale?: boolean; // default false
check_sources?: boolean; // default FALSE ⚠️
max_source_fetches?: number; // default 30
}): Promise<string> // JSON
Parámetros
| Parámetro | Tipo | Default | Descripción |
|---|---|---|---|
stale_days | number | 60 | Páginas con last_verified anterior a N días se marcan stale |
limit | number | 200 | Máximo de páginas a escanear por invocación |
apply_stale | boolean | false | Si true, actualiza status:'stale' en D1 vía db.batch() |
check_sources | boolean | false | Si verifica existencia de archivos code en GitHub API. Default OFF — riesgo de exceder límite subrequests CF Workers |
max_source_fetches | number | 30 | Límite de fetches a GitHub cuando check_sources=true |
Respuesta JSON
{
"scanned": 63,
"stale": [{ "slug": "...", "age_days": 90, "title": "..." }],
"orphans": [{ "slug": "...", "title": "..." }],
"broken_sources": [{ "slug": "...", "missing": ["path/to/file.ts"] }],
"sources_checked": 12,
"sources_budget_exhausted": false,
"applied_stale": 0
}
Algoritmo (6 pasadas)
Rediseñado en commit 2319b6c (2026-04-22) para evitar el patrón N+1 que causaba fallos en CF Workers con 60+ páginas:
Pass 1 → SELECT bulk de todas las pages active (1 query D1)
Pass 2 → Reverse-index de backlinks en memoria JS (0 subrequests)
Pass 3 → Code refs con IN chunked a ≤90 bind params por query D1
Pass 4 → Evaluación staleness + orphans en memoria
Pass 5 → [Opcional] Check sources en GitHub, limitado por max_source_fetches
Pass 6 → apply_stale via db.batch() (1 subrequest para todos los UPDATEs)
Pass 2 — Reverse-index de backlinks (detalle)
const referenced = new Set<string>();
for (const p of allPages) {
if (p.supersedes) referenced.add(p.supersedes);
const rel = JSON.parse(p.related || '[]') as string[];
for (const r of rel) referenced.add(r);
}
Una página es orphan si !referenced.has(slug) && !codeReferenced.has(slug).
Pass 3 — IN chunked (detalle)
D1 tiene límite de ~100 bind params por query. Se fragmenta en chunks de 90:
const slugs = allPages.map(p => `doc:wiki:${p.slug}`);
for (let i = 0; i < slugs.length; i += 90) {
const chunk = slugs.slice(i, i + 90);
const placeholders = chunk.map(() => '?').join(',');
await db.prepare(`SELECT ... WHERE n.qualified_name IN (${placeholders})`)
.bind(...chunk).all();
}
Footguns resueltos (2026-04-22 · commit 2319b6c)
Ver también:
concept--infra--cf-workers-d1-footguns
1. “Too many subrequests” (CF Workers Free, límite 50)
Causa: bucle for (const p of pages) hacía 1 query D1 por página para calcular orphans. Con 60+ páginas → >50 subrequests → crash.
Fix: query global única (Pass 1) + reverse-index JS (Pass 2).
2. “D1_ERROR: LIKE pattern too complex”
Causa: WHERE related LIKE '%"slug"%' con slugs complejos disparaba error de D1.
Fix: eliminado completamente. Backlinks calculados parseando related en memoria.
3. apply_stale en bucle (N UPDATEs)
Causa: loop for (const s of stale) { await db...run() } hacía N subrequests.
Fix: db.batch(statements) — 1 subrequest para todos los UPDATEs.
Cron: wiki-lint.yml
# Fragmento relevante tras el fix (commit 2319b6c)
wiki_lint_bulk:
check_sources: false # ← cambiado de true a false
limit: 200
wiki_lint_contradictions:
max_pairs_per_community: 5 # ← reducido de 10
max_total_pairs: 15 # ← reducido de 40
⚠️
check_sources: truesolo debería activarse desde el cron semanal y siempre acompañado de unmax_source_fetchesbajo (≤30) para respetar el presupuesto de subrequests.
Ubicación en el código
| Artefacto | Ruta |
|---|---|
| Handler principal | functions/api/mcp/handlers/wiki.ts → función wikiLintBulk() aprox. línea 1010 |
| Definición tool schema | functions/api/mcp/tools.ts → TOOLS[] entrada wiki_lint_bulk |
| Cron workflow | .github/workflows/wiki-lint.yml |
Trabajo pendiente
- Sesión 11: aplicar el mismo refactor (bulk query + reverse-index) a
wikiLintContradictions. Actualmente sigue con posibles N+1.
Véase también
- [[feature—supercontext—bibliotecario-lint-fase-5]] — fase del Supercontexto que introdujo esta tool
- [[entity—mcp—tool—wiki-lint-contradictions]] — tool hermana que corre en el mismo cron diario del Lint
- [[entity—mcp—tool—wiki-utility-recompute]] — tool complementaria del set wiki-lint/curator/utility
- [[concept—biblioteca—supercontexto]] — arquitectura global del sistema de bibliotecarios latentes
- [[concept—infra—cf-workers-d1-footguns]] — footguns de CF Workers/D1 que motivaron el refactor bulk+IN chunked
Referenciado desde
- Bibliotecario-Lint automatizado (Supercontexto Fase 5)
- Enrichment Agent Blueprint — Cloudflare Queues + Anthropic Batch API (50% off)
- Footguns CF Workers + D1: límites de subrequests y bind params
- MCP Tool · wiki_lint_contradictions
- MCP Tool · wiki_utility_recompute
- Reconciliación D1 ← repo del Bibliotecario-Ingest
- Tool MCP: wiki_index_metadata — Adopción de pages solo-repo a D1