Bibliotecario-Ingest Batch: commit consolidado por PR (s80)
Contexto y motivación
Antes de s80 (PR#63), cada PR mergeado disparaba 3-5 commits autónomos del Bibliotecario-Ingest al workspace — uno por cada página wiki creada o actualizada. Cada commit cancelaba el deploy CF Pages anterior, generando una cascada que alargaba el deploy final a 15-20 minutos (queja s79, @Esquembri).
Solución implementada
Se eligió la Opción A del análisis técnico: refactorizar el handler MCP y el script de ingesta para parquear los .md en una tabla D1 transitoria (bib_wiki_pending_commits) durante el agentic loop de Sonnet, y consolidar todos los archivos en un único commit al final vía GitHub Git Data Trees API multi-file.
Componentes del sistema
1. Tabla D1 bib_wiki_pending_commits (migration 0036)
Almacena temporalmente el cuerpo de cada página wiki pendiente de commit.
CREATE TABLE bib_wiki_pending_commits (
id INTEGER PRIMARY KEY AUTOINCREMENT,
trigger_ref TEXT NOT NULL,
slug TEXT NOT NULL,
file_path TEXT NOT NULL,
body TEXT NOT NULL,
operation TEXT NOT NULL CHECK (operation IN ('create', 'update')),
commit_message TEXT NOT NULL,
created_at TEXT DEFAULT (datetime('now')),
UNIQUE(trigger_ref, slug)
);
La restricción UNIQUE(trigger_ref, slug) con ON CONFLICT DO UPDATE garantiza idempotencia: si Sonnet hace create + update sobre el mismo slug en la misma sesión, gana el último cuerpo, y la operación preserva 'create' si era la original.
2. wiki_create_page y wiki_update_page — modo defer_commit
Ambas tools MCP aceptan ahora dos parámetros opcionales:
| Parámetro | Tipo | Descripción |
|---|---|---|
defer_commit | boolean | Si true, el .md se escribe en bib_wiki_pending_commits en vez de hacer commit inmediato. |
trigger_ref | string | Identificador del trigger batch (ej. PR#63). Obligatorio si defer_commit=true. |
Comportamiento por defecto sin cambios: defer_commit es false; las tools siguen committeando inmediato (ghPutFile). wiki_archive_answer y skills manuales no se ven afectados.
Para wiki_update_page: el parking solo aplica si el patch incluye content (body real). Los updates metadata-only siguen siendo D1-only sin tocar el .md.
3. Nuevo MCP tool: wiki_flush_pending_commits
Consolida todas las páginas parqueadas para un trigger_ref dado en un solo commit a main.
Flujo interno (GitHub Git Data Trees API):
GET /repos/{REPO}/git/refs/heads/{BRANCH}→ obtieneheadShaGET /repos/{REPO}/git/commits/{headSha}→ obtienebaseTreeShaPOST /repos/{REPO}/git/blobs× N (uno por archivo, base64)POST /repos/{REPO}/git/treesconbase_tree=baseTreeSha+ N entriesPOST /repos/{REPO}/git/commits→ nuevo commitPATCH /repos/{REPO}/git/refs/heads/{BRANCH}→ fast-forwardmain
Optimización N=1: si solo hay una página pendiente, usa ghPutFile clásico (3 subrequests menos que Trees API).
Idempotencia: el DELETE de filas pending solo ocurre tras commit OK. Si el flush falla, las filas persisten y se pueden re-flushear manualmente.
Parámetros:
| Parámetro | Requerido | Default | Descripción |
|---|---|---|---|
trigger_ref | ✅ | — | Identificador del batch |
actor | ❌ | agent-ingest | Quién dispara el flush |
message_prefix | ❌ | wiki(batch) | Prefijo del commit message |
Respuesta ejemplo:
{
"ok": true,
"flushed": 3,
"commit_sha": "abc1234...",
"trigger_ref": "PR#63",
"creates": ["entity--racks--model--rack"],
"updates": ["feature--monitoring--cns-v1"]
}
4. bib_ingest.py — inyección automática
El script CI inyecta defer_commit=true + trigger_ref en cada llamada a wiki_create_page / wiki_update_page durante el agentic loop:
if tool_name in ("wiki_create_page", "wiki_update_page"):
tool_input = dict(tool_input)
tool_input["defer_commit"] = True
tool_input["trigger_ref"] = trigger_ref
Al final del loop (si artifacts no está vacío), llama a wiki_flush_pending_commits:
flush_raw = call_mcp(
"wiki_flush_pending_commits",
{"trigger_ref": trigger_ref, "actor": "agent-ingest"},
mcp_token,
)
El resultado logea: [Ingest] Batch flush: N pages → commit abc1234…
Compatibilidad hacia atrás y hacia adelante
| Escenario | Comportamiento |
|---|---|
bib_ingest.py antiguo + MCP nuevo | MCP ignora defer_commit ausente → commit inmediato (legacy) |
bib_ingest.py nuevo + MCP antiguo | Flush devuelve “tool not found” como WARN no fatal; commits inmediatos siguen funcionando |
Skills manuales / wiki_archive_answer | Sin cambios — siempre commit inmediato |
Impacto en tiempos de deploy
| Antes (s79) | Después (s80) |
|---|---|
| 3-5 commits por PR → cascada de cancels | 1 commit por PR al final del loop |
| Deploy CF Pages: 15-20 min | Deploy CF Pages: ~3-5 min (un solo trigger) |
Archivos del PR#63
migrations/0036_wiki_pending_commits.sql— nueva tabla D1functions/api/mcp/handlers/wiki.ts— lógica defer +wikiFlushPendingCommitsfunctions/api/mcp/tools.ts— descriptores actualizados + tool nuevafunctions/api/mcp/index.ts—LOGGED_TOOLSextendido.github/scripts/bib_ingest.py— inyección defer + llamada flush
Véase también
- [[entity—biblioteca—table—bib-wiki-pending-commits]] — Tabla D1 que almacena temporalmente los
.mdpendientes de commit batch - [[concept—general—existe-documentacion-sobre-wikicreatepage-wikiupda]] — Contexto de las tools MCP wiki_create_page / wiki_update_page que adquieren el modo defer
Page aislada parcialmente: dominio nuevo (batch ingest pipeline) sin precedentes directos en la wiki. Se añadirán más relacionados conforme se documenten entidades del sistema Biblioteca.