La verificación adversarial detectó que, en PPTX (slide 16:9, corto), las columnas
categóricas de ALTA cardinalidad NO id-like (Ticket, Cabin) ocupaban 3 slides cada
una con el donut SEPARADO de su tabla: el top-k de 8 filas largas no cabía junto al
donut y el keep-together partía la columna. (El PDF, en A5, ya estaba 1:1 correcto.)
Arreglo SOLO en render_pptx_impl.py:
- `_fit_group_blocks` (nuevo): para un Group con figura + DataTable que no cabe en el
slide, reserva un alto mínimo para el donut (`_GROUP_MIN_FIG_H`) y recorta las filas
de la DataTable a lo que queda, de modo que el gráfico se queda en el MISMO slide,
junto a su tabla. No-op cuando ya cabe o no hay par figura+tabla (p.ej. columnas
id-like, que ya omiten la top-k).
- `_trim_data_table_to_budget` (nuevo): devuelve una COPIA de la DataTable con las
filas que caben (al menos una) + nota honesta "top N de M categorías mostradas
(recortado para caber en el slide; el PDF muestra más)". NUNCA muta el bloque
original, que es compartido con el renderer PDF (el PDF sigue mostrando la tabla
completa en A5).
- `_place_group`: aplica `_fit_group_blocks` antes de `_shrink_group_figures`.
Refuerzo de cat_distr_test.py:
- `test_golden_pptx_una_slide_por_columna_con_su_grafico`: perfil con una columna
categórica de alta cardinalidad no-id-like (40 valores largos sobre 5000 filas,
0.8% distinto) que reproduce el caso Ticket/Cabin. Asierta que CADA columna
categórica aparece en EXACTAMENTE UN slide del capítulo y que ese mismo slide lleva
su tabla (Cardinalidad/distintos) Y su donut (caption + shape Picture) — el gráfico
nunca se separa de su tabla. Sustituye al laxo `n_slides >= 2`.
Verificado con titanic_train.csv (render_automatic_eda run_models=True): 5 columnas
categóricas (Name, Sex, Ticket, Cabin, Embarked); PDF 6 páginas y PPTX 6 slides del
capítulo (intro + 1 por columna), cada columna con su donut junto a su tabla en una
sola página/slide. Ticket y Cabin pasaron de 3 slides a 1. Suite verde (122 passed).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Cada columna categórica del capítulo CAT DISTR ocupa ahora su propia página
(PDF) / slide (PPTX) con su gráfico junto a su tabla, y se elimina la
explicación larga de la entropía que duplicaba el capítulo GLOSARIO.
Cambios:
- model.Group: nuevo campo aditivo `page_break_before` (default False). Cuando
es True el renderer fuerza al grupo a empezar en página/slide nueva (salvo que
la actual esté vacía). Comportamiento de todos los capítulos existentes
intacto. Soportado también en el normalizador dict-defensivo `as_block`.
- render_pdf_impl / render_pptx_impl `_place_group`: respetan `page_break_before`.
- render_pdf_impl / render_pptx_impl `_measure_block`: medición fiel de KVTable y
DataTable (replica `_place_*`: título-heading, wrap del valor/celdas por
columna, nota). La estimación previa asumía una línea por fila e ignoraba el
título, así que el keep-together infra-presupuestaba la figura y el gráfico se
desbordaba a la página siguiente. Helpers `_measure_kv_table`/`_measure_data_table`.
- render_pptx_impl `_shrink_group_figures`: umbrales más bajos (budget>0.6,
per>0.35) para que en el slide corto 16:9 la figura se encoja y conviva con la
tabla en lugar de partir la columna (misma filosofía keep-together del PDF).
- cat_distr.py:
- build envuelve cada columna en un `Group(page_break_before=idx>0)`: una
columna por página/slide, con su tabla de cardinalidad, su top-k y su donut
juntos. La primera comparte página con la intro para no dejar una casi vacía.
- intro recortada: se elimina el párrafo que explicaba qué es la entropía
(vive en el capítulo GLOSARIO, donde el término `[[term:entropia]]` enlaza);
se conserva el término clicable y el total de filas de referencia.
- `_cardinality_block`: métricas relacionadas agrupadas por fila (distintos·%·
únicos; entropía bits·máx·norm; desbalance·longitud) sin perder ningún dato,
para que tabla + gráfico quepan en el slide 16:9.
- columnas id-like (≈100% distintas): se omite la top-k (sería una lista de
valores únicos; la nota lo explica) y el donut ocupa ese hueco.
- CHAPTER_VERSION 1.1.0 -> 1.2.0.
Verificado con titanic (render_automatic_eda run_models=True): PDF 5 páginas y
PPTX 5 slides del capítulo (intro + 1 por columna: Name, Sex, Ticket, Embarked),
cada columna con su gráfico junto a su tabla, sin cortes. Suite verde
(121 passed): pytest automatic_eda/ + render_automatic_eda_test.py.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Añade el parámetro profile_level a render_automatic_eda como preset de
consumo CPU/LLM que mapea a los flags existentes (run_models, run_series,
run_llm, sample). Tres niveles:
- lite (bajo consumo): run_llm=False, run_series=False, sample=2000 y modelos
limitados a PCA + normalidad, SIN KMeans ni IsolationForest (lo caro en CPU).
Para un vistazo rápido y barato.
- standard (default): comportamiento histórico — modelos completos, serie,
sin LLM.
- full: standard + narrativa LLM por capítulo.
Precedencia: un flag explícito del caller (run_llm=..., run_models=..., etc.)
siempre prima sobre el default que fija el preset; el preset solo aplica al
parámetro que se deja en None.
Cableado del modo lite sin tocar profile_table (lo tocan otros agentes en
paralelo): profile_table NO corre los modelos (evita pagar KMeans +
IsolationForest); este pipeline los corre con run_eda_models(run_kmeans=False,
run_isolation=False) reusando ctx['raw_numeric'], y quita raw_numeric del ctx
para que el capítulo modelos no reproyecte clusters KMeans en vivo
(project_clusters_2d). geo_points ya queda derivado, así que geospatial no se
afecta.
Cambio aditivo y retro-compatible: sin profile_level el comportamiento es
idéntico al de v1.0.0 (standard). Tests nuevos cubren lite/standard, la
precedencia flag-sobre-preset, y la equivalencia del default con el histórico.
Bump 1.0.0 -> 1.1.0 + growth log en el .md. Skill /eda documenta --lite/--full.
Verificación: golden lite/standard/full sobre titanic — lite 4.8s (PCA+norm,
sin KMeans/iso/LLM/serie), standard 7.8s (modelos completos), full 38.3s
(+LLM). Suite render_automatic_eda + automatic_eda: 96 passed. fn index sin
error.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Añade el capítulo `relaciones` al motor AutomaticEDA: analiza las
relaciones de clave de la tabla/base y se coloca tras `correlacion`,
antes de `modelos`, en CHAPTER_ORDER.
Capas que renderiza (solo las que aplican; None si no hay nada que decir):
- Claves declaradas: PK/FK/UNIQUE reales del esquema DuckDB, vía la nueva
función `detect_declared_keys_duckdb` (lee `duckdb_constraints()`).
- Candidatos a clave primaria: los `key_candidates` del TableProfile.
- FK candidatas inter-tabla: reusa `infer_fk_containment_duckdb`
(containment + señal de nombre) y `build_join_graph` (roles de nodos +
diagrama Mermaid pegable). Solo si la fuente DuckDB tiene varias tablas.
- FK candidatas intra-tabla: heurística nombre + cardinalidad, vía la nueva
función pura `suggest_intratable_fk_candidates`, marcada como sugerencia.
Engancha al glosario clicable los términos PK, FK, containment/inclusión y
cardinalidad (contrato §11.1) y usa Group (keep-together) para el grafo.
Funciones nuevas del registry (grupo `eda`):
- detect_declared_keys_duckdb (impure, datascience) + test.
- suggest_intratable_fk_candidates (pure, datascience) + test.
Tests: relaciones_test.py (golden intra + inter, edges, no-cut render) +
los tests de ambas funciones. Suite automatic_eda + render_automatic_eda
verde (89 passed). Golden end-to-end con el pipeline render_automatic_eda
verificado sobre titanic (intra) y una BD customers/orders (inter).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Implementa el modelo de calidad del report 2046 en el grupo eda.
Score de columna: 0.6·completeness + 0.4·validity con renormalización por
aplicabilidad (si la validez no es medible —texto libre o columna 100% nula— el
score se basa solo en completeness). Validez = conformidad real al tipo: nativo
numérico/fecha/bool = 1.0; texto promovido a número/fecha = parse rate
(validity_rate); texto con semantic_type = match_rate; texto libre = no aplica.
Outliers, columnas constantes e identificadores salen del score a un bloque de
observaciones analíticas (no son defectos de calidad). Se elimina el doble
conteo de la falta de datos (mostly_null ya no castiga validez) y el bug de
escala de outliers (que además ya no entran en el score).
Score de dataset: 100·(0.85·cell_quality + 0.15·row_uniqueness) en vez de la
media simple. Se pobla duplicate_rows/duplicate_pct push-down en
summarize_table_duckdb (COUNT sobre DISTINCT *, sin RAM) para habilitar la
unicidad de registro; renormaliza a solo cell_quality si no se puede calcular.
Capítulo calidad (v2.0.0): intro de dos dimensiones (60/40) que declara que los
outliers no bajan el score; tabla de scores Columna|Calidad|Completitud|Validez
(sin Consistencia, n/a cuando no aplica); DOS tablas separadas (Problemas de
calidad vs Observaciones analíticas); resumen con Unicidad de registro; glosario
clicable de completitud, validez, unicidad de registro y calidad de datos.
Verificado: 123 tests verdes (automatic_eda + render_automatic_eda +
column_quality_score + summarize_table_duckdb + profile_table). Golden EDA de
titanic (run_models+run_llm) con score recomputado a mano, outliers separados en
observaciones y glosario clicable (5 links GOTO en el PDF).
column_quality_score v2.0.0, summarize_table_duckdb v1.1.0, profile_table v1.1.0.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
El capítulo etiquetaba dos secciones por partida doble: un Heading de nivel 2
más el 'title' del propio DataTable, imprimiendo 'Diccionario de datos' y
'Datos personales (PII / RGPD)' dos veces seguidas en PDF y PPTX.
Se elimina el 'title' de ambos DataTable y se conserva el Heading único (el
patrón canónico OVERVIEW del contrato §8: el rótulo lo da el Heading, la tabla
solo repite su cabecera de columnas al paginar). El DataTable de PII mantiene su
'note' orientativa. La columna del diccionario ya lee 'Significado de negocio'.
CHAPTER_VERSION 1.0.0 -> 1.1.0. Test nuevo
test_sin_rotulos_duplicados_y_significado_de_negocio fija: tablas sin title,
cabecera exacta 'Significado de negocio', y cada rótulo una sola vez en el PDF.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
El capítulo PORTADA ahora muestra SIEMPRE el tamaño del dataset (N filas ×
M columnas) en grande, como heading junto al nombre y agrupado con él
(Group keep-together), en lugar de enterrarlo en la tabla de metadatos.
La Descripción y la Granularidad ya no salen vacías ni con placeholders:
se resuelven por cascada — ctx explícito > bloque LLM (profile['llm'].summary
/ row_meaning de eda_llm_insights) > derivación del propio perfil (forma,
mezcla de tipos y score de calidad para la descripción; columnas
key_candidates o la forma de la tabla para una frase 'Cada fila es…').
Las derivaciones son honestas (declaran que vienen del perfil) y nunca
inventan significado de negocio.
Añade chapters/portada_test.py: golden (tamaño grande + textos del LLM,
sin fila 'Tamaño' duplicada), fallbacks sin LLM (keys / forma), prioridad
de ctx, edge de perfil vacío sin lanzar, y render a PDF + PPTX.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fase 4b — extiende el glosario clicable de AutomaticEDA (mecanismo ya probado
end-to-end con `entropia` en cat_distr) a tres capítulos más, siguiendo el
contrato sección 11 (glossary.add(key,label,def) + span [[term:KEY]]texto[[/term]]):
- correlacion: Pearson, Spearman, Cramér's V, razón de correlación (η) y la
corrección por comparaciones múltiples (FDR). Los métodos se marcan en el
intro (siempre presente); FDR se registra y marca solo cuando se emite su
resumen, para no dejar entradas de glosario sin aparición que las referencie.
- modelos: PCA, KMeans, coeficiente de silueta (silhouette), Isolation Forest y
la estandarización z-score. Cada término se registra dentro de la sección que
lo usa (tras su early-return), de modo que un término solo entra al glosario
cuando su sección realmente se renderiza.
- agregacion: agrupación (split-apply-combine / groupby) y tabla dinámica
(pivot), ambos en el intro siempre presente.
Solo se añaden los enganches de glosario: ningún cambio en la lógica de datos.
El texto visible es idéntico con o sin marcador (los renderers lo eliminan),
así que el layout de línea no cambia. Sin colector en ctx (render suelto) los
capítulos degradan y no marcan nada.
Tests: un test de glosario por capítulo verifica registro + marcado y la
degradación sin colector. Suite AutomaticEDA + render pipeline: 87 passed.
Golden titanic (run_models+series+llm): los 12 términos aparecen como entradas
del glosario en PDF (16 link annotations GOTO) y PPTX (15 saltos hlinksldjump).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
La leyenda de cada histograma del capítulo de distribuciones numéricas ya
reporta el valor de la media y la mediana; ahora también reporta el valor de
la desviación estándar σ. La entrada de leyenda de la banda ±1σ pasa a incluir
el número (±1σ (σ = X)) y, cuando la banda no puede dibujarse (sin media o
std<=0) pero σ es conocido, se añade una entrada de leyenda mediante un handle
proxy sin trazo, de modo que el valor de σ se reporta siempre.
No se altera el boxplot de Tukey ni el keep-together (Group) por columna.
Se añaden tests de la leyenda: golden (σ con valor junto a media y mediana),
edge sin banda (proxy) y edge sin std (no revienta). Bump 1.1.0 -> 1.2.0.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Lanzar `fleetclaude` estando ya dentro de una flota tmux viva abría una ventana
kitty nueva (y creaba un perfil/socket nuevo fleetN+1) en vez de mostrar la flota
en el pane actual. Causa: con $TMUX definido el launcher saltaba el `exec tmux
attach` y caía a la rama `setsid kitty`.
Cambio: cuando se invoca sin --new desde dentro de una flota fleetview viva (el
socket actual, derivado de $TMUX, tiene una sesión homónima con window 'console'),
se trae la TUI al contexto/pane actual (`fleetview show`, o `tmux select-window`
de la window console como fallback sin binario) y se retorna 0 antes de las ramas
kitty/wt.exe. Nuevo flag --new para forzar el comportamiento clásico (flota+ventana
nueva) aun dentro de tmux; pasar --session con un nombre distinto al perfil actual
equivale a --new implícito. Fuera de tmux el comportamiento es intacto (exec tmux
attach reutiliza la terminal).
Fix incidental: `local left_pane="" right_pane=""` (antes `local left_pane
right_pane` reventaba con "unbound variable" bajo `set -u` al reutilizar una sesión
existente, p. ej. con --reuse/--session sobre una flota viva).
Verificación e2e con sockets aislados fctest* (sin tocar la flota del humano):
golden (reuse, exit 0, kitty invariante), --new y --session-distinto (no reuse,
ruta ventana-nueva), fuera de tmux (salta reuse, ruta attach). bash -n limpio.
Docs: launch_fleetclaude.md (signature, params --new, ejemplo, cuando usarla,
gotchas, growth log v1.7.0) + /fleet show en .claude/commands/fleet.md.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
El capitulo OVERVIEW del motor AutomaticEDA mostraba "df.head no disponible"
porque ninguna fase de calculo poblaba las primeras filas crudas de la tabla.
- build_eda_render_ctx: nuevo bloque que muestrea SELECT * LIMIT head_n
(param nuevo head_n=10) y lo expone en ctx["head_rows"] como lista de
dicts fila. Estilo dict-no-throw: si la query falla, se omite la clave.
- profile_table: puebla prof["head_rows"] reusando _sample_rows (SELECT de
las columnas LIMIT 10) tras recalcular el type_breakdown. Asi el report
JSON sidecar tambien lo lleva y el capitulo lo recoge via profile aunque
no se construya el ctx.
- overview.py: la nota del DataTable de df.head ahora indica el total de
filas del dataset cuando se conoce ("primeras 10 filas de 891"). Bump
CHAPTER_VERSION 1.0.0 -> 1.1.0.
- overview_test.py (nuevo): golden (head via profile y via ctx, render PDF
+ PPTX muestran las filas reales, placeholder ausente), edge (sin
head_rows degrada a nota honesta sin romper, None/vacio devuelven None).
Verificado end-to-end con titanic: render_automatic_eda emite PDF + PPTX con
df.head visible (Braund/Cumings/Heikkinen + columnas) y sin el placeholder.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Mejoras transversales del motor de render (no del contenido de capítulos):
1. Fix negrita pisa texto (PDF): _place_rich_lines mide el ancho REAL de cada
span con las métricas de fuente del renderer (peso correcto) en vez del
grid de ancho medio; negrita y normal en la misma línea ya no se solapan.
2. Zebra striping: filas pares sombreadas (#f6f8fa) en DataTable (PDF + PPTX),
coherente al partir tablas largas (índice de fila lógico, no por página).
3. Keep-together: bloque Group nuevo; el renderer mide el grupo entero y lo
mueve completo a la página/slide siguiente si no cabe, y encoge la figura
(height_in) para dejar sitio a su título y texto. num_distr lo usa.
4. Caption siempre visible en toda figura PPTX (fallback al heading); la figura
reserva el alto de su caption para que ambos quepan en el mismo slide.
5. Portada construida al final (con resumen agregado del análisis vía
ctx['document_summary']) pero colocada primera por build_document.
6. Glosario: capítulo nuevo (último) + GlossaryCollector en ctx; los capítulos
registran términos y marcan apariciones con [[term:key]]...[[/term]]. Links
clicables reales: PDF (PyMuPDF, link GOTO) y PPTX (slide-jump nativo).
Enganchado "entropía" en cat_distr como ejemplo end-to-end.
Funciones reutilizables delegadas a fn-constructor (tag eda):
- add_pdf_internal_links_py_datascience (PyMuPDF)
- pptx_link_run_to_slide_py_datascience (slide-jump)
Contrato docs/automatic_eda_contract.md actualizado (§1/§3/§5 + §11 nueva) con
la API de glosario, keep-together y zebra para la siguiente fase. PyMuPDF
declarado en pyproject. Suite verde (90 tests); golden titanic verificado.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- build_eda_render_ctx: arma ctx (raw_numeric, timeseries_raw, geo_points, db_path+table) desde tabla DuckDB
- pipeline render_automatic_eda: perfila + ctx + build_document -> PDF + PPTX (11 capitulos poblados)
- profile_table: flag emit_automatic emite el report AutomaticEDA (PDF+PPT) sin romper render_eda_pdf
- text_layout: render real de **negrita** en PDF y PPTX
- .claude/commands/eda.md actualizado
Los 4 capitulos que degradaban (modelos/timeseries/geospatial/agregacion) ahora salen POBLADOS end-to-end.
Actualiza el flujo del comando para que un EDA completo emita el informe
AutomaticEDA en sus dos formatos (PDF A5 móvil + PPTX 16:9) con los 11 capítulos
poblados, vía render_automatic_eda (o profile_table(emit_automatic=True)). El PDF
legacy (emit_pdf/render_eda_pdf) queda como salida independiente opcional.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Conecta el motor AutomaticEDA con los datos crudos para que los 4 capítulos
dependientes de ctx (modelos, timeseries, geospatial, agregacion) salgan
POBLADOS en vez de degradar a una nota.
- build_eda_render_ctx (datascience, impure, dict-no-throw): dado db_path+table
y el TableProfile agregado, construye el ctx con los datos crudos que el
perfil no incluye: raw_numeric {col:[float|None]} alineado por fila (modelos /
geospatial), timeseries_raw {time_col,t,series} vía extract_timeseries_raw,
geo_points {lats,lons} desde el par lat/lon detectado, y db_path/table para el
groupby/pivot push-down de agregacion. Muestrea con LIMIT (no trae la tabla
entera a RAM). Compone detect_time_column / extract_timeseries_raw /
detect_latlon_columns / duckdb_query_readonly (imports lazy para evitar ciclo).
- render_automatic_eda (pipeline): one-shot perfil -> ctx -> PDF + PPTX con los
11 capítulos poblados; devuelve rutas + manifest de versiones por capítulo.
- profile_table: flag aditivo emit_automatic=True emite el AutomaticEDA PDF+PPTX
además del flujo legacy (emit_pdf/render_eda_pdf intacto). Nuevas claves de
retorno aeda_pdf_path / aeda_pptx_path / aeda_manifest_path.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
El render de Markdown del motor AutomaticEDA quitaba los marcadores **negrita**
sin aplicar estilo. Ahora los spans **bold**/__bold__ se renderizan en negrita
real, de forma aditiva y sin romper el anti-corte:
- text_layout.py: parse_inline_bold() tokeniza spans preservando el texto
visible (== strip_inline_md) y wrap_rich() envuelve por palabras a max_chars
conservando el flag de negrita por segmento (la anchura visible no cambia, así
que la paginación es idéntica).
- render_pdf_impl.py: _place_rich_lines() dibuja cada segmento con su fontweight
avanzando x por el mismo grid de caracteres que usa el wrap (párrafos+bullets).
- render_pptx_impl.py: _add_rich_text() usa runs nativos de python-pptx con
font.bold por segmento (negrita real de PowerPoint).
- bold_render_test.py: helpers puros (no-overflow, bold preservado, marcadores
desbalanceados) + e2e que abre el .pptx y confirma un run con font.bold True.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Capítulo nuevo build_timeseries(profile, ctx) -> Chapter|None del motor
AutomaticEDA. Cuando la tabla tiene columna de fecha/datetime, grafica la
evolución de cada columna numérica por periodo (valor agregado + conteo de filas)
y los paneles de descomposición STL y autocorrelación (ACF), con el análisis de
la serie: estacionariedad (ADF+KPSS), autocorrelación (Ljung-Box), fuerzas de
tendencia/estacionalidad (Hyndman) y la transformación sugerida (retornos o
diferencias) para evitar correlaciones espurias. Sin columna temporal devuelve
None. Consolida series OHLC casi idénticas en un único gráfico conservando el
análisis de cada columna.
La serie cruda llega por ctx['timeseries_raw'] (mismo patrón que modelos con
raw_numeric); las figuras son perezosas (Figure.make) y el paginador del núcleo
garantiza no-corte en PDF y PPTX. CHAPTER_VERSION 1.0.0.
Cubre los MUST del diseño (report 2043): MUST-9.1 (línea valor-vs-tiempo + conteo
por periodo), MUST-9.2 (paneles STL + ACF), MUST-9.3 (perfil datetime +
consolidación OHLC).
Funciones nuevas del registry (grupo eda), delegadas a fn-constructor, no inline:
- detect_time_column (pure): detecta la columna temporal y las numéricas
- profile_datetime (pure): rango/frecuencia/regularidad/huecos de la fecha
- resample_timeseries (pure): agrega la serie por periodo + conteo
- extract_timeseries_raw (impure): lee la serie cruda ordenada de DuckDB/PG
Verificación: 69 tests verdes (capítulo 9 + funciones 28 + núcleo/renderers);
golden real sobre seattle-weather (estacional) y aapl (OHLC) con PDF+PPTX sin
cortar nada (cols_cortadas=[]).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Capítulo nuevo (siempre presente cuando hay categóricas agrupables) que analiza la
tabla por grupos: stats de numéricas por grupo, tablas dinámicas (pivot) y gráficos
de barras desde cero. Obtiene los datos por ctx['aggregations'] precomputado o en
vivo vía push-down (ctx['db_path']+table), siguiendo el patrón de chapters/modelos.py.
Degrada a None cuando no hay categóricas; emite los bloques del modelo (DataTable,
Markdown, Figure) para que el paginador del núcleo no corte nada en PDF ni PPTX.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Cuatro funciones nuevas del grupo eda que nutren el capítulo AGREGACION:
- select_groupby_keys (pure): elige categóricas agrupables + numéricas medida desde el TableProfile.
- groupby_stats_duckdb (impure): GROUP BY push-down en DuckDB (count/mean/median/std/min/max por grupo).
- pivot_table_duckdb (impure): pivot A×B push-down, limitado a top filas/cols para no cortar.
- suggest_aggregations_llm (impure): el LLM elige las agregaciones interesantes con fallback determinista.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Capítulo nuevo chapters/geospatial.py (CHAPTER_VERSION 1.0.0). Cuando el dataset
tiene un par de coordenadas, dibuja un scatter geográfico en proyección
equirectangular (la escala respeta la latitud para no estirar la longitud) y
analiza la extensión: bounding box, centroide, span, conteo por zona/país,
hemisferios y una interpretación. Cuando NO hay coordenadas, build_geospatial
devuelve None y el capítulo se omite.
Sigue el contrato de capítulos (firma build_<id>(profile, ctx) -> Chapter|None,
lectura defensiva, nunca lanza) y el patrón de modelos/num_distr: delega el
cálculo a las primitivas puras del registry (detect_latlon_columns,
analyze_geo_extent, build_geo_scatter) y solo dibuja la figura matplotlib de
forma perezosa. Las coordenadas crudas llegan por ctx['geo_points'] o
ctx['raw_numeric'] (como modelos lee raw_numeric); sin ellas, degrada con un
bounding box aproximado de numeric.min/max y una nota honesta.
Anti-cortes: usa DataTable/KVTable/Figure/Markdown del modelo, que el paginador
parte sin cortar. Test self-contained con golden + 6 edges + anti-cut (nombres
largos + 2100 puntos en varias regiones renderizan a PDF y PPTX sin truncar).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Tres funciones puras nuevas del dominio datascience (tags eda + geospatial) que
sostienen el capítulo GEOSPATIAL del AutomaticEDA, delegadas a fn-constructor:
- detect_latlon_columns: identifica el par (lat, lon) por nombre de columna +
rango de valores ([-90,90] / [-180,180]) desde profile['columns']. Devuelve
{lat_col, lon_col, confidence, reason}. 9 tests.
- analyze_geo_extent: bbox, centroide, span haversine, conteo por zona/país
(lookup offline con bounding boxes embebidos, KISS sin geopandas) y
hemisferios. 7 tests.
- build_geo_scatter: prepara los puntos del scatter en orden [lon, lat] con
downsampling determinista por paso fijo + aspect equirectangular 1/cos(lat)
clampado. 6 tests.
Registradas en datascience/__init__.py. Todas pure, params_schema completo,
.md autosuficiente (Ejemplo + Cuando usarla + Gotchas).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Capítulo cat_distr del motor AutomaticEDA: distribuciones categóricas con
explicación de entropía de Shannon, métricas de cardinalidad por columna
(valores distintos, % distintos, total de filas, valores únicos, entropía y
su máximo log2(k) + normalizada), tabla top-k y un donut de las categorías
más comunes (top-k + «Otros»). Marca columnas id-like y dominadas.
Delegadas a fn-constructor (grupo eda):
- categorical_cardinality_block: deriva métricas de cardinalidad/entropía.
- categorical_top_pie_figure: figura donut top-k + «Otros», leyenda lateral.
Defensivo (dict-no-throw): None si no hay columnas categóricas; normaliza
mode_pct a escala 0-100 (summarize_categorical lo emite como fracción).
Tablas vía DataTable y figura perezosa: el paginador del núcleo garantiza
no-corte en PDF y PPTX. Tests: golden + edge (sin categóricas) + anti-corte
(label largo / muchas columnas) en ambos renderers.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Suite self-contained (perfil sintético + un golden, sin DuckDB):
- golden: build_analisis_llm devuelve el Chapter y el documento entero renderiza
a PDF y PPTX con resumen, análisis sugeridos, limpieza y una columna del
diccionario presentes.
- orden: el capítulo queda inmediatamente después de `overview`.
- edges: profile sin bloque `llm` (o None/{}/malformado/llm vacío) -> None sin
lanzar; fallback a ctx['llm'].
- anti-cortes: diccionario de 40 filas + sugerencia de limpieza de ~150 chars se
reparten en varias páginas/slides sin perder ninguna fila ni palabra.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Nuevo capítulo `analisis_llm` del motor AutomaticEDA. Consume el bloque `llm`
que `eda_llm_insights` (grupo eda) ya deja en el TableProfile —no llama al LLM
ni recalcula— y lo convierte en bloques del modelo de documento para que se
renderice sin cortarse en PDF ni PPTX:
- Resumen de la tabla y significado de una fila -> bloques Markdown (el
renderer los envuelve a líneas completas, nunca pierde texto).
- Diccionario de datos y PII -> DataTable (el paginador parte por filas
repitiendo cabecera y envuelve celdas largas dentro de su columna).
- Análisis sugeridos y limpieza sugerida -> listas de viñetas Markdown; cada
entrada es una línea completa que el renderer envuelve, nunca trunca.
Lectura defensiva (.get) en todo; devuelve None si el profile no trae bloque
`llm` (p.ej. profile_table sin run_llm) para omitir el capítulo.
MUST-3.2 (report 2043): se mueve `analisis_llm` en CHAPTER_ORDER a la posición
inmediatamente posterior a `overview`, como pidió el usuario ("va junto al
overview").
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Implementa chapters/correlacion.py siguiendo el contrato de capítulos:
build_correlacion(profile, ctx) -> Chapter|None, CHAPTER_VERSION="1.0.0".
Consume profile['correlations'] (salida de association_matrix del grupo eda,
sin recalcular estadística) y emite, como bloques del modelo:
- Matriz de asociación (Figure/heatmap perezoso, RdBu_r, con signo en num-num
y magnitud en métricas mixtas; etiquetas ordenadas por conectividad y
recortadas a las 16 más conectadas para legibilidad).
- TOP de pares POSITIVOS y TOP de pares NEGATIVOS en dos DataTable separadas
(los negativos son por construcción num-num, único método con signo), con
método, valor, p-valor corregido (FDR) y significancia.
- Resumen FDR (multiple_testing) + leyenda de métodos.
- Aviso de espuriedad por niveles no estacionarios (Granger-Newbold) cuando el
profile lo marca.
Lectura defensiva en todo (None si no hay pares; nunca lanza). Anti-cortes:
sólo bloques del modelo, el paginador parte tablas repitiendo cabecera y escala
la figura entera.
Test self-contained (5 casos): golden a nivel de bloques + golden render
PDF/PPTX, edge sin pares -> None, edge sólo positivos -> nota honesta, y
anti-corte con matriz ancha + etiquetas largas (dato íntegro a nivel de bloque,
ambos renderers sin reventar).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Añade el capítulo de calidad de datos al motor AutomaticEDA, siguiendo el
contrato de capítulos (build_calidad(profile, ctx) -> Chapter | None,
CHAPTER_VERSION). El capítulo responde lo que pidió el usuario, en español y
en formato de tabla:
- Intro "Cómo se calcula la calidad": explica los tres criterios y sus pesos
(completitud 50%, validez 30%, consistencia 20%) antes de cualquier número,
más una KVTable de resumen a nivel tabla (calidad global y agregados).
- Tabla "Scores por columna": score total más su desglose en completitud /
validez / consistencia, ordenada de peor a mejor.
- Tabla "Problemas detectados": los issues en español por columna, separados de
los flags de tipo. Cuando no hay problemas, una nota honesta.
Registry-first: el desglose y los issues NO se recalculan aquí; se consumen de
la función pura del registry column_quality_score (grupo eda), que ya deriva
{score, completeness, validity, consistency, issues} del ColumnProfile. El
capítulo es render-only y compone bloques del modelo; los renderers paginan las
tablas (parten por filas repitiendo cabecera) y envuelven celdas largas, de modo
que nada se corta en PDF ni en PPTX. La lista de issues por celda se acota a
160 caracteres con "(+N más)" para que una fila nunca crezca más que una página.
Test self-contained (sin DuckDB): golden con desglose + issues ES, edges
(None/{}/sin columnas -> None; perfil limpio -> nota), y anti-cortes (perfil de
22 columnas con nombres largos renderizado a PDF y PPTX: el nombre completo
sobrevive al envolverse, sin marcador de truncado).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Capítulo NUM DISTR del motor AutomaticEDA. Por cada columna numérica emite,
como una sola Figure indivisible de dos ejes compartiendo X, un histograma con
la media (línea roja discontinua), la mediana (línea verde continua) y la banda
±1σ dibujadas como referencias, y un boxplot de Tukey debajo (caja P25–P75,
bigotes a 1,5·IQR, marca de valores fuera de las vallas). Una nota por columna
traduce el distribution_type a lenguaje llano (MUST-4.1/4.2/4.3 del report 2043).
Consume el profile del grupo eda sin recalcular: el histograma usa los bins
{lo,hi,count} de describe_numeric y las vallas del boxplot las deriva la función
pura build_boxplot_stats_py_datascience. Lectura defensiva: sin columna numérica
devuelve None; profile None/{} no lanza. Test self-contained: golden + edges +
anti-corte (8 columnas no cortan en PDF ni PPTX).
Implementa chapters/modelos.py (build_modelos / CHAPTER_VERSION) consumiendo
profile['models'] {pca,kmeans,outliers,normality} de run_eda_models. Render
markdown estructurado con bloques anti-corte:
- Intro de normalizacion z-score: por que se estandariza antes de PCA/KMeans (MUST-8.3).
- PCA: scree plot (varianza explicada + acumulada, un solo eje Y) + tablas de
varianza y cargas principales (SHOULD-8.4).
- Segmentacion KMeans: scatter PCA coloreado por cluster con centroides, en su
propia pagina/slide (MUST-8.1); tabla de tamaños; micro-analisis LLM por
cluster con titulo, cada entrada indivisible (MUST-8.2).
- Isolation Forest: explicacion de la deteccion multivariante de outliers y del
umbral + conteos (MUST-8.3).
- Normalidad: tabla por columna (Jarque-Bera / D'Agostino / Shapiro), pagina sola.
El scatter coloreado y los titulos LLM no estan en el TableProfile, asi que el
capitulo los toma de ctx (cluster_projection precomputado, o raw_numeric para
calcular project_clusters_2d en vivo, o cluster_titles/run_cluster_llm para el
micro-analisis), igual que overview lee head_rows; degrada honesto con una Note
cuando faltan. Devuelve None si el profile no trae bloque models renderizable.
Tests self-contained (sin DuckDB/sklearn/LLM/red): golden PDF+PPTX, edges
(profile None/vacio/insuficiente, kmeans sin proyeccion), anti-corte (tabla de
normalidad de 40 columnas parte repitiendo cabecera sin perder ninguna). 8/8.
Suite del nucleo render_automatic_eda_pdf/pptx sigue verde.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
project_clusters_2d (pura): PCA(2)+KMeans sobre el MISMO subset estandarizado,
devolviendo proyeccion 2D y labels alineados por fila + centroides en espacio PCA
+ perfiles de cluster desestandarizados. Es la pieza que garantiza la alineacion
points<->labels que pca_explained y kmeans_segments no cubren (estandarizan por
separado y kmeans descarta los labels). Habilita el scatter PCA coloreado por
cluster (MUST-8.1).
describe_clusters_llm (impura): micro-analisis LLM de los clusters en una sola
llamada a ask_llm (grupo claude-direct), devuelve titulo + descripcion por cluster
con degradacion dict-no-throw a titulos genericos si el LLM no responde (MUST-8.2).
Ambas re-exportadas en datascience/__init__.py. Tests: 6/6 y 9/9 (sin red).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Reorganizacion de dev/issues en subcarpetas (completed/, cpp/, gamedev/,
kanban/, trading/, imagegen/, matrix/) y cambios acumulados en cmd/fn/pyrunner,
.claude/commands y settings. Trabajo de otro LLM/sesion, commiteado a peticion
del usuario para desbloquear el working tree. Excluido logs/ardour_mcp_server.log (ruido).
Añade docs/automatic_eda_contract.md: documento autoritativo y autosuficiente
para que otros agentes escriban capítulos en paralelo (NUM DISTR, CAT DISTR,
CALIDAD, CORRELACIÓN, MODELOS, ANÁLISIS LLM, TIMESERIES, GEOSPATIAL,
AGREGACIÓN). Cubre el modelo de bloques/capítulo exacto, la firma
build_<chapter>(profile, ctx) -> Chapter|None, la declaración de
CHAPTER_VERSION, dónde colocar el módulo, cómo se registra el orden del
documento, qué claves del profile consume cada capítulo, las claves nuevas que
la fase de cálculo debe añadir (head_rows, columns[].examples) y un ejemplo
completo del capítulo de referencia OVERVIEW.
Enlaza las dos funciones nuevas y el contrato desde docs/capabilities/eda.md y
actualiza el recuento del grupo eda en el índice de capabilities.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Introduce la capa intermedia entre el contenido de un EDA y su formato de
salida. Un documento es una lista de capítulos versionados; cada capítulo es
un conjunto ordenado de bloques (heading, markdown, kv_table, data_table,
figure, image, caption, note) independientes del formato.
Núcleo (paquete de soporte python/functions/datascience/automatic_eda/):
- model.py: dataclasses de bloques + Chapter, normalizadores defensivos
(aceptan dataclass o dict, nunca lanzan), ENGINE_VERSION y el manifiesto
por capítulo (automatic_eda_manifest.json).
- text_layout.py: medición/wrapping por rejilla de caracteres compartida.
- chapters_registry.py: CHAPTER_ORDER pre-declarado + build_document con
auto-discovery de capítulos por convención (permite añadir capítulos en
paralelo sin editar el registro).
- render_pdf_impl.py: paginador A5 retrato móvil que MIDE cada bloque y nunca
corta: texto a líneas completas, tablas largas partidas por filas repitiendo
cabecera, figuras/imágenes escaladas para caber enteras. Pie versionado por
capítulo.
- render_pptx_impl.py: mismo principio sobre slides 16:9 (continúa en slide
"(cont.)"; tablas repiten cabecera; figuras exportadas a PNG escaladas).
- chapters/portada.py y chapters/overview.py: capítulos de referencia. Portada
con nombre, rótulo Automatic-EDA, fuente, almacenamiento (inferido de
source), fecha europea, filas×cols, descripción, granularidad y calidad con
criterios. Overview con df.head (placeholder honesto si falta head_rows),
diccionario de columnas (tipo/nulos/ejemplos) y describe numérico.
Funciones públicas del registry (grupo eda, dict-no-throw):
- render_automatic_eda_pdf / render_automatic_eda_pptx: aceptan capítulos o un
TableProfile (construyen los capítulos con build_document) y escriben el
manifiesto. Aditivas — no reemplazan render_eda_pdf.
Tests self-contained (sin DuckDB) para ambos renderers: golden (portada +
overview), partición de tablas largas repitiendo cabecera, no-corte de celdas
y markdown largos, profile None/{} válido de 1 página/slide, y error path en
directorio no escribible. 23 tests verdes (incluye los previos de
render_eda_pdf, intactos).
Dependencia nueva python-pptx>=1.0.2 declarada en python/pyproject.toml.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
La ruta ventana-nueva ya no asume kitty. Elige terminal según el host, sin
config por PC: kitty si está instalado y hay display ($DISPLAY/$WAYLAND_DISPLAY);
si no, en WSL abre Windows Terminal (wt.exe) ejecutando
`wsl.exe [-d $WSL_DISTRO_NAME] -- bash -lic 'tmux ... attach'`.
Arregla el síntoma "se lanza la flota pero no se ve": en WSL sin kitty la sesión
tmux se creaba pero ninguna ventana la mostraba. Mismo `fleetclaude` funciona en
un PC con kitty y en otro WSL sin kitty.
wt.exe se lanza desde un subshell con cwd /mnt/c para evitar el warning por cwd
UNC (\\wsl.localhost\...). El path de attach interactivo (terminal real fuera de
tmux) queda intacto. Bump 1.5.0 -> 1.6.0.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Los 14 hallazgos H1-H14 del benchmark estan corregidos y verificados con re-corrida.
Commits: caf8c25d (S), c4cff5ed (render H4/H9), e142ef02 (comportamiento H2/H3/H6/H7/H8/H10/H11).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Ronda 4 (verificada con re-corrida sobre los datasets afectados):
- H2: stl_decompose deriva periodo de la frecuencia del indice (seattle period=365
seasonal_strength=0.84; fin del period=2 espurio)
- H3+H10: infer_fk por senal de nombre (<X>Id->X.<X>Id) + excluir no-clave -> chinook
111->9 FK, todas reales, cero absurdas, 16-27x mas rapido; base intacta (flag off->111)
- H6: association no computa eta2 si cardinalidad~=n (Ticket-Fare espurio fuera)
- H7: id secuencial monotono excluido de correlacion y PCA/KMeans (PassengerId fuera)
- H8: correlacion de series no estacionarias marcada espuria / sobre retornos
- H11: distribution_type usa modos/cardinalidad/normalidad (quality->discrete)
- 66 tests verdes
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Promueve el caso 1 del report 0217 (animacion de sprites de personaje) a un
pipeline one-shot: de un prompt de personaje a un sprite sheet + GIF/WEBP en loop,
frame-by-frame dirigido por pose (ControlNet OpenPose + seed fija + Rembg) con cada
frame pixelizado a NxN RGBA.
Nuevas funciones reutilizables (issue 0087, crecimiento por composicion):
- comfyui_walk_cycle_oneshot (pipeline): orquesta poses -> generacion -> pixelizado
-> ensamblado. No-throw, salta frames que fallan. Modo openpose (esqueletos reales)
con fallback prompt-pose.
- render_openpose_walk_skeletons: dibuja N esqueletos OpenPose COCO-18 del walk cycle
(el insumo que el report 0217 marco como faltante).
- comfyui_pixelize_sprite_png: PNG existente -> NxN RGBA pixel-art real (compone
crop_to_content + pixeloe_downscale + comfyui_pixelize_image).
- assemble_animated_sprite: frames RGBA -> sprite sheet horizontal + WEBP/GIF loop.
- comfyui_build_walk_cycle_workflow (pura): grafo API del workflow animado para la UI
(ControlNet OpenPose -> KSampler xN seed fija -> ImageBatch -> Rembg -> SaveAnimatedWEBP).
Verificado en GPU: GIF/WEBP de caballero andando, 4 frames 32x32 (y 64x64) RGBA con
fondo transparente y 16 colores, identidad de silueta consistente, piernas que cambian.
Metodo de poses usado: OpenPose real (sin fallback). Evidencia en report 0221.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Arregla los dos defectos reportados del pipeline comfyui_pixelart_real_oneshot:
el sujeto salía diminuto respecto al frame y siempre traía fondo (sin opción de
transparencia).
Causa raíz: comfyui_pixelize_image hacía convert("RGB") y descartaba el alpha;
comfyui_build_pixelart_workflow no inyectaba rembg (a diferencia de sus hermanos
item_icon/enemy_creature); y no había ningún paso de auto-crop al contenido.
Orden correcto del pipeline ahora:
generar (rembg) -> autocrop al bbox + cuadrar -> downscale (alpha aparte por
PixelOE) -> cuantización alpha-aware -> PNG RGBA transparente.
Piezas:
- comfyui_pixelize_image (1.1.0): keep_alpha/alpha_threshold. Con RGBA cuantiza
solo el RGB (fondo transparente relleno con la moda del sujeto, fuera de la
paleta) y preserva/binariza el alpha aparte. RGB sin alpha intacto.
- crop_to_content (NUEVA, pura PIL): bbox del contenido (alpha o diff-fondo) ->
recorta -> margen -> cuadra centrando. No-throw; imagen vacía -> copia intacta.
- comfyui_build_pixelart_workflow (1.1.0): transparent=True + rembg_model.
Inyecta nodo Image Rembg tras VAEDecode (patrón de item_icon).
- comfyui_pixelart_real_oneshot (1.1.0): transparent + autocrop + crop_pad_ratio
+ rembg_model. Recombina el alpha aparte tras PixelOE (trabaja en RGB). Campos
nuevos: has_alpha, autocrop_applied.
Verificado en GPU (knight 64px): RGBA con 4 esquinas alpha==0, contenido cubre
88% del frame (antes 48%), 16 colores, 64x64. 32 tests offline en verde.
Report: reports/0218-2026-06-28-pixelart-sprite-fix.md
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Materializa el metodo ganador del report 0215: generar a alta-res con SDXL +
LoRA SDXL_pixel-art, downscale contrast-aware con PixelOE (engine=pixeloe para
sprites/personajes) o nearest (tiles), y cuantizacion dura con
comfyui_pixelize_image (16 colores libres o paleta fija pico-8/nes/game-boy).
- pixeloe_downscale_py_ml: downscale contrast-aware via lib pixeloe con bridge
de interprete (la lib vive en el venv de ComfyUI, no en el del registry).
No-throw, fallback limpio si pixeloe no disponible.
- comfyui_pixelart_real_oneshot_py_pipelines: one-shot que compone build_pixelart
+ submit + wait + fetch + pixeloe_downscale + pixelize_image. Fallback
automatico pixeloe->nearest. Sweet-spot 64px personajes, 32px iconos.
Verificado por PIL: personaje 64x64=16 colores, icono 32x32=16 colores (vs ~33k
de la imagen de difusion cruda). 100% grid duro + outline nitido.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Loop tipo GAN sin entrenar: genera con un builder del registry, juzga con el
panel multi-juez (comfyui_judge_image) y, si no alcanza el umbral, refina (nueva
seed, mas steps/cfg, prompt corregido con el feedback del juez via ask_llm) y
regenera hasta converger (verdict 'good') o agotar max_iters. Devuelve siempre
la mejor candidata por score (best-of-N), nunca lanza excepcion cruda.
Compone comfyui_submit_workflow + comfyui_wait_result + comfyui_fetch_output_image
+ comfyui_judge_image + ask_llm. Filtra kwargs por inspect.signature para ser
robusto entre builders. Caso HUD verificado: itera iter0 bad -> iter1 good.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- gamedev-2d.md: el header decía '31 builders + 5 de apoyo' (=36); inventario real = 47
funciones (36 builders: 31 de generación + 5 de transformación; 11 de apoyo: post-proceso,
puente a Godot, style presets, pipelines one-shot).
- comfyui-skill.md: añade bloque de tamaño del grupo (17 funciones tag comfyui-skill); la
página no tenía conteo interno (el 11 obsoleto vivía solo en INDEX.md).
- INDEX.md: gamedev-2d 36→47 y comfyui-skill 11→17, con descripciones actualizadas.
Cierra el drift residual señalado en el report 0210.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Cubre 15 funciones del grupo comfyui (+ las 4 de comfyui-judge) que no tenian
test, con tests offline (sin red, sin GPU, sin servidor ComfyUI):
- 5 builders puros gamedev-2d: build_asset_variant, build_directional_sprite,
build_inpaint_asset, build_outpaint_asset, build_sprite_from_sketch (estructura
del workflow en API format + cableado + determinismo + error paths).
- 3 impuras offline via PIL/stdlib: build_grid, flatten_alpha_on_color,
read_png_metadata (PNGs reales en tmp, error paths).
- 4 de comfyui-judge: score_aesthetic y score_clip_alignment por sus guards
previos al subproceso torch; judge_image (panel) y critique_image_llm con la
dependencia pesada monkeypatcheada.
- 3 que componen otras funciones: resolve_workflow_deps, import_workflow_json,
extract_recipe_from_png (dependencia de red monkeypatcheada o fallback offline).
Cada .md actualizado con tested: true + test_file_path + tests.
Cobertura del grupo comfyui (tag plano): 79 -> 90 con test (47 -> 36 sin).
comfyui-judge: 0/4 -> 4/4. pytest: 101 passed; carpeta ml/tests: 376 passed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- comfyui.md: bloque de tamaño real del grupo (126 funciones tag comfyui: 63 puras,
50 impuras, 13 pipelines) con punteros a los sub-grupos (comfyui-skill, comfyui-styles,
comfyui-judge, gamedev-2d). Corrige la firma corta de build_flux (variant/steps=None/
weight_dtype='default' + camino custom-advanced) que arrastraba drift del report 0205.
Añade sección Styles con las 5 funciones del sub-grupo.
- comfyui-styles.md (NUEVA): página madre del sub-grupo de estilo (catálogo WAS +
style presets gamedev), tabla de las 5 funciones, ejemplos canónicos alineados con
los retornos reales y fronteras.
- comfyui-overview.md: añade audio (05b) y styles (04b) al mapa cross-grupo y a la tabla
resumen; referencia las nuevas páginas madre comfyui-styles y gamedev-2d.
- INDEX.md: comfyui 29→126 con descripción actualizada; nueva fila comfyui-styles.
- comfyui_build_parallax_background_workflow.md: añade sección ## Ejemplo lanzable
(el indexer extrae example del cuerpo, no del frontmatter) — cobertura del grupo
pasa a 126/126 con ejemplo.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
El generador de runner de fn run (cmd/fn/pyrunner.go::generatePyRunner)
parsea la signature de la funcion desde el frontmatter del .md y emite
`<param> = _args[i]` por cada parametro posicional. Cuando la firma es
keyword-only (`def f(*, ...)`), el `*` se trata como un nombre de parametro
y genera la linea invalida `* = _args[0]`, que rompe el runner con
`SyntaxError: invalid syntax` antes de ejecutar la funcion.
Se quita el separador keyword-only (`*,`) de la firma — tanto en la `def`
del .py como en el campo `signature:` del .md (la fuente que lee el
indexer y el runner) — convirtiendo los parametros keyword-only en
parametros normales con su mismo default. No cambia nombres, defaults ni
comportamiento: las llamadas con keyword siguen siendo validas.
Afecta a 5 funciones detectadas en el report 0208 §3.3, todas con
SyntaxError reproducido via `fn run <id>`:
- comfyui_fetch_civitai_image_meta
- comfyui_load_skill
- comfyui_save_skill
- comfyui_import_workflow_png
- comfyui_list_skills
Se completa ademas el fix de comfyui_interrupt_queue: el commit 643ebfb8
quito el `*,` del .py pero dejo el `*,` en el campo `signature:` del .md,
que es justo lo que lee el runner — por eso `fn run comfyui_interrupt_queue`
seguia fallando. Aqui se corrige el .md.
Verificado: tras el cambio las 6 despachan sin SyntaxError (las 4 con
primer arg requerido devuelven el `missing required arg` esperado del
runner; list_skills e interrupt_queue ejecutan `ok:true`). Tests
existentes verdes (comfyui_fetch_civitai_image_meta_test.py +
tests/test_comfyui_interrupt_queue.py: 8 passed).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Alinea la funcion al contrato de control de cola (punto 3 del roadmap ComfyUI):
- firma keyword-only: clear_pending (vacia pendientes con POST /queue {clear:true}) + timeout
- output {ok, interrupted, cleared, queue_remaining, error}; GET /queue al final
- no lanza en fallo de red: degrada a {ok:False, error}
- test con mock HTTP local (golden + clear + cola vacia + error path), 4/4 verde
- .md autosuficiente con gotchas + capability growth log
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Higiene del grupo comfyui sobre las 5 funciones de la sesión:
comfyui_build_audio_workflow, comfyui_fetch_output_audio,
comfyui_build_flux_workflow, comfyui_list_templates, comfyui_extract_template.
- Tests nuevos para list_templates y extract_template (lógica pura: localización
del intérprete, error-path sin el paquete instalado, contrato del dict; golden
condicional con skip si no hay ComfyUI con comfyui-workflow-templates). 10 tests,
todos verdes.
- comfyui_list_templates.md / comfyui_extract_template.md: tested true + tests +
test_file_path.
- Fix drift de test_file_path en comfyui_fetch_output_audio.md (apuntaba a un
*_test.py inexistente; corregido a tests/test_*.py). Elimina el WARN de fn index.
- docs/capabilities/comfyui.md: subsecciones Audio (ACE-Step) y Templates oficiales.
- docs/capabilities/comfyui-overview.md: sección 05b audio, fetch_output_audio en
Outputs, Templates oficiales en Workflows I/O. (flux ya estaba documentada.)
fn index limpio (las 5 sin WARN); sin drift nuevo en fn doctor uses-functions.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Builder puro que arma el workflow ComfyUI de Flux en API format con el camino
canonico custom-advanced (UNETLoader + DualCLIPLoader[flux] + VAELoader ->
RandomNoise + KSamplerSelect + BasicScheduler -> BasicGuider ->
SamplerCustomAdvanced -> VAEDecode -> SaveImage).
- variant 'schnell' (~4 pasos, sin FluxGuidance) o 'dev' (~20 pasos, con
FluxGuidance), con unet y steps por defecto por variante.
- Parametro 'available' opcional valida los modelos contra /object_info y lanza
FileNotFoundError claro (que falta + carpeta) sin romper la pureza.
- width/height/seed/guidance/prefijo parametrizables.
- 11 tests unitarios (class_types schnell vs dev, defaults por variante, error
path, determinismo). Verificado con generaciones reales (schnell 1024 y 768,
dev 768x1024) que producen PNG en disco.
Capitaliza el descubrimiento y extraccion de los workflow templates oficiales que
trae el paquete pip comfyui-workflow-templates 0.10.3 (los del menu Browse
Templates del frontend de ComfyUI). Hasta ahora no habia forma programatica de
listarlos ni extraer su grafo de nodos.
- comfyui_list_templates: lista los 451 templates reales (nombre, bundle/categoria,
path, n_nodes, node_types). Filtra las ~16 entradas index* no-workflow.
- comfyui_extract_template: extrae el grafo + class_types de un template por nombre;
to_api convierte a API format reusando comfyui_import_workflow_json.
Desde la 0.10.x el paquete es multi-bundle y ya no expone una carpeta templates/
unica; ambas funciones usan la API oficial comfyui_workflow_templates_core via el
interprete de ComfyUI. node_types aplana subgrafos y descarta los UUID de instancia.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Renombra los 13 checkpoints/diffusion models de ComfyUI prefijando la
categoría al inicio del nombre, para que en el dropdown de carga el usuario
distinga de inmediato imagen/vídeo/3D y no cargue un modelo en el nodo
equivocado. Misma operación que se hizo con los LoRAs (report 0197) pero
sobre los modelos.
Clasificación:
- IMG_: dreamshaper_8, juggernaut_xl_v11, v1-5-pruned-emaonly-fp16,
flux1-dev-fp8-e4m3fn, flux1-schnell-fp8-e4m3fn
- VIDEO_: svd, ltx-video-2b-v0.9.5, wan2.1_t2v_1.3B_fp16
- 3D_: stable_zero123, sv3d_p, hunyuan3d-dit-v2-mini, hunyuan3d-dit-v2-mv,
hy3dgen/hunyuan3d-dit-v2-0-fp16 (mantiene subcarpeta)
A diferencia de los LoRAs aquí solo se PREFIJA la categoría conservando el
nombre completo (versión/arquitectura). Archivos físicos renombrados en
~/ComfyUI/models/checkpoints, /mnt/2tb/comfyui_models/{checkpoints,
diffusion_models} y la subcarpeta hy3dgen/. Mapa de reversión en
~/ComfyUI/models/checkpoints/_ckpt_rename_map.json.
Actualiza todas las refs (ckpt_name/unet_name + defaults + prosa) en los
builders gamedev/vídeo/3D, style presets, pipelines, tests y los workflows
de ComfyUI. Arregla de paso el default roto de comfyui_text_to_3d_oneshot
(apuntaba a v1-5-pruned-emaonly.safetensors inexistente; ahora al real
IMG_v1-5-pruned-emaonly-fp16.safetensors).
No tocados (justificado): repo-paths de HuggingFace en comfyui_install_3d_model
(<repo>/model.fp16.safetensors son rutas de descarga, no nombres de dropdown)
y el mock de stable-diffusion.cpp en test_genconfig_to_sdcpp_args.
Verificado: dropdowns CheckpointLoaderSimple + UNETLoader listan los nombres
con prefijo; 1 generación real con IMG_juggernaut_xl_v11 (node_errors vacío,
pixelart_00003_.png); 327 tests comfyui verdes.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Mueve el indicador de arquitectura del SUFIJO al PREFIJO del nombre de cada
LoRA para que el dropdown del LoraLoader muestre de inmediato que LoRA casa con
que checkpoint (evita el shape mismatch SD1.5 vs SDXL que crashea ComfyUI).
- 20 LoRAs renombradas en disco (15 SD15/SDXL en /mnt/2tb, 5 FLUX en ~/ComfyUI),
mapa de reversion en ~/ComfyUI/models/loras/_rename_map.json.
- Refs actualizadas en builders gamedev-2d, style presets, pipelines, tests y
docs/capabilities. Defaults hardcodeados (pixel-art, lcm-lora, etc.) apuntan a
los nombres con prefijo.
- Ejemplos genericos en docstrings normalizados a la convencion de prefijo.
- comfyui_replicate_civitai_oneshot::_norm ignora el token de arquitectura al
comparar, robusto al reordenado (sufijo civitai vs prefijo instalado).
Refs a repos HuggingFace (nerijs/pixel-art-xl) y checkpoints (juggernaut_xl_v11)
preservados. Verificado: dropdown LoraLoader con prefijos + generacion real
pixel-art OK + tests comfyui verdes (481 ml + 26 pipelines).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Tres funciones para gestionar y ampliar el repositorio de estilos del selector
WAS de ComfyUI (Prompt Styles Selector / Prompt Multiple Styles Selector):
- comfyui_curated_styles_catalog (pure): catálogo curado de 190 estilos en 13
categorías (photography, render3d, painting, anime, pixel, illustration,
comic, lighting, camera, material, scifi, fantasy, mood), formato WAS exacto.
- comfyui_append_styles (impure): merge+dedup no destructivo sobre el styles.json
real, con backup atómico, validación de entradas y preservación de existentes.
- comfyui_generate_styles_llm (impure): genera estilos de una categoría vía
ask_llm (grupo claude-direct); robusta (devuelve {} ante 429/JSON corrupto).
Aplicado en vivo: styles.json 269 -> 503 estilos (+190 curados +44 LLM),
backup hecho, selector verifica 503 en /object_info. Tests offline verdes.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Pipeline one-shot que aplica un style preset curado a un asset en una llamada
(kind, subject, style_preset) y auto-ejecuta el post-proceso que el estilo declara:
los estilos pixelart (gameboy, pixel-art-retro) salen ya pixelizados del pipeline,
cerrando el hueco #1 del sistema de style presets (report 0190) donde el caller
tenía que llamar comfyui_pixelize_image a mano.
Reutiliza el dispatch _SUPPORTED (kind->builder) de comfyui_generate_asset_pack_oneshot
en vez de redefinir el mapa. Parte pura aislada en styled_asset_build_only para validar
kind/estilo desconocido sin tocar la GPU. Export a Godot consciente del post (pixelart
si hubo pixelize, para fijar el filtro Nearest).
Catálogo de estilos ampliado de 3 a 6: cyberpunk-neon (prompt puro SD1.5),
low-poly-flat (prompt puro SD1.5), cartoon-cel-shaded (LoRA anime_style_box_sd15 0.7).
Verificación: 11 tests offline del pipeline + suite de presets verde (27 passed).
Prueba real en GPU: mismo "treasure chest" en cyberpunk-neon, low-poly-flat y gameboy
one-shot; gameboy pasa de 17374 colores (crudo) a 4 (paleta Game Boy) auto-pixelizado
directo del pipeline. Detalle en reports/0191.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Calidad por ESTILO en vez de por tipo: un dev fija el look del juego una vez
y todos los assets salen coherentes. Diseño (A) datos puros + helper, no
pipeline monolítico (issue 0087, crecer por composición).
- comfyui_get_gamedev_style_preset(name=None): recetas curadas o catálogo.
gameboy (sin LoRA, post pixelize paleta game-boy 4 tonos), ghibli (degrada
a watercolor_style_sd15 gratis instalado, sin LoRA Ghibli gated), pixel-art-retro
(reutiliza pixel-art-xl SDXL + juggernaut + post pixelize 16 colores). Extensible.
- comfyui_apply_style_preset(preset, subject): traduce a kwargs **spread-ables
para cualquier builder de sujeto + size/transparent/post. Pura, no muta.
- 16 tests offline verdes. Validado e2e GPU: mismo 'knight character' en 3
estilos visiblemente distintos (4 vs 78552 vs 16 colores).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Promueve a un pipeline one-shot la secuencia que hoy exige 4 llamadas a mano:
generar el set COMPLETO de un personaje de juego (imagen base 2D recortada,
sprite direccional N-way SV3D/Zero123 y malla 3D Hunyuan3D .glb), todos del
MISMO personaje. La coherencia cross-frontera se garantiza por construccion: el
direccional y el 3D parten de la MISMA base 2D aplanada (base_flat), no de tres
generaciones independientes. Es la culminacion de las 5 fronteras del grupo
gamedev-2d (issue 0087).
Compone builders del registry (enemy_creature/portrait_avatar/topdown_sprite
por introspeccion) + comfyui_flatten_alpha_on_color (nueva, aplana el sprite
recortado sobre fondo solido que SV3D/Hunyuan exigen) + comfyui_image_to_3d_oneshot
+ comfyui_build_directional_sprite_workflow + submit/wait/fetch + export Godot.
Secuencial liberando VRAM entre pasos pesados (3D antes que SV3D) para caber en
8 GB; fallo aislado deja set PARCIAL sin abortar.
Probado e2e en GPU (RTX 3070 8 GB) con 'armored paladin': base 2D RGBA 512
recortada + malla glTF 395600 triangulos + 8 vistas direccionales SV3D 576,
todos del mismo personaje. 9 tests offline verdes (incluye coherencia mockeada).
Ver reports/0189.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Builder puro (dict API format) que a partir del sprite frontal de un personaje
construye el workflow ComfyUI de N vistas direccionales consistentes (8-way
N/NE/E/SE/S/SW/W/NW o 4-way) rotando la figura en 3D. SV3D (orbit turntable) por
defecto, Stable Zero123 (batch por azimuth) como fallback de menor VRAM. Es el
puente 2.5D del catalogo gamedev-2d: consistencia rotacional real (el mismo
modelo rotado) frente a sprite_sheet (OpenPose 2D re-poza, identidad inconsistente).
Helper directional_sprite_view_order(directions) mapea frame i -> direccion i.
Funcion pura: solo construye el grafo; coste GPU al enviar con comfyui_submit_workflow.
Probado e2e en GPU: goblin enemy_creature_00001_ -> SV3D 8 direcciones elevation 15,
8 frames 576x576 en 75s, pico 7145/8192 MiB (prompt_id 8b9f75de). Consistencia
rotacional medida: MAE adyacentes 27 < frente-espalda 29.6, spread de paleta 3.83.
Report: reports/0187.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Añade la sub-sección "Animación de assets (vídeo) — caminos validados e2e" a la
capability page gamedev-2d con las tres vías para animar un asset 2D, todas
cabiendo en 8GB con la GPU vacía:
- txt2video LTX (comfyui_build_video_workflow model='ltx'): loop de elemento
desde texto. Validado e2e: portal mágico 512×320, 25 frames, VRAM pico
7717/8192 MiB (prompt_id 54eda033).
- img2vid SVD (comfyui_build_img2vid_workflow): animar un sprite/fondo ya
generado. Validado e2e: enemy_creature del pack → 512×512 RGBA 14 frames
animado, VRAM pico 7463/8192 MiB (prompt_id 5b501d03).
- txt2video Wan (enlazado + visible en /object_info, clip no generado aún).
- spritesheet AnimateDiff (ya validado en rondas previas).
Documenta el gate VRAM (el vídeo no convive con un juego AAA abierto), el gotcha
de comfyui_wait_result (lanza TimeoutError) y que SVD completa en GPU aunque el
script de orquestación expire (recuperar output sondeando /history). Los modelos
de vídeo de /mnt/2tb/comfyui_models ya estaban enlazados vía extra_model_paths.yaml
(verificado en /object_info, sin copiar, reversible). Evidencia: reports/0186.
No se creó builder nuevo: los builders del registry ya cubren el caso gamedev
(registry-first/KISS).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Quinto vertice del eje transform de gamedev-2d. Funcion pura (dict API format)
que extiende el lienzo de un asset ya pintado por uno o varios lados y genera
contenido coherente mas alla de sus bordes via el nodo nativo ImagePadForOutpaint,
que ademas de ampliar el canvas EMITE la mascara feathered de la franja nueva (la
genera el grafo, no la recibe el caller — esa es la diferencia con inpaint_asset).
Compone comfyui_build_inpaint_workflow (base; su LoadImageMask se elimina y
VAEEncodeForInpaint se reconecta a las dos salidas del pad) + comfyui_inject_lora.
Probado e2e en GPU con SD1.5: seamless_00004 512x512 extendido right=256 -> 768x512
(prompt_id aa33de05), original conservado (diff 7.2) + franja nueva coherente.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Cuarto vértice del eje transform de gamedev-2d: editar SOLO una región de un
asset 2D ya pintado vía inpaint, conservando el resto del sprite. Completa el
eje junto a txt2img (crear de cero), asset_variant (img2img: reescribe todo) y
sprite_from_sketch (ControlNet: sprite nuevo desde boceto).
Función pura (API format dict) que compone comfyui_build_inpaint_workflow (base)
+ comfyui_inject_lora (estilo opcional). Recibe asset + máscara (blanco=editar,
negro=conservar) + prompt de qué poner; VAEEncodeForInpaint codifica respetando
la máscara y dilata el borde grow_mask px para difuminar la costura; el KSampler
regenera solo esa zona. mode="noise_mask" degrada a VAEEncode+SetLatentNoiseMask
para servidores sin VAEEncodeForInpaint (error path). size escala imagen Y máscara
de forma consistente. class_types verificados contra /object_info (8GB lowvram).
Probado e2e en GPU con SD1.5: máscara circular sobre la mano del goblin
enemy_creature_00001_.png, prompt "a glowing blue magic orb" (prompt_id 88b52c66).
Solo la región enmascarada cambió: diff medio dentro 40.3 vs fuera 1.97 (ratio
20.4x), 44.6% px cambiados dentro vs 1.7% fuera. Confirmación visual: orbe azul
en la región, resto del goblin idéntico.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Tercer eje del catálogo gamedev-2d: partir del DIBUJO del dev. Recibe un
boceto/lineart + un prompt de qué es y construye un workflow txt2img guiado por
ControlNet (lineart/scribble/canny) que pinta el sprite conservando la forma
dibujada. Distinto de los builders txt2img (inventan la forma desde texto) y de
asset_variant img2img (reescribe una imagen ya pintada conservando forma+color):
aquí el dev marca la silueta y la IA pone material/color/acabado, conservando
solo la forma.
Función pura (API format). Compone comfyui_build_txt2img_workflow +
comfyui_inject_controlnet + comfyui_inject_lora; el único código propio es el
helper que interpone el preprocesador (LineArt/Scribble/Canny) entre el boceto y
el ControlNet, análogo a _inject_image_scale del hermano asset_variant.
control_type selecciona preprocesador y modelo CN emparejado; controlnet_name y
preprocess dan override para degradar al modelo disponible. Gotcha documentado:
el server 8GB solo tiene modelos CN SD1.5 canny/depth/openpose — para
lineart/scribble usar override a canny o control_type=canny (pendiente humano
descargar los modelos lineart/scribble dedicados).
Verificación: tests offline verdes (cableado txt2img guiado, 3 control_types,
clamps, errores). E2E real GPU SD1.5: boceto del goblin → CannyEdgePreprocessor →
ControlNet canny → sprite que respeta pose/orejas/hombrera/lanza/espada del
dibujo (prompt_id ea6fc372, edge corr 0.545, luminance corr -0.19 confirmando
repintado). Report en reports/0182.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Primer builder gamedev-2d de transformacion (img2img) en vez de generacion
(txt2img): parte de un asset ya generado y produce una variante coherente
(ice/fire/damaged/golden tier) cambiando material/paleta/estado y conservando
silueta, pose y composicion via denoise medio (~0.5). Compone
comfyui_build_img2img_workflow + comfyui_inject_lora + ImageScale opcional.
Probado e2e en GPU SD1.5: variante ice del goblin del demo pack
(prompt_id 5e4a5d3d) — silueta conservada (luminance corr 0.63) + paleta a
frio (blueness B-R -1.6 -> +1.9). Subseccion nueva en docs/capabilities y
report 0181.
Pipeline que genera un set de assets 2D de un mismo juego en una sola llamada,
compartiendo checkpoint, LoRA de estilo, estilo comun (inyectado al subject) y
seed derivada (base_seed + indice). Despacha 26 kinds gamedev-2d a sus builders
atomicos, encola/espera/descarga cada PNG y exporta opcionalmente a Godot.
Promocion de composicion a pipeline (issue 0087): el registry no crece inflando
builders, crece promoviendo la secuencia repetida 'N builders con el mismo estilo'
a un one-shot.
- Dispatch declarativo por kind con inyeccion de coherencia via inspect.signature
(no hardcodea nombres de param; respeta LoRAs funcionales propios).
- Fail-fast si kind desconocido (sin tocar GPU); un OOM aislado no aborta el pack.
- 9 tests offline verdes (golden + edge + error).
- Probado e2e en GPU SD1.5 512: magic sword + goblin warrior, style dark fantasy
hand-painted, seeds 42/43 -> 2/2 PNG 512x512 RGBA coherentes.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
El doctor reportaba el dominio gamedev en doble FAIL: el tag plano `gamedev`
(44 funciones) como `ungrouped_candidate` y la pagina `gamedev-2d.md` como
`doc_orphan`. Causa raiz: el INDEX declaraba `[gamedev](gamedev-2d.md)` y el
auditor solo registra el slug cuando label==target, asi que ni casaba la
pagina ni declaraba el tag.
Al revisar las 44 funciones habia dos clusters reales bajo el mismo tag, asi
que se separan en dos grupos honestos:
- gamedev-2d (tag canonico): 31 builders de workflow ComfyUI + 5 de apoyo
(post-proceso + puente a Godot) = 36. Se elimina el tag plano `gamedev` de
los builders (ya tenian `gamedev-2d`) y se reemplaza por `gamedev-2d` en las
de apoyo.
- gamedev-engine (grupo nuevo, pagina madre nueva): runtime de juego C++
multiplataforma (SDL3 + sokol_gfx + miniaudio, Issue 0072b) = 8. Game loop,
camara 2D, input unificado, sprite batch, setup render/audio, build wasm.
El tag plano `gamedev` queda eliminado (count 0). INDEX corregido: fila
gamedev-2d con label==target y conteo 36 + fila nueva gamedev-engine (8).
Verificacion: `fn index` + `fn doctor capabilities` -> ambos grupos OK
(declared_in_index=yes, doc_exists=yes, sin issues); `gamedev` plano = 0.
Solo se modifico el campo `tags` de los .md, ningun archivo de codigo.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Builder puro (dict API format) de capas de clima a pantalla completa: lluvia,
niebla, nieve, god rays, polvo, viñeta de tormenta. Capa de cobertura total
apaisada (16:9, 1024x576) que se superpone sobre la escena con blend del motor.
Dos modos via on_black: True (defecto) genera el clima brillante sobre negro
puro como insumo de comfyui_matting_luma_to_alpha (blend aditivo/screen);
False genera una pelicula translucida semi-transparente (multiply/overlay).
NO inyecta Rembg: el matting de una capa es luma->alpha de disco. Compone
comfyui_build_txt2img_workflow + comfyui_inject_lora.
Diferenciado de decal_overlay (mancha localizada) y vfx_spritesheet (secuencia
animada de un efecto puntual): aqui es una pelicula estatica de cobertura
full-screen que el motor anima por scroll/loop/shader.
10 tests offline verdes. Probado e2e en GPU SD1.5 8GB lowvram: heavy rain
on_black seed 11 1024x576 (16:9 exacto), estrias brillantes sobre negro plano
(esquinas luma 0.00, dark 89.6%) apto luma->alpha (prompt_id 5d2300d1).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Nuevo builder del grupo gamedev-2d para insignias de logro / medallas / trofeos de
sistemas de achievements/recompensas, con tier metálico (bronce/plata/oro/platino/
diamante) y fondo recortable a alpha. Función pura (dict API format) que compone
comfyui_build_txt2img_workflow + comfyui_inject_lora (estilo opcional) + Image Rembg.
Hermano de item_icon (objeto de inventario suelto) y skill_tree_node (nodo enmarcado
de la rejilla de talentos): aquí el asset es la insignia de logro/recompensa = medalla
con cinta + tier. El tier metálico y la forma de medalla/trofeo son la firma.
12 tests offline verdes. Probado e2e en GPU con SD1.5: badge="dragon slayer" tier gold
seed 77 256x256 RGBA, medalla circular dorada con emblema centrado y fondo recortado a
alpha (esquina α=0, centro α=254; prompt_id 8b8b7ede).
Builder ComfyUI puro (dict API format) para UNA trampa/peligro JUGABLE de nivel
(pinchos, sierra giratoria, foso de lava, placa de presion, llamas, trampa de
flechas, charco acido, descarga electrica): objeto de peligro aislado y centrado
a perspectiva de juego (side/top-down/iso via view), fondo limpio recortable a
alpha, estilo consistente para poblar niveles.
Hermano de comfyui_build_prop_object/structure/foliage_set_workflow: mismo patron
que compone comfyui_build_txt2img_workflow + comfyui_inject_lora (estilo opcional)
+ Image Rembg (alpha si transparent). Diferenciado de prop_object (peligro con
hitbox de dano vs decoracion inerte) y enemy_creature (trampa vs enemigo vivo);
el negativo rechaza character/person/creature/multiple objects.
Gotcha documentado: para hazards puramente etereos (llamas/electricidad/gas) usar
transparent=False + comfyui_matting_luma_to_alpha (conserva el falloff), no Rembg.
12 tests offline en verde. Probado e2e en GPU con SD1.5 — spiked floor trap side
512x512 RGBA, mecanismo de peligro centrado recortado a alpha real (alpha extrema
0-255, prompt_id ab1b1560).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Builder puro del grupo gamedev-2d: nodo de skill tree (icono de habilidad dentro de
un marco circular/hexagonal/rombo/escudo) con variante de estado visual (unlocked
brillante / locked gris), centrado, recortable a alpha. Diferenciado de item_icon
(objeto suelto sin marco), status_effect_icon (simbolo superpuesto sin marco) y
ui_hud (chrome grande): el marco y el estado son la firma del asset. Compone
comfyui_build_txt2img_workflow + comfyui_inject_lora + Image Rembg, sin reescribir el
grafo. 11 tests offline en verde. Probado e2e SD1.5 8GB lowvram: fireball hexagonal
unlocked 256x256 RGBA, prompt_id cf36b2ea, nodo enmarcado brillante centrado
(reports/0173).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Builder del contenedor de diálogo de juego (RPG, visual novel, aventura): marco
apaisado (768x256) con borde decorativo e interior plano reservado para el texto
del motor. Distinto de ui_hud (elementos sueltos): es el panel-contenedor completo.
Compone txt2img + inject_lora + Image Rembg (alpha). Pura, dict API format.
7 tests offline verdes; 1 generación real en GPU (medieval wood+gold, 768x256 RGBA).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Builder puro (dict API format) hermano de ui_hud/splash_art: compone
comfyui_build_txt2img_workflow + comfyui_inject_lora + Image Rembg. Renderiza
el nombre del juego/una palabra con un tratamiento de lettering (metálico,
tallado, neón, fuego...), formato apaisado 1024x512, fondo recortable a alpha.
El negativo NO rechaza texto (el lettering es el sujeto). Documentada la
limitación clave: la difusión no garantiza la ortografía exacta del texto
(letras de más/deformadas; una palabra-objeto como DRAGON se ilustra en vez de
escribirse). Mitigaciones: palabras cortas en mayúscula, re-roll de seeds,
SDXL > SD1.5, o pintar el texto real en el motor.
Tests 9/9 verde (offline). Verificado e2e en GPU (8GB lowvram): DRAGON/fire
engraved (SD1.5, prompt_id 6f3920b7) y AETHER/epic fantasy metallic (SDXL,
prompt_id 2a7fe8ba, logo metálico dorado + alpha). Fila en
docs/capabilities/gamedev-2d.md. Report en reports/0165.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Builder gamedev-2d nuevo: edificacion grande y completa (casa, torre, castillo, tienda,
posada, ruina, muralla, puente, templo, faro) para poblar mapas/escenarios. Diferenciado
de comfyui_build_prop_object (edificio completo vs objeto pequeno suelto): el negativo
rechaza small object/single item/prop/furniture y el scaffold empuja full building/
complete structure/single building. view (iso por defecto) fija la perspectiva del mapa.
Pura (dict API format): compone comfyui_build_txt2img_workflow + comfyui_inject_lora
(estilo/iso opcional) + Image Rembg (alpha si transparent). 12 tests offline verdes.
Probado e2e en GPU (8GB lowvram): medieval blacksmith shop iso 512x512 RGBA, edificio
centrado (centroide 0.54/0.53). Fila en docs/capabilities/gamedev-2d.md.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Builder PURO (dict API format) del grupo gamedev-2d/gamedev-vfx: UNA textura de
partícula reutilizable que el sistema de partículas del motor (Godot GPUParticles2D,
Unity VFX Graph) instancia a miles. Aislada sobre fondo negro puro, pensada para
luma→alpha (comfyui_matting_luma_to_alpha, additive blend); soft controla el borde
(glow difuso vs nítido); NO inyecta Rembg (rompería el falloff); size 256 por defecto.
Diferenciada de vfx_spritesheet (secuencia animada) y decal_overlay (mancha estática).
Compone comfyui_build_txt2img_workflow + comfyui_inject_lora. 9 tests offline verdes.
Generación real verificada e2e en GPU (spark sobre negro plano + luma→alpha RGBA).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Builder hermano de item_icon/ui_hud, diferenciado por rol: símbolo de estado
compacto (veneno/escudo/velocidad...) optimizado para 16-32 px, no objeto de
inventario ni chrome de interfaz. Pura (dict API format), 8 tests offline,
1 generación real verificada (poison 256x256 RGBA).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Builder puro (dict API format) para texturas de superposicion (sangre, grietas,
suciedad, grunge, oxido, quemaduras, salpicaduras): genera el decal aislado sobre
fondo plano (negro por defecto), pensado para extraer alpha con
comfyui_matting_luma_to_alpha (luminancia=alpha, conserva el falloff de translucidos).
NO inyecta Rembg (el matting es luma->alpha de disco, no un nodo). Compone
comfyui_build_txt2img_workflow + comfyui_inject_lora. 9 tests offline verdes;
generacion real verificada e2e en GPU (8GB lowvram, SD1.5, prompt_id 109907a4).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Builder PURO (dict API format) del grupo gamedev/gamedev-2d, hermano de
comfyui_build_card_art_workflow y comfyui_build_parallax_background_workflow.
Genera la ilustración grande de una pantalla de portada / loading screen / key
art en formato pantalla apaisado 16:9 (~1024x576), composición cinematográfica
(wide shot) con aire para superponer el título del juego. Compone
comfyui_build_hires_fix_workflow (si hires) o comfyui_build_txt2img_workflow +
comfyui_inject_lora (estilo opcional). Genera SOLO la ilustración: el negativo
por defecto rechaza text/title/logo/UI/frame para que el motor componga el
título encima.
- 9 tests offline verde (golden hires, apaisado width>height, batch_size, sin
hires, dims/mood/lora reflejados, error scene vacío, determinismo).
- .md autosuficiente (Ejemplo + Cuando usarla + Gotchas) + fila en
docs/capabilities/gamedev-2d.md.
- Probado e2e en GPU 8GB lowvram: 1 splash real (héroe ante castillo oscuro en
tormenta), 1024x576 -> 1536x864 (16:9 exacto) tras hires, 54s, SD1.5
dreamshaper_8. Evidencia en reports/0159.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
El nodo UltimateSDUpscale declara batch_size como input requerido en /object_info;
comfyui_build_hires_fix_workflow y comfyui_inject_hires_fix no lo proveian, por lo
que card_art con hires=True fallaba en runtime. Se anade batch_size: 1 a ambos
constructores + guards de regresion en los tests (card_art golden hires, builder e
inject). Verificado con una generacion real en ComfyUI (carta 768x1152, sin
node_errors, prompt_id 4033fb0b). Bump de version 1.0.0->1.0.1 en ambos .md con
growth log y gotcha.
INDEX.md: la fila gamedev decia count=10; el cluster de assets 2D documentado en
gamedev-2d.md tiene 20 funciones (15 builders tag gamedev-2d + 5 de apoyo).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Builder puro (dict API format) del grupo gamedev: sprite de personaje/objeto en
vista cenital (top-down) estilo RPG clasico/roguelike, visto desde arriba,
centrado, fondo limpio recortable a alpha. Argumento direction (south/north/east/
west) para el set de sprites de movimiento. Compone comfyui_build_txt2img_workflow
+ comfyui_inject_lora (estilo opcional) + Image Rembg (alpha). Diferenciado de
comfyui_build_sprite_sheet_workflow (vista lateral/frontal): el negativo por
defecto rechaza side/front/isometric/perspective para forzar la cenital.
Probado e2e en GPU con SD1.5 (8GB lowvram): caballero cenital, fondo transparente
(reports/0157). 10 tests offline verdes.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Builder PURO del grupo gamedev: dict API format de un prop/objeto de escenario
(barril, cofre, antorcha, planta, mueble, roca, fuente, estatua). Compone
comfyui_build_txt2img_workflow + comfyui_inject_lora opcional + Image Rembg.
Diferenciado de item_icon: objeto de MUNDO (escala de escena, perspectiva
iso/lateral) vs icono plano de inventario. 10 tests offline verdes; 1 generacion
real en GPU (cofre del tesoro, RGBA 512x512, fondo recortado). reports/0155.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Nuevo builder del grupo gamedev/gamedev-2d para enemigos/criaturas de juego
(goblin, esqueleto, slime, dragon, boss, elemental): figura de cuerpo entero,
centrada, fondo limpio recortable a alpha, estilo consistente entre criaturas
del bestiario. Variantes por nivel/elemento (ice, fire, elite, corrupted) via
el argumento variant, que se antepone a la criatura base.
Funcion pura (dict API format) que compone funciones existentes del registry:
comfyui_build_txt2img_workflow + comfyui_inject_lora (estilo opcional) +
Image Rembg (fondo transparente). Hermano de comfyui_build_item_icon /
ui_hud / sprite_sheet_workflow.
- 8 tests offline verdes (golden/edge/error/determinismo)
- .md autosuficiente (Ejemplo + Cuando usarla + Gotchas)
- fila en docs/capabilities/gamedev-2d.md
- probado e2e en GPU (8GB lowvram, SD1.5): goblin warrior ice cuerpo entero,
512x512 RGBA con alpha, prompt_id e770d050 (report 0154)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Nuevo builder PURO (dict API format) del grupo gamedev-2d para la ilustración
central de una carta coleccionable (criatura/personaje/hechizo) en formato
vertical de carta (~512x768), composición centrada y dramática. Compone
comfyui_build_txt2img_workflow / comfyui_build_hires_fix_workflow (hires opcional)
+ comfyui_inject_lora (estilo opcional). Genera solo la ilustración; marco/título/
stats son composición del motor/post (el negativo por defecto los rechaza).
- 8 tests offline verdes (golden hires + edges dims/style/lora + error + determinismo).
- Generación real en GPU verificada: dragón de fuego 512x768 vertical, SD1.5, 5s
(prompt_id 010dcdae-..., reports/0153).
- Fila en docs/capabilities/gamedev-2d.md.
Gap: el path hires=True falla hoy por bug del builder hermano
comfyui_build_hires_fix_workflow (nodo UltimateSDUpscale exige batch_size que el
builder no emite); abierta proposal improve_function. Usar hires=False hasta el fix.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Builder puro hermano de comfyui_build_item_icon_workflow: construye el dict (API
format) del workflow de UN elemento de interfaz/HUD de juego (botón, marco/panel,
barra de vida/maná/XP, icono de UI, cursor, viñeta de menú). Pieza única centrada,
fondo limpio recortable a alpha. Compone comfyui_build_txt2img_workflow +
comfyui_inject_lora + Image Rembg.
Tests offline 7/7 verdes (golden + 4 edge + error + determinismo). Generación real
verificada en GPU (8GB lowvram): ornate health bar frame -> PNG 512x512 RGBA con
alpha recortado (reports/0152). Fila añadida en docs/capabilities/gamedev-2d.md.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Builder puro (dict API format) del grupo gamedev: genera el workflow de UN
emote/expresion facial del mismo personaje (alegre, triste, enfadado,
sorprendido, neutral) para sistema de dialogo, retratos reactivos o emotes de
chat. La clave es la consistencia del personaje entre expresiones: ref_face
encadena IPAdapter-FaceID para que varie solo la expresion y el rostro sea el
mismo; facedetailer regenera la cara conservando la expresion.
Compone comfyui_build_ipadapter_workflow / comfyui_build_txt2img_workflow +
comfyui_inject_lora + comfyui_build_facedetailer_workflow. Hermano de
comfyui_build_portrait_avatar/sprite_sheet_workflow.
12 tests offline verdes (golden + edge + error) y 1 generacion real verificada
en GPU (8GB lowvram): la expresion happy/smiling se lee claramente. Fila en
docs/capabilities/gamedev-2d.md. Evidencia en reports/0151.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Builder puro (dict API format) que genera el normal map (o depth/height) de un
sprite existente para iluminacion dinamica 2.5D (Godot normal_map, Unity sprite
normal). Pipeline LoadImage -> preprocesador controlnet_aux -> SaveImage, ~0 VRAM.
method selecciona el nodo (verificados contra /object_info):
- normal (default): BAE-NormalMapPreprocessor, normal canonico azul/violeta usable
directo en motor.
- normal_midas: MiDaS, unico con control de intensidad (strength -> a).
- normal_dsine: DSINE. depth: DepthAnythingV2 (height en gris).
Nodos de normal NATIVOS, sin necesidad de depth->Sobel post (gap innecesario).
11 tests offline verdes. Probado e2e en GPU (8GB): normal map de un icono de
pocion, prompt_id d47f9943, tono azul/violeta verificado. Report 0150.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Builder puro (dict API format) del grupo gamedev-2d: genera el fondo apaisado
(txt2img) y su mapa de profundidad (DepthAnythingV2Preprocessor sobre el VAEDecode),
guardando ambos como PNG. El corte en N bandas por rango de profundidad queda como
post-proceso documentado (gap split_parallax_layers). Compone
comfyui_build_txt2img_workflow. 8 tests offline verdes; probado e2e en GPU
(RTX 3070 8GB lowvram): fondo de bosque + depth map, prompt_id
11763613-33cf-4f63-8405-34f75c1c89be. class_types verificados contra /object_info.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Builder puro (dict API format) hermano de sprite_sheet/item_icon: retrato/avatar
de personaje (busto centrado, cara al espectador, fondo simple con vinheta) para
dialogo, perfil o seleccion de personaje. Con ref_face encadena IPAdapter-FaceID
(rostro consistente entre retratos); con facedetailer regenera la cara con detalle
(FaceDetailer de Impact-Pack). Compone txt2img/ipadapter + inject_lora + facedetailer.
9 tests offline en verde + 1 generacion real verificada (mujer caballero pelirroja,
cara nitida). Fila en docs/capabilities/gamedev-2d.md. Report 0148.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Builder puro hermano de pixelart/sprite_sheet/seamless_tile: arma el dict
(API format) para iconos de items (espada/pocion/anillo/libro/escudo).
txt2img cuadrado + prompt scaffold de icono + LoRA estilo opcional + Rembg
(alpha). Compone comfyui_build_txt2img_workflow + comfyui_inject_lora.
Test offline 7/7 verde. Generacion real verificada (icono de pocion de
salud centrado, RGBA fondo recortado, prompt_id 70b7a52a, 512x512 SD1.5).
Fila en docs/capabilities/gamedev-2d.md. Detalle en report 0147.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
El nudge del orquestador apuntaba al window_id (@N) de tmux, que migra cuando
el focus-swap de FleetView recrea windows (break-pane/join-pane): el texto
acababa en el window equivocado o en otro agente (a veces no llega). Ademas,
texto y Enter en la misma invocacion hacian que el TUI no interpretara el submit.
Nueva funcion fleet_send_text_bash_infra (grupo orchestration) que:
- resuelve el pane_id (%N) estable fresco justo antes de enviar (sessionId/PID
a pane via tmux list-panes -a + walk de ancestros /proc), no el @N volatil;
- manda texto literal y Enter en invocaciones separadas;
- verifica con capture-pane que el texto llego antes del submit, con reintento;
- guards anti-self y error claro si el target no resuelve a un pane vivo.
Test (19/19) sobre socket tmux propio: confirma que tras break-pane el pane_id
no migra y el reenvio sigue llegando. orchestration.md (seccion Nudge + catalogo)
actualizado para usar la funcion en lugar del send-keys -t <@N> manual.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Cinco builders puros que devuelven dict API format, cada uno componiendo funciones
existentes del registry (comfyui_build_txt2img_workflow, comfyui_inject_*,
comfyui_build_ipadapter_workflow). class_types verificados contra /object_info.
Probados e2e en GPU (8GB lowvram): pixelart (pixel-perfect), seamless (sin costura),
vfx (AnimateDiff loop -> luma-alpha -> spritesheet RGBA). 30 tests offline verdes.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Tres funciones CPU-only del lote gamedev 2D + 2 helpers puros + grupo de capacidad:
- comfyui_pixelize_image_py_ml (impure): Fase 2 pixelart — downscale nearest +
cuantizacion a N colores / paleta fija (game-boy/pico-8/nes) + re-upscale nearest.
- comfyui_matting_luma_to_alpha_py_ml (impure): frame VFX sobre negro -> RGBA por
luminancia ponderada (translucidos con additive blend).
- comfyui_export_asset_to_godot_py_pipelines (impure): puente ComfyUI -> Godot 4 —
copia a res://assets/<dir> por kind + .import por tipo + filtro Nearest si pixelart
+ reimport headless best-effort. Compone los 2 helpers puros.
- godot_map_asset_dir_py_core, godot_clean_asset_name_py_core (pure): nucleos
reutilizables del pipeline.
- docs/capabilities/gamedev-2d.md + INDEX: grupo nuevo gamedev.
Tests 33/33 verdes (offline PIL/numpy). Golden real verificado: asset de
~/ComfyUI/output -> /tmp/godot_test_proj con .import correcto y reimport headless
real de Godot 4.7. Sin GPU, sin red, sin tocar proyectos del usuario.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Mapa de ambas estructuras (output/ de ComfyUI + res://assets/ de Godot 4),
tabla tipo-de-asset → carpeta destino → import settings clave, y propuesta
de pipeline export_asset_to_godot que compone helpers atómicos + reimport
headless (gap confirmado: 0 funciones godot en el registry).
Documenta el gotcha de Godot 4: el filtro Nearest del pixelart se setea
global (default_texture_filter=0) o por override, no por .import por defecto.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Completa la rama method='sv3d' (antes NotImplementedError) componiendo el
workflow SV3D nativo de ComfyUI (SV3D_Conditioning + VideoLinearCFGGuidance +
KSampler + VAEDecode + SaveImage): una imagen produce un orbit de N frames
equiespaciados en 360 grados en una pasada.
- _METHOD_CKPT['sv3d'] acepta sv3d_p (preferido) o sv3d_u; nuevo helper
_resolve_ckpt sustituye a _method_ckpt_key.
- nuevos params keyword-only video_frames=21, sv3d_width=576, sv3d_height=576
(configurables para densidad de orbit y control de VRAM).
- salida sv3d extendida con frames (orbit completo) + frame_count; views mapea
cada azimuth al frame del orbit mas cercano (cardinales para multi-vista).
- _collect_views_sv3d + helpers compartidos _history_images/_fetch_or_name;
_collect_views (zero123) refactorizado para reusarlos.
Probado en GPU (8 GB lowvram): sv3d_p.safetensors descargado a checkpoints/,
21 frames 576x576 en ~75 s, peak ~5.7 GB, sin OOM
(prompt_id 0caeedf4-baa0-4c8f-844a-867490ac4f85). Detalle en report 0128.
Bumpa version 1.0.0 -> 1.1.0 + Capability growth log. Pagina madre comfyui.md
marca ambos caminos (zero123/sv3d) operativos.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Nueva capacidad del grupo comfyui: dado el id/URL de una imagen de Civitai,
extrae cómo se generó (prompt, modelo, sampler, LoRAs) vía los endpoints tRPC
image.getGenerationData + image.get (la API v1 da meta=null), reconstruye el
workflow y lo replica en nuestro ComfyUI, sustituyendo el checkpoint ausente por
el más parecido instalado y reportando lo que falta en missing_models sin bajar
nada a ciegas. Respeta SFW.
Funciones nuevas (registry-first, componen 8 funciones existentes):
- comfyui_fetch_civitai_image_meta_py_ml (impura): observa la receta por id/URL.
- comfyui_map_a1111_params_py_ml (pura): traduce meta A1111 -> params ComfyUI,
familia del modelo y LoRAs.
- comfyui_replicate_civitai_oneshot_py_pipelines: orquesta fetch_meta ->
map_a1111_params -> build/embebido -> run_foreign_workflow_oneshot -> judge.
Probado en vivo (imagen SFW 23526611): receta extraída + réplica 1024x1024
generada + panel de jueces. 12 tests unitarios verdes. Capability page comfyui.md
actualizada. Report 0127.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Mezclador del grupo comfyui-skill que promueve a una sola llamada la secuencia
base -> compose -> submit -> wait -> fetch -> judge (issue 0087):
- comfyui_compose_capabilities_py_ml (PURA): aplica en orden las capacidades
activadas (loras, controlnet, ipadapter, facedetailer, hires) sobre un
workflow base, sin mutar la entrada.
- comfyui_generate_mixed_oneshot_py_pipelines: one-shot que resuelve el base
(skill/txt2img/dict), compone, encola, espera, descarga el PNG y lo puntua
con el panel comfyui-judge.
- comfyui_inject_controlnet_py_ml, comfyui_inject_ipadapter_py_ml: inyectores
encadenables que consume el compose.
- Tests (24 passed) + pagina madre docs/capabilities/comfyui-skill.md.
Prueba real en GPU: txt2img dreamshaper_8 + 2 LoRAs (3d_render_redmond +
detail_tweaker) + FaceDetailer -> imagen 512x512 en ~24s, juez verdict 'good'
(score 4.69, votos aesthetic+clip good; voto llm degradado por rate-limit 429).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
El flujo de funciones+server está creando comfyui_build_ipadapter_workflow e
comfyui_inject_multi_lora (vistos sin indexar en python/functions/ml/ el
24/06/2026). Se documentan como capacidad emergente para que el mapa esté
completo; sus IDs reales se rellenarán cuando se ejecute fn index.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Indexa las 58 funciones del stack ComfyUI (grupos comfyui + comfyui-skill +
comfyui-judge) por capacidad: txt2img, img2img/inpaint, controlnet,
skills/multiestilo-LoRA, video, upscale/detail, 3D, juez/calidad y
operación/infra. Cada capacidad mapea a sus builders/pipelines del registry,
grafos UI y skills. Añade fila en docs/capabilities/INDEX.md.
El catálogo navegable con los grafos en disco (reorganizados en subcarpetas
por capacidad bajo ~/ComfyUI/user/default/workflows/) vive fuera del repo en
~/ComfyUI/CAPABILITIES.md (no versionado).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
app.loadApiJson (lo que usa comfyui_load_workflow_ui) reconstruye el grafo pero
no llama a app.clean(), por lo que no resetea el store app.nodeOutputs ni los
previews de los nodos. Cuando un workflow nuevo reusa un node_id existente en el
store, el preview cacheado del workflow anterior se re-pinta sobre el nodo nuevo
(visto: imagen 3D pegada bajo un CheckpointLoaderSimple/SaveGLB).
- Nueva funcion comfyui_clear_node_outputs_ui: limpieza no destructiva del store
app.nodeOutputs + node.imgs/images, sin tocar la topologia del grafo.
- comfyui_load_workflow_ui v1.1.0: anade clear_outputs=True (default) que invoca
la limpieza antes de loadApiJson, replicando la garantia de loadGraphData.
Reproducido y verificado en la UI real (CDP 9222) con evidencia antes/despues.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Aplica los 3 arreglos de orden de la auditoria 0120:
- INDEX.md: anade la fila del grupo comfyui-skill (11 fns), cerrando el unico FAIL
doc_orphan de fn doctor capabilities.
- comfyui-skill.md: documenta las 2 funciones de cosecha Civitai
(comfyui_extract_recipe_from_png, comfyui_harvest_civitai_skill_oneshot) en la
tabla + seccion 'Cosecha Civitai -> skill candidata'. Cobertura 9/11 -> 11/11.
- ids_naming.md: anade save/bump/harvest/judge/critique a la allowlist documentada
(espejo del cambio en apps/registry_mcp/naming.go, en su sub-repo).
No fn index (solo docs + rule). No renames.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Cierra el gap receta->grafo del grupo comfyui-skill. La función impura
comfyui_export_skill_template compila una skill a template API format
(exports/<slug>.template.json) y, con ui_graph=True, genera el UI graph
posicionado vía CDP (load_workflow_ui + export_workflow_ui) en la carpeta
nativa de la UI (~/ComfyUI/user/default/workflows/<slug>.json), de modo que la
skill aparece en el menú Workflows del navegador y se abre como grafo visual.
Sin navegador, deja el template API y reporta el fallback (no falla).
- 4 tests offline (golden + edge + 2 error paths).
- Página madre comfyui-skill.md: fila en la tabla del grupo + sección
"Skills como grafos en el navegador".
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Cierra la 3ª pieza del sistema comfyui-skill: cosechar de Civitai imágenes con su
workflow+receta embebidos para clonar su calidad y alimentar la librería de skills.
- comfyui_search_civitai_images: GET /api/v1/images; resuelve query->versión de
modelo (el endpoint no admite query textual, da HTTP 500); token de pass; reintenta 503.
- comfyui_fetch_civitai_image: descarga el PNG original (conserva workflow embebido),
SEGREGA NSFW a <dest>/nsfw/, validación no-HTML, nombre único por UUID.
- comfyui_extract_recipe_from_png: import_workflow_png + read_png_metadata + fallback
flux (CLIPTextEncode/UNETLoader) -> receta candidata (source='civitai', score_n=0).
- comfyui_harvest_civitai_skill_oneshot (pipeline): search->fetch->extract->save_skill;
itera items, 2º pase al feed global, NO baja modelos a ciegas (missing_models).
Hallazgo: la API de Civitai ya no expone meta (null); la receta sale del workflow
ComfyUI embebido en el PNG. Política: NSFW permitido pero SIEMPRE segregado.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Tres funciones nuevas que cierran el lazo skill→generación→juicio→promoción
del grupo comfyui-skill (issue 0087):
- comfyui_bump_skill_version (impura): promueve una versión nueva SOLO si el
score del panel-juez sube (gate objetivo). Snapshot versions/vN.json
pre-mutación, deep-merge de recipe_patch, semver↑, línea en growth_log.jsonl.
force=True salta el gate. No usa datetime.now().
- comfyui_update_skill_score (impura): media incremental de score_mean/score_n
reescribiendo recipe.json in-place (sin snapshot ni growth_log).
- comfyui_generate_with_skill_oneshot (pipeline): one-shot load→build→submit→
wait→fetch→judge→score_mean. recipe_patch prueba variantes sin guardar score.
Compone 7 funciones del registry.
Tests offline: 11 passed (gate, semver, deep-merge, media incremental, errores).
Página madre docs/capabilities/comfyui-skill.md: +3 funciones, sección "Bucle de
mejora" con diagrama, fronteras de scoring actualizadas.
Demo real verificada: skill seed portrait_cinematic_sd15 (SD1.5) generó imagen
SFW real, el panel la juzgó, una variante puntuó más alto (4.787 > 4.7276) y el
gate promovió v1.0.0→v1.1.0 con el judge_run_id como evidencia.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Cuatro funciones impuras + pagina madre del grupo comfyui-judge, el gate
objetivo de calidad de imagen para tests/DoD y el bucle de mejora de skills:
- comfyui_score_aesthetic: estetico LAION-V2 (head MLP sobre CLIP ViT-L/14),
subproceso al venv ComfyUI (torch+open_clip).
- comfyui_score_clip_alignment: fidelidad prompt-imagen via similitud coseno CLIP.
- comfyui_critique_image_llm: critica LLM-vision (compone ask_llm_vision), JSON
verdict+score+reasons.
- comfyui_judge_image: agregadora, vota mayoria good/bad; degrada si un juez cae.
QuickGELU (ViT-L-14-quickgelu/openai) obligatorio: sin el, los embeddings se
degradan y el ranking de fidelidad se invierte en silencio.
Validado e2e sobre imagenes reales: golden 3 votos coherentes, asserts relativos
(nitida>ruido, alineado>desalineado), split 2-1 respeta mayoria en ambos sentidos,
degradacion ante 429/model invalido/path invalido sin crash.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Grupo nuevo comfyui-skill: recetas versionadas de generación ComfyUI que
compilan a un workflow cambiando solo el subject.
- comfyui_build_skill_workflow (pura): receta -> workflow API format,
despacha base (txt2img/flux/sdxl_refiner), sustituye {subject}+triggers,
encadena loras e inject blocks (facedetailer, hires_fix). SkillWorkflowError tipada.
- comfyui_inject_hires_fix (pura): inyecta 2ª pasada UltimateSDUpscale sobre dict.
- comfyui_save/load/list_skill (impuras): CRUD de la librería en disco con
versionado por snapshots, round-trip idéntico, filtro NSFW.
- ask_llm_vision (core, claude-direct): pregunta multimodal imagen+texto via
API directa Anthropic, para puntuar generaciones.
- Página madre docs/capabilities/comfyui-skill.md con schema canónico de recipe.json.
Tests offline: 11 verdes (6 builder + 5 inject_hires_fix). Sin GPU.
Pipeline one-shot para ejecutar workflows ComfyUI ajenos end-to-end
(import desde cualquier fuente -> resolve deps -> validate -> submit ->
wait -> fetch del output imagen/video/malla) componiendo 9 funciones
existentes del grupo comfyui. Gate de seguridad: si faltan nodos/modelos
NO encola y los reporta en `missing`; nunca descarga modelos a ciegas y
solo instala nodos custom confiables opt-in (install_nodes + node_repos).
Helper comfyui_fetch_output_video: hermana de fetch_output_image y
fetch_output_mesh para los nodos de video/animacion (SaveAnimatedWEBP,
SaveVideo nativo, VHS_VideoCombine). Localiza el output bajo images/gifs/
videos en /history y lo baja via /view a disco; acepta outputs= de
wait_result para evitar re-consultar /history.
Cierra la pieza marcada por el completeness critic (report 0107) del
roadmap 0064/0087. 13 tests unitarios de las partes puras en verde;
validacion de integracion contra server vivo sin generacion pesada
(report 0110).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Exige outputs no vacios (no solo status terminal) para dar por completado
un prompt: en jobs pesados ComfyUI marca la entry de /history como
terminada antes de poblar outputs, lo que devolvia un dict vacio mientras
el job seguia en GPU. Ahora sigue sondeando hasta que los outputs aparecen
o hasta agotar el timeout. Timeout default 180s -> 600s (cubre video/3D) y
timeout HTTP por-request acotado a 30s. Firma y contrato de retorno intactos.
Tests nuevos (mock urllib CI-safe + live opcional contra /history real):
golden, regresion del bug, edge imagen corta, timeout y error. v1.0.0 -> 1.1.0.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- comfyui_build_controlnet_workflow.md: el ejemplo usaba cn_name=control_v11p_sd15_canny.pth
pero el modelo instalado es control_v11p_sd15_canny_fp16.safetensors. Corregido para que
copia+pega funcione. Firma intacta.
- docs/capabilities/comfyui.md: añadida subsección "Lifecycle del server — dominio infra"
con comfyui_ensure_server_py_infra (faltaba: página 48 vs registry 49). Ahora 49 == 49.
Higiene del grupo comfyui (report local 0104): tests de los builders puros flux/img2vid
verificados (10/10 pasan, suite del grupo 65/65), fn doctor uses-functions sin drift.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Builder puro que construye el dict de un workflow ComfyUI img2vid (Stable Video
Diffusion) en API format a partir de una imagen estatica. Cadena de 7 nodos:
ImageOnlyCheckpointLoader(svd.safetensors, todo-en-uno) + LoadImage ->
SVD_img2vid_Conditioning -> VideoLinearCFGGuidance -> KSampler(denoise 1.0) ->
VAEDecode -> SaveAnimatedWEBP. SVD condiciona por CLIP_VISION de la imagen (sin
prompt de texto); movimiento via motion_bucket_id.
class_type/inputs verificados contra /object_info del servidor vivo. Validacion
estructural con comfyui_validate_workflow: 0 errores. 4 tests verdes. Sin submit
de generacion (GPU en uso por otro agente).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Builder puro hermano de comfyui_build_txt2img_workflow para modelos Flux
(schnell/dev): UNETLoader + DualCLIPLoader (clip_l + t5xxl, type flux) +
VAELoader -> CLIPTextEncode -> FluxGuidance + EmptySD3LatentImage ->
KSampler (cfg fijo 1.0) -> VAEDecode -> SaveImage. La guia va por FluxGuidance,
no por el cfg del sampler. fp8 + ~4 pasos para GPU de 8GB.
class_type/inputs verificados contra /object_info del server vivo. Validado
end-to-end: genera imagen real (prompt_id 909b8876, flux_builder_test_00001_.png,
status success). 6 tests unitarios verde. Pagina madre docs/capabilities/comfyui.md
actualizada con la fila del builder.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Eran gitlinks (160000) en HEAD del padre sin entrada en .gitmodules,
restos del layout legacy cpp/apps/ (deprecado tras issue 0096, las apps
C++ viven ahora en apps/). Hacian fallar 'git submodule update' en cada
/full-git-pull. El sub-repo real shaders_lab vive sano en apps/shaders_lab;
chart_demo no existe en disco. Anadido cpp/apps/*/ al .gitignore para que
no recurra (regla apps_subrepo.md: el padre nunca trackea contenido de
artefactos hijos).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
El monitor de captación scrapeaba Workana sobre el navegador personal del
usuario (chromium-personal, CDP 9222), interfiriendo con su navegación. El
scraping CDP debe correr siempre en un perfil headless dedicado.
- Nuevo pipeline monitor_freelance_projects_headless: levanta un Chromium
headless aislado con perfil dedicado (~/.config/fn_scrape_chrome, CDP 9334)
vía systemd-run, ejecuta monitor_freelance_projects contra ese puerto y
cierra la instancia al terminar (finally). Reutiliza el patrón de lifecycle
de ingest_market_trends_headless. Reutiliza un CDP vivo si el puerto ya
responde (no cierra lo ajeno).
- scrape_workana_projects y monitor_freelance_projects: default de `port`
cambiado de 9222 (chromium-personal) a 9334 (perfil dedicado). Default seguro:
correr a pelo sin Chrome en 9334 falla limpio, no contamina el 9222 personal.
Verificado: el wrapper arranca headless en 9334, scrapea 8 proyectos reales de
Workana, cierra la instancia (9334 muerto, sin proceso colgado) y deja el 9222
personal intacto.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Reflects the orchestrator_mcp change: the MCP fleet_list payload now surfaces
pane_id ("%N", the stable per-pane id) and omits tmux_window ("@N"), which
migrates with the focus swap. Documents that focus/send-keys(nudge)/kill
resolve the live window on demand against tmux, and that the nudge reads
tmux_window from the fleetview binary (which keeps it as an internal field),
never from the MCP payload. The binary's list --json field list now mentions
pane_id as the identifier alongside the internal tmux_window.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Grupo de capacidad nuevo 'sql-connect' (3 funciones) para conectar a un
Microsoft SQL Server (donde corre Navision) y consultar directamente, en
lugar del ida y vuelta manual de pegar CSVs.
- mssql_connect_py_infra: abre conexion pymssql (login_timeout acotado,
credenciales por argumento, RuntimeError claro si falla).
- mssql_query_py_infra: SELECT parametrizada con binding seguro (sin
inyeccion) sobre conexion abierta; devuelve {columns, rows, row_count};
0 filas -> lista vacia; max_rows con fetchmany; read-only.
- run_mssql_query_py_pipelines: one-shot que compone connect+query y cierra
siempre; CLI imprime JSON o CSV; contrasena desde env var (pass).
Pagina madre docs/capabilities/sql-connect.md + fila en INDEX.md.
Dependencia pymssql>=2.3.13 anadida a python/pyproject.toml + uv.lock.
Tests mock-based (11) verdes; error path verificado end-to-end contra el
driver real (host inalcanzable -> RuntimeError, acotado por login_timeout).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Reformula la última tarea pedida de forma detallada y estructurada
(objetivo, alcance, entregables, supuestos, criterios de aceptación,
fuera de alcance, dudas) para que usuario y Claude confirmen alineación
antes de ejecutar. No ejecuta la tarea: solo refleja y pregunta.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
orquestador.md + orchestration.md: la deteccion de 'estoy en una flota' se hace
por $TMUX (via detect_fleet_context), NO por $FLEET_SOCKET (fragil). kitty es
fallback SOLO cuando in_tmux=false. spawn_fleet_agent auto-detecta el socket
(ya no hace falta pasar --socket/--session). Documenta la linea CONTEXTO FLEET
del hook y anade detect_fleet_context al catalogo del grupo orchestration.
hook_fleet_state_inject.sh ahora, ademas de MODO ORQUESTADOR, llama a
detect_fleet_context (por $TMUX) e inyecta una linea CONTEXTO FLEET con
socket/session + recordatorio de usar spawn_fleet_agent (nunca kitty) cuando
in_fleet=true. No depende del venv (solo bash+tmux) y se emite antes del bloque
FLEET-STATE. Degrada limpio: si el detector falta o $TMUX esta vacia, no emite
la linea y el turno sigue intacto.
--socket/--session ahora opcionales: si no se pasan, se auto-detectan del
contexto tmux ($TMUX) via detect_fleet_context. Los explicitos siguen
primando. Aborta (exit 2) solo si tras auto-detectar siguen vacios (no hay
tmux). Elimina el bug de caer a kitty cuando $FLEET_SOCKET viene vacia pese a
estar en la flota. Bump v1.2.0 + growth log.
Funcion nueva detect_fleet_context_bash_infra (tag orchestration). Deriva
socket/session de $TMUX (senal fiable que todo proceso dentro de tmux tiene
siempre), con fallback a $FLEET_SOCKET/$FLEET_SESSION. Devuelve JSON
{in_fleet,in_tmux,socket,session,source}. Causa raiz del bug: $FLEET_SOCKET
(exportada con tmux set-environment -g por launch_fleetclaude) a veces viene
vacia en un claude resumido/relanzado pese a vivir en la flota, y el modo
orquestador caia al fallback kitty. .md self-doc (Ejemplo + Cuando usarla +
Gotchas).
Completa la promoción del flujo imagen->3D al registry (grupo de capacidad
img-to-3d), extraído de la app img_to_3d_webapp.
- remove_background_py_datascience (nueva): elimina el fondo con cascada
rembg/U2Net -> OpenCV GrabCut -> umbral NumPy, compone el objeto sobre gris
neutro y devuelve image + mask + engine. Impura, nunca lanza. Adaptada de
backend/bg_removal.py con firma de ruta (image_path) y salida dict, demo CLI
JSON-serializable.
- depth_to_relief_glb_py_datascience (v1.1.0): añade el parámetro opcional mask
para recortar la malla de relieve al objeto (descarta las caras del fondo),
cerrando la cadena con remove_background. Aditivo (mask=None = comportamiento
previo), fiel al original de backend/depth.py.
- docs/capabilities/img-to-3d.md: incorpora remove_background como paso 0
(pre-proceso), actualiza el flujo a 3 pasos encadenados, la tabla de funciones
(4), el ejemplo end-to-end con mask y las deps (rembg/opencv).
- docs/capabilities/INDEX.md: conteo del grupo 3 -> 4.
Las dos funciones ya presentes (estimate_image_depth, depth_to_relief_glb) y el
pipeline build_relief_glb_from_image fueron promovidas en una ronda previa.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
description: Modo ausente — el orquestador itera solo (lanza agentes, verifica cierres, genera tareas del roadmap, push periódico) sin supervisión, hasta que el humano vuelva. Auto-continúa con ScheduleWakeup.
---
# /ausente — orquestador autónomo desatendido
Activa un **loop autónomo del modo orquestador**: el humano se va y tú sigues trabajando solo
—lanzando agentes, verificando sus cierres, cerrando los que cumplen su DoD, generando tareas
nuevas cuando la flota se vacía, y sincronizando— **hasta que el humano vuelva**. Es el modo
orquestador (`.claude/commands/orquestador.md` + `.claude/rules/orchestration.md`) corriendo sin
prompts humanos, con un mecanismo de auto-continuación.
Requisito: estar ya en modo orquestador (`role=orchestrator`). `/ausente` NO sustituye al
orquestador, lo deja en piloto automático.
## Configuración de esta sesión (elegida por el humano)
- **Al vaciarse la flota**: seguir el **roadmap ComfyUI** — generar tareas nuevas sin parar.
- **Git**: **push periódico** — `/full-git-push` tras cada bloque de tareas cerrado.
- **Límite**: **hasta que el humano vuelva** — heartbeat ~25 min + el watcher; tope DURO de 6
ejecutores a la vez; parar en cuanto el humano escriba.
(Si se reinvoca `/ausente` en otra sesión, re-confirmar estas 3 con el humano vía AskUserQuestion.)
## El bucle (cada vez que te re-invocan: por FLEET-DONE del watcher o por el heartbeat)
1.**Drena la flota**: `./fn run drain_fleet_events`. Para cada ejecutor `DICE_TERMINADO`:
**verifica de primera mano** (lee su report + comprueba en disco/CDP que el golden existe — no
te fíes del autodeclarado). Si cumple el DoD → `set_dod_contract <sid> "<c>" met` y **ciérralo
con `kill <PID>` directo** (NUNCA `kill_fleet_agent`/`kill-window`: cierra windows ajenas y se
llevó la console de fleetview — incidente real). Si falla → nudge con el gap concreto.
2.**Nudge** a los `ESTANCADO` (idle > 10 min con DoD sin cerrar). NUNCA a `waiting`.
3.**¿Flota con hueco?** (< 6 ejecutores y hay backlog) → **genera la siguiente tarea del roadmap**
(lista abajo), escribe su prompt autocontenido con aislamiento + DoD-contrato, lánzala con
`spawn_fleet_agent --parent <tu-sid>`, fíjale nombre (`fleet_set_name`) + DoD. Respeta el tope
de 6 y la disjunción de recursos (server/venv/GPU vs functions+fn_index vs disco — ver
`orchestration.md`): solo UN agente dueño del server/venv a la vez; solo UNO toca
`functions/`+`fn index` a la vez; los descargadores de modelos van a carpetas distintas.
4.**Push periódico**: cuando cierres un bloque (>=1 tarea met e integrada), corre
`./fn run full_git_push_bash_pipelines ""` y verifica que el padre queda alineado con
`origin/master`. Diagnostica y reintenta si falla (regla de `/full-git-push`).
5.**Bitácora**: añade una línea al report de bitácora `reports/NNNN-ausente-bitacora.md` (créalo
la primera vez): timestamp + qué cerraste + qué lanzaste + push. Es lo que el humano lee al
volver.
6.**Reprograma el heartbeat**: `ScheduleWakeup(delaySeconds≈1500, prompt="/ausente",
reason="loop ausente: vigilar flota + roadmap ComfyUI")`. Si hay agentes en vuelo, el watcher
te empujará sus FLEET-DONE antes (no hace falta wakeup corto); el heartbeat es el fallback para
cuando la flota está vacía y hay que generar tareas nuevas.
## Supervivencia a la compactación de contexto
El loop es de larga duración → el contexto se llenará. **Cuando te quedes sin contexto, deja que
el harness compacte la conversación y CONTINÚA el modo ausente** — no lo trates como una parada.
El modo sobrevive porque su estado es **durable fuera del contexto**:
- El `ScheduleWakeup(prompt="/ausente")` re-inyecta el modo en cada heartbeat (y el FLEET-DONE del
watcher también te re-entra).
- La **bitácora** `reports/ausente-bitacora-2026-06-24.md` es la memoria persistente: qué se cerró,
qué se lanzó, qué falta del backlog, último push. **Tras una compactación, lo PRIMERO es releer
la bitácora** (y `fleet_list`) para reconstruir el estado y seguir donde lo dejaste.
- Mantén la bitácora al día en CADA turno (no solo al cerrar bloques) para que la compactación
nunca pierda progreso. El comando `/ausente` + `orchestration.md` reconstruyen la doctrina.
Una compactación NO es el humano volviendo — sigue iterando con normalidad.
## Parada
- **El humano vuelve** = recibes un prompt que NO es un FLEET-DONE ni el `/ausente` del heartbeat
(es texto del humano). Entonces: **no reprogrames el wakeup**, resume todo lo hecho durante la
ausencia (lee la bitácora) y vuelve al modo orquestador interactivo normal.
- Si el backlog del roadmap se agota del todo (raro): haz un último push, deja la flota cerrada,
escribe el resumen en la bitácora, programa un heartbeat largo y queda a la espera.
## Reglas duras (más estrictas sin supervisión)
- **Nada destructivo ni irreversible sin el humano**: no borrar datos/modelos/repos, no `git push
--force`, no tocar producción/VPS, no mandar nada hacia afuera (correos, mensajes, APIs con
efecto), no pagar/descargar gated de pago. Ante la duda, NO lo hagas: déjalo anotado en la
bitácora como "pendiente de revisión humana".
- **Aislamiento git por agente** SIEMPRE (sub-repo / worktree / scope disjunto). Ningún agente
commitea el padre salvo el push periódico que corres tú.
- **Tope 6 ejecutores**. Encola el resto.
- **Cierre por `kill <PID>`**, jamás `pkill`/`killall`/`kill_fleet_agent` (protege la TUI/console
de fleetview y a ti mismo).
- **Verificación adversarial**: el golden de cada cierre se comprueba en disco/CDP/ejecución, no
por lo que el agente diga. Honestidad en la bitácora (gaps incluidos).
- Cada agente full-capaz sigue registry-first y delega a `fn-constructor`; tú no escribes lógica
reutilizable inline.
## Backlog del roadmap ComfyUI (fuente de tareas a generar; prioriza arriba→abajo)
Base: `reports/0064-comfyui-roadmap-plan.md` + propuestas de los reports 0069/0073/0075/0079.
1. **Funciones 3D propuestas pendientes**: `comfyui_build_view_3d_workflow`,
description: Genera en un vault Obsidian un resumen capítulo a capítulo de uno o varios libros, siguiendo el formato de notas del vault captacion_clientes (MOC de libro + una nota por capítulo + MOC de categoría, todo enlazado con wikilinks).
---
# /capitulos — resumen de libros capítulo a capítulo en Obsidian
Genera notas de estudio de un libro (o varios) en un vault Obsidian, replicando el formato
canónico del vault `captacion_clientes`: una nota MOC por libro, una nota por capítulo, y una
nota MOC de categoría que agrupa los libros. Todo enlazado con wikilinks `[[ ]]` para que
Obsidian construya el grafo.
## Argumentos
`$ARGUMENTS` contiene, en lenguaje natural, los libros a procesar y opcionalmente el destino.
Interpreta:
- **Libros** — uno o varios títulos. Pueden venir con autor ("Forecasting de Hyndman"). Si el
usuario dice "los libros que me has dicho" o similar, usa los que se recomendaron en la
conversación previa.
- **Vault destino** — si no se especifica, **PREGUNTA** antes de escribir (ver Decisiones).
Vault por defecto de ejemplo de formato: `/home/enmanuel/Obsidian/captacion_clientes`.
- **Categoría** — la subcarpeta bajo `Libros/` que agrupa los libros (ej. "Marca y Mercado",
"Datos e Inversión"). Si no se da, propón una coherente con el tema de los libros y confírmala.
- **Profundidad** — `completo` (default, como The Mom Test: idea central + puntos clave +
citas + aplicación por capítulo) o `breve` (idea central + 3 bullets por capítulo).
## Decisiones a confirmar antes de escribir (si faltan en los argumentos)
Usa `AskUserQuestion` para resolver lo que cambie el trabajo, NO inventes:
1.**Vault y categoría destino** — dónde se crean las notas.
2.**Alcance** — qué libros exactamente y cuántos (si la lista es grande, confirma si son
todos o un subconjunto; cada libro es trabajo no trivial).
3.**Enfoque de "Aplicación"** — el ángulo desde el que se escribe la sección "Aplicación a mi
negocio / a mi caso" de cada capítulo (ej. inversión cuantitativa, data-analyst, SaaS…).
El vault de captación lo orienta al negocio del usuario; mantén ese espíritu pero ajustado
al tema real de los libros.
## Estructura de archivos a crear
```
<vault>/Libros/<Categoría>/
<Categoría> - MOC.md # MOC de categoría (crear o ACTUALIZAR, no sobrescribir)
<Libro>/
<Libro> - MOC.md # MOC del libro
01 - <Título capítulo>.md # una nota por capítulo, NN zero-padded a 2 dígitos
02 - <Título capítulo>.md
...
```
- Carpeta por libro, archivo por capítulo. Nombre de capítulo: `NN - <Título>.md` con `NN`
empezando en `01`. Si el capítulo tiene título original en otro idioma, puedes incluir la
traducción entre paréntesis como en el vault (`01 - The Mom Test (El test de la madre).md`).
- Nombres de archivo sin caracteres que rompan en Obsidian (evita `/`, `:`; los paréntesis y
acentos son válidos).
## Determinar los capítulos de cada libro
Para listar los capítulos reales de un libro:
1. Usa tu conocimiento del libro si lo conoces con fiabilidad (índice real, no inventado).
2. Si no estás seguro del índice exacto, **búscalo en la web** (`WebSearch` / `WebFetch` sobre
la tabla de contenidos del libro) antes de escribir. No inventes capítulos.
3. Indica en el MOC del libro si el índice procede de una edición concreta.
**Regla dura:** nunca te inventes el número o los títulos de los capítulos. Si no puedes
verificarlos, dilo y pregunta al usuario en vez de fabricar un índice plausible.
description: EDA (exploratory data analysis) de una tabla o de una base entera con el grupo `eda` del registry. Perfila, escribe el report (JSON + Markdown + PDF móvil) y monta un analysis Jupyter lanzado en el navegador colaborativo y ejecutado en vivo por Claude.
---
# /eda — Exploratory Data Analysis con el grupo `eda`
Cuando Enmanuel pide un EDA ("hazme un EDA de X", "analiza esta tabla", "qué hay en estos datos"), **no escribas análisis inline**: usa el grupo de capacidad `eda` del registry, escribe los reports y monta el analysis Jupyter en su navegador colaborativo, ejecutando las celdas tú mismo en vivo. Respeta la memoria `eda-workflow-registry` y la regla `.claude/rules/notebook_collaboration.md`.
Página madre del grupo: `docs/capabilities/eda.md` (léela primero para cargar el cluster entero).
## Uso
```
/eda /ruta/datos.duckdb tabla # EDA de una tabla DuckDB
/eda /ruta/datos.csv # CSV/Parquet → cargar a DuckDB y perfilar
/eda postgresql://user:pass@host:5432/db tabla # EDA de una tabla PostgreSQL (backend="postgres")
/eda /ruta/datos.duckdb --all # EDA de TODA la base (todas las tablas + FK + join graph)
/eda /ruta/datos.duckdb ventas --series --pdf # con análisis de serie temporal + PDF móvil
```
`$ARGUMENTS` lleva la fuente y, opcionalmente, la tabla y flags. Interpreta:
- **Fuente**: ruta a `.duckdb`/`.csv`/`.parquet`, o un DSN PostgreSQL (`postgresql://...` o `postgres://...`).
- **Tabla**: nombre de la tabla. Si no se da y la fuente es un único archivo CSV/Parquet, usa su nombre base. Si se pide "toda la base" / `--all`, usa `profile_database`.
- **Flags** (actívalos según lo que pida el usuario; pregunta solo si es ambiguo y costoso):
-`--pdf` → `emit_pdf=True` (PDF A5 legacy de `render_eda_pdf`, legible en móvil).
-`--legacy-only` → emite SOLO el PDF legacy (sin AutomaticEDA), para casos en que solo se quiera el PDF rápido.
-`--lite` / `--bajo-consumo` → `render_automatic_eda(profile_level="lite")`: EDA barato y rápido (CI, vistazo previo, máquina sin GPU/red). Apaga LLM y serie temporal y limita los modelos a **PCA + normalidad** (sin KMeans ni IsolationForest, lo caro en CPU), con `sample` reducido. `--full` → `profile_level="full"` (standard + narrativa LLM). Por defecto `profile_level="standard"` (comportamiento histórico). Un flag explícito (`--llm`, `--models`, ...) prima sobre el preset.
Por defecto, **un EDA completo emite SIEMPRE el informe AutomaticEDA en sus dos formatos: PDF (A5 móvil) Y PPTX (16:9 para compartir)** con los 11 capítulos poblados (portada, overview, distribuciones, calidad, correlaciones, modelos, series, geoespacial, agregación, interpretación LLM). Usa el pipeline `render_automatic_eda` (o `profile_table(emit_automatic=True)`), que activa `run_models` y `run_series` para que los capítulos de modelos/series/geoespacial/agregación salgan poblados. Deja `run_llm` para cuando el usuario lo pida o interese la interpretación semántica + narrativa por capítulo (es la única parte que gasta tokens del modelo).
## Reglas duras
1.**Registry-first**: invoca las funciones del grupo `eda`, no reescribas lógica de perfilado ni de gráficos inline (regla `registry_first.md`).
2.**CSV/Parquet/Excel** entran cargándolos antes a DuckDB (`read_csv_auto`/`read_parquet`/`read_xlsx`) — DuckDB es el motor por defecto. No traigas la tabla entera a RAM.
3.**Secretos**: si la fuente es un DSN PostgreSQL con credenciales, NO las imprimas en los reports ni en el notebook; resuélvelas vía `resolve_pg_dsn`/`pass` cuando aplique.
4.**El report es un artefacto local**: vive en `reports/` (gitignored), no se sube a Gitea ni se versiona. Compartir = pasar la ruta (regla `reports.md`).
5.**Entrega las salidas**: el informe **AutomaticEDA PDF + PPTX** (siempre, con `render_automatic_eda` / `emit_automatic=True`) + (opcional) JSON sidecar + Markdown + PDF legacy + **notebook Jupyter colaborativo ejecutado en vivo**. Comparte las rutas de PDF y PPTX.
from pipelines.profile_database import profile_database
r = profile_database("/ruta/datos.duckdb")
print(r["db_profile"]["join_graph"]["mermaid"])
PYEOF
```
Lee el Markdown resultante y resume a Enmanuel lo esencial: forma, calidad, correlaciones fuertes (ya corregidas por FDR), series no estacionarias, transformaciones sugeridas y avisos exploratorios.
## Paso 2 — Notebook Jupyter colaborativo, ejecutado en vivo por Claude
Sigue la memoria `eda-workflow-registry` y la regla `notebook_collaboration.md`:
1. Genera el notebook con `build_eda_notebook` (mismo perfil de la tabla):
(o crea un analysis dedicado con `fn run init_jupyter_analysis eda_ventas duckdb` y escribe el notebook dentro de `notebooks/`).
2. Confirma que hay Jupyter colaborativo activo con `jupyter_discover` (o lánzalo con el `run-jupyter-lab.sh` del analysis) y **ábrelo en el navegador colaborativo** para que Enmanuel lo vea en vivo.
3. **Ejecuta tú las celdas** (no se las dejes para que las corra él): usa las funciones del dominio `notebook` (`jupyter_exec` append+execute / `jupyter_read`) descritas en `notebook_collaboration.md`, o el MCP `jupyter` si está conectado en la sesión del analysis. Ejecuta de arriba a abajo, comenta cada bloque relevante y deja el notebook navegable.
## Notas
- El `TableProfile` lleva ahora, además del perfilado base y las correlaciones con FDR: `series` (por columna numérica, con `run_series`), `reexpression` por columna numérica (escalera de Tukey) y `caveats` (siempre, avisos exploratorios). El Markdown y el PDF renderizan estas secciones automáticamente cuando están presentes.
- El informe **AutomaticEDA** (`render_automatic_eda` / `emit_automatic=True`) emite el MISMO documento por capítulos a **PDF (A5 móvil)** y **PPTX (16:9)** con garantía de no-corte (texto envuelto, tablas partidas repitiendo cabecera, figuras escaladas) y negrita real (`**texto**`). Escribe `automatic_eda_manifest.json` con la versión de cada capítulo. Los capítulos modelos/series/geoespacial/agregación se pueblan con los datos crudos que `build_eda_render_ctx` muestrea de la base (no se traen tablas enteras a RAM).
- El PDF legacy (`emit_pdf`, `render_eda_pdf`) sigue disponible y es independiente del AutomaticEDA (A5 vertical, gráficos Tufte). Se escribe junto al Markdown en `reports/`.
- `run_series` ordena por la primera columna datetime si existe; si no, por el orden físico de filas. Necesita ≥8 puntos válidos por columna.
- Fuentes: DuckDB (CSV/Parquet/Excel cargados antes) y PostgreSQL (`backend="postgres"`). `profile_database` (multi-tabla + FK) es solo DuckDB por ahora.
description: "Espejo de requisitos: Claude reformula con detalle la última tarea pedida (objetivo, alcance, entregables, supuestos, criterios de aceptación, fuera de alcance y dudas) para confirmar alineación antes de ejecutar. No ejecuta nada."
argument-hint: "[opcional: matiz o foco a tener en cuenta al reformular]"
---
# /equal — confirmar alineación reformulando la tarea pedida
Mecanismo de **espejo de requisitos**. Cuando el usuario invoca `/equal`, NO ejecutas la tarea: devuelves tu interpretación detallada y estructurada del encargo más reciente, para que el usuario confirme o corrija antes de que empieces a trabajar.
El objetivo es eliminar el malentendido silencioso: prefieres gastar un turno reflejando lo que crees que se te pide que arrancar en la dirección equivocada.
## Qué hacer al invocarse
1.**Identifica la tarea más reciente que el usuario te ha pedido** en la conversación actual: la última petición de trabajo real, no el `/equal` en sí ni un comando de utilidad anterior. Si hay `$ARGUMENTS`, úsalos como matiz o foco adicional al reformular (p. ej. "céntrate en el alcance" o "asume que es solo el backend"), no como la tarea nueva.
2.**Reformula esa tarea de forma detallada y estructurada**, con estas secciones (omite una sección solo si es genuinamente no aplicable, no para abreviar):
- **Objetivo** — qué se quiere conseguir, en una o dos frases claras. El "para qué", no solo el "qué".
- **Alcance / qué incluye** — los trozos concretos de trabajo que entiendes incluidos. Lista, no párrafo.
- **Entregables** — qué archivos, cambios, salidas o artefactos concretos vas a producir.
- **Supuestos** — lo que estás asumiendo por defecto al no estar dicho explícitamente (stack, ubicación, convenciones, datos, alcance temporal). Hazlos visibles para que el usuario los pueda tumbar.
- **Criterios de aceptación** — cómo sabremos que está bien hecho. Condiciones verificables, no deseos vagos. Cuando aplique, golden + edge + caso de error (alineado con `dod_quality.md`).
- **Fuera de alcance** — lo que NO vas a hacer, para acotar expectativas y evitar scope creep.
- **Dudas / ambigüedades a confirmar** — preguntas concretas sobre lo que no está claro. Numéralas para que el usuario pueda responder por número. Si no hay dudas reales, dilo explícitamente ("sin dudas bloqueantes").
3.**Cierra pidiendo validación**: una línea clara del tipo "¿Alineado? Corrige lo que no cuadre y arranco." No empieces a trabajar hasta que el usuario confirme.
## Caso sin tarea previa
Si **no hay** una petición de trabajo clara que reformular (la conversación está vacía de encargos, o lo último fueron solo comandos de utilidad / charla), NO inventes una tarea. Dilo directamente y pide al usuario que describa qué quiere hacer, ofreciéndole la misma estructura como guía de qué información es útil (objetivo, entregables, criterios).
## Reglas duras
- **No ejecutas la tarea.** `/equal` solo refleja y pregunta. Nada de editar archivos, lanzar comandos de trabajo ni delegar a subagentes para "ir avanzando".
- **No inventes requisitos.** Lo que no se ha pedido va a *Supuestos* (marcado como asunción) o a *Dudas* (como pregunta), nunca colado como hecho.
- **Refleja lo que se pidió, no lo que crees que debería pedirse.** Si detectas una mejora obvia, propónla como una duda al final, no la incorpores al objetivo.
- **Sé concreto.** Evita reformulaciones genéricas que valdrían para cualquier tarea. Cita los detalles reales del encargo (nombres de archivos, rutas, tecnologías, cifras que el usuario mencionó).
- Mantén el comando **simple**: es un reformulador de requisitos, no un planificador de implementación ni un generador de tareas.
## Ejemplo de salida
Para una petición previa como *"añade un endpoint `/health` al servicio de deploy que devuelva 200 y la versión"*:
```
## Objetivo
Añadir un health check HTTP al deploy_server para poder verificar de forma
programática que el servicio está vivo y saber qué versión corre.
## Alcance / qué incluye
- Nuevo handler GET `/health` en el servidor HTTP de deploy_server.
- Respuesta 200 con un cuerpo JSON que incluya la versión del binario.
- Registro de la ruta en el router existente.
## Entregables
- Cambio en el código del servidor de `apps/deploy_server/` (handler + ruta).
- Sin migración de BD (no toca schema).
## Supuestos
- La versión a devolver es la que ya expone el binario (no hay que crear un
sistema de versionado nuevo).
- Formato de respuesta JSON `{"status":"ok","version":"x.y.z"}`.
- Sin autenticación en `/health` (endpoint público de liveness).
## Criterios de aceptación
- Golden: `GET /health` con el servicio arriba → 200 + JSON con `version`.
- Edge: la versión se lee correctamente aunque el binario se compile sin tag.
- Error: si un subsistema crítico no está listo, devuelve 503, no 200 falso.
## Fuera de alcance
- Readiness check de dependencias remotas (VPS, SSH).
- Métricas / observabilidad más allá del 200.
## Dudas / ambigüedades a confirmar
1. ¿`/health` debe comprobar algo real (DB, disco) o basta con responder vivo?
2. ¿La versión sale de un ldflag de build, de un fichero, o de constante?
3. ¿Puerto y router son los que ya usa `deploy_server serve`?
¿Alineado? Corrige lo que no cuadre y arranco.
```
El ejemplo es ilustrativo del **formato y el nivel de detalle**; el contenido real sale siempre de la tarea concreta que el usuario haya pedido en la conversación.
description: Muestra la flota de Claudes vivos (sessionId + objetivo + estado) y, con argumento, salta con foco a esa conversación dentro de la sesión tmux fleet.
argument-hint: "[texto|sessionId|PID para saltar — vacío = listar la flota]"
description: Muestra la flota de Claudes vivos (sessionId + objetivo + estado) y, con argumento, salta con foco a esa conversación dentro de la sesión tmux fleet.`/fleet show` trae la TUI al contexto tmux actual.
argument-hint: "[show | texto|sessionId|PID para saltar — vacío = listar la flota]"
---
# /fleet — ver y navegar la flota de Claudes
@@ -33,9 +33,32 @@ cd "${FN_REGISTRY_ROOT:-$HOME/fn_registry}/apps/fleetview" && go build -o fleetv
- la sesión actual / orquestador si la puedes identificar (su `session_id` coincide con el de quien invoca).
4. Si la lista está vacía, indícalo y sugiere que el perfil fleet podría no estar activo (revisar `$FLEET_SOCKET` y que la sesión tmux exista).
### `show` → traer la TUI al contexto tmux actual
Si `$ARGUMENTS` es exactamente `show` (alias `open`/`attach`), el usuario quiere
volver a ver el panel FleetView en el contexto/pane actual sin abrir ninguna
ventana ni arrancar una flota nueva. Ejecuta:
```bash
"${FN_REGISTRY_ROOT:-$HOME/fn_registry}/apps/fleetview/fleetview" show
```
Comportamiento (decidido por la app, no abre terminal externa):
- **dentro de tmux con la flota viva** → `select-window` de la window `console`
del socket fleet (trae la TUI al frente; no abre nada).
- **fuera de tmux** → `attach` a la sesión fleet en la terminal actual (la reutiliza).
- **sin flota viva** → error claro, exit 1, no abre nada (sugiere arrancarla con
`fleetclaude`).
Es el equivalente del comportamiento de `fleetclaude` sin args invocado dentro de
una flota viva (reuse de contexto): úsalo cuando ya tengas una flota corriendo y
solo quieras recuperar la vista del panel. Para abrir una flota NUEVA aparte, usa
`fleetclaude --new` (no este comando).
### Con argumentos → saltar con foco
El usuario quiere que la interfaz tmux salte a una conversación concreta. `$ARGUMENTS` es el query: texto del objetivo, prefijo de `sessionId`, o PID.
El usuario quiere que la interfaz tmux salte a una conversación concreta. `$ARGUMENTS` es el query: texto del objetivo, prefijo de `sessionId`, o PID (cualquier valor que no sea `show`).
El agente lee `dev/issues/*.md`, parsea frontmatter YAML con `yaml.safe_load`, aplica el filtro, imprime tabla.
El agente lee `dev/issues/**/*.md` (recursivo: incluye subcarpetas por dominio como `dev/issues/kanban/`, `dev/issues/cpp/`, ... excluyendo `completed/`), parsea frontmatter YAML con `yaml.safe_load`, aplica el filtro, imprime tabla.
zsh'`) que sobrevive al cierre de la terminal padre y deja una shell viva al terminar el claude;
devuelve el log de arranque (`/tmp/orq_<slug>_kitty.log`). Usa kitty solo fuera de un perfil fleet.
devuelve el log de arranque (`/tmp/orq_<slug>_kitty.log`). Usa kitty solo cuando NO estás en tmux
(`$TMUX` vacía); estando en una flota, kitty fragmenta la flota — usa `spawn_fleet_agent`.
### 3. Aislamiento git obligatorio por secundario (regla de oro)
@@ -204,8 +228,8 @@ Cuando un secundario termina (rama pusheada + report verde):
**Todo agente de trabajo va como terminal visible del fleet, NUNCA como sub-agente headless del Agent tool.** Un sub-agente headless corre invisible: no sale en `fleetview`, no es conmutable con `/fleet focus` ni se puede retomar. Jerarquía al lanzar un agente:
1. **En perfil fleet** (`$FLEET_SOCKET`, lo normal) → `spawn_fleet_agent` (window de la flota tmux).
2. **Fuera de un perfil fleet** → kitty con `launch_claude_agent_kitty`.
1. **Dentro de tmux/flota** (`$TMUX` seteada — comprueba con `detect_fleet_context`, NO con `$FLEET_SOCKET`) → `spawn_fleet_agent` (auto-detecta el socket; window de la flota tmux).
2. **Fuera de tmux** (`in_tmux=false`) → kitty con `launch_claude_agent_kitty`.
3. **Agent tool (sub-agente headless)** → **PROHIBIDO para lanzar un agente de trabajo.** SOLO para
utilidades internas read-only tuyas que devuelven un resultado y mueren: el **verificador**
adversarial de un cierre, el **splitter** (`Plan`), o una búsqueda puntual (`Explore`).
@@ -13,7 +13,7 @@ IDs: `{name}_{lang}_{domain}` (ej: `filter_slice_go_core`). Predictibilidad alta
Lista no exhaustiva pero cubre la mayoria. Anadir aqui (y al validator en `apps/registry_mcp/naming.go`) cuando se introduzca un verbo nuevo recurrente.
apps/fleetview/fleetview list --json # flota tipada: session_id, goal, phase, status, pane_id ("%N", el id estable), tmux_window ("@N", interno para focus/send-keys), age, idle_seconds
apps/fleetview/fleetview list # tabla legible (incluye columna AGE)
```
@@ -58,7 +58,7 @@ devuelven salida estructurada y se registran en la telemetría como cualquier MC
| Operación de la flota | Tool MCP (preferido) | Fallback `./fn run` / binario |
|---|---|---|
| Listar la flota tipada (session_id, goal, phase, status, **role, dod_contract, dod_status**, tmux_window, age, idle_seconds) | `mcp__orchestrator__fleet_list` | `apps/fleetview/fleetview list --json` (NO `./fn run list_claude_fleet`) |
| Listar la flota tipada (session_id, goal, phase, status, **role, dod_contract, dod_status**, **pane_id** (el id estable), age, idle_seconds) | `mcp__orchestrator__fleet_list` | `apps/fleetview/fleetview list --json` (NO `./fn run list_claude_fleet`) |
| Drenar la cola de transiciones del watcher (agrupada por clasificación + urgentes) | `mcp__orchestrator__fleet_drain` (`advance` true consume, false hace peek) | `./fn run drain_fleet_events` |
| Clasificar el estado de terminación de UN agente (RECLAMA/MAL_LANZADO/DICE_TERMINADO/ESTANCADO/TRABAJANDO) | `mcp__orchestrator__fleet_classify` | (Go con tests; lo consume el watcher, no se invoca a mano) |
| Escribir el DoD-contrato fijo (`dod_contract`/`dod_status`) en el `goal.json` de un agente | `mcp__orchestrator__fleet_set_dod` | `./fn run set_dod_contract` |
@@ -69,6 +69,16 @@ Ventaja extra de `fleet_list`: expone `role`/`dod_contract`/`dod_status` directa
vacíos desde el sidecar `goal.json`), así que la regla "No te vigiles a ti mismo" se resuelve sin leer
el sidecar a mano — filtra por el `role` que ya trae cada fila.
**Identifica a cada agente por su `pane_id` ("%N").** Es el id ESTABLE de por vida del pane: el
`fleet_list` del MCP lo expone como el único identificador y **omite a propósito el `tmux_window`
("@N")**, que migra cuando el focus-swap mueve el pane entre windows y por eso nunca debe usarse ni
mostrarse como id (la persona no tiene referencia mental de "@4"). Las operaciones internas que sí
necesitan la window/pane viva — `focus`, `send-keys`/nudge y `kill` — la resuelven BAJO DEMANDA contra
tmux a partir del session_id/PID (`kill_fleet_agent` y `fleetview focus` la recalculan por llamada).
Para el **nudge** NO leas ni caches el `@N`: usa `fleet_send_text` (grupo `orchestration`), que resuelve
el `pane_id` (`%N`) ESTABLE fresco a partir del `sessionId`/PID en el momento del envío — el `@N` migra
con el focus-swap y mandaría el texto al agente equivocado (ver sección Nudge).
Mantén una **tabla de seguimiento**, una fila por secundario, y actualízala en cada turno:
| slug | título kitty | PID | cwd / dir aislado | rama | log | report | estado |
@@ -123,6 +133,21 @@ existe, degrada limpio sin romper el turno (la línea de rol se sigue emitiendo)
clasificación sigues drenando (abajo). El resumen lo produce `summarize_fleet_transitions_py_infra`
sobre el feed del watcher.
Además, el mismo hook inyecta una línea **`CONTEXTO FLEET`** cuando detecta (vía
`detect_fleet_context_bash_infra`, leyendo **`$TMUX`**, no `$FLEET_SOCKET`) que el orquestador vive
dentro de una flota tmux:
```
CONTEXTO FLEET: estás dentro de la fleet tmux socket=<X> session=<Y>. Lanza ejecutores con spawn_fleet_agent (auto-detecta el socket) — NUNCA kitty/launch_claude_agent_kitty estando aquí.
```
Es el recordatorio que evita el bug de caer a kitty cuando `$FLEET_SOCKET` viene vacía pese a estar
en la flota: la detección de contexto se hace por `$TMUX` (señal fiable que todo proceso dentro de
tmux tiene siempre), no por `$FLEET_SOCKET` (a veces ausente en un claude resumido/relanzado). Esta
parte del hook no necesita venv ni python (solo bash + tmux) y se emite antes del bloque
`FLEET-STATE`; si el detector falta o `$TMUX` está vacía, simplemente no se emite la línea (turno
intacto).
Gotcha conocido: el bloque `FLEET-STATE` (peek pasivo) lista transiciones de TODA la flota, incluidas
las de otros orquestadores y sus ejecutores. Si hay más de un orquestador activo, filtra por tu propia
familia de agentes (los que tú lanzaste) — igual que en "No te vigiles a ti mismo" más abajo. El **push
@@ -238,18 +263,24 @@ verificas → `kill_fleet_agent` libera el slot. No uses `pkill`/`killall` ni `k
### Nudge — `ESTANCADO`
Agente idle con `dod_contract` sin cumplir y sin actividad > umbral (10 min). Empújalo a cerrar SU DoD
inyectando en su pane tmux:
inyectando texto en su pane con la función `fleet_send_text` (grupo `orchestration`):
**NO uses `tmux send-keys -t <window_id @N>` a mano para esto.** El `window_id` (`@N`, p.ej. `@20`) que
expone `fleetview list --json` MIGRA cuando el focus-swap recrea windows (`break-pane`+`join-pane`):
`@32` → `@34`. Enviar al `@N` viejo (cacheado por el bloque `FLEET-STATE` o leído un instante antes)
manda el texto al window equivocado o a otro agente — esa era la causa de "el nudge a veces no llega al
agente correcto". `fleet_send_text` nunca usa `@N`; usa el `pane_id` (`%N`), que no migra.
**Solo a idle/ESTANCADO. JAMÁS a un agente en `waiting`/`preguntando`** — esos te reclaman a TI, no un
empujón del bot.
@@ -302,16 +333,18 @@ en lote.
| `summarize_fleet_transitions_py_infra` | Resumir las transiciones del feed en una línea (`terminados/reclaman/estancados`); alimenta el bloque `FLEET-STATE` que el hook `UserPromptSubmit` inyecta cada turno |
| `classify_fleet_termination_go_infra` | Clasificar el estado de terminación de un agente (RECLAMA/MAL_LANZADO/DICE_TERMINADO/ESTANCADO/TRABAJANDO) — lo usa el watcher |
| `list_claude_fleet_go_infra` | Fleet tipado con goal/phase/`role` + `dod_contract`/`dod_status` + `tmux_window` (alimenta `/fleet`, el watcher y el tool `fleet_list`). **Invócala por el tool `mcp__orchestrator__fleet_list` (preferido) o el binario `apps/fleetview/fleetview list --json`**, NUNCA por `./fn run` (la despacha como `go test`). El JSON del CLI **ya expone** `role`/`dod_contract`/`dod_status` (`""` si el `goal.json` no los declara); el tool MCP además rellena los vacíos desde `~/.claude/goals/<session_id>.json` |
| `spawn_fleet_agent_bash_infra` | Lanzar un ejecutor (o el orquestador) como window de la flota tmux — preferido sobre kitty cuando hay perfil fleet. `--parent <tu-sessionId>` atribuye el ejecutor a ti y habilita el push activo del watcher |
| `detect_fleet_context_bash_infra` | Detectar si estás en una flota tmux derivando socket/session de `$TMUX` (señal fiable), con fallback a `$FLEET_SOCKET`. Devuelve JSON `{in_fleet,in_tmux,socket,session,source}`. Lo usan `spawn_fleet_agent` (auto-detección de socket) y el hook (línea `CONTEXTO FLEET`) para no caer a kitty estando en la flota |
| `spawn_fleet_agent_bash_infra` | Lanzar un ejecutor (o el orquestador) como window de la flota tmux — preferido sobre kitty siempre que estés en tmux. **Auto-detecta socket/session de `$TMUX`** (vía `detect_fleet_context`) si no se pasan `--socket`/`--session` (los explícitos priman). `--parent <tu-sessionId>` atribuye el ejecutor a ti y habilita el push activo del watcher |
| `mark_claude_role_py_infra` | Marcar `role` (orchestrator/executor) en el goal.json de un Claude resolviendo PID→sessionId |
| `mark_claude_parent_py_infra` | Marcar `parent_orchestrator` (sessionId del orquestador que lo lanzó) en el goal.json de un ejecutor resolviendo PID→sessionId. Lo invoca `spawn_fleet_agent --parent`; habilita el routing del watcher al pane del orquestador padre |
| `kill_fleet_agent_bash_infra` | Cierre dirigido de UN ejecutor: SIGTERM al claude + kill-window de su window tmux. Guards anti-orquestador y anti-self. Lo usa el orquestador para liberar el slot idle tras verificar `met` (auto-kill) |
| `fleet_send_text_bash_infra` | Empujar texto al input de UN agente (nudge) resolviendo su `pane_id` (`%N`) ESTABLE FRESCO justo antes de enviar — NO el `window_id` (`@N`), que migra con el focus-swap y manda el texto al agente equivocado. Texto literal + `Enter` en invocaciones separadas, verificado con `capture-pane` + reintento. Guard anti-self. Reemplaza el `tmux send-keys -t <@N>` manual del nudge |
| `notify_desktop_go_infra` | Notificación de escritorio del fleet (`notify-send --app-name=fleetview`, degradación silenciosa si no hay `notify-send`). La usa el orquestador/watcher para avisar a la persona de un `RECLAMA` u otro evento urgente cuando no está mirando la terminal |
**Cómo invocarlas.** Las Bash y Python del grupo se lanzan con `./fn run <id> [args]` (verificado:
IN_FLEET=$(printf'%s'"$CTX"| sed -n 's/.*"in_fleet":\(true\|false\).*/\1/p')
F_SOCKET=$(printf'%s'"$CTX"| sed -n 's/.*"socket":"\([^"]*\)".*/\1/p')
F_SESSION=$(printf'%s'"$CTX"| sed -n 's/.*"session":"\([^"]*\)".*/\1/p')
if["$IN_FLEET"="true"];then
printf'CONTEXTO FLEET: estas dentro de la fleet tmux socket=%s session=%s. Lanza ejecutores con spawn_fleet_agent (auto-detecta el socket) — NUNCA kitty/launch_claude_agent_kitty estando aqui.\n'"$F_SOCKET""$F_SESSION"
description: "Compila una app C++ del registry (cpp/apps/<name>) a WASM via emscripten. Sale build/wasm/<name>/<name>.{html,js,wasm,wasm.gz}. Falla si gzip > 2 MB."
description: "Detecta de forma robusta si el proceso corre dentro de una flota tmux FleetView, derivando socket y sesion de $TMUX (senal fiable) en vez de $FLEET_SOCKET (fragil, a veces vacia en un claude resumido/relanzado). Salida JSON con in_fleet/in_tmux/socket/session/source."
desc: "No recibe argumentos. Lee el entorno ($TMUX, con fallback a $FLEET_SOCKET/$FLEET_SESSION) y consulta el servidor tmux."
output: "JSON en stdout: {\"in_fleet\":bool, \"in_tmux\":bool, \"socket\":str, \"session\":str, \"source\":\"tmux|fleet_socket|none\"}. in_tmux=true basta para lanzar una window; in_fleet es la senal semantica de 'estoy en una flota'."
---
# detect_fleet_context
Detecta el contexto de flota del proceso actual sin depender de `$FLEET_SOCKET`.
## Por que existe
La deteccion de "estoy en una flota FleetView" dependia de la variable de
entorno `$FLEET_SOCKET`, que `launch_fleetclaude` exporta con
`tmux set-environment -g`. Esa variable solo llega a los procesos que tmux
arranca **despues** de setearla: un `claude` relanzado o resumido a mano puede
no heredarla y `$FLEET_SOCKET` queda vacia, aunque ese claude SI viva en una
window de la flota. Cuando eso pasa, el modo orquestador cae al fallback kitty
(`launch_claude_agent_kitty`) y lanza ejecutores en terminales sueltas en vez de
como windows de la flota.
La senal **fiable** es `$TMUX`: todo proceso dentro de tmux la tiene SIEMPRE, con
el formato `/tmp/tmux-<uid>/<socket>,<server_pid>,<client_id>`. De ahi se extrae
el socket (basename del path antes de la primera coma) y, con
`tmux -L <socket> display-message -p '#{session_name}'`, la sesion actual.
| `in_fleet` | Heuristica de "estoy en una flota". `true` si en tmux Y (socket/sesion casan `fleet`, O hay window `fleetview`, O la sesion tiene >= 2 windows). |
| `in_tmux` | `true` si el proceso esta dentro de tmux. Basta para lanzar una window (mejor que caer a kitty). |
| `socket` | Socket tmux derivado de `$TMUX` (o de `$FLEET_SOCKET` en fallback). |
| `session` | Sesion tmux actual resuelta con `display-message` (fallback a `$FLEET_SESSION` o al socket). |
| `source` | `tmux` (derivado de `$TMUX`), `fleet_socket` (fallback), o `none`. |
description: "Empuja texto a UN agente de la flota tmux de forma fiable, resolviendo su pane_id (%N) ESTABLE FRESCO justo antes de enviar. Es el reemplazo del nudge antiguo del orquestador, que apuntaba al window_id (@N) leido del JSON de la flota: ese @N MIGRA cuando el focus-swap de FleetView (break-pane + join-pane) recrea windows, asi que enviar al @N viejo (cacheado por el bloque FLEET-STATE o leido un instante antes) mandaba el texto al window equivocado o a otro agente. fleet_send_text resuelve sessionId -> PID (sessions/<PID>.json) -> el pane cuyo proceso (o un ancestro suyo en /proc) es pane_pid, leyendo tmux list-panes -a en el momento del envio, y usa el pane_id (%N) que NO migra. Ademas manda el texto literal (send-keys -l) y el Enter en invocaciones SEPARADAS, verificando con capture-pane que el texto aparecio en el input antes de pulsar Enter; reintenta si no aparece. Guards: NO envia a tu propio pane; error claro si el target no resuelve a un pane vivo. Por defecto EJECUTA; --dry-run imprime el plan sin enviar."
desc: "Primer arg posicional: sessionId del agente (exacto o prefijo) o su PID (todo digitos). Por sessionId se busca en sessions/*.json el que case y su archivo (<pid>.json) da el PID; por PID se usa directo."
- name: texto
desc: "Segundo arg posicional: el texto a inyectar en el input del agente (entre comillas)."
- name: --socket
desc: "Socket tmux del perfil FleetView donde vive el pane. Default: $FLEET_SOCKET, o 'fleet' si no esta seteada."
- name: --no-enter
desc: "Deja el texto en el input sin pulsar Enter (no hace submit). Por defecto envia el Enter en una invocacion separada tras el texto."
- name: --retries
desc: "Numero de reintentos si el texto no aparece en el pane tras el send (default 2). Cada reintento limpia el input con C-u antes de reenviar."
- name: --dry-run
desc: "Imprime el plan (PID, sessionId, pane, socket) y NO envia nada. Sin esto, ejecuta."
output: "Imprime una linea de plan (target, PID, sessionId, socket, pane resuelto, modo de envio) y una linea final parseable 'pane=%N intento=N status=ok|dry-run'. Exit 0 ok/dry-run; 2 uso incorrecto o target no resuelto a PID; 3 guard (target es la sesion actual); 4 no se encontro pane vivo para el target; 5 enviado pero no verificado tras los reintentos."
---
# fleet_send_text
Empuja texto al input de **un** agente de la flota tmux de forma fiable. Resuelve el `pane_id` (`%N`) **estable** del agente **fresco** justo antes de enviar (nunca cachea el `window_id``@N`, que migra con el focus-swap), manda el texto literal y el `Enter` en invocaciones **separadas**, y verifica con `capture-pane` que el texto llegó antes de hacer submit. Es el reemplazo del patrón de nudge antiguo (`tmux send-keys -t <window_id @N>`), que fallaba "a veces" porque enviaba al window equivocado tras un focus-swap.
## Ejemplo
```bash
# Nudge a un ejecutor estancado por sessionId (el orquestador lo llama tras detectar ESTANCADO):
./fn run fleet_send_text 32945650-a4e1-472b-90c9-5b38ef60a463 \
"Sigues idle con tu DoD-contrato sin cerrar. Falta: el error path con evidencia. Cierralo o reporta el bloqueo."\
--socket "$FLEET_SOCKET"
# Por prefijo de sessionId, en el socket por defecto ($FLEET_SOCKET o "fleet"):
./fn run fleet_send_text 32945650"Recuerda pushear la rama antes de cerrar."
# Dejar texto en el input sin hacer submit (--no-enter), o solo ver el plan (--dry-run):
./fn run fleet_send_text 48213"borrador..." --no-enter
./fn run fleet_send_text 48213"texto" --dry-run
```
## Cuando usarla
Úsala desde el modo orquestador siempre que necesites **inyectar texto en el input de un agente** de la flota: el **nudge** a un `ESTANCADO`, el aviso de un gap concreto a un ejecutor cuyo cierre falló la verificación, o cualquier mensaje dirigido. Sustituye al `tmux send-keys -t <window_id>` manual. Resuelve el target por sessionId (exacto o prefijo) o por PID. **Solo a idle/ESTANCADO; jamás a un agente en `waiting`/`preguntando`** (esos te reclaman a ti, no un empujón del bot). Para *cerrar* un ejecutor verificado `met` no es esto: usa `kill_fleet_agent`.
## Gotchas
- **El bug que arregla — el `window_id` (`@N`) MIGRA**: el focus-swap de FleetView (`tmux_swap_window_into_console.go`) trae el claude objetivo a la console con `break-pane` + `join-pane`, lo que **recrea windows** y cambia el `@N` del agente (`@32` → `@34`). El bloque `FLEET-STATE` y el JSON de la flota pueden traer un `@N` ya viejo. Enviar a ese `@N` manda el texto al window equivocado o a otro agente. Esta función NUNCA usa `@N`: resuelve el `pane_id` (`%N`), que se **preserva** durante toda la vida del pane aunque el pane se mueva de window. Verificado en test: tras `break-pane` el `window_id` pasa de `@0` a `@1` pero el `pane_id` sigue `%0` y el envío sigue llegando.
- **Resolución fresca**: el mapa `pane_pid → pane_id` se lee con `tmux -L <socket> list-panes -a`**en el momento del envío**, no se cachea. La resolución sube por los ancestros de `/proc` desde el PID del agente hasta casar un `pane_pid`: cubre tanto `exec claude` (pane_pid == claude pid, match directo, como hace `spawn_fleet_agent`) como un claude lanzado bajo un shell (pane_pid == shell ancestro).
- **Texto y Enter separados**: el texto va con `send-keys -l` (literal, sin interpretar nombres de tecla), luego `sleep 0.3`, y el `Enter` en una **invocación aparte**. Mandar texto+Enter juntos hace que el TUI de Claude Code a veces no interprete el Enter como submit. La verificación con `capture-pane` se hace **antes** del Enter (tras el submit el TUI vacía el input y no se podría comprobar). Si el texto no aparece, limpia el input con `C-u` y reintenta (`--retries`, default 2).
- **Impura**: inyecta teclas en un pane ajeno. Por defecto EJECUTA; usa `--dry-run` para inspeccionar el plan antes.
- **Guard anti-self**: resuelve el PID de `claude` de la sesión actual subiendo por los ancestros de `/proc`; si el target coincide, rehúsa con exit 3 ("No me autoenvio").
- **Verificación por fragmento ancla**: comprueba que aparezcan los primeros 24 caracteres del texto (no el texto completo) para no dar falso negativo cuando el input del TUI wrapea un mensaje largo en varias líneas.
- **Socket**: si no pasas `--socket`, usa `$FLEET_SOCKET` o `"fleet"`. Si el agente no está en ese socket, no se encontrará el pane (exit 4).
description: "Cierre limpio y dirigido de UN ejecutor de la flota tmux. Dado un sessionId (exacto o prefijo) o un PID, manda SIGTERM al proceso claude del ejecutor (cierre limpio, recuperable con claude --resume) y cierra su window tmux (kill-window) en el socket del perfil FleetView. Lo usa el orquestador para liberar el slot idle de cada ejecutor en cuanto verifica que su DoD-contrato esta met. Guards de seguridad: NUNCA mata a un agente con role=orchestrator (leido de su goal.json) ni a la sesion que invoca la funcion (resuelve su propio PID de claude por los ancestros de /proc). Por defecto EJECUTA; --dry-run imprime el plan sin tocar nada. Es el cierre dirigido a UN agente, frente a reboot_all_claudes que opera sobre toda la flota."
description: "Cierre limpio y dirigido de UN ejecutor de la flota tmux. Dado un sessionId (exacto o prefijo) o un PID, manda SIGTERM al proceso claude del ejecutor (cierre limpio, recuperable con claude --resume) y cierra su window tmux en el socket del perfil FleetView. Lo usa el orquestador para liberar el slot idle de cada ejecutor en cuanto verifica que su DoD-contrato esta met. Tres guards de seguridad: NUNCA mata a un agente con role=orchestrator (leido de su goal.json); NUNCA a la sesion que invoca la funcion (resuelve su propio PID de claude por los ancestros de /proc); y NUNCA cierra la window que aloja la TUI fleetview o la window 'console' con kill-window (eso se llevaria el panel de control por delante) — en ese caso cierra SOLO el pane del target con kill-pane y preserva la TUI. Por defecto EJECUTA; --dry-run imprime el plan (incluida la accion kill-pane vs kill-window) sin tocar nada. Es el cierre dirigido a UN agente, frente a reboot_all_claudes que opera sobre toda la flota."
@@ -55,11 +56,13 @@ Cierra de forma dirigida UN ejecutor de la flota tmux: SIGTERM al proceso `claud
- **Impura y destructiva**: manda SIGTERM y cierra una window tmux. Por defecto EJECUTA (es el caso de uso del bot: cerrar un ejecutor ya verificado `met`); usa `--dry-run` para inspeccionar antes.
- **Guard anti-orquestador**: si el goal.json del target tiene `role=orchestrator`, rehúsa con exit 3. Evita decapitar la flota por error. El `role` se lee de `~/.claude/goals/<sessionId>.json` (lo escribe `mark_claude_role`).
- **Guard anti-self**: resuelve el PID de `claude` de la sesión actual subiendo por los ancestros de `/proc`; si el target coincide, rehúsa con exit 3 ("No me suicido"). Es el equivalente dirigido de la regla "nunca `pkill claude`".
- **Resolución de la window**: usa `tmux -L <socket> list-panes -a` y casa `pane_pid == PID`. Funciona porque `spawn_fleet_agent` arranca el ejecutor con `exec claude`, así el `pane_pid` ES el PID de claude. Si no hay socket/tmux, la window queda "(no resuelta)" y solo se manda el SIGTERM (best-effort, no falla).
- **Guard 3 — anti-TUI/console (no decapitar el panel)**: antes de cerrar nada, comprueba si la window del target **aloja la TUI fleetview** (algún pane corre el binario `fleetview`) o se llama **`console`**. El layout FleetView mete la TUI y un Claude en la misma window `console`, y los focus-swaps (`join-pane`) pueden meter al ejecutor target en esa window; un `kill-window` ahí se llevaría la TUI por delante (causa del fallo descrito en `fleetview` v0.4.3). En ese caso la función NO usa `kill-window`: manda el SIGTERM al claude y cierra **solo su pane** con `kill-pane`, preservando el pane de la TUI. El plan (y el `--dry-run`) lo refleja como `accion: kill-pane … (aloja la TUI/console)` vs `accion: kill-window …`. El predicado es la función interna `_fleet_window_hosts_tui` (testeada). Se mantiene inline (no función propia del registry) por estar acoplada a este flujo y para no dejar una capacidad huérfana (KISS).
- **Resolución de la window y el pane**: usa `tmux -L <socket> list-panes -a` y casa `pane_pid == PID`, capturando `window_id`, `pane_id` y `window_name`. Funciona porque `spawn_fleet_agent` arranca el ejecutor con `exec claude`, así el `pane_pid` ES el PID de claude. Si no hay socket/tmux, la window queda "(no resuelta)" y solo se manda el SIGTERM (best-effort, no falla).
- **SIGTERM, no SIGKILL**: cierre limpio para que Claude Code persista su sesión; el trabajo se puede retomar con `claude --resume <sessionId>`.
- **Requiere `jq`** para leer los JSON de sessions/goals.
- **Overrides de entorno solo para tests**: `FN_FLEET_SESSIONS_DIR`, `FN_FLEET_GOALS_DIR` y `FN_FLEET_SELF_PID` redirigen los directorios y fuerzan el PID propio; no usarlos en operación normal.
## Capability growth log
(v1.0.0 — sin cambios todavía.)
- v1.1.0 (2026-06-24) — **Guard 3 anti-TUI/console** (elimina un gotcha conocido). Antes, si un focus-swap metía al ejecutor target en la window `console` (la que aloja la TUI fleetview), `kill-window` cerraba la TUI por error. Ahora, cuando la window del target aloja la TUI (pane `fleetview`) o se llama `console`, se cierra solo el pane del target con `kill-pane` y la TUI sobrevive; el resto de windows siguen cerrándose con `kill-window`. Predicado interno `_fleet_window_hosts_tui` con tests. Es la causa raíz que complementa el auto-respawn de la TUI (`supervise_fleetview_tui`).
description: "Entrypoint de FleetView: abre una ventana kitty con una sesion tmux (socket aislado por perfil) de dos panes (TUI fleetview a la izquierda, claude --dangerously-skip-permissions a la derecha) para centralizar la flota de Claudes. Soporta PERFILES multiples: sin --session/--reuse cada invocacion abre un perfil nuevo (fleet, fleet2, fleet3, ...) con su propia flota; inyecta FLEET_SOCKET/FLEET_SESSION a la TUI para que cada panel vea solo sus Claudes. Instala atajos alt+flechas/alt+enter/alt+n que controlan la TUI desde cualquier pane, y fija el ancho del sidebar con hooks."
description: "Entrypoint de FleetView: abre una ventana de terminal con una sesion tmux (socket aislado por perfil) de dos panes (TUI fleetview a la izquierda, claude --dangerously-skip-permissions a la derecha) para centralizar la flota de Claudes. REUSO DE CONTEXTO: si se invoca DENTRO de una flota tmux viva (su window 'console') sin --new, NO abre ventana ni crea un perfil nuevo; trae la TUI al pane/contexto actual (equivale a 'fleetview show'). El flag --new fuerza una flota+ventana nueva aunque estes en tmux. La terminal se AUTO-DETECTA sin config por PC: kitty si esta instalado y hay display ($DISPLAY/$WAYLAND_DISPLAY), si no Windows Terminal (wt.exe) en WSL adjuntando via wsl.exe. El pane de la TUI corre dentro del bucle supervisor supervise_fleetview_tui, que la relanza si muere (crash/panic/kill), asi el panel de control NUNCA se pierde. Soporta PERFILES multiples: fuera de tmux, o con --new, cada invocacion abre un perfil nuevo (fleet, fleet2, fleet3, ...) con su propia flota; inyecta FLEET_SOCKET/FLEET_SESSION a la TUI para que cada panel vea solo sus Claudes. Instala atajos alt+flechas/alt+enter/alt+n que controlan la TUI desde cualquier pane, y fija el ancho del sidebar con hooks."
desc: "Directorio de trabajo de ambos panes tmux. Opcional. Default: raiz del repo fn_registry, derivada dinamicamente via git rev-parse desde la ubicacion del script (sin hardcodear paths de usuario)."
- name: --bin
desc: "Ruta al binario de la TUI fleetview que corre en el pane izquierdo. Opcional. Default: <repo>/apps/fleetview/fleetview. Si no es ejecutable, el pane izquierdo muestra un mensaje de como compilarla y deja una shell viva."
- name: --session
desc: "Fija el perfil (socket+sesion tmux comparten nombre) por nombre exacto; reutiliza el existente si ya vive (idempotente sobre ese nombre). Opcional. Sin esta opcion, el perfil se elige automaticamente (primer nombre libre de la secuencia fleet, fleet2, ...)."
desc: "Fija el perfil (socket+sesion tmux comparten nombre) por nombre exacto; reutiliza el existente si ya vive (idempotente sobre ese nombre). Opcional. Sin esta opcion, el perfil se elige automaticamente (primer nombre libre de la secuencia fleet, fleet2, ...). Invocado DENTRO de tmux con un nombre DISTINTO al de la flota actual equivale a --new (pides otra flota: ventana nueva, sin reuse de contexto)."
- name: --reuse
desc: "Reattach al perfil principal 'fleet' en vez de abrir uno nuevo. Opcional. Recupera el comportamiento idempotente clasico (volver a invocar NO duplica la flota, reusa la existente)."
- name: --new
desc: "Fuerza una flota NUEVA en una ventana NUEVA (kitty/wt.exe) incluso estando dentro de una flota tmux. Opcional. Es la via explicita para abrir una FleetView aparte; sin este flag, invocado dentro de una flota viva se reusa el contexto actual (no abre ventana ni crea perfil)."
- name: --cols
desc: "Ancho en columnas del pane izquierdo (la TUI). Opcional. Default: 40."
output: "Crea/reutiliza una sesion tmux detached con dos panes y lanza una ventana kitty 'FleetView' adjunta a ella, desacoplada del shell padre (setsid). Imprime el estado por stdout. Sin valor de retorno; exit 0 en exito."
uses_functions: []
output: "Caso reuse de contexto (dentro de una flota tmux viva, sin --new): trae la TUI al pane/contexto actual con select-window de la window 'console' (o 'fleetview show' si el binario existe) y retorna 0, sin abrir nada. Caso ventana-nueva (fuera de tmux, o con --new): crea/reutiliza una sesion tmux detached con dos panes y lanza una ventana de terminal 'FleetView' adjunta (kitty o Windows Terminal segun auto-deteccion), desacoplada del shell padre. Imprime el estado por stdout. Sin valor de retorno; exit 0 en exito, !=0 con mensaje claro si no hay terminal ni contexto que reusar."
description: "Lanza un Claude como window nueva dentro de la sesion tmux de un perfil FleetView (socket aislado), opcionalmente en modo orquestador (skill embebida como primer prompt), marcado con un role en su goal.json y atribuido a su orquestador padre. Es la forma de que un ejecutor o el propio orquestador VIVAN en la flota tmux (visibles en la TUI fleetview, conmutables con /fleet focus) en vez de en kitties sueltas. Reemplaza a launch_claude_agent_kitty cuando se opera dentro de un perfil fleet ya montado. Con --parent <sid> escribe parent_orchestrator en el goal.json del nuevo Claude (via mark_claude_parent) para que el watcher de fleetview rutee sus avisos al orquestador que lo lanzo. Imprime el window_id creado."
description: "Lanza un Claude como window nueva dentro de la sesion tmux de un perfil FleetView (socket aislado), opcionalmente en modo orquestador (skill embebida como primer prompt), marcado con un role en su goal.json y atribuido a su orquestador padre. --socket/--session son opcionales: si no se pasan se auto-detectan del contexto tmux ($TMUX) via detect_fleet_context (los explicitos tienen prioridad), evitando caer a kitty cuando $FLEET_SOCKET viene vacia. Es la forma de que un ejecutor o el propio orquestador VIVAN en la flota tmux (visibles en la TUI fleetview, conmutables con /fleet focus) en vez de en kitties sueltas. Reemplaza a launch_claude_agent_kitty cuando se opera dentro de un perfil fleet ya montado. Con --parent <sid> escribe parent_orchestrator en el goal.json del nuevo Claude (via mark_claude_parent) para que el watcher de fleetview rutee sus avisos al orquestador que lo lanzo. Imprime el window_id creado."
desc: "Socket tmux del perfil FleetView (ej. fleet, fleet2). El perfil debe estar ya montado (sesion viva)."
desc: "Socket tmux del perfil FleetView (ej. fleet, fleet2). Opcional: se auto-detecta de $TMUX via detect_fleet_context si no se pasa. El perfil debe estar ya montado (sesion viva)."
- name: --session
desc: "Nombre de la sesion tmux dentro del socket (normalmente igual al socket)."
desc: "Nombre de la sesion tmux dentro del socket (normalmente igual al socket). Opcional: se auto-detecta de $TMUX si no se pasa."
- name: --cwd
desc: "Directorio de trabajo del nuevo Claude. Default: PWD."
- name: --prompt-file
@@ -54,6 +55,11 @@ Lanza un Claude dentro de un perfil FleetView (sesion tmux de un socket aislado)
./fn run spawn_fleet_agent --socket fleet2 --session fleet2 --cwd "$HOME/fn_registry"\
@@ -62,9 +68,14 @@ Cuando el orquestador (o el launcher) necesita arrancar un Claude que debe vivir
## Gotchas
- **Auto-deteccion de socket/session**: si no pasas `--socket`/`--session`, se derivan de `$TMUX` via `detect_fleet_context`. Los explicitos tienen prioridad. Solo aborta (exit 2) si tras auto-detectar siguen vacios (de verdad no hay tmux). No dependas de `$FLEET_SOCKET`: a veces viene vacia en un claude resumido/relanzado aunque viva en la flota — `$TMUX` es la senal fiable.
- El perfil (socket+session) debe estar **ya montado** (`launch_fleetclaude` primero); si la sesion no existe, falla con exit 1.
- El `--role` se aplica en **background**: el `sessionId` del nuevo Claude no existe hasta que Claude escribe `~/.claude/sessions/<PID>.json` (unos segundos). `mark_claude_role` espera ese archivo. Si el arranque es muy lento, el role puede tardar en aparecer; es no-fatal (el agente simplemente no se pinea/identifica hasta entonces).
- El `--parent` se aplica igual en **background** via `mark_claude_parent` (misma espera del `sessions/<PID>.json`). Cuando se pasan `--role` y `--parent` juntos se encadenan **secuencialmente** en el mismo subshell (primero role, luego parent) para que la segunda escritura lea el goal ya con la primera clave puesta — sin carrera de lectura-modificacion-escritura. Es no-fatal: si el sessions JSON no aparece a tiempo, el `parent_orchestrator` simplemente no se escribe.
-`--skill` envia `/<name>` como primer prompt: depende de que Claude Code interprete el primer argumento como invocacion de slash command (verificado con `/orquestador`).
- El nuevo Claude hereda `FLEET_SOCKET`/`FLEET_SESSION` del entorno del server tmux (que `launch_fleetclaude` fija con `set-environment`), asi apunta al perfil correcto.
-`--dangerously-skip-permissions` siempre (los agentes de la flota trabajan desatendidos); riesgo asumido como en el resto del modo orquestador.
## Capability growth log
- v1.2.0 (2026-06-21) — `--socket`/`--session` ahora son opcionales: se auto-detectan del contexto tmux (`$TMUX`) via `detect_fleet_context` cuando no se pasan. Elimina el gotcha de caer a kitty cuando `$FLEET_SOCKET` viene vacia pese a estar en la flota. Los valores explicitos siguen primando.
# Parseo minimo sin depender de jq: extraer "socket":"..." / "session":"...".
det_socket="$(printf'%s'"$ctx"| sed -n 's/.*"socket":"\([^"]*\)".*/\1/p')"
det_session="$(printf'%s'"$ctx"| sed -n 's/.*"session":"\([^"]*\)".*/\1/p')"
[[ -z "$socket"]]&&socket="$det_socket"
[[ -z "$session"]]&&session="$det_session"
fi
fi
[[ -z "$socket"|| -z "$session"]]&&{
echo"spawn_fleet_agent: --socket y --session son obligatorios" >&2
echo"spawn_fleet_agent: no se detecto contexto tmux (\$TMUX vacia) y no se pasaron --socket/--session. Lanza desde dentro de la flota o pasa el socket/session explicito." >&2
description: "Bucle supervisor que mantiene viva la TUI fleetview: lanza el binario y, si sale (crash, panic, kill de su proceso o pane), lo relanza tras un backoff, para que el panel de control de la flota NUNCA se pierda por un fallo puntual. Es la pieza que hace resiliente al pane izquierdo de la sesion tmux FleetView (lo invoca launch_fleetclaude). Dos valvulas de escape evitan el respawn infinito: un fichero centinela (touch <sentinel> => parada voluntaria al siguiente ciclo) y un crash-loop guard (si la TUI sale demasiado rapido muchas veces seguidas, el supervisor se rinde con rc=3 en vez de quemar CPU relanzando un binario roto)."
desc: "Ruta al binario fleetview a supervisar. Obligatorio. Si no es ejecutable, sale con rc=1 con instruccion de compilado."
- name: --socket
desc: "Socket del perfil FleetView. Solo fija el nombre del sentinel por defecto. Default: $FLEET_SOCKET, o 'fleet' si no esta seteada."
- name: --sentinel
desc: "Ruta del fichero centinela de parada voluntaria. Si existe tras una salida de la TUI, se borra y el bucle termina. Default: $HOME/.claude/fleet/tui_stop_<socket>."
- name: --backoff
desc: "Segundos de espera antes de relanzar la TUI tras una salida. Default: 1."
- name: --min-uptime
desc: "Umbral en segundos para considerar una salida 'rapida' (sospecha de crash-loop). Un arranque que dura >= este valor resetea el contador. Default: 2."
- name: --max-fast-exits
desc: "Numero de salidas rapidas seguidas tras las que el supervisor se rinde (crash-loop guard) en vez de seguir relanzando. Default: 5."
output: "No retorna valor; corre indefinidamente relanzando la TUI. Sale 0 ante parada voluntaria (sentinel), 1 ante uso incorrecto / binario no ejecutable, 3 cuando el crash-loop guard se rinde. Imprime una linea por cada relanzamiento o parada."
---
# supervise_fleetview_tui
Bucle supervisor de la TUI `fleetview`. Corre el binario y, cada vez que sale (crash, panic, `kill` de su proceso, cierre de su pane), lo **relanza** tras un pequeño backoff. Hace que el panel de control de la flota — el pane izquierdo de la sesión tmux FleetView — **nunca se pierda** por un fallo puntual. `launch_fleetclaude` lo usa como comando del pane izquierdo en vez de un `exec fleetview` de una sola vida.
## Ejemplo
```bash
# Como lo invoca el launcher en el pane izquierdo (relanza la TUI si muere):
# Pararlo voluntariamente desde otra terminal: tocar el sentinel y dejar salir la TUI.
touch ~/.claude/fleet/tui_stop_fleet
```
## Cuando usarla
Úsala como wrapper del binario `fleetview` siempre que quieras que la TUI sobreviva a un crash o a un `kill` accidental de su proceso/pane (p. ej. un `kill_fleet_agent` que cierre la window que la aloja). Es la mitad "auto-recuperación" del par de fixes que blindan FleetView; la otra mitad es el Guard 3 anti-TUI/console de `kill_fleet_agent` (la causa raíz). No la uses para supervisar Claudes (esos se relanzan con `claude --resume`, no en bucle ciego).
## Gotchas
- **Impura y de larga duración**: corre indefinidamente. Está pensada para vivir en un pane tmux con TTY, no como systemd service (la TUI necesita PTY; el watcher de fleetview sí es systemd `Restart=always`).
- **Crash-loop guard**: si la TUI sale en menos de `--min-uptime` segundos, `--max-fast-exits` veces seguidas, el supervisor se **rinde** (rc=3) en vez de relanzar para siempre un binario roto. Ajusta los umbrales si tu arranque es legítimamente lento.
- **Sentinel = única parada voluntaria limpia**: `touch <sentinel>` y deja que la TUI salga; al siguiente ciclo el supervisor ve el fichero, lo borra y termina. Sin sentinel, **relanza siempre** (es el objetivo: que no se pierda). Un sentinel huérfano de una sesión previa se limpia al arrancar para no parar de inmediato.
- **El sentinel por defecto depende del socket**: `~/.claude/fleet/tui_stop_<socket>`. Dos perfiles (`fleet`, `fleet2`) tienen sentinels distintos, así parar uno no para el otro.
- **No supervisa Claudes**: su contrato es solo la TUI. Relanzar un Claude en bucle ciego perdería su sesión; los Claudes se recuperan con `claude --resume`.
echo"[fleetview: parada solicitada via sentinel ($sentinel) — fin del supervisor]"
return0
fi
# Valvula 2 — crash-loop guard.
if[["$uptime" -lt "$min_uptime"]];then
fast_exits=$(( fast_exits +1))
else
fast_exits=0
fi
if[["$fast_exits" -ge "$max_fast_exits"]];then
echo"[fleetview: $fast_exits salidas rapidas seguidas (ultimo code=$code) — el supervisor se rinde para no hacer respawn infinito. Inspecciona el binario y relanza.]" >&2
return3
fi
echo"[fleetview salio (code=$code, uptime=${uptime}s) — relanzando en ${backoff}s. Para parar: touch $sentinel, o Ctrl-C.]"
sleep "$backoff"
done
}
# Permitir ejecutar el archivo directamente (no solo como funcion sourced).
description: "Lifecycle del engine de audio basado en miniaudio (single-header, public domain). Inicializa device default, expone master volume, y libera recursos. Cross-platform: Windows/Linux/macOS via WASAPI/ALSA/CoreAudio y WebAudio bajo emscripten. Issue 0072b — runtime gamedev nucleo. Esta TU es la unica del proyecto que define MINIAUDIO_IMPLEMENTATION."
description: "Reproduccion de audio sobre fn::audio::Engine: carga sonidos con streaming desde disco (wav/mp3/flac/ogg), play/stop/volumen por sonido, y helper fire-and-forget para one-shots sin handle. Cross-platform via miniaudio. Issue 0072b — runtime gamedev nucleo."
description: "Game loop fixed-timestep estilo Glenn Fiedler ('Fix Your Timestep'). Desacopla simulacion (on_fixed_update con dt fijo) de renderizado (on_render con factor de interpolacion). Acumulador con cap anti spiral-of-death. Branch automatico desktop (while loop bloqueante) vs __EMSCRIPTEN__ (emscripten_set_main_loop). Issue 0072b."
description: "Snapshot unificado de input por frame para SDL3. Mapea keyboard (WASD+arrows), mouse, gamepad (SDL_Gamepad) y touch a botones logicos (left/right/up/down/action_a..y/start/back) y ejes analogicos. Expone flags *_pressed con rising edge limpio cada frame. Issue 0072b — runtime gamedev PC + WASM."
signature: "make_environment() -> sg_environment; make_swapchain(int w, int h) -> sg_swapchain"
description: "Builders puros para inicializar sokol_gfx encima de un GL context creado por SDL3 (no por sokol_app). Construye sg_environment con defaults RGBA8 + depth/stencil y sg_swapchain con el default framebuffer del contexto activo. Issue 0072b — base del runtime gamedev en PC + WASM."
title: "dev_console: escaneo recursivo de dev/issues/ (subcarpetas por dominio)"
status: in-progress
type: bugfix
domain:
- meta
scope: app-scoped
priority: media
depends: []
blocks: []
related: []
created: 2026-06-30
updated: 2026-06-30
tags: [ausente-ready]
---
# 0179 — dev_console: escaneo recursivo de dev/issues/
## Contexto
Los issues activos se reorganizaron en subcarpetas por dominio dentro de `dev/issues/` (`kanban/`, `trading/`, `gamedev/`, `cpp/`, `matrix/`, `imagegen/`) para descongestionar el listado plano. El skill `/issue` ya se actualizó a glob recursivo (`dev/issues/**/*.md`, excluyendo `completed/`). Falta alinear el binario `dev_console`, que carga los issues con `LoadAllIssues(root)` / `LoadOpenIssues(root)` en `apps/dev_console/` y hoy no recorre subcarpetas — por lo que no ve los 49 issues movidos.
## Objetivo
Que `dev_console issue list/board/work` y los flujos que dependen de `LoadAllIssues`/`LoadOpenIssues` recorran `dev/issues/` de forma recursiva, excluyendo `dev/issues/completed/`, manteniendo el resto del comportamiento idéntico.
## Tareas
- [ ] Localizar la implementación de `LoadAllIssues` / `LoadOpenIssues` en `apps/dev_console/` (probable `parser.go` o equivalente).
- [ ] Cambiar el escaneo a `filepath.WalkDir` (o glob recursivo) bajo `dev/issues/`, saltando el directorio `completed/`.
- [ ] Mantener el orden de salida estable (ordenar por `id`).
- [ ] Recompilar el binario en el sub-repo de `dev_console` siguiendo TBD (`issue/0179-...`).
| Golden: lista incluye subcarpetas | e2e | `./apps/dev_console/dev_console issue list` | Aparecen issues de `cpp/`, `kanban/`, `trading/`, etc. (>= 49 que antes faltaban) |
| Edge: excluye completed/ | e2e | `dev_console issue list` | Ningún issue con `status: completado` de `completed/` aparece en el listado activo |
| Edge: conteo total coincide con /issue | e2e | comparar conteo con el glob recursivo de `/issue` | Mismo total de activos |
| Error: dev/issues vacío o ausente | unit | run en dir sin `dev/issues/` | Error claro, no panic |
## Notas
Hermano del cambio ya hecho en `.claude/commands/issue.md` (glob `**/*.md`). Hasta cerrar este issue, usar `/issue` (no `dev_console`) para vistas completas del backlog.
title: "Modo ausente sobre la cola de issues: parametrizar /ausente + DAG dag_engine + validación"
status: pendiente
type: infra
domain:
- meta
scope: multi-app
priority: alta
depends: ["0179"]
blocks: []
related: []
created: 2026-06-30
updated: 2026-06-30
tags: []
---
# 0180 — Modo ausente sobre la cola de issues (parametrizar /ausente + DAG + validación)
## Contexto
Modelo de colaboración acordado (ver memoria `modelo-colaboracion-ausente`): durante la jornada de oficina (L–J 10–14 / 15–19, V 10–16) y la noche (01–09), Claude trabaja en `/ausente` la cola de issues `ausente-ready` (39 issues hoy), sin supervisión. La curación del backlog ya está hecha (triage, taxonomía, deps de series formalizadas, tag `ausente-ready`).
Faltan 3 piezas para automatizarlo de forma segura.
## Problemas a resolver
1. **`/ausente` está acoplado al roadmap ComfyUI.** El skill (`.claude/commands/ausente.md`) hardcodea su backlog a funciones ComfyUI (secciones "Configuración" y "Backlog del roadmap ComfyUI"). Hay que **parametrizar la fuente de tareas** para que pueda tomar la cola de issues: la siguiente tarea = primer issue de `/issue list -t ausente-ready` cuyas `depends` estén todas en `completed/`, re-cruzando deps en cada ciclo (un issue se libera cuando su dep se cierra).
2. **Lanzamiento headless desde dag_engine.**`dag_engine` ejecuta steps (command/script/function), no abre una sesión Claude interactiva. Hay que resolver cómo un step arranca una sesión `role=orchestrator` en modo `/ausente` (candidatos: `launch_claude_agent_kitty_bash_infra` con DISPLAY, o `spawn_fleet_agent_bash_infra` si hay sesión tmux fleet) con el prompt autónomo + presupuesto.
3. **Presupuesto conservador aplicado.** Tope: 1–2 ejecutores concurrentes, solo issues S/M, ~1M tokens por franja, parada al llegar. Materializar el tope de tokens (hoy `orchestration.md` solo fija fan-out=6).
## Schedule objetivo (cuando se active)
- Inicio de franjas de oficina: `0 10 * * 1-5` (10:00 L–V) y `0 15 * * 1-4` (15:00 L–J, tras comida).
- Nocturno: `0 1 * * *` (01:00 diario).
- El modo, una vez lanzado, itera con `ScheduleWakeup` hasta que el humano vuelve (para al recibir prompt humano).
Borrador del DAG: `apps/dag_engine/dags/ausente-issues-queue.yaml` (creado como DRAFT sin schedule activo).
| Golden: corrida manual | e2e | lanzar `/ausente` con backlog=issues sobre 1 issue S de la cola | Coge el issue, lo implementa en worktree/sub-repo aislado, cierra DoD verde (golden+edge+error), push, bitácora actualizada |
| Edge: dep no satisfecha | e2e | cola con un issue cuya `depends` sigue activa | NO lo coge; pasa al siguiente arrancable |
| Edge: flota llena | e2e | 2 ejecutores activos (tope conservador) | Encola el resto, no lanza el 3.º |
| Error: presupuesto agotado | e2e | tope de tokens alcanzado | Para limpio, deja bitácora con lo pendiente, no deja agentes huérfanos |
| Vida útil | observabilidad | tras activar cron, 1 semana | Issues cerrados/semana > 0, 0 merges rotos a master, bitácora legible |
2. Parametrizar `/ausente` (fuente de backlog = issues ausente-ready | roadmap; pasar la fuente al invocar).
3. Resolver el step de lanzamiento headless + presupuesto de tokens.
4. **Validación manual** (golden + edges) antes de activar el cron.
5. Activar schedule en el DAG + `systemctl --user restart dag_engine.service` con `--scheduler`.
## Notas
Este issue NO es `ausente-ready` a propósito: requiere decisiones de diseño humanas (mecanismo de lanzamiento, forma del presupuesto) y toca el propio sistema que orquesta el modo ausente. Se hace JUNTOS, no desatendido.
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.