Prompt — backlog de traducción marvelousdb en el System76 (Ollama, sin cupo)

Fecha: 2026-09-07. Complementa [CREDENCIAL].md. Copia desde ”--- PROMPT ---” y pégalo a tu agente local en el System76.

--- PROMPT ---

Tarea

En ~/nef/marvelous, crea data/translate-ollama.js y úsalo para traducir el backlog de intros de lore (EN → ES y EN → DE) con Ollama local, eliminando el cupo de DeepL (free tier tardaría ~5 años para 39,825 personajes ≈ 32M chars).

Contrato de datos (NO cambiar; el servidor consume este mismo formato)

  • Salida: JSON lore-translations.json, llaves = id de personaje (string), valor { "es": "...", "de": "..." }. Ver formato actual en data/import-deepl.js:1-6.
  • Texto fuente (reutilizar loreIntro de data/import-deepl.js:47-52): primer párrafo de JSON.parse(row.data).wiki?.bio || description, cortado a 500 chars; solo candidatos con intro ≥ 40 chars. Pendientes = ids con lore cuyo id NO está ya en el archivo de traducciones del servidor.
  • El ES y el DE se generan desde el inglés original (cadena EN→ES y EN→DE), nunca ES→DE.

Pasos

  1. fly sftp get /app/data/lore-translations.json data/server-lore.json -a marvelousdb y fly sftp get /app/data/marvelousdb.sqlite data/server-marvelousdb.sqlite -a marvelousdb. Trabajar contra DB_PATH=data/server-marvelousdb.sqlite para que los ids y el backlog coincidan con producción.
  2. Script: por cada candidato, una sola llamada a Ollama (POST http://localhost:11434/api/generate, stream:false, format:"json", temperature:0.2) que devuelva {"es": "...", "de": "..."}. Modelo: ollama list y elegir el instruct más grande que quepa cómodo en RAM (p. ej. gemma3:12b o superior). Si el JSON viene roto, reintentar 2x y luego registrar el id en un translate-failures.json (no abortar el lote).
  3. Prompt interno para el modelo (usar tal cual, ajustar nombre de modelo): “You are a literary translator for a comic-book character database. Translate the following English character bio into (a) neutral Latin-American Spanish and (b) German with an encyclopedic, narrative tone. Keep proper names, codenames and in-universe terms untranslated when that is the convention. Do not add, omit or summarize. Reply ONLY with JSON: {“es”: ”…”, “de”: ”…“}\n\nTEXT: ”
  4. Chequear progreso: escribir el archivo cada 25 traducciones; reanidable por id (saltar los que ya tienen {es,de} no vacíos). Correr en fragmentos de ~1 hora (screen/tmux o cron del laptop); throughput esperado 1-2k/hora con 12B — el backlog completo son 20-40 h acumuladas, no en una sentada.
  5. Muestreo de calidad ANTES de subir: imprimir 20 pares aleatorios y leer ES y DE a ojo (nombres, comillas «», sin alucinaciones de origines).
  6. Merge y subida (el servidor manda en conflicto — el worker diario puede haber añadido ~18 entre el get y el put):
    • merge: sobre server-lore.json, añadir SOLO ids nuevos que no existan.
    • fly sftp put /app/data/lore-translations.json merged.json -a marvelousdb
    • Surtirá efecto en el siguiente wake (14:00 UTC; api.js:71 lo lee al boot). O fly machines restart si quieres verlo ya.
  7. NO comitear el JSON de 39k entradas a git (sería ~60 MB). Vive solo en el volumen de Fly; el seed del repo puede quedarse chico.

Criterio de terminado

  • data/translate-ollama.js + corrida que produzca ≥ 5,000 traducciones nuevas verificadas por muestreo, subidas al servidor.
  • En el log del servidor (fly ssh console -a marvelousdb -C "tail /app/data/cron.log") el paso deepl decrece de “39825 por traducir” día a día conforme el volumen se traduce localmente; cuando llegue a 0, el ledger lo marca EXHAUSTED solo.

--- FIN PROMPT ---

Nota de contexto para quien lo lea semanas después

Los pasos 1 y 3 de la estrategia (ledger de fuentes + gate diario + quitar el sleep 86400 del entrypoint) ya están implementados y probados en el repo (commit pendiente al momento de escribir esto). Comic Vine, Wikipedia, Browser Run y SerpAPI se auto-marcarán EXHAUSTED en sus próximas corridas completas.