Drift Check Semántico — MCP handlers + cron diario
Estado: implementado en s61 (14-05-2026). Fases 2 + 3 del ADR [[decision—20260514—knowledge-base-vs-graph]] desplegadas. Cron en modo
dry_run=truelas primeras 2 semanas para calibrar prompt y threshold.
Qué es
Sistema que detecta drift semántico en la wiki Supercontexto: cuando un commit modifica código declarado como sources de una page wiki, el sistema marca esa page como pendiente de verificación. Un cron diario invoca a Claude Haiku 4.5 para decidir si los cambios hacen que la documentación esté obsoleta. Si lo está, crea automáticamente un draft de fix que el Curator existente triará.
Resuelve el problema raíz identificado en el ADR: el corpus tenía 84 pages active con last_verified > 30 días (21% del corpus) porque ningún proceso miraba “esta page describe código que cambió, ¿sigue siendo correcta?”.
Componentes
3 handlers MCP en functions/api/mcp/handlers/wiki.ts
| Handler | Input | Output |
|---|---|---|
bib_mark_verification_pending | {file_paths: string[], actor?: string} | {ok, marked, page_slugs, paths_processed, candidates_checked} |
bib_check_drift_for_doc | {slug: string, dry_run?: boolean} | {slug, drift, confidence, summary, suggested_changes_excerpt, draft_slug_created, commits_total, commits_analyzed, haiku_input_tokens, haiku_output_tokens, haiku_cost_usd, dry_run} |
bib_run_drift_check_batch | {max_pages?: number, dry_run?: boolean} | {ok, processed, drift_detected, drafts_created, total_input_tokens, total_output_tokens, total_cost_usd, dry_run, results[]} |
bib_mark_verification_pending — tripwire trigger
Tras cada commit mergeado, el Bibliotecario-Ingest llama este handler con la lista de paths tocados. El handler:
- Carga
bib_wiki_pagesconstatus='active' AND verification_pending=0 AND sources != '[]'. - Para cada page, parsea
sources, filtratype='code', y compara con cadafile_pathrecibido (match exacto o prefijo de directorio). - UPDATE batched a 90 slugs por chunk (D1 bind limit · [[footguns_d1_bind_limit]]).
- Idempotente: pages ya pendientes se omiten.
- INSERT en
bib_wiki_logcon resumen.
bib_check_drift_for_doc — verificación individual con Haiku
Para un slug concreto:
- SELECT page → parsea
sourcestipocode. - Si no hay code sources:
{skipped: 'no_code_sources'}. - Para cada source, fetch commits de GitHub API (
GET /repos/{repo}/commits?path=...&since=last_verified&per_page=5) en el repo correcto (heurística por prefijo: workspace parafunctions/src/migrations/scripts/.github/scripts/bib_ingest, CreaRack-Pro para el resto). - Cap 10 commits totales, fetch diff de los top-5 (patches truncados a 900 chars cada uno).
- Si 0 commits: UPDATE
last_verified=NOW(), verification_pending=0+ INSERT bib_drift_checks con cost=0. Devuelve{drift: false, reason: 'no_commits_since_last_verified'}. - Si hay commits: lee body del .md (sin frontmatter, max 6KB), construye prompt sistema con reglas estrictas (JSON-only output, sin chain-of-thought), llama Haiku 4.5 con
max_tokens=600. - Si drift=true && confidence>0.7 && !dry_run: crea draft
<slug>--drift-fix-YYYYMMDDviawikiCreatePageinterno constatus=draft,kind=knowledge_base,lifecycle=refresh-90d,owner=agent-drift-check, tags['drift-check', 'auto-generated', 'pending-review']. El Curator existente lo triará en su próximo run. - Si drift=false || confidence<0.3 && !dry_run: UPDATE
last_verified=NOW(), verification_pending=0. - Si 0.3 ≤ confidence ≤ 0.7: solo registra audit en
bib_drift_checks(sin draft, sin update). - INSERT siempre en
bib_drift_checkscon(slug, drift_detected, confidence, summary, draft_slug_created, haiku_input_tokens, haiku_output_tokens, haiku_cost_usd).
bib_run_drift_check_batch — entrypoint del cron
Encapsula el bucle del workflow:
- SELECT top-N pages con
verification_pending=1, ordenadas porutility_score DESC, COALESCE(last_verified, '1970-01-01') ASC. - Para cada slug, invoca
bibCheckDriftForDocinternamente. - Agrega:
processed,drift_detected,drafts_created,total_input_tokens,total_output_tokens,total_cost_usd,results[]. - INSERT en
bib_wiki_logcon el resumen del batch.
Workflow .github/workflows/wiki-drift-check.yml
Cron diario 0 6 * * * (06:00 UTC = 08:00 Madrid CEST · 07:00 Madrid CET). Espaciado intencionalmente del Curator (05:00 UTC L+J) y del Lint bulk (04:30 UTC).
Inputs workflow_dispatch:
dry_run(defaulttruedurante las 2 primeras semanas).max_pages(default5).
Patrón: curl POST https://workspace.crearack.com/api/mcp con Bearer + headers CF Access, payload JSON-RPC con tool bib_run_drift_check_batch. Job summary humano-legible ($GITHUB_STEP_SUMMARY) + artifact JSON con el reporte completo (30 días retention).
Pausa global via vars.BIBLIOTECARIO_PAUSADO (compartido con Curator/Lint/Utility).
Extensión del Ingest
.github/scripts/bib_ingest.py (en AMBOS repos: workspace + CreaRack-Pro) tiene nueva función trigger_tripwires(changed_files, mcp_token) llamada desde main() antes del flow Tier 1/2/3. Filtra paths internos (.github/scripts/bib_*, scripts/harness/, src/content/wiki/, context/) y llama el MCP. Non-blocking: errores se loguean pero no rompen el Ingest.
Coste estimado
Pricing Haiku 4.5 (mid-2026): $0.0008 / 1K input tokens + $0.004 / 1K output tokens.
| Volumen | Por page | Por día (cap 20) | Por mes |
|---|---|---|---|
| Input típico | ~3500 tokens (doc body 1.5KB + 3 diffs) | 70K tokens | ~$1.68 |
| Output típico | ~250 tokens (JSON) | 5K tokens | ~$0.6 |
| Total | ~$0.004 | ~$0.08 | ~$2.4 |
Backfill inicial (84 stale actuales · cap 20/día → 4-5 días): **$0.34** una sola vez.
Salvaguardas
- Threshold confidence > 0.7 para draft: falsos positivos LLM cuestan ~$0.004 + el draft pasa por revisión humana del Curator. Confidence en (0.3, 0.7) NO crea draft, solo audit.
- Cap diario
max_pages: limita coste y subrequests CF Workers. Inicio en 5/día durante calibración, después 20/día. - dry_run de partida: 2 primeras semanas el cron NO crea drafts ni actualiza
last_verified. Solo registra enbib_drift_checkspara que Edu/Dani revisen veredictos y calibren el prompt. - NO auto-merge: ningún fix de drift llega a
status=activeautomáticamente. Todo pasa por Curator → revisión humana. kind: knowledge_graphno aplica: las pages auto-archivadas del Help Widget siguen su lifecycleephemeral-Nd(caducan solas), no se les ejecuta drift check.- Toggle pausa:
BIBLIOTECARIO_PAUSADO=trueen repo vars detiene el cron junto con el resto del Bibliotecario.
Métricas
Pulse del workspace · sección Bibliotecario · Drift checks 7d (Fase 5 del ADR, implementación pendiente):
- Pages procesadas (drift_detected vs no_drift).
- Drafts creados, drafts promovidos por Curator (señal de detección útil).
- Coste API 7d.
- Top-10 pages que han generado drafts (detecta áreas calientes del proyecto).
Criterio de éxito (3 meses): >40% de drafts del cron promovidos por Curator. Si <20%: prompt necesita iteración o threshold confidence demasiado bajo.
Estado de despliegue
| Fase | Estado |
|---|---|
| Fase 1 · schema Astro + D1 migration 0024 + backfill 449 pages | ✅ 14-05-2026 (PR workspace #36) |
| Fase 2 · handlers MCP + tripwire trigger | ✅ 14-05-2026 (PR workspace #37 + PR CreaRack-Pro #34) |
Fase 3 · cron wiki-drift-check.yml dry_run | ✅ 14-05-2026 (este PR · primera ejecución 15-05-2026 06:00 UTC) |
Fase 3 · activación final (dry_run=false, cap 20) | ⏳ 28-05-2026 estimado (tras 2 semanas calibración) |
| Fase 4 · Lint bulk evolucionado (respeta lifecycle) | ✅ 14-05-2026 (este PR) |
| Fase 4 · backfill 84 stale | ⏳ se ejecutará automáticamente cuando Fase 3 esté en apply=true |
| Fase 5 · métricas Pulse · widget Drift checks 7d | ⏳ implementación independiente |
Véase también
- [[decision—20260514—knowledge-base-vs-graph]]
- [[ia-tech—automatismos—metodo—bibliotecario-procesos]]
- [[entity—biblioteca—table—bib-drift-checks]]
- [[feature—biblioteca—kb-vs-graph-fase1]]
- [[feature—biblioteca—kb-vs-graph-fase2]]
- [[footguns_d1_bind_limit]]
- [[footguns_gemma4_chain_of_thought_leak]] — patrón similar de JSON-only output aplicado al prompt