Files
fn_registry/CHANGELOG.md
T
egutierrez 625569485f feat(doctor): add fn doctor CLI + 14 functions for system management
Adds `fn doctor` read-only diagnostic command with subcommands artefacts,
services, sync, uses-functions, unused, and --json flag for agents.
Each subcommand wraps a registry function in functions/infra/.

New functions:
- artefact_doctor, services_status, pc_locations_drift,
  audit_uses_functions, find_unused_functions (Go diagnostics)
- backup_sqlite_db, rotate_backups, wait_for_http, wait_for_port,
  port_kill, tail_journal, pre_commit_hook_install (bash utilities)
- notify_telegram (Go HTTP)
- backup_all pipeline (tag launcher)

Plus prior session leftovers (scan_secrets_in_dirty, append_diary_entry,
git utilities, http_session_cookie_middleware, compile/full-git pipelines).

Fixes pc_locations_drift filepath.Join bug with absolute dir_path.
Documents fn doctor in CLAUDE.md, .claude/rules/fn_doctor.md (rule 23),
docs/architecture.md, CHANGELOG.md (2026-05-07), and diary entry.

First fn doctor uses-functions run found drift in 7/12 apps (deuda
para sincronizar app.md con imports reales).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 01:42:10 +02:00

23 KiB
Raw Blame History

Changelog

Todos los cambios notables de fn_registry se documentan aquí.

Formato basado en Keep a Changelog. Al no haber releases semver formales, las entradas se ordenan por fecha.

Para contexto detallado del trabajo diario ver docs/diary/. Para decisiones arquitecturales ver docs/adr/.

[Unreleased]

2026-05-07

Added

  • fn doctor CLI (cmd/fn/doctor.go) — entrypoint unico read-only para diagnostico del registry y artefactos. Subcomandos: artefacts (git/venv/app.md/upstream), services (apps tag service + systemctl + puerto), sync (drift pc_locations BD vs disco), uses-functions (imports reales vs declarados en app.md), unused (funciones sin consumidores). Flag --json para agentes/scripts. Cada subcomando es wrapper fino sobre una funcion del registry.
  • .claude/rules/fn_doctor.md — regla 23 en INDEX.md. Documenta cuando usar, mapeo subcomando → funcion del registry, y acciones derivadas (que hacer cuando reporta un drift).
  • bash/functions/infra/backup_sqlite_db (backup_sqlite_db_bash_infra, impure) — snapshot atomico de SQLite via VACUUM INTO. Mas seguro que cp con escrituras concurrentes.
  • bash/functions/infra/rotate_backups (rotate_backups_bash_infra, impure) — retention rsnapshot-style daily.N/weekly.M/monthly.K.
  • bash/functions/infra/wait_for_http (wait_for_http_bash_infra, impure) — poll URL hasta 2xx con timeout, util en deploys/smoke tests.
  • bash/functions/infra/wait_for_port (wait_for_port_bash_infra, impure) — poll TCP host:puerto. Usa nc o /dev/tcp builtin (sin deps).
  • bash/functions/infra/port_kill (port_kill_bash_infra, impure) — mata proceso(s) escuchando un puerto. Idempotente, fallback KILL tras TERM.
  • bash/functions/infra/tail_journal (tail_journal_bash_infra, impure) — wrapper journalctl con auto-deteccion --user vs sistema, prioridad y --since.
  • bash/functions/infra/pre_commit_hook_install (pre_commit_hook_install_bash_infra, impure) — instala hook que llama scan_secrets_in_dirty_bash_cybersecurity antes de cada commit. Idempotente con marca fn_registry-pre-commit-v1.
  • functions/infra/notify_telegram (notify_telegram_go_infra, impure) — envia mensaje a chat Telegram via Bot API. Trunca >4096 chars.
  • functions/infra/artefact_doctor (artefact_doctor_go_infra, impure) — audita salud de cada app/analysis: dir existe, .git presente, manifest parseable, .venv valido (analyses), upstream configurado.
  • functions/infra/services_status (services_status_go_infra, impure) — apps con tag service + systemctl is-active (user/system) + puerto declarado en notes/description + check TCP localhost.
  • functions/infra/pc_locations_drift (pc_locations_drift_go_infra, impure) — detecta drift pc_locations BD vs disco para el PC actual (~/.fn_pc). Tres tipos: missing_on_disk, untracked_on_disk, status_should_be_active.
  • functions/infra/audit_uses_functions (audit_uses_functions_go_infra, impure) — para cada app Go/Py compara imports reales contra uses_functions del app.md. Reporta missing_in_app_md y unused_in_app_md. Heuristica documentada (puede dar falsos positivos en unused).
  • functions/infra/find_unused_functions (find_unused_functions_go_infra, impure) — funciones del registry sin consumidores en otras funciones, apps o analyses. Pipelines sin tag launcher tambien aparecen.
  • bash/functions/pipelines/backup_all (backup_all_bash_pipelines, impure, tag launcher) — orquesta backup_sqlite_db + rotate_backups sobre registry.db, cada apps/*/operations.db, y rsync --link-dest para vaults declarados en projects/*/vaults/vault.yaml.

Changed

  • .claude/CLAUDE.md — seccion CLI ampliada con comandos fn doctor [subcommand] [--json] y enlace a la regla.
  • .claude/rules/INDEX.md — anadida fila 23 para fn_doctor.md.

Fixed

  • functions/infra/pc_locations_drift.gofilepath.Join(absoluto, absoluto) producia paths corruptos cuando dir_path ya era absoluto (caso comun: filas pc_locations traen path absoluto al disco del PC). Fix: chequear filepath.IsAbs antes de unir. Sintoma previo: todos los artefactos reportados como missing_on_disk aunque existieran.
  • go.modgolang.org/x/net movido a deps directas (go mod tidy tras anadir notify_telegram).

Notes

  • Hallazgo de la primera ejecucion fn doctor uses-functions: 7/12 apps con drift real (auto_metabase, dag_engine, deploy_server, docker_tui, kanban, metabase_registry, script_navegador). Pendiente sincronizar sus app.md con los imports reales en sesion futura.
  • fn doctor unused muestra muchas funciones core sin consumidores aun (compose2_go_core, curry2_go_core, etc.). Esperado: el registry crece antes que las apps que las consuman.

2026-05-04

Added

  • cpp/functions/viz/graph_labels_select (graph_labels_select_cpp_viz, pure) — TU separado de graph_labels con los helpers puros graph_compute_degrees y graph_labels_select (frustum cull + always_for_* + top-N por size * (degree+1)). Vive en su propio archivo para que los tests unitarios lo cubran sin abrir ImGui.
  • cpp/functions/viz/graph_viewport_selection (graph_viewport_selection_cpp_viz, pure) — TU separado de graph_viewport con clear_selection, is_selected, add_to_selection, toggle_selection. Mantienen sincronizados state.selection y nodes[i].flags & NF_SELECTED.
  • cpp/functions/viz/graph_types (graph_types_cpp_viz, pure) — TU de implementacion de GraphData::update_bounds() y GraphData::find_node_by_user_data(). Pareja obligatoria del header del tipo (graph_types.h indexado en types/viz/).
  • cpp/apps/chart_demo/app.md — la demo de primitivos viz (line/scatter/bar/heatmap) ahora aparece en el registry como chart_demo_cpp_viz.
  • cpp/apps/shaders_lab/app.md — el live GLSL playground con DAG ahora tiene app.md propio (antes solo existia entrada legacy en BD sin .md en disco).

Changed

  • registry/indexer.go — el indexer ahora escanea tambien <lang>/apps/*/app.md (mismo patron que ya usaba para <lang>/functions/ y <lang>/types/). Antes solo veia apps/ y projects/*/apps/ — las apps en cpp/apps/ quedaban invisibles. ./fn index reporta 17 apps (antes 15).
  • cpp/functions/viz/graph_labels.mdsignature reducida a graph_labels_draw y graph_labels_draw_at (los helpers puros pasan a entrada propia). uses_functions apunta a la nueva entrada graph_labels_select_cpp_viz.
  • cpp/functions/viz/graph_viewport.mduses_functions añade graph_viewport_selection_cpp_viz.
  • projects/osint_graph/apps/graph_explorer/app.mduses_functions sincronizado con CMakeLists.txt: ahora declara las 23 funciones del registry que enlaza (antes 15). Añadidas: graph_viewport_selection, graph_labels_select, graph_types, graph_spatial_hash, button, icon_button, badge, empty_state.
  • projects/fn_monitoring/apps/registry_dashboard/app.mduses_functions sincronizado con CMakeLists.txt (21 deps, antes 9). Añadidas: badge, button, empty_state, icon_button, modal_dialog, page_header, process_runner, process_state_machine, select, text_input, toast, toolbar, tree_view. Removido: fps_overlay (vive en fn_framework, no se declara).

Decisions

  • ADR 0003-orphan-tu-as-separate-function-entry.md — cuando una funcion del registry necesita partir su .cpp en varios TUs por testabilidad o separacion ImGui-vs-puro, cada TU adicional se registra como entrada propia con su .md en lugar de extender file_path para listar varios archivos. El parent declara la nueva entrada en uses_functions. Razon: el indexer asume 1 .cpp = 1 .md; un file_path multi-archivo rompe la convencion y deja apps nuevas sin saber que TUs enlazar.

Added — sesion NER+RE para graph_explorer (tarde, 980 → 990 funciones)

18 funciones nuevas sobre el ecosistema NER+RE, en dos rondas de fn-constructor:

Ronda 1 — extraccion de relaciones (mREBEL/REBEL/MarianMT):

  • python/functions/datascience/parse_rebel_output.py (pure) — parser wire <triplet> REBEL/mREBEL.
  • python/functions/datascience/align_relations_to_entities.py (pure) — string-match aligner.
  • python/functions/datascience/mrebel_load_model.py (impure, CC BY-NC-SA 4.0 — NO comercial).
  • python/functions/datascience/mrebel_base_load_model.py (impure, misma licencia).
  • python/functions/datascience/rebel_load_model.py (impure, Apache 2.0, EN-only).
  • python/functions/datascience/marianmt_es_en_load_model.py (impure) — Helsinki-NLP/opus-mt-es-en.
  • python/functions/datascience/translate_es_to_en.py (impure) — wrapper traduccion frase a frase.
  • python/functions/datascience/extract_relations_mrebel.py (impure) — pipeline mREBEL frase-a-frase + alineamiento.
  • 21 tests pytest verdes.

Ronda 2 — pipeline GLiNER2 + OpenIE schema-less + composicion (tarde):

  • python/functions/core/clean_pdf_text.py (pure) — limpia artefactos PyPDF2.
  • python/functions/core/chunk_with_overlap.py (pure) — sliding window con avance forzado.
  • python/functions/core/merge_entity_aliases.py (pure) — coreferencia normalize+substring.
  • python/functions/core/filter_relations_by_entity_types.py (pure) — post-filter typed.
  • python/functions/core/aggregate_extraction_results.py (pure) — dedupe + Counter sobre N chunks.
  • python/functions/datascience/gliner2_load_model.py (impure, Apache 2.0) — fastino/gliner2-large-v1.
  • python/functions/datascience/extract_graph_gliner2.py (impure) — wrapper schema + threshold + include_confidence.
  • python/functions/datascience/spacy_es_load_model.py (impure) — es_core_news_md cacheado.
  • python/functions/datascience/extract_triples_spacy_es.py (impure) — OpenIE schema-less ES por reglas de dependencia (verbo del texto = predicado).
  • python/functions/pipelines/extract_graph_from_text.py (impure pipeline) — composicion E2E: chunk → extract_graph_gliner2 (×N) → aggregate → filter typed → merge aliases → grafo final.
  • 39 tests pytest verdes.

Added — analysis gliner_glirel_tuning

projects/osint_graph/analysis/gliner_glirel_tuning/ — investigacion empirica de modelos NER/RE. 9 notebooks ejecutados:

# Notebook Hallazgo clave
01 01_gliner_glirel_tuning.ipynb Calibracion de thresholds GLiNER+GLiREL
02 02_e2e_spanish_graph.ipynb E2E texto ES — descubrimiento del fail de GLiREL en castellano
03 03_mrebel_vs_glirel.ipynb mREBEL gana a GLiREL pero CC BY-NC-SA
04 04_gliner2_winner.ipynb GLiNER2 (Apache 2.0, NER+RE joint, 340M) elegido como motor principal
05 05_long_text_and_pdf.ipynb Pipeline PDF E2E sobre politica_proteccion_datos.pdf (BBVA, 89.882 chars)
06 06_improvements.ipynb Threshold 0.3 (vs default 0.5) → +187% relaciones; coref reduce 18% aislados
07 07_nuextract_vs_gliner2.ipynb NuExtract GPU 2.6× mas lento, calidad similar — descartado por defecto
08 08_improving_gliner2.ipynb snake_case verbal labels + post-filter typed = mejor combo
09 09_spacy_es_openie.ipynb spaCy ES dep-rules: schema-less, predicado = verbo del texto

Added — vault osint_nlp_models

projects/osint_graph/vaults/osint_nlp_models (symlink a ~/vaults/osint_nlp_models/):

  • models/ — fichas de gliner, glirel, mrebel, gliner2, candidates a probar.
  • decisions/ — 3 ADRs cortos del 2026-05-04 (mrebel-over-glirel mañana, gliner2-over-mrebel tarde, license-constraint).
  • benchmarks/corpus_v1.md + results_log.csv (15 filas de experimentos).
  • test_documents/politica_proteccion_datos.pdf (PDF de BBVA copiado para reproducibilidad).

Added — playground HTML

projects/osint_graph/analysis/gliner_glirel_tuning/playground/:

  • server.py — FastAPI con GLiNER2 cacheado, endpoints GET / (HTML) y POST /extract (texto → grafo).
  • index.html — UI: textarea, KPIs (nodos/aristas/tiempo), grafo Sigma.js, JSON exportable.
  • static/sigma.min.js + graphology.umd.min.js (servidos localmente para evitar bloqueo CDN por extensiones tipo MetaMask/SES).

Stack aplicado por el server:

  1. snake_case verbal labels (works_at, ceo_of, headquartered_in, agreement_with...)
  2. threshold 0.3 (configurable)
  3. chunking automatico > 1500 chars
  4. post-filter typed ((person, organization) validos por relacion)
  5. coreferencia normalize+substring
  6. layout server-side via networkx.spring_layout
  7. render Sigma.js (sin fisica → sin loops de ResizeObserver)

Added — issues

  • dev/issues/0050-jupyter-exec-collab-client-failure.md — bug jupyter_exec con cliente colaborativo + workaround documentado.
  • projects/osint_graph/apps/graph_explorer/issues/0041-split-confidence-thresholds.md — split confidence_threshold en entity_threshold + relation_threshold.
  • projects/osint_graph/apps/graph_explorer/issues/0042-gliner2-unified-extractor.md — sustituir GLiREL por GLiNER2 en extract_graph_hybrid. Reemplaza 0042-mrebel.
  • projects/osint_graph/apps/graph_explorer/issues/0042-mrebel-relation-extractor.md.superseded — version mREBEL del 0042 archivada al ganar GLiNER2.

Changed

  • cpp/CMakeLists.txt_GE_DIR y _DASH_DIR sobreescribibles via -D<...>=<path> para builds en worktrees (commit e72d6364). Habilita parallel-fix-issues sobre apps C++.
  • python/functions/datascience/glirel_load_model.py — workaround compat huggingface_hub 1.x: classmethod monkey-patch idempotente para inyectar proxies/resume_download que el HF nuevo dejo de pasar (commit 3b3378cf).
  • Sub-repo dataforge/graph_explorer master local: merges --no-ff de issue/0035e-polish-and-tests (commit f614a51) + issue/0013-paste-extract-panel (commit 2a49c2b). 125/125 tests pytest verdes. Sin push aun — pendiente confirmacion + validacion Windows.

Fixed (bugs encontrados + raiz + fix)

Bug Raiz Fix
chunk_with_overlap bucle infinito Frase mas larga que max_chars, no avanzaba i, OOM-killed por overlap acumulado Avance forzado: meter al menos UNA frase aunque exceda max_chars
NuExtract degenera en texto largo Sin repetition_penalty, decoder entra en bucle de tokens repetidos hasta agotar 2048 max_new_tokens repetition_penalty=1.15 + chunking obligatorio (179/179 chunks parsed OK tras fix)
NuExtract AutoProcessor.from_pretrained rota en transformers 5.x Sub-processor de video tira TypeError: argument of type 'NoneType' is not iterable (Qwen2-VL) Bypass: AutoTokenizer + AutoModelForImageTextToText directamente
Vis-network ResizeObserver loop spam (en SES/MetaMask) Vis-network usa physics simulation → ResizeObserver dispara warnings amplificados por SES Migrar a Sigma.js + layout server-side via networkx.spring_layout (sin fisica frontend)
jupyter_exec append HTTP 405 jupyter_nbmodel_client espera collab WebSocket Y.js, no soportado al 100% por jupyter-collaboration nuevo Documentado en issue 0050; workaround actual: build_notebook scripts con nbformat + nbconvert --execute
Kernel startup shadows pip packages 00_fn_registry.py añade cada subdir de python/functions/ a sys.path top-level → bigquery/datasets.py shadows HF datasets package needed by transformers Workaround per-notebook: sys.path = [p for p in sys.path if not p.startswith(_pf+'/')] + añadir solo el padre. Issue futuro pendiente.

Decisions — vault ADRs

Decision Razon
GLiNER2 (Apache 2.0) sustituye a GLiREL en extract_graph_hybrid 6/8 relaciones correctas vs 0/1 de GLiREL en es_corporate_short, 1.18s vs 22s de mREBEL, NER+RE en una pasada
mREBEL queda como fallback (no comercial) 4/5 correctas pero CC BY-NC-SA 4.0 + 25× mas lento
spaCy ES dep-rules para OpenIE schema-less Predicado = verbo del texto (querer, abrazar), 5ms/frase, sin alucinaciones
Threshold 0.3 (vs default 0.5) sweet spot +187% relaciones manteniendo precision; 0.2 mete +22% entidades dudosas
Coreferencia normalize+substring + post-filter typed = gratis y decisivos Coref 18% aislados; post-filter elimina Madrid president_of Persona
Translate ES→EN + triplet-extract EN NO vale la pena Pierdes verbos del texto (quererloves), +500ms-1s, +300MB MarianMT, riesgo nombres propios

2026-04-28

Added

  • cpp/functions/core/app_about (app_about_cpp_core) — ventana flotante About con about_window_set_info(project, version, description), about_window_menu_item("About...") y about_window_render(). Render automatico via fn::run_app (cableado en cpp/framework/app_base.cpp).
  • bash/functions/infra/ensure_repo_synced (ensure_repo_synced_bash_infra) — pipeline idempotente que compone gitea_create_repo + gitea_push_directory: crea repo Gitea si falta, inicializa .git local si falta, commitea cambios pendientes y pushea. Defaults: owner dataforge, branch master.
  • analysis.md para 6 analyses que estaban en disco pero sin indexar: agent_coding_eval, estudio_embeddings, estudio_mercados, ontology_graph, pruebas_jupyter, retrieving_graphs. Ahora ./fn index reporta 8 analyses (antes 2).
  • Repos dataforge/<name> creados en Gitea para apps y analyses que no estaban subidos: agents_and_robots, element_matrix_chat, deploy_server, shaders_lab, voice_guide, agent_coding_eval, ontology_graph, turismo_spain. Cada uno con .gitignore apropiado para excluir binarios, .venv/, node_modules/, .jupyter*, operations.db*.

Changed

  • cpp/functions/core/app_menubar: el item top-level Settings... pasa a ser un BeginMenu("Settings") con dos subitems: Settings... (ventana de app_settings) y About... (nuevo, ventana de app_about). Las apps que usan fn_ui::app_menubar(nullptr, 0, nullptr) heredan el cambio sin tocar nada.
  • projects/fn_monitoring/apps/registry_dashboard/main.cpp: cablea fn_ui::about_window_set_info("fn_registry Dashboard", "0.2.0", "...") antes de fn::run_app. Tabla Apps gana columna Git con valores remote (repo_url poblado), local (.git/ presente) o -.
  • data.h/data.cpp/data_http.cpp del dashboard: AppRow extendido con repo_url y dir_path.
  • 10 repos migrados de branch main a master para unificar convencion: apps/{docker_tui,fuzzygraph,metabase_registry,pipeline_launcher,rapid_dashboards,script_navegador}, analysis/{estudio_embeddings,estudio_mercados,pruebas_jupyter,retrieving_graphs}. Default branch en Gitea actualizado via API (PATCH /repos/{owner}/{repo} con {"default_branch":"master"}), branch main remota borrada.
  • git config --global init.defaultBranch master para que los proximos git init sean consistentes.
  • /full-git-push: descubre apps/analyses sin .git y ofrece inicializarlos con ensure_repo_synced automaticamente. Excluye subrepos/ para evitar duplicacion (mirrors upstream).
  • /full-git-pull: tras fn sync, segunda pasada que clona los dataforge/<name> registrados en apps/analysis que no existan localmente — soluciona el "no pude recuperar la app en el otro PC".
  • bash/functions/infra/ensure_repo_synced.sh: localiza dependencias via FN_REGISTRY_INFRA_DIR o FN_REGISTRY_ROOT, robusto a sourcing desde zsh/bash.

Fixed

  • projects/fn_monitoring/apps/sqlite_api/handlers.go|main.go|handlers_test.go + nuevos handlers_mutations.go y handlers_projects.go: cableados endpoints POST /add_app|add_analysis|add_vault|reindex y GET /projects para que el dashboard pueda crear artefactos y navegar projects desde la actions bar (estado pendiente de varios dias en uncommitted, ahora versionado en dataforge/sqlite_api).
  • Bug operativo en sqlite_api (Windows): SO_RCVTIMEO se pasaba como struct timeval cuando Windows espera DWORD ms → timeout efectivo de 5 ms. Ya documentado en app.md del dashboard.

2026-04-24

Added

  • 6 funciones bash/infra/systemd_local_* (install_unit, enable, start, restart, status, uninstall) para gestionar servicios systemd del sistema desde el registry (complementa las versiones remotas SSH ya existentes).
  • Pipeline install_systemd_service_bash_pipelines que compone las anteriores: genera unit file + install + enable + start + status.
  • Servicio systemd sqlite_api.service instalado y habilitado en aurgi-pc — arranque automático al iniciar WSL en 127.0.0.1:8484.
  • projects/fn_monitoring/launcher.sh — launcher del dashboard (arranca API si no está + lanza ventana + cleanup).
  • Regla .claude/rules/kiss.md — filosofía KISS para proyectos y apps.
  • Documentación ADR en docs/adr/ con plantilla y ADR 0001 (experimento GitButler).
  • Diario en docs/diary/ + slash command /entrada_diario para añadir entradas.
  • CHANGELOG.md (este archivo).
  • Submódulo cpp/vendor/glfw re-registrado con path limpio (antes heredado con path absoluto /home/lucas/...).
  • aurgi-pc registrado en el server centralizado (registry.organic-machine.com) con 18 pc_locations.

Changed

  • registry.db ahora está gitignorada. Es regenerable con fn index + completable con fn sync. Evita conflictos entre ramas y PCs.
  • sqlite_api ahora se distribuye como binario compilado (projects/fn_monitoring/apps/sqlite_api/sqlite_api) en lugar de go run al vuelo.

Fixed

  • http_client.cpp del dashboard: añadido #include <cstdint> requerido por mingw-w64 para cross-compile Windows (g++ Linux lo incluía transitivamente).
  • registry_dashboard.exe (Windows) ya no abre ventana de consola al lanzarse — enlazado como GUI app (WIN32_EXECUTABLE TRUE / -mwindows).

Added (design system C++)

  • cpp/functions/core/tokens — design tokens para dashboards ImGui (colors, spacing, radius, font_size) inspirados en @fn_library (Mantine v9). Paleta dark + indigo primary. apply_dark_theme() aplica los tokens al ImGuiStyle global.
  • cpp/functions/core/badge — etiqueta inline con 6 variantes (Default/Success/Warning/Error/Info/Outline). Equivalente a <Badge> de @fn_library.
  • cpp/functions/core/empty_state — placeholder centrado para tablas/listas vacías.
  • cpp/functions/core/page_header — header de página con título/subtítulo + hueco para acciones + separator.
  • registry_dashboard migrado a los nuevos componentes: page_header_begin/end en el header, empty_state en las 4 tablas cuando están vacías, apply_dark_theme() al primer frame. Sin hardcode de colores disperso.
  • systemd_local_{enable,start,restart}: stdout de systemctl redirigido a stderr para no contaminar el JSON capturado por el pipeline.
  • .gitmodules: entry fantasma cpp/vendor/glfw con path absoluto /home/lucas/... que bloqueaba git submodule status y el cross-compile Windows.

Removed

  • Integración de GitButler de Claude Code — binario ~/.local/bin/but, plugin gitbutler-tools, skill .claude/skills/gitbutler/, hooks en settings.json, ramas gitbutler/* + e-branch-*, estado interno .git/gitbutler/. Ver ADR 0001 para motivos.