chore: avance acumulado de sesiones previas (reorg dev/issues + ajustes)
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).
This commit is contained in:
@@ -0,0 +1,187 @@
|
||||
---
|
||||
id: "0027"
|
||||
title: "C++ gl_compute_shader + gl_pingpong_fbo + DAG node Compute"
|
||||
status: pendiente
|
||||
type: feature
|
||||
domain:
|
||||
- cpp-stack
|
||||
scope: multi-app
|
||||
priority: alta
|
||||
depends: []
|
||||
blocks: []
|
||||
related: []
|
||||
created: 2026-05-17
|
||||
updated: 2026-05-17
|
||||
tags: []
|
||||
---
|
||||
# 0027 — C++ gl_compute_shader + gl_pingpong_fbo + DAG node Compute
|
||||
|
||||
## APP Metadata
|
||||
|
||||
| Campo | Valor |
|
||||
|-------|-------|
|
||||
| **ID** | 0027 |
|
||||
| **Estado** | pendiente |
|
||||
| **Prioridad** | alta |
|
||||
| **Tipo** | feature — C++ gfx (cpp/functions/gfx) |
|
||||
|
||||
## Dependencias
|
||||
|
||||
Recomendado leer primero `gl_loader_cpp_gfx`, `gl_shader_cpp_gfx`, `gl_framebuffer_cpp_gfx`, `dag_catalog_cpp_gfx`, `dag_compile_cpp_gfx`. Sin bloqueos.
|
||||
|
||||
**Desbloquea:** simulaciones GPGPU (particulas, fluidos), feedback loops (reaction-diffusion, blur multipass, bloom), cualquier shader que necesite estado entre frames. Triplica el alcance del DAG visual de `shaders_lab`.
|
||||
|
||||
---
|
||||
|
||||
## Objetivo
|
||||
|
||||
Tres adiciones complementarias:
|
||||
|
||||
1. **`gl_compute_shader_cpp_gfx`** — carga + dispatch de compute shaders (GLSL `#version 430 core` + `layout(local_size_*) in`). API parejo a `gl_shader`.
|
||||
2. **`gl_pingpong_fbo_cpp_gfx`** — wrapper de **dos** `gl_framebuffer` con swap A↔B en cada paso. Permite usar el output del frame N-1 como input del frame N (feedback).
|
||||
3. **DAG kind `Compute`** — nuevo `DagNodeKind::Compute` en `dag_catalog`/`dag_compile`. Nodo que ejecuta un compute shader sobre la imagen actual, con N iteraciones configurable.
|
||||
|
||||
Demo en `primitives_gallery`: reaction-diffusion (Gray-Scott) usando ping-pong FBO + compute shader.
|
||||
|
||||
## Contexto
|
||||
|
||||
`gl_shader` solo soporta fragment shaders. El DAG actual es estrictamente fragment-pipeline (gen → op → blend). No hay forma de:
|
||||
- Persistir estado entre frames (necesario para feedback effects).
|
||||
- Hacer GPGPU verdadero (compute con buffers / images).
|
||||
- Implementar efectos como fluid sim, particulas o blur multipass eficientes.
|
||||
|
||||
OpenGL compute shaders requieren `#version 430 core` (= GL 4.3). El proyecto ya pide GL 3.3+; subir el minimo solo para los nodos compute es aceptable (graceful degrade si la GPU no lo soporta).
|
||||
|
||||
## Arquitectura
|
||||
|
||||
```
|
||||
cpp/functions/gfx/
|
||||
├── gl_compute_shader.h # NEW
|
||||
├── gl_compute_shader.cpp # NEW
|
||||
├── gl_compute_shader.md # NEW
|
||||
├── gl_pingpong_fbo.h # NEW
|
||||
├── gl_pingpong_fbo.cpp # NEW
|
||||
├── gl_pingpong_fbo.md # NEW
|
||||
├── dag_types.h # MOD: añadir DagNodeKind::Compute
|
||||
├── dag_catalog.cpp/.h # MOD: registrar nodos compute (ej: ReactionDiffusion)
|
||||
├── dag_compile.cpp/.h # MOD: emitir paso compute en pipeline
|
||||
└── dag_uniforms.cpp/.h # MOD si los compute tienen params
|
||||
cpp/apps/primitives_gallery/
|
||||
├── demos_compute.cpp # NEW
|
||||
├── demos.h # MOD
|
||||
├── main.cpp # MOD
|
||||
└── CMakeLists.txt # MOD
|
||||
```
|
||||
|
||||
### Pure core / impure shell
|
||||
|
||||
- `gl_compute_shader` y `gl_pingpong_fbo`: ambos `purity: impure`, `kind: function`.
|
||||
- `dag_catalog`/`dag_compile`: ya son `pure`, mantener.
|
||||
- `dag_node_editor` / `dag_panel`: ya son `impure`, no se tocan en este issue (los nodos compute aparecen automaticamente al añadirse al catalog).
|
||||
|
||||
### API propuesta
|
||||
|
||||
```cpp
|
||||
namespace fn {
|
||||
|
||||
// gl_compute_shader.h
|
||||
struct GlComputeProgram {
|
||||
GLuint program = 0;
|
||||
int local_x = 1, local_y = 1, local_z = 1;
|
||||
bool ok() const { return program != 0; }
|
||||
};
|
||||
|
||||
GlComputeProgram gl_compute_compile(const char* glsl_body); // body sin version
|
||||
void gl_compute_destroy(GlComputeProgram&);
|
||||
|
||||
// Dispatch: bind images, set uniforms (callback opcional), glDispatchCompute, barrier.
|
||||
void gl_compute_dispatch(const GlComputeProgram&, int groups_x, int groups_y, int groups_z = 1);
|
||||
void gl_compute_bind_image(int unit, GLuint texture, GLenum access /*GL_READ_ONLY|WRITE|READ_WRITE*/, GLenum format = GL_RGBA8);
|
||||
|
||||
const char* gl_compute_last_error();
|
||||
|
||||
// gl_pingpong_fbo.h — dos FBO RGBA8 del mismo tamaño con A/B swap.
|
||||
struct PingpongFbo {
|
||||
Framebuffer a, b;
|
||||
bool a_is_front = true; // front = ultimo escrito
|
||||
};
|
||||
|
||||
PingpongFbo pingpong_create(int w, int h);
|
||||
void pingpong_destroy(PingpongFbo&);
|
||||
void pingpong_resize(PingpongFbo&, int w, int h);
|
||||
void pingpong_swap(PingpongFbo&);
|
||||
const Framebuffer& pingpong_front(const PingpongFbo&); // ultima lectura
|
||||
const Framebuffer& pingpong_back (const PingpongFbo&); // siguiente write target
|
||||
}
|
||||
```
|
||||
|
||||
### DAG: nuevo kind Compute
|
||||
|
||||
`dag_catalog` registra al menos un nodo de ejemplo: `compute_blur_separable` o `compute_reaction_diffusion`. Tras este issue queda abierto añadir mas (cada uno = un nuevo `DagNode`).
|
||||
|
||||
`dag_compile`: cuando emite un paso `Compute`, decide bindings (image read = ping-front, image write = pong-back) y devuelve metadatos para que el host haga `dispatch + swap` correctamente.
|
||||
|
||||
## Tareas
|
||||
|
||||
### Fase 1 — gl_compute_shader
|
||||
|
||||
- 1.1 Implementar wrapper minimo. Detectar version GL >= 4.3 al inicializar; si no, retornar `program == 0` y guardar error "compute shaders require OpenGL 4.3+".
|
||||
- 1.2 Helper `gl_compute_bind_image` con `glBindImageTexture`.
|
||||
- 1.3 `.md` con frontmatter (`purity: impure`, `kind: function`, `error_type`).
|
||||
|
||||
### Fase 2 — gl_pingpong_fbo
|
||||
|
||||
- 2.1 Implementar wrapper que reusa el `gl_framebuffer` existente. Resize propaga a los dos.
|
||||
- 2.2 `.md` con frontmatter.
|
||||
|
||||
### Fase 3 — DAG Compute kind
|
||||
|
||||
- 3.1 Añadir `DagNodeKind::Compute` en `dag_types.h` + serializacion (json IO).
|
||||
- 3.2 `dag_catalog`: registrar nodo de ejemplo `compute_blur_2pass` (separable Gaussian blur, 2 dispatches via flag uniform `direction`).
|
||||
- 3.3 `dag_compile`: emitir step compute con suficiente metadata para que el host (un nuevo helper) haga el dispatch.
|
||||
- 3.4 Helper `dag_compute_run_step()` — recibe `DagStep` compute + `PingpongFbo` + uniforms y hace `dispatch + swap`.
|
||||
|
||||
### Fase 4 — Gallery demo
|
||||
|
||||
- 4.1 `demos_compute.cpp` con `demo_compute_reaction_diffusion()`: `PingpongFbo` 512×512 + compute shader Gray-Scott con sliders (feed, kill, dt). Usa `ImGui::Image` para mostrar el front buffer cada frame. Boton "reset" rellena con seed.
|
||||
- 4.2 Registrar en gallery.
|
||||
|
||||
### Fase 5 — Tests + docs
|
||||
|
||||
- 5.1 Test (Linux + GPU disponible, opcional via env var `FN_GPU_TESTS=1`): compila un compute simple que escribe `vec4(1,0,0,1)` y verifica via `glReadPixels`.
|
||||
- 5.2 Test puro de `dag_compile` con un pipeline que mezcla nodos `Gen` + `Compute`.
|
||||
- 5.3 `./fn index` + `./fn show *_cpp_gfx` para los nuevos.
|
||||
|
||||
## Ejemplo de uso
|
||||
|
||||
```cpp
|
||||
auto cs = fn::gl_compute_compile(R"glsl(
|
||||
layout(local_size_x=8, local_size_y=8) in;
|
||||
layout(rgba8, binding=0) uniform image2D src;
|
||||
layout(rgba8, binding=1) uniform image2D dst;
|
||||
void main() {
|
||||
ivec2 p = ivec2(gl_GlobalInvocationID.xy);
|
||||
vec4 c = imageLoad(src, p);
|
||||
imageStore(dst, p, vec4(1.0 - c.rgb, 1.0));
|
||||
}
|
||||
)glsl");
|
||||
|
||||
auto pp = fn::pingpong_create(512, 512);
|
||||
glUseProgram(cs.program);
|
||||
fn::gl_compute_bind_image(0, fn::pingpong_front(pp).color_tex, GL_READ_ONLY);
|
||||
fn::gl_compute_bind_image(1, fn::pingpong_back (pp).color_tex, GL_WRITE_ONLY);
|
||||
fn::gl_compute_dispatch(cs, 64, 64);
|
||||
fn::pingpong_swap(pp);
|
||||
```
|
||||
|
||||
## Decisiones de diseño
|
||||
|
||||
- **Subir minimo a GL 4.3 solo para nodos Compute** — el resto del pipeline sigue compilando con 3.3. `gl_compute_compile` falla con error claro en GPUs viejas.
|
||||
- **Ping-pong fuera de FBO** — wrapper aparte, no fusionado con `gl_framebuffer` (mantenemos primitivos atomicos).
|
||||
- **Catalog del DAG abierto** — un solo nodo compute en este issue. Otros (fluid, particles) se añaden en issues posteriores reusando los wrappers.
|
||||
|
||||
## Riesgos
|
||||
|
||||
- **macOS no soporta compute shaders en GL 4.1** — documentar limitacion. En macOS el path Compute queda inactivo.
|
||||
- **Memory barriers**: olvidar `glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT)` produce datos basura. Encapsular en `gl_compute_dispatch`.
|
||||
- **Dispatch grande bloquea UI**: documentar que dispatches > 1M pixels deben dividirse o bajar local_size.
|
||||
@@ -0,0 +1,187 @@
|
||||
---
|
||||
id: "0030"
|
||||
title: "C++ audio reactivo (capture + FFT + uniform feed + viz)"
|
||||
status: pendiente
|
||||
type: feature
|
||||
domain:
|
||||
- cpp-stack
|
||||
- frontend
|
||||
scope: multi-app
|
||||
priority: media
|
||||
depends: []
|
||||
blocks: []
|
||||
related: []
|
||||
created: 2026-05-17
|
||||
updated: 2026-05-17
|
||||
tags: []
|
||||
---
|
||||
# 0030 — C++ audio reactivo (capture + FFT + uniform feed + viz)
|
||||
|
||||
## APP Metadata
|
||||
|
||||
| Campo | Valor |
|
||||
|-------|-------|
|
||||
| **ID** | 0030 |
|
||||
| **Estado** | pendiente |
|
||||
| **Prioridad** | media |
|
||||
| **Tipo** | feature — C++ multi-domain (core, gfx, viz) |
|
||||
|
||||
## Dependencias
|
||||
|
||||
`gl_loader_cpp_gfx`, `time_series_buffer_cpp_core`. Independiente de los demas issues.
|
||||
|
||||
**Desbloquea:** shaders audio-reactivos en `shaders_lab`, dashboards en vivo con waveform/spectrum, debugging visual de pipelines de audio.
|
||||
|
||||
---
|
||||
|
||||
## Objetivo
|
||||
|
||||
Cinco primitivos coherentes:
|
||||
|
||||
1. **`audio_capture_cpp_core`** — input mic/loopback con [miniaudio](https://github.com/mackron/miniaudio) (single-header, dominio publico). Lock-free ring buffer.
|
||||
2. **`audio_fft_cpp_core`** — FFT real → magnitudes + smoothing (puro). Tamaño configurable (256/512/1024/2048).
|
||||
3. **`audio_uniform_feed_cpp_gfx`** — sube espectro como `sampler1D` o `texture2D 1×N` al shader activo.
|
||||
4. **`waveform_view_cpp_viz`** — dibuja muestras crudas en un line plot ImPlot.
|
||||
5. **`spectrum_view_cpp_viz`** — dibuja magnitudes FFT en bar plot logaritmico.
|
||||
|
||||
Demo en `primitives_gallery`: input live → fft → vis dual + opcion de bind a un shader fullscreen reactivo.
|
||||
|
||||
## Contexto
|
||||
|
||||
`shaders_lab` no tiene input externo dinamico. Los visuals reaccionan a `u_time` y `u_mouse` solamente. Audio reactivo es uno de los casos mas comunes de uso creativo de shaders y abre demos espectaculares con muy poco codigo añadido.
|
||||
|
||||
## Arquitectura
|
||||
|
||||
```
|
||||
cpp/
|
||||
├── vendor/miniaudio/
|
||||
│ └── miniaudio.h # NEW (~80k LOC, public domain)
|
||||
├── functions/core/
|
||||
│ ├── audio_capture.h/.cpp/.md # NEW (impure)
|
||||
│ └── audio_fft.h/.cpp/.md # NEW (pure)
|
||||
├── functions/gfx/
|
||||
│ └── audio_uniform_feed.h/.cpp/.md # NEW (impure)
|
||||
└── functions/viz/
|
||||
├── waveform_view.h/.cpp/.md # NEW (pure component)
|
||||
└── spectrum_view.h/.cpp/.md # NEW (pure component)
|
||||
cpp/apps/primitives_gallery/
|
||||
├── demos_audio.cpp # NEW
|
||||
├── demos.h # MOD
|
||||
├── main.cpp # MOD
|
||||
└── CMakeLists.txt # MOD
|
||||
cpp/CMakeLists.txt # MOD
|
||||
```
|
||||
|
||||
### API propuesta
|
||||
|
||||
```cpp
|
||||
namespace fn {
|
||||
|
||||
// audio_capture (impure)
|
||||
struct AudioCapture;
|
||||
struct AudioCaptureConfig {
|
||||
int sample_rate = 48000;
|
||||
int channels = 1; // mixdown a mono
|
||||
int buffer_samples = 8192; // ring buffer
|
||||
bool loopback = false; // Win/Linux pulse: capturar output del sistema
|
||||
};
|
||||
AudioCapture* audio_capture_start(const AudioCaptureConfig&);
|
||||
void audio_capture_stop(AudioCapture*);
|
||||
// Drena hasta `out_capacity` muestras float [-1,1]. Retorna count efectivo.
|
||||
int audio_capture_read(AudioCapture*, float* out, int out_capacity);
|
||||
const char* audio_capture_last_error();
|
||||
|
||||
// audio_fft (pure)
|
||||
struct FftResult {
|
||||
std::vector<float> magnitudes; // size = fft_size/2
|
||||
float dc = 0.f;
|
||||
float peak_freq_hz = 0.f;
|
||||
};
|
||||
FftResult audio_fft_compute(const float* samples, int n, int sample_rate, float smoothing = 0.7f, FftResult* prev = nullptr);
|
||||
|
||||
// audio_uniform_feed (impure)
|
||||
struct AudioTexture { GLuint tex = 0; int n = 0; };
|
||||
AudioTexture audio_texture_create(int n);
|
||||
void audio_texture_destroy(AudioTexture&);
|
||||
void audio_texture_upload(AudioTexture&, const float* magnitudes, int n);
|
||||
void audio_texture_bind_uniform(GLuint program, const char* name, const AudioTexture&, int unit);
|
||||
|
||||
// waveform_view (pure component)
|
||||
void waveform_view(const char* id, const float* samples, int n, ImVec2 size = {-1, 100});
|
||||
// spectrum_view (pure component)
|
||||
void spectrum_view(const char* id, const float* magnitudes, int n, int sample_rate, ImVec2 size = {-1, 150});
|
||||
}
|
||||
```
|
||||
|
||||
## Tareas
|
||||
|
||||
### Fase 1 — Vendor
|
||||
|
||||
- 1.1 `cpp/vendor/miniaudio/miniaudio.h` (pin commit). Crear `cpp/vendor/miniaudio/miniaudio_impl.cpp` con `#define MINIAUDIO_IMPLEMENTATION`.
|
||||
- 1.2 Añadir source al CMakeLists.
|
||||
|
||||
### Fase 2 — audio_capture
|
||||
|
||||
- 2.1 Implementar wrapper. Backend default por OS (alsa/pulse en Linux, wasapi en Win). Lock-free ring buffer (single-producer/single-consumer) accumulando muestras del callback.
|
||||
- 2.2 `loopback`: Linux pulse loopback monitor source; Windows wasapi loopback. Documentar limitaciones.
|
||||
- 2.3 `.md` (`kind: function`, `purity: impure`, `error_type`).
|
||||
|
||||
### Fase 3 — audio_fft (puro)
|
||||
|
||||
- 3.1 Usar implementacion FFT minima (Cooley-Tukey radix-2) o vendorear [pffft](https://github.com/marton78/pffft) (BSD). Decidir y documentar.
|
||||
- 3.2 Aplicar Hann window antes de FFT.
|
||||
- 3.3 Smoothing exponencial frame-a-frame con `prev`.
|
||||
- 3.4 Tests con seno puro a frecuencia conocida; `peak_freq_hz` debe coincidir.
|
||||
- 3.5 `.md`.
|
||||
|
||||
### Fase 4 — audio_uniform_feed
|
||||
|
||||
- 4.1 Crear textura 1D (o 2D 1×N) `GL_R32F`. Subida con `glTexSubImage1D`/`2D`.
|
||||
- 4.2 `.md`.
|
||||
|
||||
### Fase 5 — waveform_view + spectrum_view
|
||||
|
||||
- 5.1 `waveform_view`: linea con `ImPlot::PlotLine`. Eje y fijado [-1, 1]. Eje x = sample index.
|
||||
- 5.2 `spectrum_view`: bar plot con eje x logaritmico (Hz), eje y dB (20*log10(magnitude+eps)).
|
||||
- 5.3 `.md` para cada uno.
|
||||
|
||||
### Fase 6 — Gallery demo
|
||||
|
||||
- 6.1 `demos_audio.cpp` con `demo_audio_reactive()`: start capture, fft cada frame, dos vistas (waveform + spectrum), shader fullscreen 256×256 al lado que samplea la `audio_texture` y desplaza colores en funcion del bin medio.
|
||||
- 6.2 Boton start/stop. Mostrar device en uso.
|
||||
|
||||
### Fase 7 — Tests + docs
|
||||
|
||||
- 7.1 Test FFT con seno conocido.
|
||||
- 7.2 Test ring buffer (productor escribe N, consumidor lee, sin perdidas).
|
||||
- 7.3 Smoke test: capture 100ms, asserts `read > 0`.
|
||||
- 7.4 `./fn index` + `./fn show` de los 5.
|
||||
|
||||
## Ejemplo de uso
|
||||
|
||||
```cpp
|
||||
auto* cap = fn::audio_capture_start({});
|
||||
fn::FftResult prev{};
|
||||
|
||||
fn::run_app("audio", [&]{
|
||||
float buf[2048];
|
||||
int n = fn::audio_capture_read(cap, buf, 2048);
|
||||
if (n >= 1024) {
|
||||
prev = fn::audio_fft_compute(buf, 1024, 48000, 0.7f, &prev);
|
||||
fn::waveform_view("##wav", buf, n);
|
||||
fn::spectrum_view("##spec", prev.magnitudes.data(), prev.magnitudes.size(), 48000);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Decisiones de diseño
|
||||
|
||||
- **miniaudio** vs portaudio: miniaudio es header-only, sin build extra; gana por simplicidad de integracion.
|
||||
- **FFT minimo propio o pffft**: empezar con propio (50 LOC) — performance suficiente para 1024 samples a 60Hz. Si hace falta velocidad, swap por pffft.
|
||||
- **`spectrum_view` y `waveform_view` puros**: no tocan el audio, solo dibujan.
|
||||
|
||||
## Riesgos
|
||||
|
||||
- **Permisos mic en macOS**: documentar Info.plist NSMicrophoneUsageDescription si se distribuye.
|
||||
- **Loopback no estandar entre OSes**: documentar plataformas soportadas y degradar grácilmente si no esta disponible.
|
||||
- **Latencia**: ring buffer vs callback puede acumular. Documentar tamaño y politica de drop.
|
||||
@@ -0,0 +1,160 @@
|
||||
---
|
||||
id: "0033"
|
||||
title: "C++ http_inspector + websocket_client"
|
||||
status: pendiente
|
||||
type: feature
|
||||
domain:
|
||||
- cpp-stack
|
||||
scope: multi-app
|
||||
priority: baja
|
||||
depends: []
|
||||
blocks: []
|
||||
related: []
|
||||
created: 2026-05-17
|
||||
updated: 2026-05-17
|
||||
tags: [ausente-ready]
|
||||
---
|
||||
# 0033 — C++ http_inspector + websocket_client
|
||||
|
||||
## APP Metadata
|
||||
|
||||
| Campo | Valor |
|
||||
|-------|-------|
|
||||
| **ID** | 0033 |
|
||||
| **Estado** | pendiente |
|
||||
| **Prioridad** | baja |
|
||||
| **Tipo** | feature — C++ core (cpp/functions/core) |
|
||||
|
||||
## Dependencias
|
||||
|
||||
`text_editor_cpp_core` (issue 0025) recomendado para editar bodies; fallback a `InputTextMultiline`. `time_series_buffer_cpp_core`.
|
||||
|
||||
**Desbloquea:** debugging visual de APIs internas y feeds en tiempo real desde C++ apps. Util para apps de monitoring que se conectan a servicios propios.
|
||||
|
||||
---
|
||||
|
||||
## Objetivo
|
||||
|
||||
Dos componentes complementarios:
|
||||
|
||||
1. **`http_inspector_cpp_core`** — panel ImGui tipo Postman minimo: input URL, dropdown method (GET/POST/PUT/DELETE/PATCH), headers (key/value list), body, boton Send. Muestra status, headers de respuesta, body con pretty-print JSON.
|
||||
2. **`websocket_client_cpp_core`** — panel WebSocket: input ws://… , botones Connect/Disconnect, area de mensajes (timeline), input para enviar texto, contador msg/s y plot rate.
|
||||
|
||||
Vendorea [cpp-httplib](https://github.com/yhirose/cpp-httplib) (MIT, header-only) para HTTP. Para WS usa la API client de cpp-httplib (>=0.20 trae soporte ws basico) o vendorea [websocketpp](https://github.com/zaphoyd/websocketpp). Decidir en Fase 1.
|
||||
|
||||
Demo en `primitives_gallery` con un endpoint HTTP de prueba (httpbin.org) y un WS echo (`wss://echo.websocket.events`).
|
||||
|
||||
## Contexto
|
||||
|
||||
Apps C++ que consumen APIs internas (registry_api, deploy_server) suelen requerir scripts curl/Postman aparte para debugging. Tener un inspector embebido reduce friccion.
|
||||
|
||||
## Arquitectura
|
||||
|
||||
```
|
||||
cpp/
|
||||
├── vendor/cpp-httplib/
|
||||
│ └── httplib.h # NEW (header-only, MIT)
|
||||
├── functions/core/
|
||||
│ ├── http_inspector.h/.cpp/.md # NEW (impure component)
|
||||
│ └── websocket_client.h/.cpp/.md # NEW (impure component)
|
||||
└── apps/primitives_gallery/
|
||||
├── demos_net.cpp # NEW
|
||||
├── demos.h # MOD
|
||||
├── main.cpp # MOD
|
||||
└── CMakeLists.txt # MOD
|
||||
cpp/CMakeLists.txt # MOD
|
||||
```
|
||||
|
||||
### API propuesta
|
||||
|
||||
```cpp
|
||||
namespace fn {
|
||||
|
||||
struct HttpHeader { std::string key, value; };
|
||||
struct HttpInspectorState {
|
||||
std::string url = "https://httpbin.org/get";
|
||||
std::string method = "GET";
|
||||
std::vector<HttpHeader> headers;
|
||||
std::string body;
|
||||
int last_status = 0;
|
||||
std::vector<HttpHeader> last_headers;
|
||||
std::string last_body;
|
||||
double last_ms = 0.0;
|
||||
bool in_flight = false; // request en background
|
||||
std::string error;
|
||||
};
|
||||
void http_inspector(const char* id, HttpInspectorState&, ImVec2 size = {-1, -1});
|
||||
|
||||
struct WsMessage { double t; bool incoming; std::string text; };
|
||||
struct WebSocketClientState {
|
||||
std::string url = "wss://echo.websocket.events";
|
||||
bool connected = false;
|
||||
std::string send_buf;
|
||||
std::vector<WsMessage> messages; // capped at 500
|
||||
float rate_msgs_s = 0.f;
|
||||
std::string error;
|
||||
};
|
||||
void websocket_client(const char* id, WebSocketClientState&, ImVec2 size = {-1, -1});
|
||||
}
|
||||
```
|
||||
|
||||
## Tareas
|
||||
|
||||
### Fase 1 — Vendor + decision WS
|
||||
|
||||
- 1.1 Vendorear `httplib.h` (pinear version reciente con TLS opcional).
|
||||
- 1.2 Decidir cliente WS:
|
||||
- Opcion A: cpp-httplib `WebSocketClient` si la version vendoreada lo trae.
|
||||
- Opcion B: vendorear `websocketpp` (header-only-ish, depende de Asio header-only).
|
||||
- 1.3 Documentar la decision en `cpp/vendor/<lib>/README.md`.
|
||||
|
||||
### Fase 2 — http_inspector
|
||||
|
||||
- 2.1 Implementar el componente. Request en background con `std::thread` (ver `process_runner_cpp_core` como patrón).
|
||||
- 2.2 Pretty-print: si `Content-Type: application/json`, formatear con un mini parser/printer JSON (o copiar uno minimal; no necesitamos validacion exhaustiva).
|
||||
- 2.3 `.md` con frontmatter (`kind: component`, `purity: impure`, `error_type`).
|
||||
|
||||
### Fase 3 — websocket_client
|
||||
|
||||
- 3.1 Implementar `connect/disconnect` en thread; lockear cola de mensajes con mutex; copiar a `state.messages` en `websocket_client()` (main thread).
|
||||
- 3.2 Calcular `rate_msgs_s` como contador por segundo con ventana deslizante (puede usar `time_series_buffer_cpp_core`).
|
||||
- 3.3 `.md`.
|
||||
|
||||
### Fase 4 — Gallery demo
|
||||
|
||||
- 4.1 `demos_net.cpp` con `demo_http_inspector()` y `demo_websocket_client()`.
|
||||
- 4.2 Registrar.
|
||||
|
||||
### Fase 5 — Tests + docs
|
||||
|
||||
- 5.1 Test pretty-print JSON sobre payload conocido.
|
||||
- 5.2 Test ratelimit del rate_msgs_s.
|
||||
- 5.3 Smoke test (opcional, requires net): GET a 127.0.0.1 contra un servidor levantado en el test.
|
||||
- 5.4 `./fn index` + `./fn show`.
|
||||
|
||||
## Ejemplo de uso
|
||||
|
||||
```cpp
|
||||
fn::HttpInspectorState http;
|
||||
fn::WebSocketClientState ws;
|
||||
|
||||
fn::run_app("net", [&]{
|
||||
if (ImGui::BeginTabBar("net")) {
|
||||
if (ImGui::BeginTabItem("HTTP")) { fn::http_inspector("##h", http); ImGui::EndTabItem(); }
|
||||
if (ImGui::BeginTabItem("WS")) { fn::websocket_client("##w", ws); ImGui::EndTabItem(); }
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Decisiones de diseño
|
||||
|
||||
- **cpp-httplib** vs libcurl: header-only gana en complejidad de build. TLS via OpenSSL si esta disponible; degradar a HTTP si no.
|
||||
- **Mensajes WS capped**: 500 maximo en buffer para no crecer indefinidamente; descartar oldest.
|
||||
- **Sin authentication helpers**: el header `Authorization` se mete a mano. UX simple.
|
||||
|
||||
## Riesgos
|
||||
|
||||
- **TLS**: cpp-httplib requiere OpenSSL para `https://`. Documentar en CMake como opt-in.
|
||||
- **WS reconnect**: no implementar auto-reconnect en MVP. El usuario reconecta manualmente.
|
||||
- **Threading**: cuidado con marshalling de strings entre thread y UI. Mutex obligatorio en mensajes.
|
||||
@@ -0,0 +1,138 @@
|
||||
---
|
||||
id: "0035"
|
||||
title: "C++ map_tiles (slippy map OSM)"
|
||||
status: pendiente
|
||||
type: feature
|
||||
domain:
|
||||
- cpp-stack
|
||||
scope: multi-app
|
||||
priority: baja
|
||||
depends: []
|
||||
blocks: []
|
||||
related: []
|
||||
created: 2026-05-17
|
||||
updated: 2026-05-17
|
||||
tags: []
|
||||
---
|
||||
# 0035 — C++ map_tiles (slippy map OSM)
|
||||
|
||||
## APP Metadata
|
||||
|
||||
| Campo | Valor |
|
||||
|-------|-------|
|
||||
| **ID** | 0035 |
|
||||
| **Estado** | pendiente |
|
||||
| **Prioridad** | baja |
|
||||
| **Tipo** | feature — C++ viz (cpp/functions/viz) |
|
||||
|
||||
## Dependencias
|
||||
|
||||
`gl_texture_load_cpp_gfx` (issue 0026), `gl_loader_cpp_gfx`. Vendor: `cpp-httplib` (si ya esta vendoreado por 0033) o reusar.
|
||||
|
||||
**Desbloquea:** apps GIS minimas, dashboards con localizacion (entities con lat/lon), debugging de scrapers geograficos.
|
||||
|
||||
---
|
||||
|
||||
## Objetivo
|
||||
|
||||
Componente ImGui que renderiza un slippy map estilo Leaflet sobre OpenStreetMap tiles, con pan/zoom y soporte para markers + polylines.
|
||||
|
||||
## Contexto
|
||||
|
||||
Hay datos geograficos en varios proyectos (registry de proveedores, viajes, infrastructura). No hay forma de visualizarlos en C++; se exporta a una notebook con folium o se monta un frontend React.
|
||||
|
||||
## Arquitectura
|
||||
|
||||
```
|
||||
cpp/functions/viz/
|
||||
├── map_tiles.h # NEW
|
||||
├── map_tiles.cpp # NEW
|
||||
└── map_tiles.md # NEW (impure)
|
||||
cpp/apps/primitives_gallery/
|
||||
├── demos_map.cpp # NEW
|
||||
├── demos.h # MOD
|
||||
├── main.cpp # MOD
|
||||
└── CMakeLists.txt # MOD
|
||||
```
|
||||
|
||||
### API propuesta
|
||||
|
||||
```cpp
|
||||
namespace fn {
|
||||
|
||||
struct MapMarker { double lat, lon; ImU32 color; const char* label; };
|
||||
struct MapPolyline { std::vector<ImVec2> latlon; ImU32 color; float thickness = 2.f; }; // ImVec2 = (lat, lon)
|
||||
struct MapState {
|
||||
double lat = 40.4168; // Madrid default
|
||||
double lon = -3.7038;
|
||||
int zoom = 6; // 0..18
|
||||
std::string tile_url = "https://tile.openstreetmap.org/{z}/{x}/{y}.png";
|
||||
std::string user_agent = "fn-registry-map/0.1 (egutierrez@aurgi.com)";
|
||||
bool show_attribution = true; // OSM atribucion obligatoria
|
||||
};
|
||||
|
||||
void map_tiles(const char* id, MapState&,
|
||||
const MapMarker* markers, int n_markers,
|
||||
const MapPolyline* polylines, int n_polylines,
|
||||
ImVec2 size = {-1, -1});
|
||||
}
|
||||
```
|
||||
|
||||
## Tareas
|
||||
|
||||
### Fase 1 — Tile cache
|
||||
|
||||
- 1.1 Cache en disco bajo `~/.cache/fn-registry/tiles/{z}/{x}/{y}.png`. Crear directorio si no existe.
|
||||
- 1.2 Cache en RAM (LRU) de `GlTexture` ya subidas, max 256 entradas.
|
||||
- 1.3 Funcion `tile_get_or_fetch(z, x, y)`: si en RAM → return; si en disco → load (gl_texture_load); si no → spawn thread que descarga via cpp-httplib y guarda en disco. Mientras tanto, devolver placeholder.
|
||||
|
||||
### Fase 2 — Proyeccion + interaction
|
||||
|
||||
- 2.1 Funciones puras `lonlat_to_xy(lat, lon, zoom) -> (px, py)` y `xy_to_lonlat`.
|
||||
- 2.2 `map_tiles_handle_input`: drag pan, wheel zoom (clamp 0-18), mantener punto bajo cursor durante zoom.
|
||||
|
||||
### Fase 3 — Render
|
||||
|
||||
- 3.1 Calcular tiles visibles dado el viewport y zoom.
|
||||
- 3.2 Para cada tile visible: `ImGui::Image(texture, screen_rect)` o `AddImage` en el DrawList.
|
||||
- 3.3 Markers: `AddCircleFilled` + texto.
|
||||
- 3.4 Polylines: convertir lat/lon a screen y `AddPolyline`.
|
||||
- 3.5 Atribucion en esquina inferior derecha: `"© OpenStreetMap contributors"`.
|
||||
|
||||
### Fase 4 — User-Agent + ToS
|
||||
|
||||
- 4.1 Enviar `User-Agent` custom (OSM lo exige). Documentar en `.md` que el usuario debe poner su contacto.
|
||||
- 4.2 Cap a 2 requests/segundo simultaneos para no abusar del tile server.
|
||||
|
||||
### Fase 5 — Gallery demo
|
||||
|
||||
- 5.1 `demos_map.cpp` con `demo_map_tiles()`: mapa centrado en Espana, 5 markers en ciudades, polyline conectandolas.
|
||||
- 5.2 Registrar.
|
||||
|
||||
### Fase 6 — Tests + docs
|
||||
|
||||
- 6.1 Tests puros de proyeccion (Madrid → conocidos pixels a zoom 10).
|
||||
- 6.2 `./fn index` + `./fn show map_tiles_cpp_viz`.
|
||||
|
||||
## Ejemplo de uso
|
||||
|
||||
```cpp
|
||||
fn::MapState m;
|
||||
fn::MapMarker markers[] = {
|
||||
{40.4168, -3.7038, IM_COL32(255,80,80,255), "Madrid"},
|
||||
{41.3851, 2.1734, IM_COL32(80,150,255,255), "Barcelona"},
|
||||
};
|
||||
fn::map_tiles("##m", m, markers, 2, nullptr, 0);
|
||||
```
|
||||
|
||||
## Decisiones de diseño
|
||||
|
||||
- **OSM tile server por defecto**: gratis, sin API key. Documentar limite y alternativa (carto, stamen) cambiando `tile_url`.
|
||||
- **Cache en `~/.cache/fn-registry/tiles/`**: respeta XDG; persistente entre runs.
|
||||
- **Sin GeoJSON parsing en MVP**: solo markers/polylines via API. GeoJSON en issue futuro.
|
||||
|
||||
## Riesgos
|
||||
|
||||
- **Bloqueo de IP por OSM**: documentar terminos de uso (max 1 req/s recomendado, User-Agent unico).
|
||||
- **Tiles a alta latitud**: documentar que la proyeccion Web Mercator distorsiona >85°.
|
||||
- **Threading de descargas**: cuidado con marshalling de texturas GL (deben crearse en hilo con contexto). Patron: descargar PNG bytes en thread, crear GL texture en main thread.
|
||||
@@ -0,0 +1,160 @@
|
||||
---
|
||||
id: "0036"
|
||||
title: "C++ image_canvas + webcam_texture"
|
||||
status: pendiente
|
||||
type: feature
|
||||
domain:
|
||||
- cpp-stack
|
||||
scope: multi-app
|
||||
priority: baja
|
||||
depends: []
|
||||
blocks: []
|
||||
related: []
|
||||
created: 2026-05-17
|
||||
updated: 2026-05-17
|
||||
tags: []
|
||||
---
|
||||
# 0036 — C++ image_canvas + webcam_texture
|
||||
|
||||
## APP Metadata
|
||||
|
||||
| Campo | Valor |
|
||||
|-------|-------|
|
||||
| **ID** | 0036 |
|
||||
| **Estado** | pendiente |
|
||||
| **Prioridad** | baja |
|
||||
| **Tipo** | feature — C++ core/gfx (cpp/functions/core, cpp/functions/gfx) |
|
||||
|
||||
## Dependencias
|
||||
|
||||
`gl_texture_load_cpp_gfx` (issue 0026) — recomendado. `tokens_cpp_core`. Independiente de los demas.
|
||||
|
||||
**Desbloquea:** UIs para etiquetado/anotacion de imagenes (CV, OCR debugging) y entrada de webcam para `shaders_lab` reactivo a video.
|
||||
|
||||
---
|
||||
|
||||
## Objetivo
|
||||
|
||||
Dos primitivos:
|
||||
|
||||
1. **`image_canvas_cpp_core`** — viewer de imagen con pan/zoom + capa de anotaciones (rectangulos, puntos, polilineas, texto). Drag para crear nuevas anotaciones; click para seleccionar; tecla Del para borrar.
|
||||
2. **`webcam_texture_cpp_gfx`** — captura de webcam (V4L2 en Linux, Media Foundation en Windows) → GL texture actualizada cada frame. API parejo a `audio_capture`.
|
||||
|
||||
## Contexto
|
||||
|
||||
Casos de uso identificados:
|
||||
- Inspeccionar resultados de un detector (rectangulos overlay).
|
||||
- Etiquetar manualmente datasets pequeños in-app.
|
||||
- Pasar webcam a shaders en `shaders_lab` (filtros video en tiempo real).
|
||||
|
||||
Ningun primitivo del registry actual cubre estos casos.
|
||||
|
||||
## Arquitectura
|
||||
|
||||
```
|
||||
cpp/functions/core/
|
||||
├── image_canvas.h # NEW
|
||||
├── image_canvas.cpp # NEW
|
||||
└── image_canvas.md # NEW (impure component)
|
||||
cpp/functions/gfx/
|
||||
├── webcam_texture.h # NEW
|
||||
├── webcam_texture.cpp # NEW
|
||||
└── webcam_texture.md # NEW (impure)
|
||||
cpp/apps/primitives_gallery/
|
||||
├── demos_image_webcam.cpp # NEW
|
||||
├── demos.h # MOD
|
||||
├── main.cpp # MOD
|
||||
└── CMakeLists.txt # MOD
|
||||
```
|
||||
|
||||
### API propuesta
|
||||
|
||||
```cpp
|
||||
namespace fn {
|
||||
|
||||
// --- image_canvas ---
|
||||
struct Annotation {
|
||||
enum Kind { Rect, Point, Polyline, Text } kind;
|
||||
std::vector<ImVec2> points; // image-space coords (no screen)
|
||||
ImU32 color = IM_COL32(255, 80, 80, 255);
|
||||
std::string label;
|
||||
};
|
||||
struct ImageCanvasState {
|
||||
GLuint texture = 0; // caller-owned
|
||||
int img_w = 0, img_h = 0;
|
||||
float zoom = 1.f;
|
||||
ImVec2 pan = {0, 0};
|
||||
std::vector<Annotation> annotations;
|
||||
int selected = -1;
|
||||
enum Tool { ToolNone, ToolRect, ToolPoint, ToolPolyline } tool = ToolNone;
|
||||
};
|
||||
void image_canvas(const char* id, ImageCanvasState&, ImVec2 size = {-1, -1});
|
||||
|
||||
// --- webcam_texture ---
|
||||
struct WebcamTextureConfig { int width = 640; int height = 480; int fps = 30; int device = 0; };
|
||||
struct WebcamTexture { GLuint id = 0; int w = 0; int h = 0; bool ok() const { return id != 0; } };
|
||||
WebcamTexture webcam_texture_start(const WebcamTextureConfig&);
|
||||
void webcam_texture_stop(WebcamTexture&);
|
||||
bool webcam_texture_update(WebcamTexture&); // copia ultimo frame a GPU; true si hubo nuevo
|
||||
const char* webcam_texture_last_error();
|
||||
}
|
||||
```
|
||||
|
||||
## Tareas
|
||||
|
||||
### Fase 1 — image_canvas
|
||||
|
||||
- 1.1 Render: `ImGui::Image(texture, ...)` con transform por pan/zoom. Drag con MMB para pan, wheel para zoom.
|
||||
- 1.2 Tool == Rect: drag con LMB en image-space crea anotacion `Rect` con 2 puntos.
|
||||
- 1.3 Tool == Point: click LMB anade un Point.
|
||||
- 1.4 Tool == Polyline: click anade puntos; doble-click cierra.
|
||||
- 1.5 Hit-test para selection; Del key elimina la seleccionada.
|
||||
- 1.6 `.md`.
|
||||
|
||||
### Fase 2 — webcam_texture (Linux V4L2)
|
||||
|
||||
- 2.1 Abrir `/dev/video<device>`, MMAP buffers, VIDIOC_STREAMON.
|
||||
- 2.2 En `update`, dequeue buffer, convert YUYV → RGBA (CPU), upload a GL texture, requeue.
|
||||
- 2.3 `.md` (`kind: function`, `purity: impure`, `error_type`).
|
||||
|
||||
### Fase 3 — webcam_texture (Windows Media Foundation, opcional)
|
||||
|
||||
- 3.1 Si `_WIN32`, usar `IMFSourceReader`. Convert NV12 → RGBA.
|
||||
- 3.2 Documentar que macOS no se soporta en MVP.
|
||||
|
||||
### Fase 4 — Gallery demo
|
||||
|
||||
- 4.1 `demos_image_webcam.cpp`:
|
||||
- `demo_image_canvas()`: carga `assets/sample.png` (si existe — fallback a rendered placeholder), permite pintar rectangulos.
|
||||
- `demo_webcam_texture()`: start cam, mostrar preview + sliders RGB sobre un shader fullscreen que samplea la cam.
|
||||
- 4.2 Registrar.
|
||||
|
||||
### Fase 5 — Tests + docs
|
||||
|
||||
- 5.1 Tests puros de transform image-space ↔ screen-space.
|
||||
- 5.2 Smoke test webcam (skip si no hay /dev/video0).
|
||||
- 5.3 `./fn index`.
|
||||
|
||||
## Ejemplo de uso
|
||||
|
||||
```cpp
|
||||
auto cam = fn::webcam_texture_start({});
|
||||
if (!cam.ok()) std::fprintf(stderr, "%s\n", fn::webcam_texture_last_error());
|
||||
|
||||
fn::run_app("cam", [&]{
|
||||
fn::webcam_texture_update(cam);
|
||||
ImGui::Image((ImTextureID)(intptr_t)cam.id, {cam.w*1.f, cam.h*1.f});
|
||||
});
|
||||
```
|
||||
|
||||
## Decisiones de diseño
|
||||
|
||||
- **Anotaciones en image-space**: pan/zoom no afectan coordenadas guardadas. Permite serializar a JSON (issue futuro).
|
||||
- **YUYV → RGBA en CPU**: simple, ~5ms/frame a 640×480. Si hace falta, hacer un compute shader (issue 0027).
|
||||
- **macOS sin webcam por ahora**: AVFoundation requiere ObjC++; no compensa en MVP.
|
||||
|
||||
## Riesgos
|
||||
|
||||
- **Permisos webcam**: documentar troubleshooting (`/dev/video0` permisos en Linux).
|
||||
- **Driver V4L2 raro**: algunos formatos de pixel no son YUYV. Documentar fallback a MJPEG (con stb_image decode) o error claro.
|
||||
- **`ImTextureID` semantics** entre backends: documentar.
|
||||
@@ -0,0 +1,215 @@
|
||||
---
|
||||
id: "57"
|
||||
title: "Extraer paneles ImGui reutilizables de las apps C++ a cpp/functions/ — roadmap"
|
||||
status: pendiente
|
||||
type: epic
|
||||
domain:
|
||||
- cpp-stack
|
||||
- registry-quality
|
||||
scope: cross-stack
|
||||
priority: media
|
||||
depends: []
|
||||
blocks: []
|
||||
related: []
|
||||
created: 2026-05-09
|
||||
updated: 2026-05-17
|
||||
tags: []
|
||||
---
|
||||
|
||||
## Contexto
|
||||
|
||||
Las apps C++ del registry (`cpp/apps/*`, `projects/*/apps/*`) llevan acumulando paneles ImGui que comparten patron pero viven dentro de cada app. Hay duplicacion observable y mas que va a aparecer (navegator_dashboard quiere chat panel igual que graph_explorer; el dashboard de Tabs va a parecerse al panel "Browsers").
|
||||
|
||||
Hoy ya estan en el registro `cpp/functions/core/`: `dashboard_panel`, `fullscreen_window`, `log_window`, `modal_dialog`, `tree_view`, `selectable_text`. La capa visual (tokens, theming, iconos, settings/about/menubar) vive en `fn_framework`. Esto cubre primitivas. Falta una tier intermedia: **paneles compuestos** (mas grandes que un widget, mas pequeños que una app).
|
||||
|
||||
Este issue documenta el roadmap de que extraer, cuando, y por que.
|
||||
|
||||
## Principios
|
||||
|
||||
1. **Rule of three** — no extraer hasta que haya 2-3 consumidores reales. Pre-extraer crea abstracciones malas.
|
||||
2. **Consumidor primero** — escribir el panel inline en la app. Si una segunda app lo necesita, copiar+adaptar. Si una tercera lo pide, extraer.
|
||||
3. **Parametrizable, no monolitico** — los paneles extraidos reciben un struct de config (callbacks, paths, env vars) en vez de hardcodear nombres.
|
||||
4. **No exponer estado global del modulo extraido** — usar contexto opaco que cada app crea/destruye.
|
||||
|
||||
## Catalogo: paneles candidatos detectados
|
||||
|
||||
Revisado todas las apps C++ vivas. Por cada panel: lineas, consumidores actuales, consumidores potenciales, decision.
|
||||
|
||||
### Tier 0 — ya extraidos ✓
|
||||
|
||||
| Panel | Ubicacion |
|
||||
|---|---|
|
||||
| dashboard cards grid | `cpp/functions/core/dashboard_panel.{h,cpp}` |
|
||||
| fullscreen window wrapper | `cpp/functions/core/fullscreen_window.{h,cpp}` |
|
||||
| log window con buffer | `cpp/functions/core/log_window.{h,cpp}` |
|
||||
| modal dialog | `cpp/functions/core/modal_dialog.{h,cpp}` |
|
||||
| tree view | `cpp/functions/core/tree_view.{h,cpp}` |
|
||||
| selectable text | `cpp/functions/core/selectable_text.{h,cpp}` |
|
||||
|
||||
Estos definen el "lower-tier" — primitivas. Los nuevos extracts componen sobre estos.
|
||||
|
||||
### Tier 1 — extraer cuando aparezca el 3er consumidor
|
||||
|
||||
#### 1.1 `claude_chat_panel` (origen: graph_explorer/chat.cpp, 1241 LoC)
|
||||
|
||||
**Que es:** panel ImGui que arranca un subprocess `claude -p`, le envia mensajes del usuario, parsea stream-json, renderiza historial con tool-use detectado, expone callbacks para que la app recargue cuando el agente muta su BD.
|
||||
|
||||
**Consumidores hoy:** graph_explorer.
|
||||
|
||||
**Consumidores potenciales:**
|
||||
- navegator_dashboard (issue navegator/0001) — chat para controlar Chrome.
|
||||
- kanban (futuro) — agente para crear/mover cards.
|
||||
- registry_dashboard — agente para crear functions/proposals.
|
||||
|
||||
**Por que tier 1 (no tier 0):**
|
||||
- 59 referencias a `gx-cli`, `GX_OPS_DB`, `GX_APP_DB`, `agent_mutations`. Acoplamiento alto a graph_explorer.
|
||||
- Refactor a config parametrizado es ~4-6h y modifica codigo que YA funciona — riesgo regresion.
|
||||
- Con solo 2 consumidores no hay claridad de que parametrizar.
|
||||
|
||||
**Cuando extraer:** 3er consumidor pide chat. Hasta entonces: copiar+adaptar (graph_explorer mantiene el suyo, navegator copia el suyo).
|
||||
|
||||
**API propuesta cuando se extraiga:**
|
||||
|
||||
```cpp
|
||||
struct ClaudeChatConfig {
|
||||
const char* claude_bin; // "claude" o WSL path
|
||||
const char* cli_tool_name; // "gx-cli", "cdp-cli", "kanban-cli", ...
|
||||
const char* cli_tool_dir;
|
||||
std::map<std::string, std::string> env_vars; // GX_*, NAVD_*, ...
|
||||
const char* mcp_config_path; // o func que lo genera on-demand
|
||||
const char* system_prompt; // opcional
|
||||
std::function<int()> mutations_counter; // app-side reload trigger
|
||||
const char* log_file_path;
|
||||
};
|
||||
|
||||
namespace fn_ui {
|
||||
struct ChatHandle; // opaco
|
||||
ChatHandle* chat_create(const ClaudeChatConfig& cfg);
|
||||
void chat_send(ChatHandle*, const char* user_text);
|
||||
void chat_render(ChatHandle*, bool* p_open);
|
||||
void chat_destroy(ChatHandle*);
|
||||
}
|
||||
```
|
||||
|
||||
#### 1.2 `jobs_queue_panel` (origen: graph_explorer/jobs.cpp + odr_console/jobs)
|
||||
|
||||
**Que es:** panel + sistema de jobs asincronos. Lista jobs en cola/running/done con barra de progreso, stdout/stderr capture, cancelable, persistente entre sesiones.
|
||||
|
||||
**Consumidores hoy:** graph_explorer (jobs.cpp + views_jobs.cpp), odr_console (panel Jobs).
|
||||
|
||||
**Consumidores potenciales:**
|
||||
- navegator_dashboard — scraping jobs (un crawl es un job).
|
||||
- registry_dashboard — fn index, fn run jobs.
|
||||
|
||||
**Por que tier 1:** ya hay 2 consumidores, parece duplicacion clara. **Inspeccionar diff entre ambos antes de extraer** — pueden estar implementados distinto y no reusarse del todo.
|
||||
|
||||
**Cuando extraer:** despues de auditar que graph_explorer/jobs.cpp y odr_console/jobs comparten >70% del codigo. Si si: 3er consumidor (navegator scraping) detona la extraccion.
|
||||
|
||||
**API propuesta:**
|
||||
|
||||
```cpp
|
||||
namespace fn_ui {
|
||||
struct Job {
|
||||
std::string id;
|
||||
std::string title;
|
||||
std::function<int(JobContext&)> run; // retorna exit code
|
||||
// ...
|
||||
};
|
||||
void jobs_panel_init(const char* persistence_db_path);
|
||||
void jobs_panel_submit(const Job&);
|
||||
void jobs_panel_render(bool* p_open);
|
||||
}
|
||||
```
|
||||
|
||||
### Tier 2 — extraer cuando aparezca el 2do consumidor (todavia no detectado)
|
||||
|
||||
#### 2.1 `project_switcher_panel` (origen: graph_explorer/project_manager.cpp)
|
||||
|
||||
**Que es:** sidebar/menu para cambiar entre subcarpetas de proyectos (cada proyecto tiene su DB y settings). Persiste el ultimo proyecto abierto.
|
||||
|
||||
**Hoy en:** graph_explorer.
|
||||
|
||||
**Potencial:** navegator_dashboard (perfiles browser como proyectos), registry_dashboard (apps como proyectos), kanban (boards).
|
||||
|
||||
**Cuando extraer:** cuando navegator_dashboard quiera multi-perfil persistente con settings por perfil. v3 del dashboard probablemente.
|
||||
|
||||
#### 2.2 `key_value_inspector_panel` (origen: graph_explorer/views.cpp `Inspector`)
|
||||
|
||||
**Que es:** panel "Inspector" generico que muestra propiedades de un objeto seleccionado en formato key:value, con edicion inline opcional. Hoy es ad-hoc para entities.
|
||||
|
||||
**Potencial:** navegator_dashboard (Tab Detail mostrar propiedades de pestaña), registry_dashboard (function detail), kanban (card details).
|
||||
|
||||
**Cuando extraer:** cuando dos paneles de "ver propiedades de cosa seleccionada" tengan codigo casi identico.
|
||||
|
||||
#### 2.3 `paste_and_extract_panel` (origen: graph_explorer/extract_panel.cpp)
|
||||
|
||||
**Que es:** panel que recibe texto pegado, invoca enricher Python, muestra entities extraidas + boton para insertarlas.
|
||||
|
||||
**Potencial:** navegator_dashboard (extraer texto seleccionado de pestaña), registry_dashboard (extraer descripcion para auto-fill function metadata).
|
||||
|
||||
**Cuando extraer:** 2do consumidor con flujo similar.
|
||||
|
||||
#### 2.4 `http_request_inspector_panel` (origen: registry_dashboard/http_client.cpp + futuro navegator_dashboard/network)
|
||||
|
||||
**Que es:** panel para ver una peticion HTTP (request line, headers, body) renderizado bonito. Read-only.
|
||||
|
||||
**Potencial:** navegator_dashboard (Network panel renderiza HAR entries), registry_dashboard (mostrar requests outgoing del fn_registry_api).
|
||||
|
||||
**Cuando extraer:** cuando 0070b (HAR record) sea consumido por algo que quiera renderizarlo bien.
|
||||
|
||||
### Tier 3 — NO extraer (especificos de su app)
|
||||
|
||||
| Panel | Por que NO |
|
||||
|---|---|
|
||||
| graph_explorer/types_registry | Acoplado a `types.yaml` schema especifico. |
|
||||
| graph_explorer/node_groups | DuckDB + grafo + tipo "Table" del schema graph_explorer. |
|
||||
| graph_explorer/layout_store | Posiciones de nodos del grafo, schema especifico. |
|
||||
| graph_explorer/views.cpp::Legend | Mapa de tipos→colores especifico del grafo. |
|
||||
| graph_explorer/views.cpp::Stats | Conteos por tipo de entity, schema especifico. |
|
||||
| navegator_dashboard/browsers panel | Detector chrome.exe Win32, hereda WMI/CIM, especifico Windows. |
|
||||
| odr_console/datasets panel | Listado especifico del flujo ODR. |
|
||||
|
||||
Estos viven en su app. Si emerge un patron despues, se reconsidera.
|
||||
|
||||
### Tier 4 — primitivas que sirven a TODOS los paneles (extraccion oportunista, sin app-driver)
|
||||
|
||||
Util tener antes de los Tier 1-2 para hacer la extraccion mas limpia:
|
||||
|
||||
| Primitiva | Para que |
|
||||
|---|---|
|
||||
| `subprocess_streamer.{h,cpp}` | Encapsula spawn + pipes + read-thread + queue. Lo usaria chat_panel + jobs_panel. |
|
||||
| `mcp_config_writer.{h,cpp}` | Genera `.mcp.json` para Claude. Lo usaria chat_panel. |
|
||||
| `app_db_init.{h,cpp}` | Patron generico de "abrir SQLite junto al exe + aplicar migrations embed.FS". Hoy cada app reimplementa. |
|
||||
| `key_input_history.{h,cpp}` | Input + flecha arriba/abajo recupera entradas previas. Util en chat + REPL evaluate. |
|
||||
| `json_log_renderer.{h,cpp}` | Render de stream JSON estilo claude-code (rol, tool_use, contenido). Util tanto en chat de graph_explorer como en futuros chats. |
|
||||
|
||||
Estas primitivas se extraen cuando empieza la extraccion de un Tier 1 que las necesita (no antes — caen en el "rule of three" tambien).
|
||||
|
||||
## Estrategia de adopcion
|
||||
|
||||
Por **demanda**:
|
||||
|
||||
1. **Hoy (tras este issue):** copiar+adaptar `chat.cpp` a `navegator_dashboard`. Documentar la duplicacion. NO extraer.
|
||||
2. **Cuando aparezca 3er consumidor de chat** (o `jobs`, o `project_switcher`): pausar feature work, refactorizar el extract con vista 3D.
|
||||
3. **Auditoria periodica trimestral**: cada 3 meses revisar este issue, contar consumidores actuales, decidir si activar extracts.
|
||||
|
||||
## Sub-issues iniciales (no abrir hasta tener 3er consumidor identificado)
|
||||
|
||||
- `0071a` — extract `claude_chat_panel` (cuando 3 apps lo pidan).
|
||||
- `0071b` — extract `jobs_queue_panel` (auditar duplicacion graph_explorer/odr_console primero).
|
||||
- `0071c` — extract `project_switcher_panel`.
|
||||
- `0071d` — extract `key_value_inspector_panel`.
|
||||
- `0071e` — extract `http_request_inspector_panel`.
|
||||
|
||||
## Definicion de hecho (issue 0071 cerrado)
|
||||
|
||||
- Catalogo arriba esta vivo, refleja apps actuales.
|
||||
- Auditoria trimestral mantiene la lista actualizada.
|
||||
- Sub-issues 0071a-e: cada uno abre cuando se cumple su criterio.
|
||||
- Cuando todos los Tier 1 + 2 esten extraidos, este issue se marca completed.
|
||||
|
||||
## Anti-patron a evitar
|
||||
|
||||
- **Big-bang refactor**: extraer 5 paneles a la vez, con interfaces no validadas, rompiendo todas las apps. NO.
|
||||
- **Pre-extraer "por si acaso"**: el panel mas dificil es el que solo tiene 1 consumidor — no sabes que parametrizar. NO.
|
||||
- **Library wrapper sin control**: meter `claude_chat_panel` con 30 hooks/callbacks "para flexibilidad". KISS — solo expone lo que el 3er consumidor demuestra que necesita.
|
||||
@@ -0,0 +1,169 @@
|
||||
---
|
||||
id: "0072a"
|
||||
title: "gamedev — smoke SDL3 + sokol_gfx + ImGui (PC + WASM)"
|
||||
status: pendiente
|
||||
type: feature
|
||||
domain:
|
||||
- cpp-stack
|
||||
- gamedev
|
||||
scope: multi-app
|
||||
priority: alta
|
||||
depends: []
|
||||
blocks: []
|
||||
related: []
|
||||
created: 2026-05-10
|
||||
updated: 2026-05-17
|
||||
tags: [ausente-ready, gamedev, cpp, wasm]
|
||||
---
|
||||
|
||||
## Objetivo
|
||||
|
||||
Validar de forma temprana que el stack SDL3 + sokol_gfx + Dear ImGui compila y corre en PC (Windows + Linux) y WASM (Emscripten) con un binario "Hello sprite + shader" antes de invertir tiempo en runtime real.
|
||||
|
||||
## Salida esperada
|
||||
|
||||
App `cpp/apps/engine_smoke/` que:
|
||||
1. Abre ventana SDL3 (1280x720, redimensionable).
|
||||
2. Inicializa sokol_gfx (GL en desktop, WebGL2 en WASM).
|
||||
3. Pinta:
|
||||
- Un quad con textura cargada via `stbi_load` (ya en stack).
|
||||
- Un fullscreen shader (gradiente animado).
|
||||
- Un panel ImGui con FPS y boton "exit".
|
||||
4. Compila a:
|
||||
- `engine_smoke.exe` (Windows, MSVC o MinGW)
|
||||
- `engine_smoke` (Linux, gcc/clang)
|
||||
- `engine_smoke.html` + `engine_smoke.wasm` + `engine_smoke.js` (Emscripten)
|
||||
5. WASM gzip ≤ **1.5 MB** (objetivo agresivo de Fase 0). Si no se cumple, documentar de donde viene el peso y plan de reduccion.
|
||||
|
||||
## Tareas
|
||||
|
||||
### 1. Vendoring de dependencias
|
||||
|
||||
`cpp/vendor/` ya existe. Añadir:
|
||||
- `sokol/` — `sokol_gfx.h`, `sokol_app.h` NO (usamos SDL3), `sokol_log.h`, `sokol_glue.h` adaptado para SDL3 (init manual del contexto).
|
||||
- `sdl3/` — clonar build estatico de SDL 3.x.
|
||||
- ImGui ya esta en `cpp/vendor/imgui/`. Añadir backends `imgui_impl_sdl3.cpp` + `imgui_impl_sokol.cpp`. Si no existe oficial sokol backend, escribir uno minimo (~200 lineas).
|
||||
|
||||
### 2. CMake
|
||||
|
||||
```cmake
|
||||
# cpp/apps/engine_smoke/CMakeLists.txt
|
||||
add_executable(engine_smoke main.cpp)
|
||||
target_link_libraries(engine_smoke PRIVATE SDL3::SDL3 imgui)
|
||||
target_compile_definitions(engine_smoke PRIVATE SOKOL_GLCORE)
|
||||
|
||||
if(EMSCRIPTEN)
|
||||
target_link_options(engine_smoke PRIVATE
|
||||
-sUSE_WEBGL2=1
|
||||
-sFULL_ES3=1
|
||||
-sALLOW_MEMORY_GROWTH=1
|
||||
-sINITIAL_MEMORY=33554432 # 32MB
|
||||
--shell-file=${CMAKE_CURRENT_SOURCE_DIR}/shell.html
|
||||
)
|
||||
endif()
|
||||
```
|
||||
|
||||
NO usar `add_imgui_app` (esa macro asume GLFW + framework desktop). Esta app es deliberadamente standalone para no contaminar `fn_framework` hasta que validemos el stack.
|
||||
|
||||
### 3. main.cpp minimo
|
||||
|
||||
```cpp
|
||||
#include <SDL3/SDL.h>
|
||||
#define SOKOL_IMPL
|
||||
#include "sokol_gfx.h"
|
||||
#include "imgui.h"
|
||||
#include "imgui_impl_sdl3.h"
|
||||
#include "imgui_impl_sokol.h"
|
||||
|
||||
int main(int, char**) {
|
||||
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO);
|
||||
SDL_Window* win = SDL_CreateWindow("engine_smoke", 1280, 720,
|
||||
SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
|
||||
SDL_GLContext ctx = SDL_GL_CreateContext(win);
|
||||
|
||||
sg_setup({ .environment = sokol_glue_env() });
|
||||
ImGui::CreateContext();
|
||||
ImGui_ImplSDL3_InitForOpenGL(win, ctx);
|
||||
ImGui_ImplSokol_Init();
|
||||
|
||||
bool running = true;
|
||||
while (running) {
|
||||
SDL_Event e;
|
||||
while (SDL_PollEvent(&e)) {
|
||||
ImGui_ImplSDL3_ProcessEvent(&e);
|
||||
if (e.type == SDL_EVENT_QUIT) running = false;
|
||||
}
|
||||
// render: clear, sprite quad, fullscreen shader, ImGui overlay
|
||||
sg_begin_pass({ .swapchain = sokol_glue_swapchain() });
|
||||
// ... pipeline + draw calls
|
||||
ImGui_ImplSokol_NewFrame();
|
||||
ImGui_ImplSDL3_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
ImGui::Begin("Smoke"); ImGui::Text("FPS: %.1f", ImGui::GetIO().Framerate);
|
||||
if (ImGui::Button("exit")) running = false;
|
||||
ImGui::End();
|
||||
ImGui::Render();
|
||||
ImGui_ImplSokol_RenderDrawData(ImGui::GetDrawData());
|
||||
sg_end_pass();
|
||||
sg_commit();
|
||||
SDL_GL_SwapWindow(win);
|
||||
}
|
||||
sg_shutdown();
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Shell HTML para WASM
|
||||
|
||||
`shell.html` minimo: canvas + loader. Sin Bootstrap, sin nada extra. Ver Emscripten template default y stripear.
|
||||
|
||||
### 5. Pipeline de build
|
||||
|
||||
Funcion bash nueva: `bash/functions/pipelines/build_wasm_cpp_app.sh` que:
|
||||
1. Verifica `emcc` instalado (descarga `emsdk` si falta).
|
||||
2. `emcmake cmake -B build/wasm -S cpp/apps/engine_smoke`
|
||||
3. `cmake --build build/wasm --target engine_smoke`
|
||||
4. Reporta tamaños raw + gzip de `.wasm`, `.js`, `.html`.
|
||||
5. Falla si supera budget.
|
||||
|
||||
### 6. e2e_checks en `app.md`
|
||||
|
||||
```yaml
|
||||
e2e_checks:
|
||||
- id: build_pc
|
||||
cmd: "cmake --build build --target engine_smoke -j"
|
||||
timeout_s: 300
|
||||
- id: build_wasm
|
||||
cmd: "bash bash/functions/pipelines/build_wasm_cpp_app.sh engine_smoke"
|
||||
timeout_s: 600
|
||||
- id: size_budget_wasm
|
||||
cmd: "test $(stat -c%s build/wasm/engine_smoke.wasm.gz) -lt 1572864" # 1.5 MB
|
||||
```
|
||||
|
||||
## Decisiones a documentar
|
||||
|
||||
- ¿`sokol_gfx` o usar GL directo? — sokol_gfx, abstrae backend mobile despues sin tocar codigo de juego.
|
||||
- ¿`sokol_app` o `SDL3` para windowing? — SDL3, mejor soporte mobile + audio + gamepad.
|
||||
- ¿Imgui-sokol backend oficial? — verificar `floooh/sokol-samples` repo. Si no, escribirlo (renderable en una sesion, ~200 LoC).
|
||||
|
||||
## Criterio de exito
|
||||
|
||||
- [x] Compila en Linux + Windows (MSVC).
|
||||
- [x] Compila en WASM con emscripten ≥3.1.50.
|
||||
- [x] WASM gzip ≤ 1.5 MB.
|
||||
- [x] Sprite + shader + ImGui visibles en navegador (Chrome + Firefox).
|
||||
- [x] FPS estable ≥60 en navegador moderno.
|
||||
|
||||
## Riesgos
|
||||
|
||||
- Backend ImGui+sokol — si no hay oficial, escribir uno (no bloqueante).
|
||||
- SDL3 still relativamente nuevo (release oct 2024). Si bugs gordos, fallback a SDL2.
|
||||
- Emscripten + sokol_gfx WebGL2 — verificado en `floooh/sokol-samples`, debe funcionar.
|
||||
|
||||
## No-objetivos (Fase 0)
|
||||
|
||||
- NO audio funcional aun.
|
||||
- NO touch input.
|
||||
- NO build mobile (Android/iOS).
|
||||
- NO integracion con `fn_framework`.
|
||||
- NO assets reales.
|
||||
@@ -0,0 +1,102 @@
|
||||
---
|
||||
id: "0082"
|
||||
title: "Compilar binario `sd` (stable-diffusion.cpp) para sdcli_generate_go_ml"
|
||||
status: pendiente
|
||||
type: feature
|
||||
domain:
|
||||
- cpp-stack
|
||||
- imagegen
|
||||
scope: multi-app
|
||||
priority: media
|
||||
depends: []
|
||||
blocks: []
|
||||
related: []
|
||||
created: 2026-05-13
|
||||
updated: 2026-05-17
|
||||
tags: [ausente-ready]
|
||||
---
|
||||
|
||||
## Objetivo
|
||||
|
||||
Compilar el binario `sd` de [leejet/stable-diffusion.cpp](https://github.com/leejet/stable-diffusion.cpp)
|
||||
con backend CUDA en este host (WSL2 + RTX 3070) e instalarlo en `$PATH`. Habilita
|
||||
los tests reales de `sdcli_generate_go_ml` y el wrapper Go subprocess (Ola 3.C ya
|
||||
construido pero con tests en `skip` por falta de binario).
|
||||
|
||||
## Contexto
|
||||
|
||||
- Funcion Go `sdcli_resolve_binary_go_ml` busca `sd` o `sd-cli` en `$PATH`.
|
||||
- `sdcli_generate_go_ml` orquesta args via `genconfig_to_sdcli_args_go_ml`, lanza
|
||||
subproceso con `subprocess_stream_go_core`, parsea progreso con
|
||||
`sdcli_parse_progress_go_ml`, lee PNG de salida.
|
||||
- Tests `TestSdcliResolveBinary_NotFound`, `..._Hint` pasan; `TestSdcliGenerate_RequiresBinary`
|
||||
hace `t.Skip()` porque `sd` no existe en `$PATH`.
|
||||
- Backend `sdcpp_python_load_py_ml` ya validado con SD Turbo (CPU, 27s/imagen).
|
||||
El binario Go nativo deberia ser comparable o mejor con CUDA.
|
||||
|
||||
## Arquitectura
|
||||
|
||||
Archivos NUEVOS sugeridos:
|
||||
|
||||
- `bash/functions/infra/build_sd_cpp.sh` + `.md` — funcion del registry que clona y
|
||||
compila stable-diffusion.cpp con flags configurables (`-DSD_CUDA=ON`, `-DSD_FLASH_ATTN=ON`,
|
||||
`-DSD_FAST_SOFTMAX=ON`). Idempotente.
|
||||
- `bash/functions/infra/install_sd_cpp_bin.sh` + `.md` — copia el binario compilado
|
||||
a `~/.local/bin/sd` o equivalente en `$PATH`.
|
||||
|
||||
NO modificar:
|
||||
- `functions/ml/sdcli_*.go` — su contrato no cambia, solo se desbloquea el path feliz.
|
||||
|
||||
## Tareas
|
||||
|
||||
1. Compilacion
|
||||
1.1. Clonar `https://github.com/leejet/stable-diffusion.cpp` en `sources/stable-diffusion.cpp/`.
|
||||
1.2. Verificar requisitos: `cmake >= 3.18`, `gcc`, CUDA toolkit (instalable con
|
||||
`cuda_toolkit_check_bash_infra`). Si CUDA toolkit falta, instalarlo o
|
||||
documentar pasos manuales.
|
||||
1.3. Crear `bash/functions/infra/build_sd_cpp.sh` que:
|
||||
- Acepta flag `--backend cuda|cpu|vulkan`
|
||||
- cmake -B build -DSD_CUDA=ON (segun flag)
|
||||
- cmake --build build -j
|
||||
- Verifica que `build/bin/sd` o `build/sd` existe.
|
||||
1.4. Crear `bash/functions/infra/install_sd_cpp_bin.sh` que copia `sd` a
|
||||
`~/.local/bin/` y verifica `command -v sd`.
|
||||
|
||||
2. Smoke test
|
||||
2.1. Ejecutar `sd --version` desde Go: `SdcliResolveBinary("")` debe encontrarlo.
|
||||
2.2. Generar 1 imagen con SD Turbo `.safetensors` y comparar tiempo vs
|
||||
`sdcpp_python` (esperado: similar o mejor con CUDA).
|
||||
|
||||
3. Indexar
|
||||
3.1. `./fn index` y verificar 2 funciones nuevas.
|
||||
|
||||
4. Cleanup
|
||||
4.1. Re-run `CGO_ENABLED=1 go test -tags fts5 -run TestSdcliGenerate ./functions/ml/`
|
||||
— `TestSdcliGenerate_RequiresBinary` debe pasar sin skip.
|
||||
|
||||
## Ejemplo de uso
|
||||
|
||||
```bash
|
||||
fn run build_sd_cpp --backend cuda
|
||||
fn run install_sd_cpp_bin
|
||||
sd --help # ya en PATH
|
||||
./fn doctor ml # sd_cli debe pasar a "ok"
|
||||
```
|
||||
|
||||
## Decisiones
|
||||
|
||||
- **Compilar en `sources/`** (gitignored) — no commitear binario.
|
||||
- **Instalar en `~/.local/bin/`** — sin sudo, en `$PATH` por defecto en shells.
|
||||
- **Backend CUDA preferido** — esta maquina tiene RTX 3070 (8GB). CPU es fallback.
|
||||
|
||||
## Prerequisitos
|
||||
|
||||
- Issues 3.B/3.C completados (sdcpp_python + sdcli go scaffolding).
|
||||
- Modelo SD Turbo en vault (ya esta).
|
||||
|
||||
## Riesgos
|
||||
|
||||
- CUDA toolkit no instalado: `nvcc` ausente segun `fn doctor ml`. Mitigacion:
|
||||
fallback CPU (`-DSD_CUDA=OFF`) o instalar toolkit primero.
|
||||
- API rota entre versiones de `sd`: pinear release concreto (tag git) en el script.
|
||||
- Binario grande (~200MB con CUDA libs estaticas): vale, sources/ esta gitignored.
|
||||
@@ -0,0 +1,172 @@
|
||||
---
|
||||
id: "0095"
|
||||
title: "Frontend C++ ImGui para `dag_engine`"
|
||||
status: pendiente
|
||||
type: feature
|
||||
domain:
|
||||
- cpp-stack
|
||||
- frontend
|
||||
scope: multi-app
|
||||
priority: alta
|
||||
depends: []
|
||||
blocks: []
|
||||
related: []
|
||||
created: 2026-05-17
|
||||
updated: 2026-05-17
|
||||
tags: []
|
||||
---
|
||||
# 0095 — Frontend C++ ImGui para `dag_engine`
|
||||
|
||||
**Status:** pendiente
|
||||
**Created:** 2026-05-15
|
||||
**Type:** app
|
||||
**Blocks:** 0096 (data_factory) — necesita frontend C++ unificado para scheduler
|
||||
**Related:** `apps/dag_engine` (Go + Vite/React actual), `projects/fn_monitoring/apps/registry_dashboard` (referencia pubsub)
|
||||
|
||||
## Problema
|
||||
|
||||
`dag_engine` (alternativa propia a Dagu, ya existente) hoy sirve dos modos:
|
||||
- CLI (`run/list/status/validate/server`).
|
||||
- Web (Vite + React + Mantine en `apps/dag_engine/frontend/`).
|
||||
|
||||
El resto del ecosistema de monitorizacion del registry esta en C++ ImGui (`registry_dashboard`, `call_monitor`, futuro `data_factory`). Forzar al usuario a saltar al navegador para gestionar/observar DAGs rompe el flujo. Ademas la app `data_factory` (issue 0096) necesita embeber vista de scheduler para mostrar que DAG dispara cada extractor — si esa vista es web, no encaja en su UI ImGui.
|
||||
|
||||
## Objetivo
|
||||
|
||||
Anadir app C++ ImGui `dag_engine_ui` que cubre el caso "ver/lanzar/inspeccionar DAGs" con el **mismo backend** (`dag_engine server` HTTP+WS) y reusa el patron pubsub de `registry_dashboard` (HTTP REST + WebSocket live updates).
|
||||
|
||||
NO sustituye el frontend web — convive. El web sigue util para acceso remoto al VPS; el C++ es para uso local en escritorio.
|
||||
|
||||
## Piezas
|
||||
|
||||
### Backend (apps/dag_engine, cambios minimos)
|
||||
|
||||
1. **WS hub `DagRunHub`** en `apps/dag_engine/events.go` siguiendo el patron de `CallMonitorHub` (sqlite_api/events.go):
|
||||
- Hub global con N subscribers WS.
|
||||
- Ticker arranca solo con >=1 subscriber.
|
||||
- Polling watermark a tabla `dag_runs` + `dag_step_results` cada 500ms.
|
||||
- Snapshot inicial al conectar (lista DAGs + ultimos runs).
|
||||
- Broadcast de eventos: `run_started`, `step_completed`, `run_finished`, `schedule_updated`.
|
||||
2. **Endpoint** `GET /api/ws/dagruns` (upgrade WebSocket).
|
||||
3. **Endpoint** `POST /api/dags/:name/run` (Run Now) — ya existe; verificar que devuelve `run_id` inmediato y el hub broadcastea el progreso.
|
||||
4. **CORS** ya abierto en `middleware.go`.
|
||||
|
||||
### App C++ ImGui (`apps/dag_engine_ui/`)
|
||||
|
||||
Scaffolding via `fn run init_cpp_app dag_engine_ui --desc "Frontend ImGui para dag_engine"`.
|
||||
|
||||
1. **Capa HTTP**: `data_http.{cpp,h}` clona patron de `registry_dashboard/data_http.{cpp,h}` (cpp-httplib + nlohmann/json). Endpoints:
|
||||
- `GET /api/dags` -> lista DAGs.
|
||||
- `GET /api/dags/:name` -> detalle.
|
||||
- `GET /api/dags/:name/runs` -> historial.
|
||||
- `GET /api/runs/:id` -> timeline pasos.
|
||||
- `POST /api/dags/:name/run` -> dispara.
|
||||
- `POST /api/dags/:name/validate` -> valida YAML.
|
||||
2. **Capa WS**: `ws_client.{cpp,h}` reusado tal cual de `registry_dashboard`. Conecta a `ws://127.0.0.1:8090/api/ws/dagruns`.
|
||||
3. **Tabs** (panels via `cfg.panels`):
|
||||
- **DAG List** — tabla: name, schedule (cron), last_status, last_run_at, next_run_at, tags. Reusa `table_view_cpp_viz`. Click fila -> DAG Detail.
|
||||
- **DAG Detail** — header + metadata + boton "Run Now" + historial ultimos N runs (`table_view_cpp_viz`). Reusa `page_header_cpp_core`, `button_cpp_core`, `badge_cpp_core`.
|
||||
- **Run Detail** — timeline de steps con stdout/stderr expandible. Reusa `tree_view_cpp_core` y un componente nuevo `timeline_cpp_viz` si no existe (delegar a fn-constructor).
|
||||
- **Schedule** — vista cron: para cada DAG con `schedule:` lo siguiente que va a disparar (`next_cron_time_go_core`). Mini-calendario opcional.
|
||||
- **Health** — kpis: runs_24h, success_rate, p95_duration, failed_runs. Reusa `kpi_card_cpp_viz`.
|
||||
4. **Live updates**: WS recibe eventos -> push a ringbuffer en memoria -> tabs leen del ringbuffer en cada `render`. Run en curso se anima (spinner en columna status).
|
||||
5. **Config**: `--api-url http://127.0.0.1:8090` (default localhost). Persistencia en `app_settings.ini` (gestionado por `app_settings_cpp_core`).
|
||||
|
||||
### Frontmatter `app.md`
|
||||
|
||||
```yaml
|
||||
---
|
||||
name: dag_engine_ui
|
||||
lang: cpp
|
||||
domain: tui
|
||||
description: "Frontend ImGui para dag_engine. Lista, lanza e inspecciona DAGs con live updates via WS. Equivalente local al frontend web."
|
||||
tags: [imgui, dashboard, dag, scheduler, http, websocket]
|
||||
uses_functions:
|
||||
- kpi_card_cpp_viz
|
||||
- bar_chart_cpp_viz
|
||||
- table_view_cpp_viz
|
||||
- dashboard_panel_cpp_core
|
||||
- dashboard_grid_cpp_core
|
||||
- page_header_cpp_core
|
||||
- badge_cpp_core
|
||||
- button_cpp_core
|
||||
- icon_button_cpp_core
|
||||
- toolbar_cpp_core
|
||||
- text_input_cpp_core
|
||||
- select_cpp_core
|
||||
- tree_view_cpp_core
|
||||
- empty_state_cpp_core
|
||||
- modal_dialog_cpp_core
|
||||
- toast_cpp_core
|
||||
uses_types: []
|
||||
framework: "imgui"
|
||||
entry_point: "main.cpp"
|
||||
dir_path: "apps/dag_engine_ui"
|
||||
repo_url: "https://gitea-dgg044oo04woo4ggcsws4gk0.organic-machine.com/dataforge/dag_engine_ui"
|
||||
---
|
||||
```
|
||||
|
||||
Tag de grupo: anadir `scheduler` (grupo nuevo si >=3 funciones lo respaldan; revisar antes — si no, dejar plano).
|
||||
|
||||
### Funciones nuevas a delegar (estimacion)
|
||||
|
||||
- `timeline_cpp_viz` — componente ImGui que pinta pasos de un run con duracion + status + expand stdout/stderr. Reusable por `data_factory` v2.
|
||||
- `cron_explain_go_core` (puro) — dado `"0 */15 * * *"` devuelve string humano "every 15 min". Usado en DAG List.
|
||||
- `http_poll_json_cpp_core` (impuro) — wrapper sobre cpp-httplib que hace GET periodico con backoff y publica deltas via callback. Reusable.
|
||||
|
||||
Si patrones se repiten en `registry_dashboard` (cabecera HTTP+WS hibrido) -> proposal de extraer un mini-cliente comun `cpp/functions/core/http_ws_client.{cpp,h}`. NO crear inline.
|
||||
|
||||
## Aceptacion
|
||||
|
||||
- `fn run init_cpp_app dag_engine_ui` ejecutado, scaffolding limpio.
|
||||
- `dag_engine server` expone `/api/ws/dagruns` (hub con register/unregister/snapshot/delta).
|
||||
- App C++ compila en Linux y Windows (`build_cpp_windows_bash_infra dag_engine_ui`).
|
||||
- Smoke: con `dag_engine server` corriendo y un DAG ejemplo, abrir app -> ve DAG en lista, click "Run Now" -> timeline aparece en vivo sin refresh.
|
||||
- `e2e_checks` en `app.md`:
|
||||
- `build_cmake` — `cmake --build cpp/build -j --target dag_engine_ui`.
|
||||
- `self_test` — `./dag_engine_ui --self-test` arranca, valida conexion HTTP a `dag_engine`, sale 0/1.
|
||||
- `pytest` opcional — script que arranca `dag_engine server` con DAG dummy, lanza UI headless (xvfb), spera evento WS, sale 0/1.
|
||||
- `fn doctor cpp-apps` no reporta drift sobre `dag_engine_ui`.
|
||||
- `uses_functions` declarado en `app.md` y los `.cpp` del registry listados en `CMakeLists.txt` coinciden (`fn doctor uses-functions`).
|
||||
|
||||
## No-objetivos
|
||||
|
||||
- Editar YAML de DAGs desde la UI (v2 — por ahora solo lectura + Run Now + Validate).
|
||||
- Generar nuevos DAGs visualmente tipo drag&drop (v2).
|
||||
- Sustituir el frontend web — convive.
|
||||
- Soporte multi-engine (varios `dag_engine` remotos). Asume 1 backend local.
|
||||
|
||||
## Riesgos
|
||||
|
||||
| Riesgo | Mitigacion |
|
||||
|---|---|
|
||||
| WS hub anade peso al binario `dag_engine` | Mismo patron que `sqlite_api` (~150 LOC); negligible. |
|
||||
| `cpp-httplib` no soporta WS upgrade limpio | `registry_dashboard/ws_client.cpp` ya implementa RFC 6455 manual sobre TCP. Reusar tal cual. |
|
||||
| Doble UI (web + C++) -> deriva | Un solo backend, mismos endpoints. Si web cambia, C++ se entera por contrato HTTP. |
|
||||
| Cron parser duplica logica si se anade `cron_explain` | Funcion pura nueva, atomica, justificada (UX). |
|
||||
|
||||
## Dependencias
|
||||
|
||||
- `apps/dag_engine` corriendo y aceptando conexiones.
|
||||
- `cpp/functions/viz/*` y `cpp/functions/core/*` ya existentes (ver `uses_functions`).
|
||||
- `cpp-httplib` y `nlohmann/json` vendored (igual que `registry_dashboard`).
|
||||
|
||||
## Plan de ejecucion (sub-tareas)
|
||||
|
||||
1. **Backend**: anadir `events.go` con `DagRunHub` + handler WS en `dag_engine`. Commit.
|
||||
2. **Scaffolding**: `fn run init_cpp_app dag_engine_ui`. Commit.
|
||||
3. **Capa HTTP**: copiar y adaptar `data_http.{cpp,h}` desde `registry_dashboard`. Endpoints DAG. Commit.
|
||||
4. **Capa WS**: copiar `ws_client.{cpp,h}` tal cual. Conectar a `/api/ws/dagruns`. Commit.
|
||||
5. **Tabs DAG List + Detail + Run Detail**. Commit por tab.
|
||||
6. **Schedule + Health tabs**. Commit.
|
||||
7. **Funciones nuevas** (`timeline_cpp_viz`, `cron_explain_go_core`, `http_poll_json_cpp_core`) — delegar a fn-constructor en paralelo en mismo turno. Commit por funcion.
|
||||
8. **e2e_checks + redeploy_cpp_app_windows**. Commit final.
|
||||
|
||||
Cada paso: rama TBD propia (`issue/0095-<slug>`), merge `--no-ff` a master.
|
||||
|
||||
## Telemetria objetivo
|
||||
|
||||
Tras este issue:
|
||||
- `dag_engine_ui` aparece en `function_stats` con `calls > 0` (lanzamientos via `is_cpp_app_running_windows_bash_infra`).
|
||||
- `cron_explain_go_core` con `consumer_apps_count >= 1`.
|
||||
- `timeline_cpp_viz` con consumidor declarado en `uses_functions` de `dag_engine_ui` + candidato a reuso en `data_factory` (issue 0096).
|
||||
@@ -0,0 +1,134 @@
|
||||
---
|
||||
id: "0110"
|
||||
title: "Gap registry: helper HTTP cliente C++ (curl/popen) reutilizable"
|
||||
status: pendiente
|
||||
type: feature
|
||||
domain:
|
||||
- cpp-stack
|
||||
- registry-quality
|
||||
scope: registry
|
||||
priority: media
|
||||
depends: []
|
||||
blocks:
|
||||
- "0111"
|
||||
related:
|
||||
- "0106"
|
||||
created: 2026-05-18
|
||||
updated: 2026-05-18
|
||||
tags: [http, cpp, registry-gap, curl, helper, ausente-ready]
|
||||
---
|
||||
|
||||
# 0110 — Helper HTTP cliente C++ en el registry
|
||||
|
||||
## Problema
|
||||
|
||||
Hoy no existe funcion HTTP cliente reutilizable en `cpp/functions/`. Cada app C++ que necesita
|
||||
golpear un endpoint reinventa la capa:
|
||||
|
||||
| App | Fichero | Tecnica | LOC aprox |
|
||||
|---|---|---|---|
|
||||
| `apps/services_monitor/` | `http_client.cpp` | cURL popen/WinHTTP segun plataforma | ~150 |
|
||||
| `apps/dag_engine_ui/` | inline en `main.cpp` | curl CLI via popen + parse | ~80 |
|
||||
| `apps/data_factory/` | inline | popen curl | ~60 |
|
||||
| `cpp/functions/core/llm_anthropic.cpp` | propio | cURL popen — solo Anthropic | — |
|
||||
| `apps/process_explorer/` (issue 0111) | `http_client.cpp` local | pendiente — clonara services_monitor | ~150 esperados |
|
||||
|
||||
El patron ya supera el umbral `>2x` que dispara la regla de promocion (CLAUDE.md
|
||||
"Si patron se repite >2x → propose nueva funcion via fn-constructor"). Cada app duplica:
|
||||
|
||||
- Detection de plataforma (Linux: `popen("curl -s ...")`, Win: `WinHTTPOpen`/popen)
|
||||
- Manejo de basicAuth / Bearer tokens
|
||||
- Timeouts
|
||||
- Captura de body + status code
|
||||
- Manejo de errores transitorios (DNS, conexion rechazada)
|
||||
|
||||
## Decision
|
||||
|
||||
Anadir al registry dos funciones C++ en dominio `core` (o `infra`):
|
||||
|
||||
### `http_request_cpp_core` (impure)
|
||||
|
||||
```cpp
|
||||
namespace fn_http {
|
||||
struct Request {
|
||||
std::string method; // "GET", "POST", "PUT", "DELETE"
|
||||
std::string url;
|
||||
std::vector<std::pair<std::string,std::string>> headers;
|
||||
std::string body; // raw bytes (JSON, etc.)
|
||||
int timeout_ms = 5000;
|
||||
std::string bearer_token; // shortcut: anade Authorization: Bearer <token>
|
||||
std::string basic_user; // shortcut: anade Authorization: Basic base64(user:pass)
|
||||
std::string basic_pass;
|
||||
};
|
||||
struct Response {
|
||||
int status = 0; // 0 = error de transporte
|
||||
std::string body;
|
||||
std::vector<std::pair<std::string,std::string>> headers;
|
||||
std::string error; // vacio si OK
|
||||
int64_t duration_ms = 0;
|
||||
};
|
||||
Response request(const Request& req);
|
||||
}
|
||||
```
|
||||
|
||||
Implementacion: cURL via popen (portable WSL+Win+Linux, igual que `llm_anthropic`).
|
||||
Si en el futuro queremos rendimiento real, swap a libcurl linkado estaticamente
|
||||
o WinHTTP via `#ifdef _WIN32` — interfaz Request/Response no cambia.
|
||||
|
||||
### `http_get_json_cpp_core` (impure, pure wrapper)
|
||||
|
||||
Helper que envuelve `http_request` + parse JSON (via `nlohmann::json` o similar
|
||||
ya disponible en el repo) para los casos comunes:
|
||||
|
||||
```cpp
|
||||
namespace fn_http {
|
||||
// Devuelve parsed JSON o lanza si status != 2xx
|
||||
nlohmann::json get_json(const std::string& url,
|
||||
const std::string& bearer_token = "",
|
||||
int timeout_ms = 5000);
|
||||
}
|
||||
```
|
||||
|
||||
## Plan de migracion
|
||||
|
||||
Tras crear las funciones, abrir issue separado por cada consumer para migrar:
|
||||
|
||||
1. `apps/services_monitor/http_client.cpp` -> usar `fn_http::request`
|
||||
2. `apps/dag_engine_ui/main.cpp` (inline)
|
||||
3. `apps/data_factory/` (inline)
|
||||
4. `cpp/functions/core/llm_anthropic.cpp` — refactor para usar `fn_http::request` por
|
||||
debajo (mantiene API publica)
|
||||
5. `apps/process_explorer/` (issue 0111) — nace ya usando el helper
|
||||
|
||||
## Criterios de aceptacion
|
||||
|
||||
- [ ] `cpp/functions/core/http_request.{cpp,h,md}` registrado en `registry.db`
|
||||
- [ ] `cpp/functions/core/http_get_json.{cpp,h,md}` idem
|
||||
- [ ] Tests visuales o de integracion contra `httpbin.org` (200/404/timeout/auth)
|
||||
- [ ] Frontmatter completo (`params`/`output`/`tags`/`example`)
|
||||
- [ ] `.md` cumple contrato self-doc (`## Ejemplo`, `## Cuando usarla`, `## Gotchas`)
|
||||
- [ ] Al menos 1 consumer migrado para validar API (recomendado `services_monitor`)
|
||||
- [ ] `fn doctor uses-functions` limpio
|
||||
|
||||
## Gotchas conocidos
|
||||
|
||||
- cURL popen en Windows necesita `curl.exe` en PATH — todos los WSL/Win lo tienen,
|
||||
pero documentar en `## Gotchas`.
|
||||
- Bodies binarios: popen complica el escape; primera version solo string bodies.
|
||||
- TLS verify: por defecto on; permitir `req.insecure = true` solo para testing.
|
||||
- Timeouts: cURL `--max-time` cubre handshake+transfer; documentar diferencia con
|
||||
read-timeout puro.
|
||||
|
||||
## Por que no usar libcurl linkado
|
||||
|
||||
- `popen("curl ...")` no requiere anadir libcurl al toolchain MinGW cross-compile
|
||||
(que ya costo configurar). `llm_anthropic` lleva meses funcionando asi.
|
||||
- Cuando aparezca un caso real de latencia (>10 req/s sostenido), abrimos issue
|
||||
separado para swap a libcurl.
|
||||
|
||||
## Out of scope (no en este issue)
|
||||
|
||||
- WebSocket / SSE — cliente WS C++ es otro gap; abrir issue propio cuando aplique.
|
||||
- Cliente gRPC.
|
||||
- Streaming responses (SSE chunk-by-chunk) — usar caso de `dag_engine_ui` para
|
||||
decidir cuando.
|
||||
@@ -0,0 +1,121 @@
|
||||
---
|
||||
id: "0130"
|
||||
title: Kanban C++ v2 — gestor de dev/issues y dev/flows con backend Go + frontend ImGui
|
||||
status: pendiente
|
||||
type: epic
|
||||
domain:
|
||||
- cpp-stack
|
||||
- apps-infra
|
||||
- dev-ux
|
||||
scope: multi-app
|
||||
priority: alta
|
||||
depends: []
|
||||
blocks: []
|
||||
related:
|
||||
- "0112"
|
||||
- "0119"
|
||||
tags:
|
||||
- kanban
|
||||
- cpp
|
||||
- imgui
|
||||
- dev_ux
|
||||
- issues
|
||||
- flows
|
||||
created: "2026-05-22"
|
||||
updated: "2026-05-22"
|
||||
---
|
||||
|
||||
# 0130 — Kanban C++ v2
|
||||
|
||||
**Status:** pendiente
|
||||
|
||||
## Por que
|
||||
|
||||
La v1 (`apps/kanban_cpp` borrada el 2026-05-22) mezclaba paneles ajenos al dominio kanban (agent runs, DoD, worktrees, calendar) y un backend que no era reutilizable. Para gestionar los 98 issues activos + 12 flows del proyecto necesitamos una vista board nativa, sin web, con edicion bidireccional de los archivos markdown.
|
||||
|
||||
## Que entrega
|
||||
|
||||
App kanban_cpp v2 con dos piezas:
|
||||
|
||||
1. **Backend Go** (`apps/kanban_cpp/backend/`) — service HTTP en puerto 8487.
|
||||
- Parser bidireccional MD <-> SQLite (cache).
|
||||
- Watcher fsnotify sobre `dev/issues/` (+ `completed/`) y `dev/flows/`.
|
||||
- Endpoints REST: `/api/issues`, `/api/issues/{id}` (GET/PATCH), `/api/flows`, `/api/flows/{id}`, `/api/meta`, `/api/sse`.
|
||||
- PATCH a issue reescribe el frontmatter en disco preservando body + orden de campos.
|
||||
|
||||
2. **Frontend C++ ImGui** (`apps/kanban_cpp/`) sobre el framework `fn::run_app`.
|
||||
- Panel **Board**: columnas por status (pendiente / in-progress / bloqueado / completado). Drag-drop = PATCH status.
|
||||
- Panel **Flows**: lista de flows con detalle.
|
||||
- Panel **Filtros** (Aside): multi-select domain, scope, priority, tags.
|
||||
- Panel **Detalle**: edicion de campos frontmatter de un issue (status, priority, scope, tags, depends, blocks).
|
||||
- SSE para refrescar tras cambios externos en disco.
|
||||
|
||||
## Sub-issues
|
||||
|
||||
- **0130a** — parser MD + scan dirs (funciones registry).
|
||||
- **0130b** — backend Go: schema + handlers + watcher + SSE.
|
||||
- **0130c** — frontend C++: paneles + http client.
|
||||
|
||||
Cada sub-issue mergeable independiente en su rama corta TBD.
|
||||
|
||||
## Reusa del registry
|
||||
|
||||
Backend Go:
|
||||
- `sqlite_open_go_infra`, `sqlite_apply_migrations_go_infra`
|
||||
- `http_router_go_infra`, `http_serve_go_infra`, `http_middleware_chain_go_infra`
|
||||
- `http_cors_middleware_go_infra`, `http_logger_middleware_go_infra`
|
||||
- `http_json_response_go_infra`, `http_error_response_go_infra`, `http_parse_body_go_infra`
|
||||
- `random_hex_id_go_core`
|
||||
|
||||
Frontend C++:
|
||||
- `http_request_cpp_core`
|
||||
- `sse_client_cpp_core`
|
||||
- `data_table_cpp_viz` (lista flows)
|
||||
- `kpi_card_cpp_viz` (contadores por status)
|
||||
|
||||
## Crea (delegadas a fn-constructor en 0130a)
|
||||
|
||||
- `parse_issue_md_go_infra` — lee .md → struct (frontmatter YAML + body).
|
||||
- `write_issue_md_go_infra` — escribe struct → .md preservando body + orden de campos.
|
||||
- `scan_issues_dir_go_infra` — walk `dev/issues/` + `dev/issues/completed/`.
|
||||
- `scan_flows_dir_go_infra` — walk `dev/flows/`.
|
||||
- `watch_dir_fsnotify_go_infra` (si no existe) — events channel.
|
||||
|
||||
## DoD
|
||||
|
||||
- `fn doctor` verde para ambas apps (artefacts + e2e).
|
||||
- `e2e_checks` en ambos `app.md` (build + health + self-test).
|
||||
- Drag-drop en frontend reescribe el `.md` correspondiente y `git diff` lo muestra (solo frontmatter, body intacto).
|
||||
- Trio obligatorio (`description` + `icon.phosphor` + `icon.accent`) en ambos `app.md`.
|
||||
- Sub-repos Gitea creados (`dataforge/kanban_cpp` reactivado o nuevo, mismo nombre).
|
||||
|
||||
dod_evidence_schema:
|
||||
- id: backend_health
|
||||
kind: cmd
|
||||
expected: "curl -fsS http://localhost:8487/api/health == 200"
|
||||
required: true
|
||||
- id: api_issues_count
|
||||
kind: cmd
|
||||
expected: "curl -fsS http://localhost:8487/api/issues | jq 'length' >= 90"
|
||||
required: true
|
||||
- id: patch_writes_md
|
||||
kind: cmd
|
||||
expected: "PATCH /api/issues/0130 status=in-progress reescribe dev/issues/0130-*.md (git diff muestra solo status)"
|
||||
required: true
|
||||
- id: frontend_self_test
|
||||
kind: cmd
|
||||
expected: "./cpp/build/linux/apps/kanban_cpp/kanban_cpp --self-test exit 0"
|
||||
required: true
|
||||
- id: board_screenshot
|
||||
kind: screenshot
|
||||
expected: "kanban_cpp Board panel con 4 columnas pobladas con issues reales"
|
||||
required: true
|
||||
|
||||
## Anti-scope
|
||||
|
||||
NO incluye en esta version:
|
||||
- Grafo de dependencias (depends/blocks/related visual).
|
||||
- Edicion de body MD desde la app (solo frontmatter).
|
||||
- Multi-PC sync (backend es local).
|
||||
- Crear issues nuevos desde la UI (solo editar existentes).
|
||||
- DoD evidence panel, agent runs, calendar, worktrees (la v1 los mezclaba — fuera).
|
||||
@@ -0,0 +1,85 @@
|
||||
---
|
||||
id: "0130c"
|
||||
title: "Frontend C++ ImGui kanban_cpp v2: board + flows + filtros + detalle"
|
||||
status: pendiente
|
||||
type: app
|
||||
domain:
|
||||
- cpp-stack
|
||||
- dev-ux
|
||||
scope: app-scoped
|
||||
priority: alta
|
||||
depends: ["0130b"]
|
||||
blocks: []
|
||||
related:
|
||||
- "0130"
|
||||
created: 2026-05-22
|
||||
updated: 2026-05-22
|
||||
tags: [cpp, imgui, kanban, frontend]
|
||||
flow: "0130"
|
||||
---
|
||||
|
||||
# 0130c — Frontend C++ ImGui kanban_cpp v2
|
||||
|
||||
**Status:** pendiente
|
||||
|
||||
## Por que
|
||||
|
||||
UI nativa sobre el backend 0130b. Aprovecha el framework `fn::run_app` (menubar, layouts, settings, about, log) y los componentes del registry (`data_table`, `kpi_card`, `http_request`, `sse_client`).
|
||||
|
||||
## Estructura
|
||||
|
||||
```
|
||||
apps/kanban_cpp/
|
||||
app.md
|
||||
appicon.ico
|
||||
CMakeLists.txt
|
||||
main.cpp # fn::run_app + cfg.panels
|
||||
data.h / data.cpp # http client + state global (issues, flows, filters)
|
||||
panel_board.cpp # 4 columnas + drag-drop
|
||||
panel_flows.cpp # tabla via data_table_cpp_viz
|
||||
panel_filters.cpp # Aside con multi-select
|
||||
panel_detail.cpp # form editable del issue seleccionado
|
||||
panels.h
|
||||
```
|
||||
|
||||
## Trio obligatorio (`app.md`)
|
||||
|
||||
```yaml
|
||||
description: "Kanban C++ v2 para gestionar dev/issues y dev/flows del registry"
|
||||
icon:
|
||||
phosphor: "kanban"
|
||||
accent: "#a855f7"
|
||||
```
|
||||
|
||||
## Paneles
|
||||
|
||||
1. **Board** (`TI_KANBAN " Board"`) — 4 columnas (pendiente / in-progress / bloqueado / completado). Cada card: id + title (trunc 60) + priority badge + first domain chip. Drag-drop con `ImGui::BeginDragDropSource/Target` -> PATCH status.
|
||||
2. **Flows** (`TI_FLOW " Flows"`) — `data_table_cpp_viz` con columnas id/title/status/kind. Click fila → carga detail.
|
||||
3. **Filters** (`TI_FUNNEL " Filters"`) — AppShell.Aside-equivalente (panel lateral fijo). Multi-select por domain, scope, priority, tags. Estado local; rebuild request query.
|
||||
4. **Detail** (`TI_INFO " Detail"`) — modal/panel lateral con form: status (combo), priority (combo), scope (combo), tags (chips editables), depends/blocks (listas), body (read-only multiline).
|
||||
|
||||
## HTTP client (data.cpp)
|
||||
|
||||
- `fetch_issues(filters)` → GET con query string → parse JSON → vector<Issue>.
|
||||
- `fetch_flows()` → similar.
|
||||
- `patch_issue(id, partial)` → PATCH JSON → recibe issue actualizado.
|
||||
- `subscribe_sse()` thread aparte → push events a queue mutex → consumir en main loop → re-fetch afectados.
|
||||
|
||||
Usa `http_request_cpp_core` + `sse_client_cpp_core`. JSON via `nlohmann/json` (ya en cpp/vendor o sacar al header-only).
|
||||
|
||||
## DoD
|
||||
|
||||
- `cmake --build cpp/build/linux --target kanban_cpp -j` verde.
|
||||
- `./cpp/build/linux/apps/kanban_cpp/kanban_cpp --self-test` exit 0:
|
||||
- inicializa contexto ImGui sin display.
|
||||
- parsea respuesta JSON sintetica.
|
||||
- no toca red salvo si `--backend http://...` se pasa.
|
||||
- e2e_checks en `app.md`: build + self_test + backend_health (corre backend en background) + smoke (drag-drop reescribe MD).
|
||||
- Captura screenshot board con 4 columnas pobladas → guardar en `dod_evidence/board_screenshot.png`.
|
||||
|
||||
## Anti-scope
|
||||
|
||||
- Sin grafo de dependencias (epic 0130 lo describe como anti-scope v1).
|
||||
- Sin crear issues nuevos (solo editar existentes).
|
||||
- Sin edicion de body MD (solo frontmatter).
|
||||
- Sin syntax highlighting markdown.
|
||||
@@ -0,0 +1,90 @@
|
||||
---
|
||||
id: "0131"
|
||||
title: "Modulo C++ chat_panel — panel ImGui para chat con agentes"
|
||||
status: pendiente
|
||||
type: app
|
||||
domain:
|
||||
- cpp-stack
|
||||
- agents
|
||||
- dev-ux
|
||||
scope: cross-stack
|
||||
priority: alta
|
||||
depends:
|
||||
- "0113"
|
||||
blocks: []
|
||||
related:
|
||||
- "0130"
|
||||
created: 2026-05-22
|
||||
updated: 2026-05-22
|
||||
tags: [cpp, imgui, agents, chat, module, sse]
|
||||
flow: ""
|
||||
---
|
||||
|
||||
# 0131 — Modulo C++ chat_panel
|
||||
|
||||
**Status:** pendiente
|
||||
|
||||
## Por que
|
||||
|
||||
Tras lanzar un agente desde kanban_cpp (issue 0130), no hay forma de interactuar con el desde la propia app. Hoy el flujo es: lanzar agente, abrir terminal aparte, `tail -f /tmp/wt-.../agent.log`. Queremos un panel C++ reutilizable que cualquier app embebra para chatear con un agente (Claude headless o futuros) y ver su output en streaming.
|
||||
|
||||
## Que entrega
|
||||
|
||||
Modulo `cpp/functions/viz/chat_panel/` (paquete del registry, kind: function, lang: cpp, domain: viz). API:
|
||||
|
||||
```cpp
|
||||
namespace fn_chat {
|
||||
struct ChatPanel {
|
||||
// run_id del agent_runner_api; null = panel vacio "no agent attached"
|
||||
std::string run_id;
|
||||
std::string backend_url = "http://127.0.0.1:8486"; // agent_runner_api
|
||||
bool auto_scroll = true;
|
||||
};
|
||||
void render(ChatPanel& panel);
|
||||
}
|
||||
```
|
||||
|
||||
Comportamiento:
|
||||
- Conecta SSE `/api/runs/<run_id>/sse` en background thread (reusa `sse_client_cpp_core`).
|
||||
- Parsea eventos `state`, `log`, `evidence`, `finish` y los renderiza:
|
||||
- `log` → linea cruda en buffer scrollable.
|
||||
- `state` → badge superior con status (`pending/running/done/aborted/failed`).
|
||||
- `evidence` → chip lateral con kind + payload_url.
|
||||
- `finish` → marca run terminada, deja conexion para ver historico.
|
||||
- Input box inferior (multiline) + boton "Send". POST a `/api/runs/<run_id>/message` (endpoint A IMPLEMENTAR en agent_runner_api — extension paralela; si no existe, boton se deshabilita).
|
||||
- Toolbar: `Abort run`, `Clear buffer`, `Show evidence panel`.
|
||||
|
||||
## Estructura
|
||||
|
||||
```
|
||||
cpp/functions/viz/chat_panel/
|
||||
chat_panel.h
|
||||
chat_panel.cpp
|
||||
chat_panel.md
|
||||
chat_panel_test.cpp
|
||||
```
|
||||
|
||||
## Reusa del registry
|
||||
|
||||
- `sse_client_cpp_core` — SSE async.
|
||||
- `http_request_cpp_core` — POST mensajes / abort.
|
||||
- `selectable_text_cpp_viz` — copy log lines.
|
||||
- `data_table_cpp_viz` — opcional para tabla de evidencias.
|
||||
|
||||
## DoD
|
||||
|
||||
- Modulo compila en Linux + Windows.
|
||||
- Demo en `primitives_gallery` o app dedicada `agent_chat_demo` con run_id fijo + mock SSE feeder.
|
||||
- Integracion en kanban_cpp v2: nuevo panel "Chat" que se abre al hacer click en card con agent_active, run_id se pasa automatico.
|
||||
- `e2e_checks`: smoke con mock SSE; assertion: tras emitir 3 eventos de log, panel los muestra en orden.
|
||||
|
||||
## Anti-scope (v1)
|
||||
|
||||
- No persiste history local (refresh = perdemos buffer; agent.log es la fuente).
|
||||
- No syntax highlight markdown / codigo.
|
||||
- Sin multi-run (un panel = un run).
|
||||
- Sin file diff inline (kind:evidence con kind:diff queda como link a `git show`).
|
||||
|
||||
## Notas
|
||||
|
||||
Si el endpoint POST `/api/runs/:id/message` no existe en agent_runner_api, abrir issue paralelo `0131b` para anadirlo (claude headless aceptara mensajes via stdin del subprocess — el runner debe forwardearlos). Para v1 se acepta panel read-only.
|
||||
@@ -0,0 +1,92 @@
|
||||
---
|
||||
id: "0132"
|
||||
title: "Modulo C++ terminal_panel — emulador TTY ImGui embebible"
|
||||
status: pendiente
|
||||
type: app
|
||||
domain:
|
||||
- cpp-stack
|
||||
- dev-ux
|
||||
- apps-infra
|
||||
scope: cross-stack
|
||||
priority: alta
|
||||
depends: []
|
||||
blocks: []
|
||||
related:
|
||||
- "0130"
|
||||
- "0131"
|
||||
created: 2026-05-22
|
||||
updated: 2026-05-22
|
||||
tags: [cpp, imgui, terminal, pty, module, ausente-ready]
|
||||
flow: ""
|
||||
---
|
||||
|
||||
# 0132 — Modulo C++ terminal_panel
|
||||
|
||||
**Status:** pendiente
|
||||
|
||||
## Por que
|
||||
|
||||
Apps del ecosistema (kanban_cpp, services_monitor, agents_dashboard) necesitan ver output crudo de comandos shell sin abrir un terminal externo. Tipico: tail de un log, watch de un curl, ejecutar `git status` rapido. Solucion estandar: modulo `terminal_panel` reusable que arranca un shell hijo via PTY y lo renderiza en ImGui.
|
||||
|
||||
## Que entrega
|
||||
|
||||
Modulo `cpp/functions/viz/terminal_panel/`:
|
||||
|
||||
```cpp
|
||||
namespace fn_term {
|
||||
struct TerminalPanel {
|
||||
std::string shell; // "/bin/bash" linux, "powershell.exe" windows; default auto
|
||||
std::string cwd; // working dir; default = current
|
||||
std::vector<std::string> env; // KEY=VAL extras
|
||||
int scrollback_lines = 5000;
|
||||
bool readonly = false; // true = no input forwarding (tail-only)
|
||||
};
|
||||
void open(TerminalPanel& panel); // crea proceso hijo + PTY
|
||||
void render(TerminalPanel& panel);
|
||||
void send(TerminalPanel& panel, const std::string& text); // stdin
|
||||
void close(TerminalPanel& panel);
|
||||
}
|
||||
```
|
||||
|
||||
Implementacion:
|
||||
- Linux: `forkpty` + `read/write` non-blocking en background thread.
|
||||
- Windows: ConPTY (CreatePseudoConsole) + ReadFile en thread.
|
||||
- Buffer circular `scrollback_lines` filas; render con `ImGui::TextUnformatted` por chunk para minimizar costo.
|
||||
- Soporte minimo de ANSI: cursor pos, color FG/BG basico (16 colores), clear screen. NO soporte completo (no Vim, no top, no curses pesado).
|
||||
- Toolbar: clear, copy selection, reset shell, scroll-to-bottom.
|
||||
|
||||
## Estructura
|
||||
|
||||
```
|
||||
cpp/functions/viz/terminal_panel/
|
||||
terminal_panel.h
|
||||
terminal_panel.cpp
|
||||
terminal_panel.md
|
||||
terminal_panel_linux.cpp // forkpty path
|
||||
terminal_panel_windows.cpp // ConPTY path
|
||||
terminal_panel_test.cpp
|
||||
```
|
||||
|
||||
## Reusa del registry
|
||||
|
||||
- `logger_cpp_core` (fn_log) — log errores spawn/io.
|
||||
- `ansi_parser_cpp_core` — si existe, parsear secuencias ANSI. Si no, delegar a `fn-constructor` para crearlo dentro de este issue (sub-deliverable).
|
||||
|
||||
## DoD
|
||||
|
||||
- Compila Linux + Windows.
|
||||
- Demo: `primitives_gallery` muestra terminal corriendo `bash -i` (linux) / `cmd.exe` (windows).
|
||||
- Smoke test: spawn `echo hello && exit 0` → buffer contiene "hello".
|
||||
- Integracion en kanban_cpp v2: panel "Logs" que toma `run_id` de issue activa y arranca `tail -f /tmp/wt-<slug>-<runid>/agent.log` (readonly=true).
|
||||
- FPS sin caida bajo carga de `yes "x"` (saturado): 60fps target con scrollback truncado.
|
||||
|
||||
## Anti-scope (v1)
|
||||
|
||||
- Sin soporte completo ANSI (no italics, no 256 colores, no Unicode wide).
|
||||
- Sin Vim / programas curses-pesados (cursor visible solo).
|
||||
- Sin SSH remoto (solo shell local).
|
||||
- Sin tabs multiples en un panel (un panel = un proceso).
|
||||
|
||||
## Notas
|
||||
|
||||
ConPTY requiere Windows 10 v1809+. Si target inferior, fallback a CreatePipe sin PTY (sin redimensionado).
|
||||
@@ -0,0 +1,74 @@
|
||||
---
|
||||
id: "0133"
|
||||
title: "data_table: optimizar para 10M filas sin caida de FPS (finalize modulo)"
|
||||
status: pendiente
|
||||
type: refactor
|
||||
domain:
|
||||
- cpp-stack
|
||||
- data-ingest
|
||||
scope: app-scoped
|
||||
priority: alta
|
||||
depends: []
|
||||
blocks: []
|
||||
related:
|
||||
- "0081"
|
||||
- "0097"
|
||||
created: 2026-05-22
|
||||
updated: 2026-05-22
|
||||
tags: [cpp, imgui, performance, data_table, finalize]
|
||||
flow: ""
|
||||
---
|
||||
|
||||
# 0133 — data_table 10M rows sin caida FPS
|
||||
|
||||
**Status:** pendiente
|
||||
|
||||
## Por que
|
||||
|
||||
`data_table_cpp_viz` (modulo `fn_module_data_table` / `fn_table_viz`) actualmente maneja decenas de miles de filas con `ImGuiListClipper` y rinde bien. Apps reales (call_monitor con telemetria, services_monitor con escalado, futuro graph_explorer con nodos) ya nos llevan a millones de filas. Objetivo: cerrar el modulo con benchmark estable de **10M filas a >=60fps** en hardware tipico (Ryzen 5 / i5 8th gen + 16GB).
|
||||
|
||||
## Que entrega
|
||||
|
||||
Refactor del modulo manteniendo API publica + un benchmark suite.
|
||||
|
||||
### Cambios tecnicos
|
||||
|
||||
1. **Storage columnar** — hoy `std::vector<std::vector<Cell>>` row-major. Cambiar a column-major (`Column { type; vector<T> data }`) para localidad de cache + iteracion. Las celdas se materializan solo para las filas visibles.
|
||||
2. **String interning** — columnas de tipo string usan tabla de strings global con `uint32_t` indices. 10M filas con 50% strings repetidas → ahorra 60-70% RAM.
|
||||
3. **Lazy filter/sort indices** — en vez de re-ordenar el storage, mantener `vector<uint32_t> visible_rows` que apunta al storage subyacente. Filter/sort solo reescribe ese vector.
|
||||
4. **Computed columns en bloques** — `compute_stage_cpp_core` ahora corre por cell; cambiar a procesar bloques de 1024 filas con SIMD via `OpenMP` (ya esta linkeado en fn_framework).
|
||||
5. **Render path** — `ImGuiListClipper` sigue siendo el frontend, pero el callback de render no debe asignar memoria por fila. Pre-formatear strings de display en `column.display_cache[row_idx]` con LRU de 100k entradas; resto se formatea on-the-fly.
|
||||
6. **Color rules** — `data_table_color_rules_cpp_viz` se evalua hoy por celda visible. Cachear el rule_id resuelto por row_idx tras primer paint.
|
||||
7. **Stats** — `compute_column_stats_cpp_core` solo se recalcula cuando cambia el filtro, no cada frame.
|
||||
|
||||
### Benchmark suite
|
||||
|
||||
`cpp/apps/data_table_bench/`:
|
||||
- Genera dataset sintetico 10M filas x 20 cols (mix int/float/string/timestamp).
|
||||
- Mide FPS sostenido durante:
|
||||
- scroll lineal full range (down → bottom).
|
||||
- filter por string match (`LIKE %foo%`).
|
||||
- sort por columna numerica.
|
||||
- color rule `value > p95`.
|
||||
- Output: `fps_p50`, `fps_p1`, `mem_rss_mb`, `cpu_pct`.
|
||||
- Asercion DoD: `fps_p1 >= 60` en cada escenario.
|
||||
|
||||
## DoD
|
||||
|
||||
- Refactor entregado sin romper apps consumidoras (call_monitor, services_monitor, graph_explorer, navegator_dashboard, kanban_cpp future).
|
||||
- Benchmark suite ejecutable: `./data_table_bench --rows 10000000 --duration 30`.
|
||||
- Resultados de benchmark guardados en `apps/data_table_bench/operations.db` con assertion `fps_p1 >= 60`.
|
||||
- `e2e_checks` corriendo benchmark con dataset reducido (100k filas) en CI; full bench manual.
|
||||
- Modulo marcado `version: 1.0.0` y `tags: [stable]` en su `.md`.
|
||||
- Guia "porting old call sites" si la API publica cambia (en `cpp/functions/viz/data_table/MIGRATION.md`).
|
||||
|
||||
## Anti-scope
|
||||
|
||||
- Sin GPU rendering (sigue siendo CPU + ImGui).
|
||||
- Sin paginacion remota (sigue todo in-memory).
|
||||
- Sin streaming append-while-rendering (snapshot al frame inicio).
|
||||
- Sin virtualizacion horizontal (todas las cols se renderizan; assumed N_cols <= 100).
|
||||
|
||||
## Notas
|
||||
|
||||
Issue 0081 introdujo la migracion inline → modulo. Issue 0097 cerro el wrapping en fn_module/fn_table_viz. Esta issue es el **finalize**: lo deja `1.0.0` con benchmark + suficiente performance para que las apps de telemetria/graph no necesiten paginar manual.
|
||||
Reference in New Issue
Block a user