Interfaz de línea de comandos (CLI)
Introducción
blunderDB incluye una interfaz de línea de comandos (CLI) completa en el mismo ejecutable que la interfaz gráfica. La CLI resulta especialmente útil para:
la importación masiva de partidas: importar un directorio entero de archivos de partidas (XG, SGF, MAT, BGF…) con un solo comando,
la automatización: integrar blunderDB en scripts de shell para copias de seguridad periódicas, exportaciones programadas o cadenas de procesamiento,
el uso en servidor: manipular bases de datos en máquinas sin entorno gráfico,
la inspección rápida: comprobar el contenido o la integridad de una base de datos sin abrir la interfaz gráfica.
La CLI comparte exactamente el mismo formato de base de datos que la interfaz gráfica: ambas escriben el mismo archivo, no hay nada que sincronizar.
Nota
Si la aplicación está abierta mientras un script escribe. El archivo está en modo WAL: una lectura nunca bloquea una escritura, y los dos programas trabajan sobre la misma base sin estorbarse. Dos escrituras, en cambio, se suceden — la segunda espera el bloqueo de escritura (diez segundos por instrucción, más algunos reintentos) y solo falla si se agota la espera, con un mensaje que nombra SQLite:
Error: failed to import match: sqlite: save match: database is locked (5) (SQLITE_BUSY)
La interfaz gráfica no vigila el archivo: sigue mostrando lo que había cargado hasta que CTRL-R recarga las posiciones. No se pierde nada, pero la pantalla queda por detrás de la base.
Sintaxis general
El modo se detecta automáticamente: si el primer argumento es un comando de la CLI, blunderDB se ejecuta en modo headless; de lo contrario, abre la interfaz gráfica.
# GUI
./blunderdb
# CLI
./blunderdb <command> [options]
Los ejemplos de esta página escriben ./blunderdb: el binario tal como se descarga, invocado desde la carpeta donde se encuentra. Instalado por un paquete, o enlazado desde una carpeta del PATH (ver Descarga e instalación), se llama simplemente blunderdb.
Las opciones booleanas anunciadas «por defecto: sí» se desactivan con la forma --option=false — --recursive=false, --analysis=false. La forma separada por un espacio no existe: --recursive false deja la opción en su valor por defecto y trata false como un argumento de más.
Comandos disponibles
Comando |
Descripción |
|---|---|
create |
Crea una nueva base de datos. |
import |
Importa datos (partida, posición, lote). |
export |
Exporta datos. |
identity |
Muestra o traslada la identidad de emisor (clave de firma de las filigranas). |
open |
Convierte un archivo protegido por contraseña (.dbx) en una base ordinaria. |
search |
Busca posiciones con filtros. |
list |
Muestra el contenido de la base de datos. |
match |
Muestra las posiciones y los análisis de una partida. |
collection |
Gestiona las colecciones (lista, contenido, creación, cambio de nombre, eliminación, exportación). |
anki |
Mazos de repetición espaciada (lista, estadísticas, previsión, sincronización). |
rollout |
Juega una posición hasta el final para desempatar sus jugadas o su decisión de cubo (XGID u OGID). |
epc |
Calcula el Effective Pip Count y el veredicto de cubo de una posición de retirada (XGID u OGID). |
bearoff |
Genera, lista, verifica y elimina las bases bearoff. |
analyze |
Escribe un análisis gammonNet para cada posición que no tiene ninguno. |
info |
Muestra los metadatos de la base de datos. |
edit |
Modifica los metadatos y los umbrales de la base de datos. |
verify |
Verifica la integridad de la base de datos. |
vacuum |
Compacta el archivo de base de datos y recupera el espacio liberado. |
repair |
Recalcula lo que la base deriva de lo que almacena. |
delete |
Elimina datos. |
healthcheck |
Consulta un demonio |
mcp |
Ofrece las herramientas de la base a un asistente de IA (Model Context Protocol). |
completion |
Muestra un script de autocompletado de shell (bash, zsh, fish). |
help |
Muestra la ayuda. |
version |
Muestra la versión. |
serve, migrate, call |
Modo servidor y migración a PostgreSQL: ver Modo headless (servidor). |
Cada comando acepta la opción --help para mostrar su ayuda detallada.
create — Crear una base de datos
Crea un nuevo archivo de base de datos con metadatos opcionales.
./blunderdb create --db <path> [--user <name>] [--description <text>] [--force]
Opciones:
--db— Ruta del archivo de base de datos a crear (obligatorio).--user— Nombre del propietario de la base de datos.--description— Descripción de la base de datos.--force— Sobrescribir el archivo si ya existe.--format— Formato de salida:text(por defecto) ojson(ruta, versión, usuario, descripción, fecha de creación).
La extensión .db se añade automáticamente si falta. Los directorios superiores se crean si es necesario.
Ejemplo:
./blunderdb create --db mes_matchs.db --user "Jean" --description "Matchs de tournoi 2025"
import — Importar datos
Importa archivos de partidas o de posiciones en la base de datos.
./blunderdb import --db <path> --type <type> [options]
Opciones:
--db— Ruta de la base de datos (obligatorio).--type— Tipo de importación:match,positionobatch(obligatorio).--file— Archivo a importar (paramatchyposition).--dir— Directorio a importar (parabatch).--recursive— Explorar recursivamente los subdirectorios (por defecto: sí).--watch— Con--type batch: no se detiene, e importa cada fichero de partido a medida que aparece en--dir(Ctrl-C para parar).--watch-every— Cada cuánto mira--watch(por defecto: 10s, mínimo 2s).--format— Formato de salida:text(por defecto) ojson.--fail-on-error— Falla si al menos un elemento (positionobatch) no se pudo importar, incluso si otros tuvieron éxito.
El código de retorno obedece a cuatro reglas:
nada fue reconocido — cada archivo falló — : error, se pase o no
--fail-on-error;solo duplicados — cada archivo ya estaba en la base — : éxito. Un directorio relanzado sin ningún archivo nuevo, la noche ordinaria de un script, termina en 0 con
duplicatescomo único contador distinto de cero;fallo parcial (algunos elementos importados, otros rechazados): error solo si se pasa
--fail-on-error;al menos un elemento nuevo importado, sin
--fail-on-error: éxito, con los archivos rechazados listados en la tabla.
Vigilar una carpeta
--watch convierte la importación de directorio en una vigilancia: el comando no devuelve el control e importa cada fichero de partido que aparece en la carpeta. Es la forma sin interfaz de la carpeta vigilada de la aplicación.
# Importer ce que le dossier contient déjà, puis surveiller ce qui arrive
./blunderdb import --db base.db --type batch --dir ~/XG/Matches
./blunderdb import --db base.db --type batch --dir ~/XG/Matches --watch
Solo se importan los ficheros que aparecen: lo que la carpeta contiene al arrancar se registra como conocido y se deja en paz — apuntar una vigilancia a cuatro años de partidos no debe importarlos todos. Los dos comandos anteriores se componen, pues, exactamente como uno espera.
Un fichero se importa solo cuando su tamaño se ha estabilizado, es decir, visto dos veces sin cambios: un partido que otro programa está escribiendo crece de una mirada a la siguiente, e importarlo a medio escribir daría un error de sintaxis sobre el que nadie puede actuar. La carpeta no se recorre recursivamente. Un recurso de red que se ha vuelto ilegible no detiene la vigilancia, y su contenido no pasa por nuevo cuando vuelve.
Ctrl-C para entre dos ficheros, nunca en medio de uno: el fichero en curso termina su importación y su informe se muestra antes de que el comando devuelva el control.
Importar una partida
Formatos compatibles: eXtreme Gammon (.xg, .xgp), GNUbg (.sgf), Jellyfish (.mat, .txt), BGBlitz (.bgf) y HedgeHog (.ogxm).
./blunderdb import --db base.db --type match --file match.xg
# Successfully imported match (ID: 1)
#
# Match Details:
# Players: Kévin Unger vs Maxence Job
# Event: HSBT Paris 2023
# Match Length: 7
# Games: 7
--format json entrega los mismos campos en un único documento:
{
"type": "match",
"match_id": 1,
"player1": "Kévin Unger",
"player2": "Maxence Job",
"event": "HSBT Paris 2023",
"location": "Paris, Fédération Française de Bridge",
"match_length": 7,
"games": 7
}
Importar posiciones
Importa posiciones desde un archivo de texto, una posición JSON por línea. Es exactamente lo que escribe export --type positions: los dos comandos se corresponden, una exportación se reimporta tal cual, sin retocar nada.
./blunderdb import --db base.db --type position --file positions.txt
# Successfully imported 4 positions
Una línea, tal como la produce export — el tablero ocupa la mayor parte, veintiséis puntos seguidos del bearoff:
{"id":1,"board":{"points":[{"checkers":0,"color":0},{"checkers":1,"color":1},…],"bearoff":[0,0]},"cube":{"owner":-1,"value":0},"dice":[0,0],"score":[7,7],"player_on_roll":0,"decision_type":1,"has_jacoby":0,"has_beaver":0,"individually_imported":true,"flagged":false}
El análisis y los comentarios no viajan por este formato: transporta la posición, nada más. Para mover una biblioteca entera, hay que usar export --type database.
Importación por lotes
Importa todos los archivos de partidas de un directorio en una sola operación. Es el método más eficiente para importar un gran número de partidas.
./blunderdb import --db base.db --type batch --dir ./matchs/
./blunderdb import --db base.db --type batch --dir ./matchs/ --recursive=false
./blunderdb import --db base.db --type batch --dir ./matchs/ --format json --fail-on-error
Una tabla resumen indica para cada archivo si la importación tuvo éxito (✓), falló (✗) o fue un duplicado (⊘). Un duplicado no cuenta como un fallo, y un lote que solo contiene duplicados es un éxito (véanse las reglas anteriores).
Batch importing from: ./matchs/ (recursive: true)
Found 3 match file(s) to import
[1/3] Importing: 02_NDT_FR.txt... ERROR: failed to parse file: ingest: parse gnubg file: invalid MAT file: no match header found
[2/3] Importing: test.mat... DUPLICATE
[3/3] Importing: test.xg... OK (ID: 1, 341 positions)
====================================================================
IMPORT SUMMARY
====================================================================
Status File ID Player 1 Player 2 Games Positions Error
------ ---- -- -------- -------- ----- --------- -----
✗ 02_NDT_FR.txt 0 0 failed to parse file: ingest: ...
⊘ test.mat 0 0
✓ test.xg 1 Kévin Unger Maxence Job 7 341
--------------------------------------------------------------------
Total: 3 files | Success: 1 | Duplicates: 1 | Failed: 1 | Positions imported: 341
--format json da lo mismo, explotable por un script: un objeto por archivo en files, luego los totales. Una noche tranquila deja solo duplicates distinto de cero y failed en cero, y el código de retorno en 0; solo un lote donde nada fue reconocido termina en error.
{
"files": [
{"file_path": "02_NDT_FR.txt", "success": false, "error": "failed to parse file: …"},
{"file_path": "test.xg", "success": true, "positions": 341}
],
"total": 3,
"success": 1,
"duplicates": 1,
"failed": 1,
"positions_imported": 341
}
export — Exportar datos
Exporta el contenido de la base de datos a archivos.
./blunderdb export --db <path> --type <type> --file <output> [options]
Opciones:
--db— Base de datos de origen (obligatorio).--type— Tipo de exportación:database,positions,matchesomat(exportación de una o varias partidas en transcripción Jellyfish.mat) (obligatorio).--file— Archivo de salida (obligatorio, salvo para--type matutilizado con--dir).--dir— Directorio de salida para la exportación.matpor lotes (varias partidas, un archivo por partida; sin--match-ids, se exportan todas las partidas).--analysis— Incluir los análisis (por defecto: sí).--comments— Incluir los comentarios (por defecto: sí).--filters— Incluir la biblioteca de filtros (por defecto: sí).--played-moves— Incluir las jugadas realizadas (por defecto: sí).--matches— Incluir las partidas (por defecto: sí).--collections— Incluir las colecciones (por defecto: no).--collection-ids— IDs de las colecciones a exportar (separados por comas).--match-ids— IDs de las partidas a exportar (separados por comas, vacío = todas).--tournament-ids— IDs de los torneos a exportar (separados por comas).--password— Envuelve el resultado en un contenedor cifrado (.dbx).--watermark— Escribe una declaración de origen firmada en el archivo exportado (véase Distribuir una base de datos: origen y contraseña).--watermark-note— Texto libre asociado a la filigrana (condiciones de uso, contacto); se usa con--watermark.--format— Formato de salida:text(por defecto) ojson(un documento que resume la exportación: ruta, tamaño en bytes, recuentos).
Ejemplos:
./blunderdb export --db base.db --type database --file sauvegarde.db
./blunderdb export --db base.db --type positions --file positions.txt
./blunderdb export --db base.db --type matches --file selection.db --match-ids 1,3,5
# .mat : un match, puis plusieurs (ou tous) dans un répertoire
./blunderdb export --db base.db --type mat --match-ids 5 --file match5.mat
./blunderdb export --db base.db --type mat --match-ids 5,9,12 --dir sorties/
./blunderdb export --db base.db --type mat --dir sorties/
# .dbx : filigrané et protégé par mot de passe
./blunderdb export --db cours.db --type database --file cours-diffusion.dbx \
--watermark "Cours de Jean Dupont — 12 mars 2026" \
--watermark-note "Merci de ne pas rediffuser." \
--password secret
Una filigrana se firma con la identidad de emisor local (véase el comando identity más abajo): es infalsificable, pero no inamovible — el archivo sigue siendo una base SQLite ordinaria. No protege nada, solo indica de dónde viene el archivo. Una contraseña protege el transporte del archivo (la copia extraviada, el adjunto enviado por error), no la base en sí: cualquiera que haya recibido la contraseña puede abrirla. blunderDB nunca registra nada del lado del destinatario (ningún registro, ningún diario) — véase ADR-0007.
identity — Identidad de emisor
Muestra o traslada su identidad de emisor: la clave Ed25519 que firma cada filigrana. Se crea por sí sola al colocar la primera filigrana; no hay nada que configurar. Pertenece a una persona, no a una base de datos: todo lo que usted marca lleva una única huella pública.
./blunderdb identity
./blunderdb identity --name "Jean Dupont"
./blunderdb identity --export jean.bdbid --passphrase pw
./blunderdb identity --import jean.bdbid --passphrase pw
Opciones:
--name— Cambia el nombre mostrado de la identidad.--export— Exporta la identidad a un archivo.bdbid.--import— Importa una identidad desde un archivo.bdbid.--passphrase— Frase de contraseña opcional que protege el archivo exportado/importado (la identidad local queda deliberadamente sin proteger).--format— Formato de salida:text(por defecto) ojson(nombre, huella, ruta de almacenamiento).
El archivo exportado permite a cualquiera que lo posea firmar en su nombre — no lo comparta. Renombrar solo cambia una etiqueta: los archivos ya marcados conservan el nombre con el que fueron sellados y siguen verificándose.
open — Abrir un archivo protegido
Convierte un archivo protegido por contraseña (.dbx) en una base ordinaria. La contraseña se pide una sola vez; después es un archivo normal.
./blunderdb open --db cours.dbx --password secret
./blunderdb open --db cours.dbx --password secret --file ./mon-cours.db
Opciones:
--db— Archivo.dbxque se va a abrir (obligatorio).--password— Contraseña del contenedor (obligatorio).--file— Ruta de salida para la base ordinaria (por defecto: mismo nombre, extensión.db).
Lo que protege la contraseña: el transporte del archivo — la copia olvidada en una carpeta de descargas, el adjunto enviado por error. No la base: cualquiera que haya recibido la contraseña puede abrirla. La cabecera del contenedor está en claro, de modo que blunderdb info lee el origen de un archivo protegido sin su contraseña.
search — Buscar posiciones
Busca posiciones en la base de datos según criterios combinables.
./blunderdb search --db <path> [options]
Opciones principales:
--db— Base de datos (obligatorio).--format— Formato de salida:table,jsonoxgid(por defecto:table).--limit— Número máximo de resultados (0 = ilimitado).--offset— Ignorar los n primeros resultados antes de empezar a contar; junto con--limit, es la paginación.--export— Exportar los resultados a una nueva base de datos.--query-help— Muestra la lista de tokens que--queryentiende, y se detiene ahí. No se abre ninguna base:--dbes inútil.
Filtros disponibles:
--decision— Tipo de decisión:checkerocube.--dice— Tirada de dados.5,3busca las posiciones en las que coinciden ambos dados (sin importar el orden).5busca las posiciones en las que aparece un 5 en cualquiera de los dos dados (se ignora el valor del segundo dado). Implica--decision checkersi no se especifica ningún valor de--decision.--pip-min/--pip-max— Intervalo de diferencia de pip count.--winrate-min/--winrate-max— Intervalo de porcentaje de victoria (%).--cube— Valor del cubo.--score1/--score2— Marcadores de los jugadores.--match-length— Duración del match.--error-min— Umbral sobre lo que cuesta un error en la posición: la diferencia entre la mejor jugada y la segunda, o el mayor de los tres errores de cubo. En puntos de equidad —--error-min 0.1conserva las posiciones donde equivocarse cuesta al menos una décima de punto. No dice nada de lo que se jugó allí.--move-error-min/--move-error-max— Umbral sobre el error de la jugada efectivamente jugada por el jugador 1. En milésimas de equidad (milipuntos):--move-error-min 50, es decir, una veinteava parte de punto. Es el tokenEde la gramática de búsqueda, escrito tal cual.--has-analysis— Solo las posiciones con análisis.--off1-min/--off2-min— Fichas retiradas mínimas (jugador 1/2).--match-ids— Filtrar por IDs de partidas (separados por comas).--tournament-ids— Filtrar por IDs de torneos (separados por comas).--position-ids— Filtrar por IDs de posiciones: intervalo2,7(posiciones 2 a 7) o lista explícita separada por puntos y comas5;10;15.--individual— Solo las posiciones importadas por separado, es decir, las que usted añadió y no las que trajo la importación de una partida.--flagged— Solo las posiciones marcadas (flag) para estudio en el programa de origen (marcas de eXtreme Gammon). No es retroactivo: las partidas ya importadas deben importarse de nuevo para entregar sus marcas.--has-comment— Solo las posiciones que llevan un comentario. No se distingue el origen: una nota escrita a mano y un comentario aportado por la importación de una partida cuentan ambos. Los comentarios de partida o de torneo no se consultan.--no-comment— Solo las posiciones sin comentario. Mutuamente excluyente con--has-comment.
Advertencia
--error-min y --move-error-min no miden lo mismo y no toman la misma unidad: el factor es mil. El primero se da en puntos de equidad (0.1), los otros dos en milésimas (100) — un punto vale 1000 milésimas. Es --move-error-min el que responde a «dónde me equivoqué»; --error-min responde a «qué posiciones eran delicadas».
Lo que imprime search:
--format table (el valor por defecto) da una línea por posición: el identificador, el score, el valor del cubo, el tipo de decisión, la tirada, la mejor decisión y su equidad. Las dos últimas columnas quedan vacías para una posición sin análisis.
Found 5 position(s)
ID Score Cube Type Dice Best Move Equity
-- ----- ---- ---- ---- --------- ------
2 7-7 0 cube No Double -0.005
4 7-7 0 cube No Double -0.027
6 7-7 0 cube No Double -0.161
8 7-7 0 cube No Double 0.256
10 7-7 0 cube No Double -0.234
--format json da un array de las mismas posiciones. Los campos son id, score, cube, decision_type (checker o cube) y dice, siempre presentes; best_move, equity y xgid solo aparecen si la posición lleva un análisis que los informa. La línea Found n position(s) sigue imprimiéndose antes del array: un script que solo espera JSON debe saltar la primera línea, o pasar por --export.
[
{
"id": 5266,
"score": [
5,
4
],
"cube": 1,
"decision_type": "checker",
"dice": [
4,
3
],
"best_move": "10/3",
"equity": 0.565
}
]
--format xgid imprime un XGID por línea, y nada más. Solo imprime las posiciones cuyo análisis registrado lleva un XGID: una posición pegada en la aplicación desde una exportación de texto, o un archivo BGF que transporta uno. Las posiciones aportadas por la importación de una partida XG, GNUbg o Jellyfish no llevan ninguno, y la salida queda entonces vacía. El subcomando collection show, por su parte, regenera el XGID a partir del tablero.
El lenguaje de consulta:
Las opciones anteriores cubren solo una parte de los filtros. --query da acceso al lenguaje de consulta de la aplicación — el de la barra de comandos — y por tanto a todos los filtros que no se dibujan en el tablero: patrón de movimiento, texto de comentario, jugador, fecha, equidad, dados excluidos, zonas y blots.
La gramática solo está escrita en un único lugar, Filtros de búsqueda. Su tabla da cada token, su forma, y la opción de search que le corresponde cuando existe. Esta página no la repite.
./blunderdb search --db base.db --query 's p>30 E>50'
./blunderdb search --db base.db --query 's m"13/11" t"blunder" pl"Alice" T>2026/01/01'
La clasificación de las vecinas de una posición pasa por la misma gramática: --query 's like42', sola o seguida de otros tokens para restringir el conjunto ordenado.
--query-help recuerda la lista sin abrir ninguna base:
$ ./blunderdb search --query-help
blunderdb search --query — the interface's query language
A query is the same text the application's command bar takes:
s cube p>30 E>50 cube decisions, 30+ pips behind, 50+ millipoints of error
s m"13/11" T>2026/01/01 played 13/11, imported this year
Flags (no value):
cube score match the cube / the score of the position on the board
d match the decision type (checker or cube)
…
Ranges — each takes x>n, x<n or xa,b (lower-case: you; upper-case: the opponent):
p P pip count difference / absolute pip count
…
E error of the played move, in millipoints
T creation date, T>2026/01/01
Values:
t"tag" comment text (";" separates alternatives)
…
--query sustituye a las opciones de filtro en lugar de sumarse a ellas: combinarlas se rechaza, nombrando la opción en cuestión. Las opciones que dicen dónde buscar y cómo mostrar — --db, --format, --limit, --offset, --export — siguen siendo válidas.
Un token que nada reconoce hace fallar la orden en lugar de reducir la búsqueda en silencio. Dos límites derivan de la ausencia de tablero en la línea de órdenes: el patrón de fichas no se teclea, y los cinco tokens que leen el tablero — cube, score, d, D/D1 y x — se comparan aquí con un tablero vacío. Una búsqueda que necesite alguno de ellos se escribe enteramente con opciones, ya que --query no se combina con ellos — por ejemplo, las decisiones de cubo con 30 pips de retraso y un error de al menos 50 milipuntos:
./blunderdb search --db base.db --decision cube --pip-min 30 --move-error-min 50
Ejemplos:
./blunderdb search --db base.db --decision cube
./blunderdb search --db base.db --individual
./blunderdb search --db base.db --error-min 0.1
./blunderdb search --db base.db --tournament-ids 1 --export cubes.db
# 6-5 dans les deux ordres, puis un 6 sur l'un des deux dés
./blunderdb search --db base.db --dice 6,5
./blunderdb search --db base.db --dice 6
# Pagination
./blunderdb search --db base.db --format json --limit 10 --offset 20
list — Listar el contenido
Muestra el contenido de la base de datos.
./blunderdb list --db <path> --type <type> [--limit <n>] [--offset <n>]
Tipos:
matches— Lista de las partidas importadas.tournaments— Lista de los torneos.positions— Lista de posiciones (10 por defecto;--offset <n>omite las n primeras). Solo se lee la ventana mostrada, sea cual sea el tamaño de la base. Con--format csvse convierte en una exportación tabular: una fila por posición, con su XGID, fase, marcador, cubo, pips y las columnas de análisis derivadas.imports— Las importaciones registradas, de la más reciente a la más antigua: identificador, fecha, formato, origen, partidas importadas / omitidas / enriquecidas, archivos ilegibles y posiciones nuevas. Con--batch <id>muestra el informe completo de una importación: posiciones marcadas, posiciones sin análisis, PR de ese lote y sus cinco peores decisiones (véase El informe de importación).stats— Informe de estadísticas de rendimiento: PR / Snowie ER / MWC (global, fichas, cubo), PR deslizante sobre las N últimas decisiones, peores errores, reparto por acción de cubo e histograma de las magnitudes de error.players— Tabla comparativa, una fila por jugador de la base: partidas, victorias/derrotas, decisiones contadas, PR global / fichas / cubo, Snowie ER, errores, blunders y suerte. Es el equivalente en línea de comandos de la pestaña Jugadores del panel Estadísticas.moves— Exportación tabular de las jugadas registradas, una por fila, con el partido al que pertenecen repetido en cada fila: identificadores, fecha, jugadores, longitud, número y tipo de jugada, posición, dados, jugada realizada, acción de cubo, suerte.--format csvobligatorio.analyses— Exportación tabular de los análisis almacenados, uno por fila: motor, profundidad, mejor jugada y su equidad, error de la jugada realizada, mejor acción de cubo y su error, las seis tasas de victoria.--format csvobligatorio.tags— Vocabulario de etiquetas de la base: cada#palabraescrita en un comentario, con el número de posiciones que la llevan, de la más usada a la menos usada. En una base sin ninguna etiqueta, muestra el vocabulario recomendado en lugar de una lista vacía (véase Las etiquetas). Acepta--format jsony--format csv.
Exportaciones tabulares
Tres tipos — positions, moves y analyses — se exportan en CSV para un notebook, una hoja de cálculo o un script:
./blunderdb list --db base.db --type positions --format csv > positions.csv
./blunderdb list --db base.db --type moves --format csv > moves.csv
./blunderdb list --db base.db --type analyses --format csv > analyses.csv
--limit se aplica solo si lo pasa. Su valor por defecto (10) existe para que un list mostrado en un terminal no haga desfilar toda la base; una exportación, en cambio, va a un fichero que lee un programa, y truncarla en silencio a diez filas sería una trampa que no se nota hasta que las cifras son falsas.
Las columnas son un contrato. Un notebook o un script escrito contra estos nombres debe seguir funcionando: las columnas se añaden al final, nunca se renombran ni se reordenan. Todas las equidades están en milipuntos enteros, porque así se almacenan y porque un decimal en un CSV invita a una locale a reformatearlo.
Parquet no se ofrece, y es una decisión medida más que dogmática: una biblioteca columnar pesa varios megabytes en un binario cuyo tamaño es una preocupación seguida, mientras que todo aquello para lo que sirve esta exportación lee CSV en una línea (pd.read_csv, polars.read_csv, read.csv, una hoja de cálculo). Parquet se justifica con decenas de millones de filas; una biblioteca de backgammon de diez años tiene cien mil. Si algún día la diferencia se mide sobre una base real, esa medida será lo que reabra la cuestión.
Un notebook Jupyter de ejemplo acompaña estas exportaciones (notebooks/blunderdb-analyse.ipynb en el repositorio): PR en el tiempo, distribución de las magnitudes de error, diez peores decisiones con su XGID. No usa nada más que esos tres ficheros CSV, y se ejecuta cada noche en integración continua — un notebook que nadie lanza es un notebook que ha dejado de funcionar sin que nadie lo sepa.
Opciones (solo para el tipo stats):
--metric— Métrica mostrada:promwc(por defecto:pr).--player— Restringir al jugador indicado.--tournament— Restringir a uno o varios IDs de torneos (separados por comas).--from— Fecha de inicio (AAAA-MM-DD).--to— Fecha de fin (AAAA-MM-DD).--decision-type— Tipo de decisión:all,checkerocube(por defecto:all).--top-blunders— Número de peores errores listados (por defecto: 10).--format— Formato de salida:textojson(por defecto:text).
Opciones (solo tipo imports):
--batch— Identificador de un lote: muestra su informe completo en lugar de la lista.--queue— Con--batch: la cola de estudio del lote en vez de su informe — las posiciones que merecen una segunda mirada, en el orden en que recorrerlas (véase La cola de estudio). Primero las decisiones que costaron algo, luego las posiciones marcadas en el programa de origen, luego las decisiones de cubo ajustadas; una posición figura solo una vez.--format— Formato de salida:textojson(por defecto:text).
La mitad medida del informe se recalcula en cada llamada: un lote cuyas posiciones se han analizado desde entonces devuelve las cifras de hoy, no las del día de la importación.
Opciones (solo tipo players):
--from/--to— Límites de fechas (AAAA-MM-DD), por ejemplo los días de una competición.--tournament— Restringir a uno o varios IDs de torneos.--format— Formato de salida:text,jsonocsv(por defecto:text).
--player y --decision-type no se aplican a este tipo: la tabla abarca a todos los jugadores y ya desglosa fichas y cubo en columnas distintas.
Nota
Un guion «—» (campo vacío en CSV) señala un valor nunca medido, que no debe confundirse con cero. Es el caso de la suerte para toda partida importada antes de la versión 2.15.0 del esquema, así como para los formatos que no la transportan (BGF, Jellyfish .mat): reimporte los archivos de origen para obtenerla. La columna luck_rolls indica sobre cuántas tiradas se calcula la media.
Cada tipo imprime un bloque por elemento, precedido del total encontrado. La última línea indica qué ventana se muestra, truncada por --limit (cuyo valor por defecto es 10 para las posiciones) y desplazada por --offset:
Found 3859 position(s):
ID: 1
Score: 7-7
Player on roll: 0
Decision: Checker play
ID: 2
Score: 7-7
Player on roll: 0
Decision: Cube action
…
(Showing 1-10 of 3859 positions, use --offset and --limit to see more)
Ejemplos:
# Les imports enregistrés, puis le compte rendu de l'un d'eux
./blunderdb list --db base.db --type imports
./blunderdb list --db base.db --type imports --batch 3
./blunderdb list --db base.db --type stats
./blunderdb list --db base.db --type stats --metric mwc --player "Alice"
./blunderdb list --db base.db --type stats --decision-type checker --from 2026-01-01
./blunderdb list --db base.db --type stats --format json
# Un tableau par joueur, borné aux dates d'une compétition
./blunderdb list --db base.db --type players --from 2026-03-01 --to 2026-03-08
./blunderdb list --db base.db --type players --format csv
./blunderdb list --db base.db --type matches
./blunderdb list --db base.db --type positions --limit 20
match — Mostrar una partida
Muestra las posiciones y los análisis de una partida importada.
./blunderdb match --db <path> --id <id> [--format <format>] [--output <file>]
Opciones:
--db— Base de datos (obligatorio).--id— ID de la partida a mostrar (obligatorio).--format— Formato de salida:json,textosummary(por defecto:json).--output— Archivo de salida (por defecto: salida estándar).
Ejemplos:
./blunderdb match --db base.db --id 1 --format summary
./blunderdb match --db base.db --id 1 --format text
./blunderdb match --db base.db --id 1 --output match1.json
collection — Gestionar las colecciones
Gestiona las colecciones, esos conjuntos de posiciones elegidas a mano en el panel Colecciones de la interfaz gráfica. Cada subcomando toma --db; list y show aceptan --format text (por defecto), json o csv, igual que list.
./blunderdb collection <subcommand> [options]
Subcomandos:
list— Lista de las colecciones: id, nombre, número de posiciones, descripción.show --id <id>— Posiciones de una colección: id, índice (el número 1-based mostrado en la barra de estado de la interfaz gráfica), puntuación, tipo de decisión y XGID.create --name <nombre> [--description <texto>]— Crea una colección vacía.filter --id <id> --query <consulta>— Hace que una colección sea viva: su contenido pasa a ser el resultado de una búsqueda, reevaluado cada vez que se abre. La consulta se escribe en la gramática de búsqueda de la aplicación (véase Filtros de búsqueda).--clearla devuelve a una lista hecha a mano, conservando las posiciones que contenía.rename --id <id> --name <nombre> [--description <texto>]— Renombra una colección (la descripción se conserva si no se indica).delete --id <id> [--confirm]— Elimina una colección; sus posiciones permanecen en la base de datos.export --id <id[,id…]> --out <archivo.db> [--analysis=false] [--comments=false] [--watermark <texto>] [--watermark-note <texto>]— Exporta una o varias colecciones a un nuevo archivo de base de datos, mediante la misma llamada que la ventana de exportación de la interfaz gráfica (véase el comandoexportpara la filigrana).
El XGID mostrado por show es el registrado con el análisis de la posición cuando existe (importaciones BGF y XGP); en caso contrario se genera desde el tablero exactamente como lo hace Copiar la posición en la interfaz gráfica — la longitud de la partida es entonces la mayor de las dos puntuaciones restantes, ya que una posición guardada no conserva la verdadera.
Ejemplos:
./blunderdb collection list --db base.db
# Found 2 collection(s):
#
# ID Name Positions Description
# -- ---- --------- -----------
# 1 Ouvertures blitz 0 À revoir
# 2 Videaux ratés 0
Una base sin colecciones responde No collections found in database y aun así termina con el código 0.
./blunderdb collection show --db base.db --id 3 --format csv
./blunderdb collection create --db base.db --name "Ouvertures blitz"
./blunderdb collection rename --db base.db --id 3 --name "Ouvertures"
./blunderdb collection delete --db base.db --id 3 --confirm
# Exporter deux collections, marquées de leur origine
./blunderdb collection export --db base.db --id 3,4 --out ouvertures.db \
--watermark "Cours de Jean Dupont - 12 mars 2026"
anki — Mazos de repetición espaciada
Consulta y mantiene los mazos de repetición espaciada (FSRS) del panel Anki de la interfaz gráfica. La revisión de una carta requiere el tablero y permanece en la interfaz gráfica; la CLI lista, mide y resincroniza.
./blunderdb anki <subcommand> [options]
Subcomandos:
decks [--format text|json|csv]— Lista de los mazos: origen, número de cartas, cartas pendientes, cartas nuevas.stats --deck <id> [--format text|json]— Estadísticas de repaso de un mazo: total, nuevas, en aprendizaje, para repasar, pendientes ahora, y sus parámetros FSRS.forecast [--deck <id>] [--days <n>] [--format text|json|csv]— Cartas que vencen por día natural (UTC) en los próximosndías (por defecto 30, máximo 365); el día 0 absorbe todas las cartas atrasadas;--deck 0(por defecto) cubre todos los mazos.sync --deck <id>— Añade una carta por cada posición del origen del mazo que aún no tenga una; las cartas existentes conservan su planificación.retention --deck <id> [--format text|json]— retención medida de un mazo, comparada con el objetivo que eligió su propietario.card --id <id> --action suspend|unsuspend|bury|remove [--format text|json]— actúa sobre una carta. Suspender la aparta sin perder su historial (ya no sale en sesión); enterrar la oculta hasta el día siguiente, sin decir nada de su valor; retirar la borra del mazo — la posición sigue en la biblioteca, ya que un mazo no es más que una lista de estudio puesta encima.log [--deck <id>] [--limit <n>] [--format text|json]— el registro de revisiones, la más reciente primero (--deck 0, el valor por defecto, cubre todos los mazos;--limites 20 por defecto). El registro es lo que realmente se le dijo al planificador, por oposición a lo que prevé hoy: el único sitio donde se ve una nota introducida por error.
Un mazo basado en una colección vuelve a leer su colección. Un mazo basado en una búsqueda conserva la búsqueda tal como la guardó la interfaz gráfica (comando, tablero e identificadores de las posiciones encontradas en ese momento): la gramática de búsqueda vive en la interfaz gráfica, así que la CLI resincroniza a partir de los identificadores guardados y lo indica en la salida de error — abra el mazo en la interfaz gráfica para volver a ejecutar la búsqueda en sí.
Ejemplos:
./blunderdb anki decks --db base.db
./blunderdb anki stats --db base.db --deck 2 --format json
./blunderdb anki forecast --db base.db --deck 2 --days 14
./blunderdb anki sync --db base.db --deck 2
./blunderdb anki card --db base.db --id 12 --action suspend
./blunderdb anki log --db base.db --deck 2 --limit 50
# Day Due
# --- ---
# 2026-09-02 12
# 2026-09-03 4
# ...
#
# 37 card(s) due over 14 day(s)
stats — Errores recurrentes
Agrupa los errores de un filtro por plan de juego y por tema, el más costoso primero: la tabla Errores recurrentes de la pestaña Errores del panel Stats (véase Panel Stats). Las estadísticas globales siguen en list --type stats.
./blunderdb stats recurring --db <fichier> [options]
Opciones:
--player <nom>— Solo las decisiones de este jugador.--tournament <ids>,--from <AAAA-MM-JJ>,--to <AAAA-MM-JJ>,--decision-type all|checker|cube— El mismo filtro quelist --type stats.--limit <n>— Número de grupos mostrados en texto (por defecto 20,0para todos).--format text|json— El JSON incluye cada grupo con la lista completa de sus posiciones.--quiz— Sortea posiciones entre las de los tres grupos más costosos (--quiz-size <n>, 20 por defecto) y las muestra: son los identificadores que juzgan el quiz yquiz_grade. En JSON, el campoQuiz.--deck <nombre>— Crea un mazo Anki con ese nombre, lleno con todas las posiciones de los tres grupos más costosos.--group <rango>— Con--quizo--deck: el grupo de ese rango (1 para el más costoso) en lugar de los tres primeros.
Un tema de jugada de fichas es gammon, blots, point o passive; un tema de cubo es offer_missed, offer_premature, answer_wrong_pass o answer_wrong_take. Los errores que ninguna regla nombra salen de la clasificación: se listan aparte, una línea por plan de juego (campo Unthemed en JSON), porque la explicación solo se pronuncia a partir de 60 mp, por encima del umbral Error. La columna COST (PR) es la parte del PR del filtro que representa el grupo.
Ejemplos:
./blunderdb stats recurring --db base.db --player "Alice"
./blunderdb stats recurring --db base.db --decision-type checker --format json
./blunderdb stats recurring --db base.db --quiz --format json
./blunderdb stats recurring --db base.db --group 1 --deck "Mon pire groupe"
stats training — El PR del quiz Decisión, el PR de las partidas y la retención de Anki, agrupados por ventana de calendario, como la pestaña Entrenamiento del panel Stats (véase Panel Stats).
./blunderdb stats training --db <fichier> [options]
Opciones:
--window week|month— La ventana de calendario (por defectoweek).--player <nombre>,--tournament <ids>,--from <AAAA-MM-DD>,--to <AAAA-MM-DD>,--decision-type all|checker|cube— El filtro de partidas; los diarios del quiz y de Anki no llevan jugador.--format text|json— El JSON incluye también la lista de sesiones de quiz.
Cada serie conserva su número de muestras: una ventana sin decisiones es un guion en texto y un recuento nulo en JSON, nunca un valor cero.
Ejemplos:
./blunderdb stats training --db base.db --player "Alice"
./blunderdb stats training --db base.db --window month --format json
cubematrix — Matriz del cubo
Da el veredicto del cubo de una posición en todos los marcadores de un partido: para cada casilla away × away, si la posición se dobla y si se toma. Cálculo puro: no se abre ninguna base de datos, la posición llega como XGID o como OGID (OpenGammon).
./blunderdb cubematrix [options] '<XGID|OGID>'
Opciones:
--format— Formato de salida:textojson(por defecto:text).--match-length— Longitud del partido que cubre la cuadrícula, de 1 a 25 (por defecto: 7).--ply— Profundidad de búsqueda de cada casilla,0o2(por defecto: 2).--prune-k— Número de jugadas candidatas conservadas por la red de poda (por defecto: 12).--jobs— Búsquedas ejecutadas en paralelo (por defecto: una por núcleo). La cuadrícula es idéntica sea cual sea el valor; solo cambia el tiempo.
El marcador propio de la posición se ignora — la cuadrícula lo sustituye — pero su cubo se conserva: la pregunta es a qué marcador giraría este cubo. La cuadrícula es posterior a Crawford de principio a fin.
Cada casilla es una búsqueda propia, porque el motor tiene en cuenta el marcador: una sola búsqueda releída a través de equidades de partido distintas sería falsa justo donde el marcador importa.
Ejemplos:
# Grille d'un match en 5 points
./blunderdb cubematrix --match-length 5 'XGID=-b----E-C---eE---c-e----B-:0:0:1:00:0:0:0:7:10'
# Les équités de chaque case, pour un script
./blunderdb cubematrix --format json '<XGID>'
Salida text: una cuadrícula cuyas filas son los puntos que aún necesita el jugador en turno y cuyas columnas son los del adversario, luego la leyenda de las siglas ND / DT / DP / TG y el motivo de cada casilla rechazada.
rollout — Rollout de una posición
Juega una posición un gran número de veces con gammonNet, para decidir lo que una búsqueda no decide: dos jugadas separadas por unas milésimas, o una decisión de cubo en la que el modelo duda. Con dados se juegan sus jugadas (las mejores a la profundidad del rollout, al menos 2 ply, o las indicadas con --move); sin dados, su decisión de cubo (No doblar y Doblar/Aceptar; Doblar/Rechazar vale exactamente +1). La posición proviene de un XGID o de un OGID, o de una base (--db y --id); sin --store, no se registra nada.
./blunderdb rollout [options] '<XGID|OGID>'
./blunderdb rollout --db <path> --id <position> [--store] [options]
Opciones:
--preset— Ajuste de partida:fast(por defecto: 216 partidas truncadas a 7 medias jugadas, parada en JSD 3 tras 108) ostandard(1296 partidas truncadas a 11, parada en JSD 3 tras 324). Ambos juegan a 0 ply — la red sola, para las jugadas, el cubo y las hojas;--ply 1o más juega más profundo, con un tiempo varias veces mayor. Las opciones siguientes lo sustituyen una a una.--games,--min-games,--truncation,--jsd,--ply,--candidates— Los parámetros del rollout (--truncation 0juega cada partida hasta el final,--jsd 0no se detiene nunca antes del final).--move— Una jugada que jugar, en notación blunderDB (repetible).--seed— Semilla de los dados, fija por defecto: el mismo comando da los mismos números.--jobs— Partidas jugadas en paralelo (por defecto: una por núcleo); solo cambia el tiempo.--format— Formato de salida:textojson(por defecto:text).--db,--id— La base y el identificador de la posición que se va a jugar, en lugar de un XGID.--store— Registra el rollout terminado en la posición, como un segundo análisis con sus propios ajustes, junto al análisis importado o evaluado, al que nunca reemplaza. Un rollout interrumpido no se registra; de dos rollouts con los mismos ajustes se conserva la serie más larga, y un rollout con otros ajustes se añade al lado.--list— Muestra los rollouts guardados en la posición, del más reciente al más antiguo, en lugar de jugar uno.
Todos los candidatos juegan los mismos dados, la suerte de cada tirada se elimina del resultado de cada partida (reducción de varianza), las dos primeras tiradas se estratifican y una partida se detiene allí donde la base de bearoff two-sided la cubre. Cada línea da la equidad, su intervalo al 95 %, el número de partidas jugadas y la JSD, la diferencia con la mejor en desviaciones típicas de la diferencia. El cubo se juega durante las partidas: la clasificación es más fiable que la equidad absoluta. Ctrl-C muestra lo que han establecido las partidas terminadas.
Ejemplos:
./blunderdb rollout 'XGID=-b----E-C---eE---c-e----B-:0:0:1:31:0:0:0:0:10'
./blunderdb rollout --move '8/5 6/5' --move '24/23 13/10' '<XGID>'
./blunderdb rollout --db base.db --id 42 --preset standard --store
./blunderdb rollout --db base.db --id 42 --list
epc — Calculadora EPC
Calcula el Effective Pip Count, la probabilidad de victoria y el veredicto de cubo money de una posición de retirada dada por XGID o por OGID (OpenGammon). Cálculo puro: no interviene ningún archivo de base de datos.
./blunderdb epc [options] '<XGID|OGID>'
Opciones:
--format— Formato de salida:textojson(por defecto:text).--bearoff-ts— Base bearoff two-sided opcional (.bd) que amplía la integrada TS-06-06 (también se lee de la variable de entornoBLUNDERDB_TS_PATH). Gana la base válida más amplia; un archivo no válido se ignora con una advertencia.
Regímenes. En el dominio que cubre la base two-sided, la probabilidad de victoria y el análisis money del cubo (cubeless, ND, D/T, D/P, veredicto) son exactos. Fuera de él, la probabilidad de victoria se estima (convolución de las distribuciones de tiradas one-sided más una corrección calibrada) y se muestra con su margen de error medido; el veredicto de cubo nunca se estima, deliberadamente (véase ADR-0009).
Ejemplos:
# Régime exact : six pions ou moins de chaque côté
./blunderdb epc 'XGID=-BBB------------------bbb-:0:0:1:00:0:0:0:0:10'
# Avec la table TS-06-11 calculée : exact jusqu'à onze pions par joueur
./blunderdb epc --bearoff-ts ~/.local/share/blunderdb/gnubg_ts6x11.bd 'XGID=…'
bearoff — Bases de bearoff
Fabrica y gestiona las bases de bearoff. No se descarga nada ni hay nada incrustado: una tabla se calcula aquí y se verifica contra la huella que gnubg produce para su dominio. Ningún subcomando habla con una base de datos — una tabla de bearoff es aritmética sobre el juego, no sobre las posiciones de nadie — así que ninguno toma --db.
./blunderdb bearoff generate --ts <domain> [options]
./blunderdb bearoff list [options]
./blunderdb bearoff verify <file.bd> [options]
./blunderdb bearoff delete --ts <domain> [options]
El dominio se escribe como en makebearoff: 6x9 para la tabla de dos lados con nueve fichas por jugador, os8 para la tabla de un lado de ocho puntos (os solo equivale a os6).
Las dos familias no responden a la misma pregunta. Una tabla de dos lados amplía el dominio donde la probabilidad de victoria y el veredicto de cubo son exactos; una tabla de un lado amplía la distancia a la que una ficha puede estar sin que el EPC se calle (hasta diez puntos).
generate. Indica el tamaño, la memoria y el tiempo estimado antes de empezar, y después muestra el porcentaje y el tiempo restante medido.
--ts— Dominio de dos lados a calcular, por ejemplo6x9.--os— Dominio de un lado a calcular, en número de puntos: 6 a 12. Se requiere exactamente uno de los dos.--cores— Núcleos a usar (por defecto: todos menos uno).--data-dir— Dónde escribir (por defecto: la carpeta de datos de la aplicación).--quiet— Sin línea de progreso.
CTRL-C pausa. La señal se captura: el estado se escribe junto a la tabla y el mismo comando ejecutado de nuevo continúa donde se detuvo en lugar de recalcularlo todo. Media hora de aritmética merece escribirse. bearoff delete descarta una reanudación pendiente. Solo el barrido de dos lados se pausa; el de un lado es secuencial y --cores no le sirve de nada.
list. Cifra cada dominio — tamaño, memoria, tiempo en esta máquina — y dice cuáles ya están presentes, con su veredicto, y cuáles tienen un cálculo en pausa. --format json para un script, --cores para cambiar la hipótesis de la estimación.
verify. Responde verified (los mismos bytes que la referencia), unverified (bien formada, pero no hay huella registrada para ese dominio) o corrupt (el archivo se contradice). Sale con error en el último caso: este comando está hecho para ponerse en un script.
delete. Retira la tabla, la reanudación pendiente y los restos de un cálculo muerto. Un dominio predeterminado se recalcula en el siguiente arranque de la aplicación; uno más amplio no.
Ejemplos:
# Ce que cette machine a, et ce que chaque domaine coûterait
./blunderdb bearoff list
./blunderdb bearoff generate --ts 6x9 --cores 4
# OS-08 : l'EPC répond alors jusqu'à un pion sur la 8
./blunderdb bearoff generate --os 8
# Sur un serveur, dans le volume que lit le démon
./blunderdb bearoff generate --ts 6x11 --data-dir /srv/bearoff
./blunderdb bearoff verify /srv/bearoff/gnubg_ts6x11.bd
analyze — Puesta al día gammonNet
Escribe un análisis gammonNet para cada posición que no tiene ninguno — la puesta al día de una biblioteca constituida antes de que existiera esta funcionalidad (ADR-0013, ADR-0015). Es la misma operación que el disparo automático tras la importación y el botón « Analizar ahora » de la interfaz gráfica, y que el punto de acceso /v1/gammonnet.analyzeMissing del demonio serve para un tenant — tres formas distintas de la misma operación, no tres lógicas separadas (véase Modo headless (servidor)).
./blunderdb analyze --db <path> [options]
Opciones:
--db— Base de datos (obligatorio).--ply— Profundidad de búsqueda (por defecto: 2, el parámetro canónico).--prune-k— Anchura de poda (por defecto: 12, el parámetro canónico).--candidates— Número de jugadas candidatas conservadas por decisión de fichas (por defecto: 10).--jobs— Número de posiciones analizadas en paralelo (por defecto: el número de núcleos de la máquina).--match— Restringe el lote a las posiciones de una sola partida (0, el valor por defecto, significa toda la biblioteca).--compare— No escribe nada: compara gammonNet con los análisis importados en lugar de rellenar huecos (véase más abajo).--limit— Con--compare, se detiene tras ese número de posiciones (0 = todas).--format— Formato de salida:text(por defecto, con progreso) ojson(un único documento resumen, impreso al final).--rollout— Hace un rollout de las posiciones que elige--queryen lugar de rellenar huecos (véase más abajo).--query— Con--rollout, las posiciones que se van a jugar, en el lenguaje de consulta de la búsqueda (search --query-help); vacío, todas.
Un partido importado sin análisis obtiene así un PR. Es el caso de un partido jugado en línea, o de un fichero Jellyfish .mat, que nadie ha pasado por XG. blunderDB conocía sus posiciones y las jugadas realizadas, pero nada decía cuánto valían; una vez pasado el lote, la jugada realmente hecha se compara con la clasificación de gammonNet y la diferencia alimenta el PR y todos los demás indicadores. La jugada realizada viene de la tabla de jugadas del partido, escrita en la importación, llevara el fichero un análisis o no — nunca se adivina.
Una base analizada con una versión anterior a esta no necesita volver a evaluarse: repair recalcula las columnas a partir de lo que ya está almacenado y devuelve su PR a esos partidos.
Una sola partida (--match). Con el identificador que muestra list --type matches, el lote solo recorre las posiciones de esa partida: la misma regla del hueco, las mismas garantías, un alcance más estrecho. Una partida recién importada recibe sus análisis sin que se recorra el resto de la biblioteca, y una partida corregida y analizada por segunda vez solo cuesta las posiciones creadas por la corrección, puesto que todas las demás ya llevan un análisis. La opción no se combina ni con --stale ni con --compare, que miran ambas posiciones que ya tienen un análisis: pedir las dos es un error, en lugar de un alcance ignorado en silencio.
El paralelismo (--jobs). Las posiciones de un lote son independientes — ninguna búsqueda informa a la siguiente —, así que se reparten entre --jobs hilos de ejecución, cada uno con su propio evaluador. Los análisis escritos son idénticos sea cual sea el valor de --jobs; solo cambia el tiempo de cálculo. --jobs 1 deja la máquina libre para otras cosas. La cancelación no se ve afectada: Ctrl-C detiene el lote antes de cualquier nueva posición, y todo lo ya calculado se escribe.
La regla del hueco (ADR-0013). Una posición que ya lleva un análisis — XG, GNUbg, BGBlitz o una pasada anterior de gammonNet — nunca se toca, sea cual sea el motor que falte. Solo se escribe una posición sin ningún análisis. El comando puede por tanto relanzarse en cualquier momento sin riesgo, e interrumpirse limpiamente: Ctrl-C cancela sin perder nada de lo ya escrito, y la siguiente ejecución retoma exactamente donde se detuvo la anterior — no hace falta ningún registro, puesto que « las posiciones sin análisis » se recalcula en cada lanzamiento.
Ejemplo:
./blunderdb analyze --db base.db
# Analyzing 1204 position(s) with gammonNet (2-ply, k=12, 16 job(s))...
# 1/1204 (0%)
# 61/1204 (5%)
# ...
# 1204/1204 (100%)
# Done.
./blunderdb analyze --db base.db --jobs 1
# Un seul match, celui qui vient d'être importé
./blunderdb analyze --db base.db --match 12
Rollouts por lotes (--rollout). Cada posición que elige --query se juega mediante un rollout, una tras otra en todos los núcleos, y el rollout se registra junto a su análisis, nunca en su lugar. El valor es un preajuste — fast (216 partidas truncadas a 7) o standard (1296 partidas truncadas a 11) — o ajustes libres: un preajuste opcional, luego games=, min-games=, truncation=, jsd=, ply=, candidates=, seed=, separados por comas. Una posición que ya lleva un rollout con los mismos ajustes se omite: una ejecución interrumpida con Ctrl-C se reanuda donde se detuvo, descartándose por completo la posición en curso. Una posición que solo analiza un rollout se encuentra en la búsqueda a través de él; una posición ya analizada conserva las columnas de su análisis.
./blunderdb analyze --db base.db --rollout fast --query 'E>80'
./blunderdb analyze --db base.db --rollout 'standard,ply=1' --query 'c'
--compare: ¿cuánto vale gammonNet sobre su base?
La precisión del motor se mide en otro sitio contra corpus de referencia y contra la tabla de retirada exacta. Ninguna de esas medidas responde a la pregunta que un usuario se hace de verdad, que trata de sus posiciones: en las partidas importadas de XG, ¿dónde está el motor integrado en desacuerdo con el análisis que vino en el archivo, y cuánto costaría ese desacuerdo?
--compare responde, y no escribe nada. No es una precaución sino el interés del comando: la ADR-0013 protege incondicionalmente un análisis importado, y la comparación puede por tanto lanzarse sobre una base que uno no quiere ver reescrita.
El informe da:
la tasa de acuerdo sobre la mejor respuesta, separada entre jugadas de fichas y decisiones de cubo — ambas no tienen nada que ver y una tasa única ocultaría cuál de las dos flaquea;
el coste del desacuerdo, valorado en la escala del análisis importado: lo que vale la jugada preferida por gammonNet según el motor importado, menos lo que vale su propia mejor jugada. Ese sentido es el único que ambos motores pueden cifrar juntos; valorar un desacuerdo dos veces invitaría a leer el menor de los dos números;
el desglose por fase de la partida, que es lo que dice dónde se concentran los desacuerdos;
los diez desacuerdos más caros, posición por posición.
Dos motores escriben la misma jugada de forma distinta — XG escribe «13/7» donde gammonNet escribe «13/8 8/7», los golpes se marcan de un lado y no del otro, la repetición a veces se condensa en «(2)». Esas diferencias son dialecto y no desacuerdo: la comparación reduce ambas notaciones a una forma canónica antes de compararlas. Sin eso, un corpus de prueba mostraba un 78,8 % de acuerdo en lugar de un 93,2 % — quince puntos de falsos desacuerdos.
Una jugada que el motor importado no ha listado no puede valorarse en su escala: cuenta como un desacuerdo de coste nulo en lugar de uno inventado.
# Comparer sur un échantillon de 500 positions
./blunderdb analyze --db base.db --compare --limit 500
# compared: 118 decision(s) (refused 2, failed 0)
# same best answer: 93.2% (110/118)
# checker play: 93.7% (59/63)
# cube decision: 92.7% (51/55)
# ...
transcribe — Reproducir una transcripción
Reproduce una transcripción e informa de lo que la relectura encuentra en ella. La fuente es un archivo .mat, un partido de la biblioteca o un borrador de transcripción — exactamente una de las tres. Un partido se lee mediante el .mat que produciría al exportarlo: lo que se reproduce es, pues, lo que contendría una exportación.
./blunderdb transcribe --mat <fichier> [--check] [--render <sortie>]
./blunderdb transcribe --db <path> --match <id> --check
./blunderdb transcribe --db <path> --draft <id> --check
./blunderdb transcribe --db <path> --match <id> --edit [--accept-losses]
./blunderdb transcribe --db <path> --draft <id> --finish|--abandon
Opciones:
--mat— Archivo.matque se reproduce.--db— Base de datos, para--matchy--draft.--match— Identificador del partido de la biblioteca que se reproduce.--draft— Identificador del borrador de transcripción que se reproduce.--check— Lista las incoherencias encontradas (comportamiento por defecto).--render— Reescribe la transcripción como.maten esta ruta.--format— Formato de salida:text(por defecto) ojson.--edit— Abre un borrador sobre el--match(o devuelve el que ya está abierto sobre él).--accept-losses— Con--editsobre un partido importado: acepta que sus análisis y comentarios puedan perderse.--finish— Termina el--draft: escribe su partido, o sustituye aquel del que se abrió, y libera el borrador.--abandon— Abandona el--draft: lo elimina sin partido; un partido del que se abrió queda tal cual.--yes— Con--abandonsobre un borrador que nunca produjo un partido: confirma que todo lo escrito en él se pierde.
--check nombra cada incoherencia con el número de la acción y la partida en la que se encuentra: jugada ilegal, dos turnos seguidos del mismo jugador, acción de cubo imposible, acción más allá del final del partido, jugada cuyos pasos no utilizan sus propios dados, primera jugada de una partida con un doble, que ninguna tirada de apertura puede ser, jugada no registrada — la celda ??? que gnubg escribe cuando no ha conservado la jugada realizada, y que no es un baile, marcador anunciado incoherente — una partida cuya línea de marcador no es la que dan las partidas anteriores, rejugada con el marcador escrito.
Una incoherencia se informa, nunca se opone: nada se rechaza por ella y el código de salida sigue siendo 0 sea lo que sea que encuentre la relectura. Un código distinto de 0 señala un fallo real — archivo ilegible, base que no se abre, salida imposible de escribir. Un script que quiera actuar sobre las observaciones las lee en --format json, donde un archivo roto y una partida que contiene una jugada ilegal no se confunden.
--render reescribe la transcripción como .mat, lo que permite comprobar la ida y vuelta sobre un archivo real, fuera de las pruebas.
Solo tres opciones escriben, con los mismos métodos que el panel Transcripción: --edit abre un borrador sobre un partido existente, --finish lo termina —el partido se sustituye con el mismo identificador— y --abandon elimina un borrador sin partido, y exige --yes para un borrador nunca terminado, que se lleva todo lo escrito en él. Un partido importado lleva análisis y comentarios que un .mat no lleva: --edit da como máximo su número y se niega sin --accept-losses.
Ejemplo:
./blunderdb transcribe --mat match.mat --check
# match.mat: 7 point match, 4 game(s), 203 action(s)
# Final score: 9-2
# Inconsistencies: none
tournament — Leer un torneo dirigido
Lee un torneo dirigido sin interfaz gráfica. Dirigir un torneo de forma interactiva es función de la consola del motor Nicomaque; estos subcomandos leen, ninguno espera una entrada, y solo move escribe.
./blunderdb tournament <sous-commande> --db <chemin> [options]
Subcomandos:
list [--format text|json]— Los torneos dirigidos de la base, con su estado, la versión del motor, el evento al que pertenece cada uno (vacío si ninguno) y la fecha de la última decisión.verify --id N [--format text|json]— Vuelve a reproducir la dirección y señala cualquier aviso residual. Sale con error si queda alguno: es la verificación posterior al torneo, y un script que la pasa sobre las bases de una temporada quiere un código de retorno, no una línea que filtrar.standings --id N— La clasificación en CSV, premios incluidos, en el idioma de la interfaz.ranking --season [--rencontre N] [--from AAAA-MM-JJ] [--to AAAA-MM-JJ] [--points 25,18,15] [--participation P] [--elo] [--format csv|json]— La clasificación de temporada: los torneos cerrados de un evento o de un período (límites incluidos, según la fecha del torneo; sin filtro, todos los torneos dirigidos), con cada puesto convertido en puntos por el baremo (el ganador primero; por defecto 25, 18, 15, 12, 10, 8, 6, 4, 2, 1), más--participationpor torneo jugado. Los empatados se reparten la media de los puestos que ocupan. Una persona se reconoce de un torneo al siguiente por su nombre.--eloañade un Elo de club recalculado sobre los partidos de la temporada (fórmula de FIBS, partiendo de 1500). El CSV da una fila por persona y una columna de puntos por torneo; un torneo no cerrado aparece en la lista pero no aporta nada.page --id N|--rencontre N [--out <carpeta>]— La página HTML de visualización de una prueba (--id), o la página mural de un evento (--rencontre: una línea por mesa, sea cual sea la prueba que la ocupa). Se requiere exactamente uno de los dos. Sin--outsale por la salida estándar; con él, se escribe en la carpeta, que pasa a ser la de la dirección o la del evento.export --id N— El registro de eventos en bruto, reproducible por las herramientas del motor. El registro es toda la verdad de una dirección: la clasificación, los cuadros y los avisos se reproducen a partir de él. Una herramienta que lea esta salida no necesita blunderDB en absoluto.move --id N --match M --table T [--format text|json]— Cambia la mesa de un partido en curso, como arrastrar una casilla sobre otra en la cuadrícula. Si la mesa de destino está ocupada, los dos partidos intercambian sus mesas; una mesa fuera de servicio se rechaza. En un evento, si la mesa está ocupada por otra prueba, el intercambio se hace entre las dos pruebas: un cambio de mesa se escribe en el registro de cada una. Muestra la mesa de cada partido en curso.hall --rencontre N [--format text|json]— Todas las mesas de un evento: una línea por mesa, sea cual sea la prueba que la ocupa (prueba, partido, jugadores), y luego las propuestas de cada prueba. Es la cuadrícula que muestra la vista Todas las mesas de la Dirección. Las mesas se agrupan por sala cuando el evento tiene salas, y se nombran cuando tienen nombre.tables --rencontre N|--tournament N [--format text|json]— Las propiedades de las mesas (nombre, sala, reservada, asignada a) y las salas donde juega cada prueba de un evento (--rencontre), o las propiedades de una prueba que juega sola (--tournament). Solo lectura: la escritura pasa porcall(rencontres.setTables,rencontres.setEventRooms,directions.setTables).
Opciones comunes: --db (obligatorio), --id (obligatorio salvo para list, page --rencontre hall y tables), --format.
Ejemplos:
./blunderdb tournament list --db base.db
./blunderdb tournament verify --db base.db --id 3
./blunderdb tournament standings --db base.db --id 3 > classement.csv
./blunderdb tournament ranking --db base.db --season --from 2026-01-01 --to 2026-12-31 --elo > saison.csv
./blunderdb tournament page --db base.db --id 3 --out /tmp/affichage
./blunderdb tournament page --db base.db --rencontre 1 --out /tmp/evenement
./blunderdb tournament export --db base.db --id 3 > journal.json
./blunderdb tournament move --db base.db --id 3 --match m4 --table 7
./blunderdb tournament hall --db base.db --rencontre 1
./blunderdb tournament tables --db base.db --rencontre 1
trash — La papelera
Lo que se ha eliminado, y con qué devolverlo. Una eliminación sigue siendo una eliminación: se escribe antes una instantánea JSON de lo que desaparece, y nada más en la base sabe que esa tabla existe — ningún filtro de búsqueda, ninguna estadística, ninguna regla de retención.
./blunderdb trash <sous-commande> --db <chemin> [options]
Subcomandos:
list— Lo que hay en la papelera, de lo más recientemente eliminado a lo más antiguo.restore --id N— Devuelve la entrada N y la retira de la papelera.discard --id N— Elimina la entrada N ahora mismo, sin restaurarla.empty [--older-than D]— Vacía la papelera, o solo lo que tiene más de D días.delete --kind K --id N— Elimina un objeto por la papelera, para que el gesto se pueda deshacer.Kesposition,collectionocomment.
Opciones comunes: --db (obligatoria), --kind, --limit (por defecto 50), --format (text o json).
Nota
blunderdb delete sigue eliminando sin red: un script que elimina una posición espera que desaparezca, y dejar una instantánea en silencio haría crecer un archivo que nadie ha pedido que crezca. Es trash delete el que conserva la anulación.
Restaurar una posición vuelve a pasar por la deduplicación Zobrist: nunca crea un duplicado, pero no devuelve su identificador antiguo — la fila original ya no existe. Una posición restaurada es la misma posición, con un número nuevo.
Lo que tiene más de treinta días lo elimina blunderdb vacuum — nunca al abrir una base.
Ejemplos:
# Supprimer une position en gardant l'annulation
./blunderdb trash delete --db base.db --kind position --id 412
# Voir la corbeille, puis remettre une entrée
./blunderdb trash list --db base.db
./blunderdb trash restore --db base.db --id 3
# Ne garder que ce qui a moins de trente jours
./blunderdb trash empty --db base.db --older-than 30
info — Metadatos de la base de datos
Muestra los metadatos y las estadísticas de una base de datos.
./blunderdb info --db <path> [--format <format>]
Opciones:
--db— Base de datos (obligatorio).--format— Formato de salida:textojson(por defecto:text).
Ejemplos:
./blunderdb info --db base.db
# Database Information
# ==================================================
# Path: /home/jean/bg/base.db
#
# Metadata:
# Version: 2.20.0
# User: Jean
# Description: Matchs de tournoi 2025
# Date of Creation: 2026-09-06 02:43:51
#
# Statistics:
# Positions: 3859
# Analyses: 3855
# Matches: 11
# Games: 61
# Moves: 3766
--format json añade el origen del archivo — issuance lleva la filigrana si la hay, y la identidad de emisor de esta máquina:
./blunderdb info --db base.db --format json
{
"issuance": {
"watermarked": false,
"issuerFingerprint": "1186-57FA-060C-9378",
"issuerName": "unger"
},
"metadata": {
"database_version": "2.20.0",
"dateOfCreation": "2026-09-06 02:43:51",
"description": "Matchs de tournoi 2025",
"user": "Jean"
},
"path": "/home/jean/bg/base.db",
"stats": {
"analysis_count": 3855,
"game_count": 61,
"match_count": 11,
"move_count": 3766,
"position_count": 3859
}
}
edit — Modificar los metadatos
Modifica el nombre de usuario, la descripción o los umbrales de una base de datos.
./blunderdb edit --db <path> [options]
Opciones:
--db— Base de datos (obligatorio).--user— Nuevo nombre de usuario.--description— Nueva descripción.--clear-user— Borrar el nombre de usuario.--clear-description— Borrar la descripción.--error-threshold— Umbral de error, en milipuntos: una decisión que cuesta al menos eso es un error.--blunder-threshold— Umbral de blunder, en milipuntos: un error que cuesta al menos eso es un blunder.--format— Formato de salida:text(por defecto) ojson({"changes": [...]}).
Se requiere al menos una opción de modificación.
Ejemplos:
./blunderdb edit --db base.db --user "Marie" --description "Ma collection"
./blunderdb edit --db base.db --clear-description
./blunderdb edit --db base.db --error-threshold 20 --blunder-threshold 80
verify — Verificar la integridad
Verifica la integridad de la base de datos y, opcionalmente, compara una partida con su archivo de origen.
./blunderdb verify --db <path> [--match <id>] [--mat <file.mat>]
Opciones:
--db— Base de datos (obligatorio).--match— ID de la partida a verificar.--mat— Archivo MAT a comparar (se usa con--match).--format— Formato de salida:text(por defecto) ojson(estadísticas, huérfanos, desviación de esquema, y la verificación del partido si la hay).
Sin la opción --match, el comando muestra las estadísticas generales de la base de datos. Con --match, verifica los datos de la partida y puede compararlos con el archivo de origen original.
Cada ejecución comprueba también la integridad referencial: cuenta las filas huérfanas — partidas sin partido, jugadas sin partida, análisis de jugada sin jugada, análisis sin posición, entradas del diario de repaso sin mazo o sin posición — y muestra una línea WARNING con el total si las hay. Una base sana responde Orphaned rows: none. Pueden quedar huérfanas en una base escrita por una versión que no aplicaba las claves foráneas en todas las conexiones, o antes de que el diario de repaso tuviera las suyas; no pertenecen a ningún partido ni a ningún mazo y solo ocupan espacio. La orden termina igualmente con el código de salida 0.
Cada ejecución compara también el esquema con la DDL de referencia y enumera las tablas, columnas e índices que faltan en la base. Al abrir una base se añade lo que falta cuando es posible y solo se registra en el diario lo que no se puede añadir (típicamente un índice UNIQUE que filas duplicadas impiden reconstruir): es aquí donde esa diferencia se hace visible, y una consulta que nombre uno de esos elementos falla hasta que se corrija la causa. Una base sana responde Schema: matches the reference DDL. Como los huérfanos, una desviación del esquema es una constatación, no un fallo: el código de salida sigue siendo 0.
Cada ejecución comprueba por último las reglas que la DDL actual enuncia pero que SQLite no puede añadir a una tabla ya creada: las restricciones CHECK de rango (dados entre 0 y 6, cubo y pips no negativos, de 0 a 15 fichas sacadas, calificación de repaso entre 1 y 4), el hash Zobrist que a una fila nunca debería faltarle y la unicidad de un análisis por posición. Una base creada desde la versión 2.18.0 del esquema las aplica; una más antigua todavía puede contener filas que una base nueva rechazaría, y son esas las que se cuentan aquí, regla por regla. Una base sana responde Constraints: every row satisfies the current DDL. Una constatación más: no se repara nada y el código de salida sigue siendo 0.
Cada ejecución recalcula por último los dos contadores desnormalizados, match.game_count y game.move_count, a partir de las filas que dicen contar, e indica cuántos discrepan y en cuánto en el peor caso. Ambos se escriben una sola vez, en la importación, según lo que contenía el archivo de origen, y son los que muestran la lista de partidos y la vista de una partida: una pequeña diferencia suele ser una importación que se saltó lo que no supo convertir. No se reescribe nada — sustituir el contador por lo almacenado borraría justamente la discrepancia que conviene mirar. Una base sana responde Counters: game_count and move_count agree with the rows.
Ejemplos:
./blunderdb verify --db base.db
./blunderdb verify --db base.db --match 1
./blunderdb verify --db base.db --match 1 --mat original.mat
Convertirlo en una comprobación automática. El código de salida vale 0 sea lo que sea lo que encuentre la orden: es --format json el que lleva el veredicto, y un script debe leer los contadores por sí mismo.
{
"stats": {
"analysis_count": 3855,
"game_count": 61,
"match_count": 11,
"move_count": 3766,
"position_count": 3859
},
"orphans": {
"games_without_match": 0,
"moves_without_game": 0,
"move_analyses_without_move": 0,
"analyses_without_position": 0,
"reviews_without_deck": 0,
"reviews_without_position": 0
},
"orphan_total": 0,
"schema_drift": {
"missing_tables": null,
"missing_columns": null,
"missing_indexes": null
},
"schema_drift_count": 0,
"constraint_violations": [
{"name": "position.zobrist_hash NOT NULL", "count": 0},
{"name": "position.dice_1 BETWEEN 0 AND 6", "count": 0}
],
"constraint_violation_total": 0,
"counter_drift": {
"matches_with_wrong_game_count": 0,
"games_with_wrong_move_count": 53,
"worst_game_count_gap": 0,
"worst_move_count_gap": 2
},
"counter_drift_total": 53
}
Tres campos valen como alarma: orphan_total, schema_drift_count y constraint_violation_total. Si no son nulos, describen una base que hay que reparar.
./blunderdb verify --db base.db --format json \
| jq -e '.orphan_total == 0 and .schema_drift_count == 0 and .constraint_violation_total == 0'
counter_drift_total no forma parte de ellos, y el ejemplo anterior lo muestra: la base que lo produjo acababa de ser importada y ya muestra 53 partidas cuyo contador de jugadas difiere de lo que contienen las líneas. Estos contadores vienen del archivo de origen, no de la base; una diferencia cuenta la historia de la importación, no señala una corrupción. Obsérvelo, no lo use como un umbral.
vacuum — Compactar la base de datos
Recupera el espacio en disco dejado por las eliminaciones (partidas, torneos, purgas): SQLite nunca reduce el archivo por sí solo cuando se borran datos, hay que pedírselo explícitamente. Es la única forma de desencadenar una compactación — nunca ocurre automáticamente al abrir una base, porque su coste es imprevisible en una base grande.
./blunderdb vacuum --db <path>
Opciones:
--db— Base de datos (obligatorio).--format— Formato de salida:text(por defecto) ojson({"size_before", "size_after", "reclaimed"}, en bytes).
El comando empieza por un wal_checkpoint(TRUNCATE) para que el tamaño mostrado antes de la compactación sea honesto, comprueba que queda en el disco aproximadamente el doble del tamaño actual del archivo (SQLite reconstruye por completo la base antes de pasar a ella), ejecuta el VACUUM y después un ANALYZE para refrescar las estadísticas que usa el planificador de consultas. Si falta espacio en disco, el comando se niega a arrancar con un mensaje explícito en lugar de arriesgar una compactación interrumpida.
Ejemplo:
./blunderdb vacuum --db base.db
# Compacting database...
# Before: 128.4 MiB
# After: 41.2 MiB
# Reclaimed: 87.2 MiB
repair — Recalcular lo que es derivado
Recalcula lo que la base extrae de lo que almacena: las columnas escalares de cada análisis a partir del propio análisis, del que no son más que una proyección; la fase y el tipo de juego de cada posición a partir de su tablero; y el centinela de Crawford de cada marcador a partir de la partida de la que procede la posición, o del XGID con el que entró. Los análisis no se tocan: lo que se rehace son los valores que se habían extraído de ellos.
./blunderdb repair --db <path>
Opciones:
--db— Base de datos (obligatorio).--format— Formato de salida:text(por defecto) ojson— un contador por pasada:repaired(columnas de análisis),phases(posiciones reclasificadas) ycrawford(posiciones con hash recalculado). Cada uno indica el número de filas realmente modificadas.
Útil tras una corrección de la manera en que se lee un análisis importado. El caso ya se ha dado dos veces. El importador XG escribe un «no doblar» de dos formas, y la segunda se entendía como un doble real — la columna llevaba entonces el error de un doble que nunca ocurrió. Y un análisis que blunderDB había calculado él mismo no sabía qué jugada se había hecho, de modo que un partido importado sin análisis conservaba un error nulo por todas partes y un PR de 0,00; la columna se recalcula ahora a partir de las jugadas del partido. Corregir la lectura no cambia nada en las filas ya escritas; este comando las rehace.
La pasada de Crawford, en cambio, toca las posiciones mismas. Un marcador a 1 significa «falta un punto, y esta partida ES la de Crawford»; 0 significa «falta un punto, la de Crawford ya quedó atrás». Mientras los importadores no escribieron esa distinción, toda posición posterior a Crawford se registró como una posición de Crawford, y por tanto se leyó con el cubo muerto — allí donde el que va detrás dobla en realidad a la primera ocasión. Corregir el marcador cambia el hash de la posición: la fila se rehashea, pues, y se fusiona con su gemela correcta si la base ya tiene una — el análisis, los comentarios, las colecciones, las tarjetas Anki y su registro de repasos, las jugadas de la partida y las entradas de la papelera que la nombran siguen a la fila superviviente. Una posición a la que ninguna partida apunta solo se corrige por la palabra del XGID que trajo de otro programa (XG, BGBlitz…): cuando el campo Crawford de ese XGID dice que la partida no es la de Crawford, y el XGID describe de verdad esta posición. En sentido inverso, una posición sin partida guardada a 0 por ambos lados pasa a 1 cuando el XGID que trajo es el de un match a 1 punto y la describe: la única partida de un match a 1 punto empieza a un punto de la meta, así que es la de Crawford, como la escriben los importadores. El DMP posterior a la de Crawford de un match más largo se queda en 0: su XGID da la longitud de ese match. Un XGID que blunderDB reescribió por sí mismo solo repite el marcador guardado y no prueba nada. Cualquier otra posición sin partida se deja tal cual: nada contradice lo que su marcador anuncia.
Nada lo desencadena automáticamente, y es deliberado: reescribir las columnas de análisis de todo el mundo, o rehashear posiciones, por el mero hecho de abrir una base no es algo que una herramienta deba hacer a espaldas de su usuario.
Ejemplo:
./blunderdb repair --db base.db
# 42 analyses repaired.
# 7 positions reclassified.
# 3 positions rehashed onto the right Crawford sentinel.
delete — Eliminar datos
Elimina una partida y todos los datos asociados (juegos, jugadas, análisis).
./blunderdb delete --db <path> --type match --id <id> [--confirm]
Opciones:
--db— Base de datos (obligatorio).--type— Tipo de eliminación:match(obligatorio).--id— ID del elemento a eliminar (obligatorio).--confirm— Eliminar sin pedir confirmación.--format— Formato de salida:text(por defecto) ojson({"match_id": N, "deleted": true}).
Ejemplos:
# Confirmation interactive, puis sans confirmation (scripts)
./blunderdb delete --db base.db --type match --id 1
./blunderdb delete --db base.db --type match --id 1 --confirm
healthcheck — Sondear un demonio
Pregunta a un demonio serve en marcha (véase Modo headless (servidor)) si está disponible: una petición GET /readyz, código de retorno 0 si el demonio responde 200 (almacenamiento accesible, esquema en la versión esperada), 1 en caso contrario — almacenamiento inaccesible, esquema obsoleto o nada escucha en la dirección. No se abre ningún archivo de base de datos.
./blunderdb healthcheck [--addr host:port] [--timeout 2s]
Opciones:
--addr— Dirección en la que escucha el demonio (por defectoBLUNDERDB_ADDR, si no:8080). Una dirección sin host (:8080) o con un host genérico (0.0.0.0,[::]) se sondea en la interfaz de bucle local.--timeout— Tiempo tras el cual la sonda abandona (2spor defecto).
Es el comando que ejecuta el HEALTHCHECK de la imagen de contenedor (una imagen distroless, sin curl); el binario serve construido desde cmd/serve también lo entiende. Sirve igualmente en un script o en una unidad systemd.
Ejemplo:
./blunderdb serve --db base.db --addr 127.0.0.1:8080 &
./blunderdb healthcheck --addr 127.0.0.1:8080 && echo "démon disponible"
# ready
En caso de fallo se muestra el motivo, que docker inspect reproduce para un contenedor unhealthy:
Error: healthcheck: http://127.0.0.1:8080/readyz answered 503 Service Unavailable (version_mismatch)
mcp — Ofrecer la base a un asistente de IA
Sirve las herramientas de la base a un asistente de IA mediante el Model Context Protocol, por la entrada y la salida estándar: es el asistente quien lanza el comando. Las herramientas buscan posiciones en la gramática de la barra de comandos, leen una posición y su análisis, explican un error, calculan las estadísticas de un jugador, listan partidas, torneos y colecciones, y plantean un quiz. Solo leen, salvo con --write. La lista completa y el equivalente HTTP del demonio: Herramientas para un asistente de IA (MCP).
./blunderdb mcp --db base.db [--write]
Opciones:
--db— Archivo de base de datos (obligatorio).--write— Ofrece también las herramientas que escriben: guardar una posición, comentarla, crear y llenar una colección. Nada se borra.
Como call, el comando migra el esquema de una base antigua al abrirla, incluso sin --write.
Ejemplo: declarar la base en Claude Code.
claude mcp add blunderdb -- blunderdb mcp --db /chemin/vers/base.db
completion — Autocompletado de shell
Muestra en la salida estándar un script de autocompletado para los nombres de subcomandos. La lista de comandos incrustada en cada script se genera a partir de la misma tabla que leen blunderdb help y el despacho de main.go (handlers()): un nuevo subcomando se ofrece por tanto en el autocompletado en cuanto se conecta, sin nada que mantener a mano.
./blunderdb completion <bash|zsh|fish>
Ejemplos:
# bash
source <(blunderdb completion bash)
blunderdb completion bash | sudo tee /etc/bash_completion.d/blunderdb > /dev/null
# zsh : un répertoire déjà sur $fpath
blunderdb completion zsh > "${fpath[1]}/_blunderdb"
# fish
blunderdb completion fish | source
Los paquetes lo instalan automáticamente: el .deb/.rpm (nfpm) y el paquete AUR generan los tres scripts a partir del binario empaquetado en el momento de la compilación, y el cask de Homebrew ejecuta blunderdb completion <shell> una vez durante la instalación mediante generate_completions_from_executable. Nada se incluye en el repositorio, así que el autocompletado nunca puede desviarse de la tabla de subcomandos.
version — Mostrar la versión
Muestra la versión de blunderDB y la del esquema de base de datos que escribe este binario; es lo primero que hay que adjuntar a un informe de error.
./blunderdb version
# blunderDB version 0.36.0 (database schema 2.20.0)
Ejemplos de flujos de trabajo
Importar un directorio de torneo
./blunderdb create --db tournoi_paris.db --user "Jean" --description "Open de Paris 2025"
./blunderdb import --db tournoi_paris.db --type batch --dir ./matchs_open_paris/
./blunderdb list --db tournoi_paris.db --type stats
Copia de seguridad periódica
./blunderdb export --db production.db --type database --file sauvegarde-$(date +%Y%m%d).db
Análisis de errores
# Les positions délicates, puis celles de videau
./blunderdb search --db production.db --error-min 0.1 --export blunders.db
./blunderdb search --db production.db --decision cube --error-min 0.05 --export cube_errors.db
# Les coups réellement fautifs : au moins 100 millièmes d'équité perdus
./blunderdb search --db production.db --move-error-min 100 --format json
Códigos de salida
0— Éxito.1— Error.
Esto permite usar la CLI en scripts con gestión de errores:
if ./blunderdb import --db base.db --type match --file match.xg; then
echo "OK"
else
echo "KO"
exit 1
fi