merge: quick/registry-dashboard-and-rules — Dashboard ImGui, viewport multi-ventana, reglas tags y issues DAG engine

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-08 01:18:36 +02:00
25 changed files with 272713 additions and 21 deletions
+42 -6
View File
@@ -20,6 +20,8 @@ Si no se proporciona nombre, preguntar al usuario que quiere construir.
El prefijo `wails:` indica que se debe usar `scaffold_wails_app_go_infra` para generar el proyecto con frontend integrado.
El prefijo `service:` indica que la app es un proceso de larga duracion (API, daemon, watcher). Añadir tag `service` automaticamente.
---
## PASO 0: Entender que se va a construir
@@ -97,15 +99,15 @@ mkdir -p /home/lucas/fn_registry/apps/{app_name}
```yaml
---
name: {app_name}
lang: {go|py|bash|ts}
lang: {go|py|bash|ts|cpp}
domain: {domain}
description: "{descripcion}"
tags: [{tags}]
tags: [{tags}] # Añadir "service" si es proceso de larga duracion
uses_functions:
- {id_funcion_1}
- {id_funcion_2}
uses_types: []
framework: "{bubbletea|wails|httpx|...}"
framework: "{bubbletea|wails|httpx|imgui|...}"
entry_point: "{main.go|main.py|main.sh}"
dir_path: "apps/{app_name}"
repo_url: ""
@@ -120,6 +122,11 @@ repo_url: ""
{Notas adicionales, dependencias externas, configuracion necesaria}
```
**Si es un service** (tag `service`), documentar ademas en el app.md:
- Puerto que usa (si expone HTTP/gRPC)
- Como lanzarlo y pararlo
- Health check (como comprobar que esta vivo)
### .gitignore (OBLIGATORIO)
```
@@ -217,9 +224,9 @@ Si el recopilador detecta problemas, corregirlos antes de continuar.
---
## PASO 5: PUBLICAR en Gitea (@gitea)
## PASO 5: PUBLICAR en Gitea (@gitea) — OBLIGATORIO
Una vez la app esta funcionando y auditada, publicarla como repo independiente en Gitea.
Toda app nueva DEBE publicarse en Gitea. Este paso NO es opcional.
**Como invocar:**
@@ -315,17 +322,46 @@ Para ejecutar:
2. Source pattern con REGISTRY_ROOT
3. set -euo pipefail obligatorio
### App C++ (ImGui)
1. Codigo fuente va en `apps/{app_name}/` (no en `cpp/apps/`)
2. `cpp/CMakeLists.txt` referencia la app con `add_subdirectory(../apps/{app_name} ...)`
3. Funciones C++ del registry se incluyen como .cpp en el CMakeLists.txt de la app
4. Para Windows: cross-compile con `cmake -DCMAKE_TOOLCHAIN_FILE=toolchains/mingw-w64.cmake`
### Service (tag `service`)
1. Detectar si el usuario pide un servicio (API, daemon, watcher, server) o usa prefijo `service:`
2. Añadir tag `service` al array `tags` del app.md
3. Documentar en app.md: puerto, como lanzar/parar, health check
4. Estructura tipica para un HTTP service en Go:
```
apps/{service_name}/
├── app.md # tags: [service, api, ...]
├── main.go # Bind port, listen, graceful shutdown
├── handlers.go # HTTP handlers que componen funciones del registry
├── go.mod
├── .gitignore
```
5. El service se ejecuta como: `go run . --port 8080`
6. Para consultar services existentes: `sqlite3 registry.db "SELECT id, name, description FROM apps WHERE tags LIKE '%service%';"`
---
## Reglas
- **Codigo reutilizable** va en `functions/`, NO en la app → usar fn-constructor
- **Codigo especifico** de la app va en `apps/{app_name}/`
- **Todas las apps van en `apps/`**, incluidas C++, TypeScript, etc. Nunca en `cpp/apps/` ni otros subdirectorios
- **operations.db** SOLO dentro de la app, NUNCA en la raiz
- **registry.db** SOLO en la raiz, NUNCA en apps
- Toda app DEBE tener `app.md` con frontmatter completo
- `uses_functions` en app.md DEBE listar TODAS las funciones del registry importadas
- Siempre `./fn index` despues de crear/modificar la app
- Siempre `./fn index` despues de crear/modificar la app — **verificar que aparece en registry.db**
- Siempre auditar con fn-recopilador antes de publicar
- **Siempre publicar en Gitea** (PASO 5) — toda app tiene repo en `dataforge/{app_name}`
- **Siempre actualizar `repo_url`** en app.md despues de publicar y re-indexar
- **Tag `service`**: añadir a apps que son procesos de larga duracion (APIs, daemons, watchers, schedulers)
- **Tag `launcher`**: añadir a pipelines que deben aparecer en Pipeline Launcher TUI
$ARGUMENTS
+3
View File
@@ -266,5 +266,8 @@ Para usar estas funciones:
- **NO crear funciones especificas de una app** — solo codigo reutilizable y generico
- Si el usuario pide algo que ya existe, informar y sugerir reutilizar en vez de duplicar
- Si una funcion del batch falla, las demas del mismo batch pueden continuar independientemente
- **Tags con significado especial** — ver `.claude/rules/function_tags.md`:
- `launcher`: pipelines que deben aparecer en Pipeline Launcher TUI. Añadir cuando se crea un pipeline ejecutable desde el launcher. NO añadir a pipelines interactivos/TUIs.
- `service`: para apps que son procesos de larga duracion (usado en /app, no en funciones)
$ARGUMENTS
+1 -1
View File
@@ -11,7 +11,7 @@ Reglas operativas del proyecto. Cada archivo es una regla independiente.
| 05 | [stubs.md](stubs.md) | Stubs impuros para dependencias externas |
| 06 | [assertions.md](assertions.md) | Kinds de assertions son texto libre |
| 07 | [proposals.md](proposals.md) | Quien crea proposals y cuando |
| 08 | [tag_launcher.md](tag_launcher.md) | Tag launcher para Pipeline Launcher TUI |
| 08 | [function_tags.md](function_tags.md) | Tags con significado especial: launcher, service |
| 09 | [go_packages.md](go_packages.md) | Nombre de paquete Go = nombre del directorio |
| 10 | [apps_vs_functions.md](apps_vs_functions.md) | Codigo reutilizable en functions/, no reutilizable en apps/ |
| 11 | [sources.md](sources.md) | Extraccion de funciones desde repos externos |
+30
View File
@@ -0,0 +1,30 @@
Los pipelines con tag `launcher` aparecen en el Pipeline Launcher TUI (`apps/pipeline_launcher`).
Sin el tag, el pipeline no es lanzable desde la TUI. Añadir `launcher` al array `tags` del .md al crear un pipeline ejecutable desde el launcher.
Pipelines interactivos (TUIs) o que no son subprocesos NO deben llevar este tag.
## Tag `service`
Las apps con tag `service` son procesos de larga duracion: APIs, daemons, watchers, servers.
Diferencia con una app normal:
- Una **app** se ejecuta, hace su trabajo, y termina (CLI, TUI, script)
- Un **service** se lanza y queda corriendo indefinidamente (API server, scheduler, watcher)
Añadir `service` al array `tags` del `app.md` cuando la app esta diseñada para correr como proceso persistente.
Un service sigue siendo una app — vive en `apps/`, tiene `app.md`, se indexa igual. El tag es solo metadata para filtrar:
```sql
-- Listar services
SELECT id, name, description FROM apps WHERE tags LIKE '%service%';
-- Listar apps que NO son services
SELECT id, name, description FROM apps WHERE tags NOT LIKE '%service%';
```
Documentar en el `app.md` del service:
- El puerto que usa (si expone HTTP/gRPC)
- Como lanzarlo y pararlo
- Como comprobar que esta vivo (health check)
-5
View File
@@ -1,5 +0,0 @@
Los pipelines con tag `launcher` aparecen en el Pipeline Launcher TUI (`apps/pipeline_launcher`).
Sin el tag, el pipeline no es lanzable desde la TUI. Añadir `launcher` al array `tags` del .md al crear un pipeline ejecutable desde el launcher.
Pipelines interactivos (TUIs) o que no son subprocesos NO deben llevar este tag.
+1
View File
@@ -54,3 +54,4 @@ Thumbs.db
.local
broken_paths.txt
imgui.ini
+6 -1
View File
@@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.16)
project(fn_registry_cpp LANGUAGES CXX)
project(fn_registry_cpp LANGUAGES C CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
@@ -98,3 +98,8 @@ endfunction()
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/apps/chart_demo/CMakeLists.txt)
add_subdirectory(apps/chart_demo)
endif()
# --- Registry Dashboard (lives in apps/ per project convention) ---
if(EXISTS ${CMAKE_SOURCE_DIR}/../apps/registry_dashboard/CMakeLists.txt)
add_subdirectory(${CMAKE_SOURCE_DIR}/../apps/registry_dashboard ${CMAKE_BINARY_DIR}/apps/registry_dashboard)
endif()
+20
View File
@@ -52,8 +52,20 @@ int run_app(AppConfig config, std::function<void()> render_fn) {
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
if (config.viewports) {
io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;
}
ImGui::StyleColorsDark();
// When viewports are enabled, tweak WindowRounding/WindowBg so
// platform windows look consistent with the main window
if (config.viewports) {
ImGuiStyle& style = ImGui::GetStyle();
style.WindowRounding = 0.0f;
style.Colors[ImGuiCol_WindowBg].w = 1.0f;
}
ImGui_ImplGlfw_InitForOpenGL(window, true);
ImGui_ImplOpenGL3_Init("#version 330");
@@ -80,6 +92,14 @@ int run_app(AppConfig config, std::function<void()> render_fn) {
glClear(GL_COLOR_BUFFER_BIT);
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
// Multi-viewport: update and render platform windows
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) {
GLFWwindow* backup_ctx = glfwGetCurrentContext();
ImGui::UpdatePlatformWindows();
ImGui::RenderPlatformWindowsDefault();
glfwMakeContextCurrent(backup_ctx);
}
glfwSwapBuffers(window);
#ifdef TRACY_ENABLE
+1
View File
@@ -9,6 +9,7 @@ struct AppConfig {
int width = 1280;
int height = 720;
bool vsync = true;
bool viewports = false; // Enable multi-viewport: ImGui windows become real OS windows
float bg_r = 0.1f;
float bg_g = 0.1f;
float bg_b = 0.1f;
+19
View File
@@ -0,0 +1,19 @@
#include "core/fullscreen_window.h"
#include "imgui.h"
bool fullscreen_window_begin(const char* id) {
const ImGuiViewport* vp = ImGui::GetMainViewport();
ImGui::SetNextWindowPos(vp->WorkPos);
ImGui::SetNextWindowSize(vp->WorkSize);
return ImGui::Begin(id, nullptr,
ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoBringToFrontOnFocus |
ImGuiWindowFlags_NoNavFocus);
}
void fullscreen_window_end() {
ImGui::End();
}
+16
View File
@@ -0,0 +1,16 @@
#pragma once
// fullscreen_window — ImGui window that covers the entire viewport.
// No title bar, no resize, no move, no collapse. Supports scrolling.
//
// Usage:
// if (fullscreen_window_begin()) {
// // render content here
// }
// fullscreen_window_end(); // ALWAYS call, even if begin returned false
//
// The default id "##fullscreen" is invisible (## prefix suppresses display).
// Use a different id if you need multiple fullscreen windows stacked.
bool fullscreen_window_begin(const char* id = "##fullscreen");
void fullscreen_window_end();
+66
View File
@@ -0,0 +1,66 @@
---
name: fullscreen_window
kind: component
lang: cpp
domain: core
version: "0.1.0"
purity: pure
signature: "bool fullscreen_window_begin(const char* id = \"##fullscreen\"); void fullscreen_window_end()"
description: "Ventana ImGui fullscreen sin decoraciones que ocupa todo el viewport, elimina la necesidad de usar el sistema de ventanas interno"
tags: [imgui, layout, fullscreen, window]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: ""
imports: [imgui]
tested: false
tests: []
test_file_path: ""
file_path: "cpp/functions/core/fullscreen_window.cpp"
framework: imgui
params:
- name: id
desc: "Identificador ImGui de la ventana, default ##fullscreen (el prefijo ## oculta el texto del titulo)"
output: "true si la ventana es visible (siempre true en fullscreen); llamar siempre fullscreen_window_end() independientemente del valor de retorno"
---
# fullscreen_window
Wrapper que crea una ventana ImGui que ocupa exactamente el viewport de trabajo (`WorkPos` / `WorkSize`). Elimina todas las decoraciones: title bar, resize grip, move, collapse. Ideal como capa raiz de una aplicacion ImGui donde el contenido propio gestiona el layout.
## Uso
```cpp
if (fullscreen_window_begin()) {
// todo el layout de la app va aqui
dashboard_grid_begin(3, 8.0f);
// ...
dashboard_grid_end();
}
fullscreen_window_end(); // siempre llamar
```
Con ID explicito (si se necesitan multiples capas):
```cpp
if (fullscreen_window_begin("##background")) {
render_background();
}
fullscreen_window_end();
```
## Implementacion
- `GetMainViewport()` obtiene el viewport principal (compatible con viewports multi-monitor de ImGui)
- `SetNextWindowPos(vp->WorkPos)` posiciona en el area de trabajo (excluye menu bars del OS)
- `SetNextWindowSize(vp->WorkSize)` ocupa exactamente el area disponible
- Flags: `NoTitleBar | NoResize | NoMove | NoCollapse | NoBringToFrontOnFocus | NoNavFocus`
- `NoBringToFrontOnFocus` y `NoNavFocus` evitan que la ventana fullscreen robe el foco de ventanas superpuestas
## Notas
- Pura en el sentido de que no hace I/O ni tiene estado propio; solo configura el estado next-frame de ImGui.
- `WorkPos`/`WorkSize` respetan los menu bars del sistema operativo (en plataformas que los tienen). Para ocupar literalmente toda la pantalla usar `Pos`/`Size` del viewport.
- Compatible con `dashboard_grid` y `dashboard_panel`: el fullscreen_window actua como contenedor raiz y los paneles/grids se renderizan dentro.
- El patron begin/end es idiomatico en ImGui: `end` debe llamarse siempre para cerrar la ventana correctamente, aunque `begin` retorne false.
+4 -5
View File
@@ -6,11 +6,10 @@ void pie_chart(const char* title, const char* const* labels, const float* values
ImPlot::SetupAxes(nullptr, nullptr, ImPlotAxisFlags_NoDecorations, ImPlotAxisFlags_NoDecorations);
ImPlot::SetupAxesLimits(0, 1, 0, 1);
if (radius < 0.0f) {
// Donut mode: outer = |radius|, inner = 0.2
ImPlot::PlotPieChart(labels, values, count, 0.5, 0.5, -radius, "%.1f", 90.0, ImPlotPieChartFlags_None);
ImPlot::PlotPieChart(labels, values, count, 0.5, 0.5, static_cast<double>(-radius), "%.1f", 90.0);
} else {
float r = (radius > 0.0f) ? radius : 0.4f;
ImPlot::PlotPieChart(labels, values, count, 0.5, 0.5, r, "%.1f", 90.0, ImPlotPieChartFlags_None);
ImPlot::PlotPieChart(labels, values, count, 0.5, 0.5, static_cast<double>(r), "%.1f", 90.0);
}
ImPlot::EndPlot();
}
@@ -21,10 +20,10 @@ void pie_chart(const char* title, const char* const* labels, const double* value
ImPlot::SetupAxes(nullptr, nullptr, ImPlotAxisFlags_NoDecorations, ImPlotAxisFlags_NoDecorations);
ImPlot::SetupAxesLimits(0, 1, 0, 1);
if (radius < 0.0) {
ImPlot::PlotPieChart(labels, values, count, 0.5, 0.5, -radius, "%.1f", 90.0, ImPlotPieChartFlags_None);
ImPlot::PlotPieChart(labels, values, count, 0.5, 0.5, -radius, "%.1f", 90.0);
} else {
double r = (radius > 0.0) ? radius : 0.4;
ImPlot::PlotPieChart(labels, values, count, 0.5, 0.5, r, "%.1f", 90.0, ImPlotPieChartFlags_None);
ImPlot::PlotPieChart(labels, values, count, 0.5, 0.5, r, "%.1f", 90.0);
}
ImPlot::EndPlot();
}
+257679
View File
File diff suppressed because it is too large Load Diff
+13425
View File
File diff suppressed because it is too large Load Diff
+719
View File
@@ -0,0 +1,719 @@
/*
** 2006 June 7
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This header file defines the SQLite interface for use by
** shared libraries that want to be imported as extensions into
** an SQLite instance. Shared libraries that intend to be loaded
** as extensions by SQLite should #include this file instead of
** sqlite3.h.
*/
#ifndef SQLITE3EXT_H
#define SQLITE3EXT_H
#include "sqlite3.h"
/*
** The following structure holds pointers to all of the SQLite API
** routines.
**
** WARNING: In order to maintain backwards compatibility, add new
** interfaces to the end of this structure only. If you insert new
** interfaces in the middle of this structure, then older different
** versions of SQLite will not be able to load each other's shared
** libraries!
*/
struct sqlite3_api_routines {
void * (*aggregate_context)(sqlite3_context*,int nBytes);
int (*aggregate_count)(sqlite3_context*);
int (*bind_blob)(sqlite3_stmt*,int,const void*,int n,void(*)(void*));
int (*bind_double)(sqlite3_stmt*,int,double);
int (*bind_int)(sqlite3_stmt*,int,int);
int (*bind_int64)(sqlite3_stmt*,int,sqlite_int64);
int (*bind_null)(sqlite3_stmt*,int);
int (*bind_parameter_count)(sqlite3_stmt*);
int (*bind_parameter_index)(sqlite3_stmt*,const char*zName);
const char * (*bind_parameter_name)(sqlite3_stmt*,int);
int (*bind_text)(sqlite3_stmt*,int,const char*,int n,void(*)(void*));
int (*bind_text16)(sqlite3_stmt*,int,const void*,int,void(*)(void*));
int (*bind_value)(sqlite3_stmt*,int,const sqlite3_value*);
int (*busy_handler)(sqlite3*,int(*)(void*,int),void*);
int (*busy_timeout)(sqlite3*,int ms);
int (*changes)(sqlite3*);
int (*close)(sqlite3*);
int (*collation_needed)(sqlite3*,void*,void(*)(void*,sqlite3*,
int eTextRep,const char*));
int (*collation_needed16)(sqlite3*,void*,void(*)(void*,sqlite3*,
int eTextRep,const void*));
const void * (*column_blob)(sqlite3_stmt*,int iCol);
int (*column_bytes)(sqlite3_stmt*,int iCol);
int (*column_bytes16)(sqlite3_stmt*,int iCol);
int (*column_count)(sqlite3_stmt*pStmt);
const char * (*column_database_name)(sqlite3_stmt*,int);
const void * (*column_database_name16)(sqlite3_stmt*,int);
const char * (*column_decltype)(sqlite3_stmt*,int i);
const void * (*column_decltype16)(sqlite3_stmt*,int);
double (*column_double)(sqlite3_stmt*,int iCol);
int (*column_int)(sqlite3_stmt*,int iCol);
sqlite_int64 (*column_int64)(sqlite3_stmt*,int iCol);
const char * (*column_name)(sqlite3_stmt*,int);
const void * (*column_name16)(sqlite3_stmt*,int);
const char * (*column_origin_name)(sqlite3_stmt*,int);
const void * (*column_origin_name16)(sqlite3_stmt*,int);
const char * (*column_table_name)(sqlite3_stmt*,int);
const void * (*column_table_name16)(sqlite3_stmt*,int);
const unsigned char * (*column_text)(sqlite3_stmt*,int iCol);
const void * (*column_text16)(sqlite3_stmt*,int iCol);
int (*column_type)(sqlite3_stmt*,int iCol);
sqlite3_value* (*column_value)(sqlite3_stmt*,int iCol);
void * (*commit_hook)(sqlite3*,int(*)(void*),void*);
int (*complete)(const char*sql);
int (*complete16)(const void*sql);
int (*create_collation)(sqlite3*,const char*,int,void*,
int(*)(void*,int,const void*,int,const void*));
int (*create_collation16)(sqlite3*,const void*,int,void*,
int(*)(void*,int,const void*,int,const void*));
int (*create_function)(sqlite3*,const char*,int,int,void*,
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
void (*xFinal)(sqlite3_context*));
int (*create_function16)(sqlite3*,const void*,int,int,void*,
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
void (*xFinal)(sqlite3_context*));
int (*create_module)(sqlite3*,const char*,const sqlite3_module*,void*);
int (*data_count)(sqlite3_stmt*pStmt);
sqlite3 * (*db_handle)(sqlite3_stmt*);
int (*declare_vtab)(sqlite3*,const char*);
int (*enable_shared_cache)(int);
int (*errcode)(sqlite3*db);
const char * (*errmsg)(sqlite3*);
const void * (*errmsg16)(sqlite3*);
int (*exec)(sqlite3*,const char*,sqlite3_callback,void*,char**);
int (*expired)(sqlite3_stmt*);
int (*finalize)(sqlite3_stmt*pStmt);
void (*free)(void*);
void (*free_table)(char**result);
int (*get_autocommit)(sqlite3*);
void * (*get_auxdata)(sqlite3_context*,int);
int (*get_table)(sqlite3*,const char*,char***,int*,int*,char**);
int (*global_recover)(void);
void (*interruptx)(sqlite3*);
sqlite_int64 (*last_insert_rowid)(sqlite3*);
const char * (*libversion)(void);
int (*libversion_number)(void);
void *(*malloc)(int);
char * (*mprintf)(const char*,...);
int (*open)(const char*,sqlite3**);
int (*open16)(const void*,sqlite3**);
int (*prepare)(sqlite3*,const char*,int,sqlite3_stmt**,const char**);
int (*prepare16)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
void * (*profile)(sqlite3*,void(*)(void*,const char*,sqlite_uint64),void*);
void (*progress_handler)(sqlite3*,int,int(*)(void*),void*);
void *(*realloc)(void*,int);
int (*reset)(sqlite3_stmt*pStmt);
void (*result_blob)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_double)(sqlite3_context*,double);
void (*result_error)(sqlite3_context*,const char*,int);
void (*result_error16)(sqlite3_context*,const void*,int);
void (*result_int)(sqlite3_context*,int);
void (*result_int64)(sqlite3_context*,sqlite_int64);
void (*result_null)(sqlite3_context*);
void (*result_text)(sqlite3_context*,const char*,int,void(*)(void*));
void (*result_text16)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_text16be)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_text16le)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_value)(sqlite3_context*,sqlite3_value*);
void * (*rollback_hook)(sqlite3*,void(*)(void*),void*);
int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*,
const char*,const char*),void*);
void (*set_auxdata)(sqlite3_context*,int,void*,void (*)(void*));
char * (*xsnprintf)(int,char*,const char*,...);
int (*step)(sqlite3_stmt*);
int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*,
char const**,char const**,int*,int*,int*);
void (*thread_cleanup)(void);
int (*total_changes)(sqlite3*);
void * (*trace)(sqlite3*,void(*xTrace)(void*,const char*),void*);
int (*transfer_bindings)(sqlite3_stmt*,sqlite3_stmt*);
void * (*update_hook)(sqlite3*,void(*)(void*,int ,char const*,char const*,
sqlite_int64),void*);
void * (*user_data)(sqlite3_context*);
const void * (*value_blob)(sqlite3_value*);
int (*value_bytes)(sqlite3_value*);
int (*value_bytes16)(sqlite3_value*);
double (*value_double)(sqlite3_value*);
int (*value_int)(sqlite3_value*);
sqlite_int64 (*value_int64)(sqlite3_value*);
int (*value_numeric_type)(sqlite3_value*);
const unsigned char * (*value_text)(sqlite3_value*);
const void * (*value_text16)(sqlite3_value*);
const void * (*value_text16be)(sqlite3_value*);
const void * (*value_text16le)(sqlite3_value*);
int (*value_type)(sqlite3_value*);
char *(*vmprintf)(const char*,va_list);
/* Added ??? */
int (*overload_function)(sqlite3*, const char *zFuncName, int nArg);
/* Added by 3.3.13 */
int (*prepare_v2)(sqlite3*,const char*,int,sqlite3_stmt**,const char**);
int (*prepare16_v2)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
int (*clear_bindings)(sqlite3_stmt*);
/* Added by 3.4.1 */
int (*create_module_v2)(sqlite3*,const char*,const sqlite3_module*,void*,
void (*xDestroy)(void *));
/* Added by 3.5.0 */
int (*bind_zeroblob)(sqlite3_stmt*,int,int);
int (*blob_bytes)(sqlite3_blob*);
int (*blob_close)(sqlite3_blob*);
int (*blob_open)(sqlite3*,const char*,const char*,const char*,sqlite3_int64,
int,sqlite3_blob**);
int (*blob_read)(sqlite3_blob*,void*,int,int);
int (*blob_write)(sqlite3_blob*,const void*,int,int);
int (*create_collation_v2)(sqlite3*,const char*,int,void*,
int(*)(void*,int,const void*,int,const void*),
void(*)(void*));
int (*file_control)(sqlite3*,const char*,int,void*);
sqlite3_int64 (*memory_highwater)(int);
sqlite3_int64 (*memory_used)(void);
sqlite3_mutex *(*mutex_alloc)(int);
void (*mutex_enter)(sqlite3_mutex*);
void (*mutex_free)(sqlite3_mutex*);
void (*mutex_leave)(sqlite3_mutex*);
int (*mutex_try)(sqlite3_mutex*);
int (*open_v2)(const char*,sqlite3**,int,const char*);
int (*release_memory)(int);
void (*result_error_nomem)(sqlite3_context*);
void (*result_error_toobig)(sqlite3_context*);
int (*sleep)(int);
void (*soft_heap_limit)(int);
sqlite3_vfs *(*vfs_find)(const char*);
int (*vfs_register)(sqlite3_vfs*,int);
int (*vfs_unregister)(sqlite3_vfs*);
int (*xthreadsafe)(void);
void (*result_zeroblob)(sqlite3_context*,int);
void (*result_error_code)(sqlite3_context*,int);
int (*test_control)(int, ...);
void (*randomness)(int,void*);
sqlite3 *(*context_db_handle)(sqlite3_context*);
int (*extended_result_codes)(sqlite3*,int);
int (*limit)(sqlite3*,int,int);
sqlite3_stmt *(*next_stmt)(sqlite3*,sqlite3_stmt*);
const char *(*sql)(sqlite3_stmt*);
int (*status)(int,int*,int*,int);
int (*backup_finish)(sqlite3_backup*);
sqlite3_backup *(*backup_init)(sqlite3*,const char*,sqlite3*,const char*);
int (*backup_pagecount)(sqlite3_backup*);
int (*backup_remaining)(sqlite3_backup*);
int (*backup_step)(sqlite3_backup*,int);
const char *(*compileoption_get)(int);
int (*compileoption_used)(const char*);
int (*create_function_v2)(sqlite3*,const char*,int,int,void*,
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
void (*xFinal)(sqlite3_context*),
void(*xDestroy)(void*));
int (*db_config)(sqlite3*,int,...);
sqlite3_mutex *(*db_mutex)(sqlite3*);
int (*db_status)(sqlite3*,int,int*,int*,int);
int (*extended_errcode)(sqlite3*);
void (*log)(int,const char*,...);
sqlite3_int64 (*soft_heap_limit64)(sqlite3_int64);
const char *(*sourceid)(void);
int (*stmt_status)(sqlite3_stmt*,int,int);
int (*strnicmp)(const char*,const char*,int);
int (*unlock_notify)(sqlite3*,void(*)(void**,int),void*);
int (*wal_autocheckpoint)(sqlite3*,int);
int (*wal_checkpoint)(sqlite3*,const char*);
void *(*wal_hook)(sqlite3*,int(*)(void*,sqlite3*,const char*,int),void*);
int (*blob_reopen)(sqlite3_blob*,sqlite3_int64);
int (*vtab_config)(sqlite3*,int op,...);
int (*vtab_on_conflict)(sqlite3*);
/* Version 3.7.16 and later */
int (*close_v2)(sqlite3*);
const char *(*db_filename)(sqlite3*,const char*);
int (*db_readonly)(sqlite3*,const char*);
int (*db_release_memory)(sqlite3*);
const char *(*errstr)(int);
int (*stmt_busy)(sqlite3_stmt*);
int (*stmt_readonly)(sqlite3_stmt*);
int (*stricmp)(const char*,const char*);
int (*uri_boolean)(const char*,const char*,int);
sqlite3_int64 (*uri_int64)(const char*,const char*,sqlite3_int64);
const char *(*uri_parameter)(const char*,const char*);
char *(*xvsnprintf)(int,char*,const char*,va_list);
int (*wal_checkpoint_v2)(sqlite3*,const char*,int,int*,int*);
/* Version 3.8.7 and later */
int (*auto_extension)(void(*)(void));
int (*bind_blob64)(sqlite3_stmt*,int,const void*,sqlite3_uint64,
void(*)(void*));
int (*bind_text64)(sqlite3_stmt*,int,const char*,sqlite3_uint64,
void(*)(void*),unsigned char);
int (*cancel_auto_extension)(void(*)(void));
int (*load_extension)(sqlite3*,const char*,const char*,char**);
void *(*malloc64)(sqlite3_uint64);
sqlite3_uint64 (*msize)(void*);
void *(*realloc64)(void*,sqlite3_uint64);
void (*reset_auto_extension)(void);
void (*result_blob64)(sqlite3_context*,const void*,sqlite3_uint64,
void(*)(void*));
void (*result_text64)(sqlite3_context*,const char*,sqlite3_uint64,
void(*)(void*), unsigned char);
int (*strglob)(const char*,const char*);
/* Version 3.8.11 and later */
sqlite3_value *(*value_dup)(const sqlite3_value*);
void (*value_free)(sqlite3_value*);
int (*result_zeroblob64)(sqlite3_context*,sqlite3_uint64);
int (*bind_zeroblob64)(sqlite3_stmt*, int, sqlite3_uint64);
/* Version 3.9.0 and later */
unsigned int (*value_subtype)(sqlite3_value*);
void (*result_subtype)(sqlite3_context*,unsigned int);
/* Version 3.10.0 and later */
int (*status64)(int,sqlite3_int64*,sqlite3_int64*,int);
int (*strlike)(const char*,const char*,unsigned int);
int (*db_cacheflush)(sqlite3*);
/* Version 3.12.0 and later */
int (*system_errno)(sqlite3*);
/* Version 3.14.0 and later */
int (*trace_v2)(sqlite3*,unsigned,int(*)(unsigned,void*,void*,void*),void*);
char *(*expanded_sql)(sqlite3_stmt*);
/* Version 3.18.0 and later */
void (*set_last_insert_rowid)(sqlite3*,sqlite3_int64);
/* Version 3.20.0 and later */
int (*prepare_v3)(sqlite3*,const char*,int,unsigned int,
sqlite3_stmt**,const char**);
int (*prepare16_v3)(sqlite3*,const void*,int,unsigned int,
sqlite3_stmt**,const void**);
int (*bind_pointer)(sqlite3_stmt*,int,void*,const char*,void(*)(void*));
void (*result_pointer)(sqlite3_context*,void*,const char*,void(*)(void*));
void *(*value_pointer)(sqlite3_value*,const char*);
int (*vtab_nochange)(sqlite3_context*);
int (*value_nochange)(sqlite3_value*);
const char *(*vtab_collation)(sqlite3_index_info*,int);
/* Version 3.24.0 and later */
int (*keyword_count)(void);
int (*keyword_name)(int,const char**,int*);
int (*keyword_check)(const char*,int);
sqlite3_str *(*str_new)(sqlite3*);
char *(*str_finish)(sqlite3_str*);
void (*str_appendf)(sqlite3_str*, const char *zFormat, ...);
void (*str_vappendf)(sqlite3_str*, const char *zFormat, va_list);
void (*str_append)(sqlite3_str*, const char *zIn, int N);
void (*str_appendall)(sqlite3_str*, const char *zIn);
void (*str_appendchar)(sqlite3_str*, int N, char C);
void (*str_reset)(sqlite3_str*);
int (*str_errcode)(sqlite3_str*);
int (*str_length)(sqlite3_str*);
char *(*str_value)(sqlite3_str*);
/* Version 3.25.0 and later */
int (*create_window_function)(sqlite3*,const char*,int,int,void*,
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
void (*xFinal)(sqlite3_context*),
void (*xValue)(sqlite3_context*),
void (*xInv)(sqlite3_context*,int,sqlite3_value**),
void(*xDestroy)(void*));
/* Version 3.26.0 and later */
const char *(*normalized_sql)(sqlite3_stmt*);
/* Version 3.28.0 and later */
int (*stmt_isexplain)(sqlite3_stmt*);
int (*value_frombind)(sqlite3_value*);
/* Version 3.30.0 and later */
int (*drop_modules)(sqlite3*,const char**);
/* Version 3.31.0 and later */
sqlite3_int64 (*hard_heap_limit64)(sqlite3_int64);
const char *(*uri_key)(const char*,int);
const char *(*filename_database)(const char*);
const char *(*filename_journal)(const char*);
const char *(*filename_wal)(const char*);
/* Version 3.32.0 and later */
const char *(*create_filename)(const char*,const char*,const char*,
int,const char**);
void (*free_filename)(const char*);
sqlite3_file *(*database_file_object)(const char*);
/* Version 3.34.0 and later */
int (*txn_state)(sqlite3*,const char*);
/* Version 3.36.1 and later */
sqlite3_int64 (*changes64)(sqlite3*);
sqlite3_int64 (*total_changes64)(sqlite3*);
/* Version 3.37.0 and later */
int (*autovacuum_pages)(sqlite3*,
unsigned int(*)(void*,const char*,unsigned int,unsigned int,unsigned int),
void*, void(*)(void*));
/* Version 3.38.0 and later */
int (*error_offset)(sqlite3*);
int (*vtab_rhs_value)(sqlite3_index_info*,int,sqlite3_value**);
int (*vtab_distinct)(sqlite3_index_info*);
int (*vtab_in)(sqlite3_index_info*,int,int);
int (*vtab_in_first)(sqlite3_value*,sqlite3_value**);
int (*vtab_in_next)(sqlite3_value*,sqlite3_value**);
/* Version 3.39.0 and later */
int (*deserialize)(sqlite3*,const char*,unsigned char*,
sqlite3_int64,sqlite3_int64,unsigned);
unsigned char *(*serialize)(sqlite3*,const char *,sqlite3_int64*,
unsigned int);
const char *(*db_name)(sqlite3*,int);
/* Version 3.40.0 and later */
int (*value_encoding)(sqlite3_value*);
/* Version 3.41.0 and later */
int (*is_interrupted)(sqlite3*);
/* Version 3.43.0 and later */
int (*stmt_explain)(sqlite3_stmt*,int);
/* Version 3.44.0 and later */
void *(*get_clientdata)(sqlite3*,const char*);
int (*set_clientdata)(sqlite3*, const char*, void*, void(*)(void*));
};
/*
** This is the function signature used for all extension entry points. It
** is also defined in the file "loadext.c".
*/
typedef int (*sqlite3_loadext_entry)(
sqlite3 *db, /* Handle to the database. */
char **pzErrMsg, /* Used to set error string on failure. */
const sqlite3_api_routines *pThunk /* Extension API function pointers. */
);
/*
** The following macros redefine the API routines so that they are
** redirected through the global sqlite3_api structure.
**
** This header file is also used by the loadext.c source file
** (part of the main SQLite library - not an extension) so that
** it can get access to the sqlite3_api_routines structure
** definition. But the main library does not want to redefine
** the API. So the redefinition macros are only valid if the
** SQLITE_CORE macros is undefined.
*/
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
#define sqlite3_aggregate_context sqlite3_api->aggregate_context
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_aggregate_count sqlite3_api->aggregate_count
#endif
#define sqlite3_bind_blob sqlite3_api->bind_blob
#define sqlite3_bind_double sqlite3_api->bind_double
#define sqlite3_bind_int sqlite3_api->bind_int
#define sqlite3_bind_int64 sqlite3_api->bind_int64
#define sqlite3_bind_null sqlite3_api->bind_null
#define sqlite3_bind_parameter_count sqlite3_api->bind_parameter_count
#define sqlite3_bind_parameter_index sqlite3_api->bind_parameter_index
#define sqlite3_bind_parameter_name sqlite3_api->bind_parameter_name
#define sqlite3_bind_text sqlite3_api->bind_text
#define sqlite3_bind_text16 sqlite3_api->bind_text16
#define sqlite3_bind_value sqlite3_api->bind_value
#define sqlite3_busy_handler sqlite3_api->busy_handler
#define sqlite3_busy_timeout sqlite3_api->busy_timeout
#define sqlite3_changes sqlite3_api->changes
#define sqlite3_close sqlite3_api->close
#define sqlite3_collation_needed sqlite3_api->collation_needed
#define sqlite3_collation_needed16 sqlite3_api->collation_needed16
#define sqlite3_column_blob sqlite3_api->column_blob
#define sqlite3_column_bytes sqlite3_api->column_bytes
#define sqlite3_column_bytes16 sqlite3_api->column_bytes16
#define sqlite3_column_count sqlite3_api->column_count
#define sqlite3_column_database_name sqlite3_api->column_database_name
#define sqlite3_column_database_name16 sqlite3_api->column_database_name16
#define sqlite3_column_decltype sqlite3_api->column_decltype
#define sqlite3_column_decltype16 sqlite3_api->column_decltype16
#define sqlite3_column_double sqlite3_api->column_double
#define sqlite3_column_int sqlite3_api->column_int
#define sqlite3_column_int64 sqlite3_api->column_int64
#define sqlite3_column_name sqlite3_api->column_name
#define sqlite3_column_name16 sqlite3_api->column_name16
#define sqlite3_column_origin_name sqlite3_api->column_origin_name
#define sqlite3_column_origin_name16 sqlite3_api->column_origin_name16
#define sqlite3_column_table_name sqlite3_api->column_table_name
#define sqlite3_column_table_name16 sqlite3_api->column_table_name16
#define sqlite3_column_text sqlite3_api->column_text
#define sqlite3_column_text16 sqlite3_api->column_text16
#define sqlite3_column_type sqlite3_api->column_type
#define sqlite3_column_value sqlite3_api->column_value
#define sqlite3_commit_hook sqlite3_api->commit_hook
#define sqlite3_complete sqlite3_api->complete
#define sqlite3_complete16 sqlite3_api->complete16
#define sqlite3_create_collation sqlite3_api->create_collation
#define sqlite3_create_collation16 sqlite3_api->create_collation16
#define sqlite3_create_function sqlite3_api->create_function
#define sqlite3_create_function16 sqlite3_api->create_function16
#define sqlite3_create_module sqlite3_api->create_module
#define sqlite3_create_module_v2 sqlite3_api->create_module_v2
#define sqlite3_data_count sqlite3_api->data_count
#define sqlite3_db_handle sqlite3_api->db_handle
#define sqlite3_declare_vtab sqlite3_api->declare_vtab
#define sqlite3_enable_shared_cache sqlite3_api->enable_shared_cache
#define sqlite3_errcode sqlite3_api->errcode
#define sqlite3_errmsg sqlite3_api->errmsg
#define sqlite3_errmsg16 sqlite3_api->errmsg16
#define sqlite3_exec sqlite3_api->exec
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_expired sqlite3_api->expired
#endif
#define sqlite3_finalize sqlite3_api->finalize
#define sqlite3_free sqlite3_api->free
#define sqlite3_free_table sqlite3_api->free_table
#define sqlite3_get_autocommit sqlite3_api->get_autocommit
#define sqlite3_get_auxdata sqlite3_api->get_auxdata
#define sqlite3_get_table sqlite3_api->get_table
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_global_recover sqlite3_api->global_recover
#endif
#define sqlite3_interrupt sqlite3_api->interruptx
#define sqlite3_last_insert_rowid sqlite3_api->last_insert_rowid
#define sqlite3_libversion sqlite3_api->libversion
#define sqlite3_libversion_number sqlite3_api->libversion_number
#define sqlite3_malloc sqlite3_api->malloc
#define sqlite3_mprintf sqlite3_api->mprintf
#define sqlite3_open sqlite3_api->open
#define sqlite3_open16 sqlite3_api->open16
#define sqlite3_prepare sqlite3_api->prepare
#define sqlite3_prepare16 sqlite3_api->prepare16
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
#define sqlite3_profile sqlite3_api->profile
#define sqlite3_progress_handler sqlite3_api->progress_handler
#define sqlite3_realloc sqlite3_api->realloc
#define sqlite3_reset sqlite3_api->reset
#define sqlite3_result_blob sqlite3_api->result_blob
#define sqlite3_result_double sqlite3_api->result_double
#define sqlite3_result_error sqlite3_api->result_error
#define sqlite3_result_error16 sqlite3_api->result_error16
#define sqlite3_result_int sqlite3_api->result_int
#define sqlite3_result_int64 sqlite3_api->result_int64
#define sqlite3_result_null sqlite3_api->result_null
#define sqlite3_result_text sqlite3_api->result_text
#define sqlite3_result_text16 sqlite3_api->result_text16
#define sqlite3_result_text16be sqlite3_api->result_text16be
#define sqlite3_result_text16le sqlite3_api->result_text16le
#define sqlite3_result_value sqlite3_api->result_value
#define sqlite3_rollback_hook sqlite3_api->rollback_hook
#define sqlite3_set_authorizer sqlite3_api->set_authorizer
#define sqlite3_set_auxdata sqlite3_api->set_auxdata
#define sqlite3_snprintf sqlite3_api->xsnprintf
#define sqlite3_step sqlite3_api->step
#define sqlite3_table_column_metadata sqlite3_api->table_column_metadata
#define sqlite3_thread_cleanup sqlite3_api->thread_cleanup
#define sqlite3_total_changes sqlite3_api->total_changes
#define sqlite3_trace sqlite3_api->trace
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_transfer_bindings sqlite3_api->transfer_bindings
#endif
#define sqlite3_update_hook sqlite3_api->update_hook
#define sqlite3_user_data sqlite3_api->user_data
#define sqlite3_value_blob sqlite3_api->value_blob
#define sqlite3_value_bytes sqlite3_api->value_bytes
#define sqlite3_value_bytes16 sqlite3_api->value_bytes16
#define sqlite3_value_double sqlite3_api->value_double
#define sqlite3_value_int sqlite3_api->value_int
#define sqlite3_value_int64 sqlite3_api->value_int64
#define sqlite3_value_numeric_type sqlite3_api->value_numeric_type
#define sqlite3_value_text sqlite3_api->value_text
#define sqlite3_value_text16 sqlite3_api->value_text16
#define sqlite3_value_text16be sqlite3_api->value_text16be
#define sqlite3_value_text16le sqlite3_api->value_text16le
#define sqlite3_value_type sqlite3_api->value_type
#define sqlite3_vmprintf sqlite3_api->vmprintf
#define sqlite3_vsnprintf sqlite3_api->xvsnprintf
#define sqlite3_overload_function sqlite3_api->overload_function
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
#define sqlite3_clear_bindings sqlite3_api->clear_bindings
#define sqlite3_bind_zeroblob sqlite3_api->bind_zeroblob
#define sqlite3_blob_bytes sqlite3_api->blob_bytes
#define sqlite3_blob_close sqlite3_api->blob_close
#define sqlite3_blob_open sqlite3_api->blob_open
#define sqlite3_blob_read sqlite3_api->blob_read
#define sqlite3_blob_write sqlite3_api->blob_write
#define sqlite3_create_collation_v2 sqlite3_api->create_collation_v2
#define sqlite3_file_control sqlite3_api->file_control
#define sqlite3_memory_highwater sqlite3_api->memory_highwater
#define sqlite3_memory_used sqlite3_api->memory_used
#define sqlite3_mutex_alloc sqlite3_api->mutex_alloc
#define sqlite3_mutex_enter sqlite3_api->mutex_enter
#define sqlite3_mutex_free sqlite3_api->mutex_free
#define sqlite3_mutex_leave sqlite3_api->mutex_leave
#define sqlite3_mutex_try sqlite3_api->mutex_try
#define sqlite3_open_v2 sqlite3_api->open_v2
#define sqlite3_release_memory sqlite3_api->release_memory
#define sqlite3_result_error_nomem sqlite3_api->result_error_nomem
#define sqlite3_result_error_toobig sqlite3_api->result_error_toobig
#define sqlite3_sleep sqlite3_api->sleep
#define sqlite3_soft_heap_limit sqlite3_api->soft_heap_limit
#define sqlite3_vfs_find sqlite3_api->vfs_find
#define sqlite3_vfs_register sqlite3_api->vfs_register
#define sqlite3_vfs_unregister sqlite3_api->vfs_unregister
#define sqlite3_threadsafe sqlite3_api->xthreadsafe
#define sqlite3_result_zeroblob sqlite3_api->result_zeroblob
#define sqlite3_result_error_code sqlite3_api->result_error_code
#define sqlite3_test_control sqlite3_api->test_control
#define sqlite3_randomness sqlite3_api->randomness
#define sqlite3_context_db_handle sqlite3_api->context_db_handle
#define sqlite3_extended_result_codes sqlite3_api->extended_result_codes
#define sqlite3_limit sqlite3_api->limit
#define sqlite3_next_stmt sqlite3_api->next_stmt
#define sqlite3_sql sqlite3_api->sql
#define sqlite3_status sqlite3_api->status
#define sqlite3_backup_finish sqlite3_api->backup_finish
#define sqlite3_backup_init sqlite3_api->backup_init
#define sqlite3_backup_pagecount sqlite3_api->backup_pagecount
#define sqlite3_backup_remaining sqlite3_api->backup_remaining
#define sqlite3_backup_step sqlite3_api->backup_step
#define sqlite3_compileoption_get sqlite3_api->compileoption_get
#define sqlite3_compileoption_used sqlite3_api->compileoption_used
#define sqlite3_create_function_v2 sqlite3_api->create_function_v2
#define sqlite3_db_config sqlite3_api->db_config
#define sqlite3_db_mutex sqlite3_api->db_mutex
#define sqlite3_db_status sqlite3_api->db_status
#define sqlite3_extended_errcode sqlite3_api->extended_errcode
#define sqlite3_log sqlite3_api->log
#define sqlite3_soft_heap_limit64 sqlite3_api->soft_heap_limit64
#define sqlite3_sourceid sqlite3_api->sourceid
#define sqlite3_stmt_status sqlite3_api->stmt_status
#define sqlite3_strnicmp sqlite3_api->strnicmp
#define sqlite3_unlock_notify sqlite3_api->unlock_notify
#define sqlite3_wal_autocheckpoint sqlite3_api->wal_autocheckpoint
#define sqlite3_wal_checkpoint sqlite3_api->wal_checkpoint
#define sqlite3_wal_hook sqlite3_api->wal_hook
#define sqlite3_blob_reopen sqlite3_api->blob_reopen
#define sqlite3_vtab_config sqlite3_api->vtab_config
#define sqlite3_vtab_on_conflict sqlite3_api->vtab_on_conflict
/* Version 3.7.16 and later */
#define sqlite3_close_v2 sqlite3_api->close_v2
#define sqlite3_db_filename sqlite3_api->db_filename
#define sqlite3_db_readonly sqlite3_api->db_readonly
#define sqlite3_db_release_memory sqlite3_api->db_release_memory
#define sqlite3_errstr sqlite3_api->errstr
#define sqlite3_stmt_busy sqlite3_api->stmt_busy
#define sqlite3_stmt_readonly sqlite3_api->stmt_readonly
#define sqlite3_stricmp sqlite3_api->stricmp
#define sqlite3_uri_boolean sqlite3_api->uri_boolean
#define sqlite3_uri_int64 sqlite3_api->uri_int64
#define sqlite3_uri_parameter sqlite3_api->uri_parameter
#define sqlite3_uri_vsnprintf sqlite3_api->xvsnprintf
#define sqlite3_wal_checkpoint_v2 sqlite3_api->wal_checkpoint_v2
/* Version 3.8.7 and later */
#define sqlite3_auto_extension sqlite3_api->auto_extension
#define sqlite3_bind_blob64 sqlite3_api->bind_blob64
#define sqlite3_bind_text64 sqlite3_api->bind_text64
#define sqlite3_cancel_auto_extension sqlite3_api->cancel_auto_extension
#define sqlite3_load_extension sqlite3_api->load_extension
#define sqlite3_malloc64 sqlite3_api->malloc64
#define sqlite3_msize sqlite3_api->msize
#define sqlite3_realloc64 sqlite3_api->realloc64
#define sqlite3_reset_auto_extension sqlite3_api->reset_auto_extension
#define sqlite3_result_blob64 sqlite3_api->result_blob64
#define sqlite3_result_text64 sqlite3_api->result_text64
#define sqlite3_strglob sqlite3_api->strglob
/* Version 3.8.11 and later */
#define sqlite3_value_dup sqlite3_api->value_dup
#define sqlite3_value_free sqlite3_api->value_free
#define sqlite3_result_zeroblob64 sqlite3_api->result_zeroblob64
#define sqlite3_bind_zeroblob64 sqlite3_api->bind_zeroblob64
/* Version 3.9.0 and later */
#define sqlite3_value_subtype sqlite3_api->value_subtype
#define sqlite3_result_subtype sqlite3_api->result_subtype
/* Version 3.10.0 and later */
#define sqlite3_status64 sqlite3_api->status64
#define sqlite3_strlike sqlite3_api->strlike
#define sqlite3_db_cacheflush sqlite3_api->db_cacheflush
/* Version 3.12.0 and later */
#define sqlite3_system_errno sqlite3_api->system_errno
/* Version 3.14.0 and later */
#define sqlite3_trace_v2 sqlite3_api->trace_v2
#define sqlite3_expanded_sql sqlite3_api->expanded_sql
/* Version 3.18.0 and later */
#define sqlite3_set_last_insert_rowid sqlite3_api->set_last_insert_rowid
/* Version 3.20.0 and later */
#define sqlite3_prepare_v3 sqlite3_api->prepare_v3
#define sqlite3_prepare16_v3 sqlite3_api->prepare16_v3
#define sqlite3_bind_pointer sqlite3_api->bind_pointer
#define sqlite3_result_pointer sqlite3_api->result_pointer
#define sqlite3_value_pointer sqlite3_api->value_pointer
/* Version 3.22.0 and later */
#define sqlite3_vtab_nochange sqlite3_api->vtab_nochange
#define sqlite3_value_nochange sqlite3_api->value_nochange
#define sqlite3_vtab_collation sqlite3_api->vtab_collation
/* Version 3.24.0 and later */
#define sqlite3_keyword_count sqlite3_api->keyword_count
#define sqlite3_keyword_name sqlite3_api->keyword_name
#define sqlite3_keyword_check sqlite3_api->keyword_check
#define sqlite3_str_new sqlite3_api->str_new
#define sqlite3_str_finish sqlite3_api->str_finish
#define sqlite3_str_appendf sqlite3_api->str_appendf
#define sqlite3_str_vappendf sqlite3_api->str_vappendf
#define sqlite3_str_append sqlite3_api->str_append
#define sqlite3_str_appendall sqlite3_api->str_appendall
#define sqlite3_str_appendchar sqlite3_api->str_appendchar
#define sqlite3_str_reset sqlite3_api->str_reset
#define sqlite3_str_errcode sqlite3_api->str_errcode
#define sqlite3_str_length sqlite3_api->str_length
#define sqlite3_str_value sqlite3_api->str_value
/* Version 3.25.0 and later */
#define sqlite3_create_window_function sqlite3_api->create_window_function
/* Version 3.26.0 and later */
#define sqlite3_normalized_sql sqlite3_api->normalized_sql
/* Version 3.28.0 and later */
#define sqlite3_stmt_isexplain sqlite3_api->stmt_isexplain
#define sqlite3_value_frombind sqlite3_api->value_frombind
/* Version 3.30.0 and later */
#define sqlite3_drop_modules sqlite3_api->drop_modules
/* Version 3.31.0 and later */
#define sqlite3_hard_heap_limit64 sqlite3_api->hard_heap_limit64
#define sqlite3_uri_key sqlite3_api->uri_key
#define sqlite3_filename_database sqlite3_api->filename_database
#define sqlite3_filename_journal sqlite3_api->filename_journal
#define sqlite3_filename_wal sqlite3_api->filename_wal
/* Version 3.32.0 and later */
#define sqlite3_create_filename sqlite3_api->create_filename
#define sqlite3_free_filename sqlite3_api->free_filename
#define sqlite3_database_file_object sqlite3_api->database_file_object
/* Version 3.34.0 and later */
#define sqlite3_txn_state sqlite3_api->txn_state
/* Version 3.36.1 and later */
#define sqlite3_changes64 sqlite3_api->changes64
#define sqlite3_total_changes64 sqlite3_api->total_changes64
/* Version 3.37.0 and later */
#define sqlite3_autovacuum_pages sqlite3_api->autovacuum_pages
/* Version 3.38.0 and later */
#define sqlite3_error_offset sqlite3_api->error_offset
#define sqlite3_vtab_rhs_value sqlite3_api->vtab_rhs_value
#define sqlite3_vtab_distinct sqlite3_api->vtab_distinct
#define sqlite3_vtab_in sqlite3_api->vtab_in
#define sqlite3_vtab_in_first sqlite3_api->vtab_in_first
#define sqlite3_vtab_in_next sqlite3_api->vtab_in_next
/* Version 3.39.0 and later */
#ifndef SQLITE_OMIT_DESERIALIZE
#define sqlite3_deserialize sqlite3_api->deserialize
#define sqlite3_serialize sqlite3_api->serialize
#endif
#define sqlite3_db_name sqlite3_api->db_name
/* Version 3.40.0 and later */
#define sqlite3_value_encoding sqlite3_api->value_encoding
/* Version 3.41.0 and later */
#define sqlite3_is_interrupted sqlite3_api->is_interrupted
/* Version 3.43.0 and later */
#define sqlite3_stmt_explain sqlite3_api->stmt_explain
/* Version 3.44.0 and later */
#define sqlite3_get_clientdata sqlite3_api->get_clientdata
#define sqlite3_set_clientdata sqlite3_api->set_clientdata
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
/* This case when the file really is being compiled as a loadable
** extension */
# define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api=0;
# define SQLITE_EXTENSION_INIT2(v) sqlite3_api=v;
# define SQLITE_EXTENSION_INIT3 \
extern const sqlite3_api_routines *sqlite3_api;
#else
/* This case when the file is being statically linked into the
** application */
# define SQLITE_EXTENSION_INIT1 /*no-op*/
# define SQLITE_EXTENSION_INIT2(v) (void)v; /* unused parameter */
# define SQLITE_EXTENSION_INIT3 /*no-op*/
#endif
#endif /* SQLITE3EXT_H */
+7
View File
@@ -0,0 +1,7 @@
{
"dag-engine": {
"enabled": false,
"issue": "0007",
"description": "Sistema propio de orquestacion de DAGs para reemplazar Dagu. Incluye parser YAML, executor con paralelismo, process manager, execution store y scheduler cron."
}
}
+137
View File
@@ -0,0 +1,137 @@
# 0007a — Funciones core del DAG engine
## Metadata
| Campo | Valor |
|-------|-------|
| **ID** | 0007a |
| **Estado** | pendiente |
| **Prioridad** | alta |
| **Tipo** | feature |
## Dependencias
| ID | Título | Estado | Requerido |
|----|--------|--------|-----------|
| — | Ninguna | — | — |
**Bloqueada por:** ninguna
**Desbloquea:** `#0007b, #0007c, #0007d, #0007e`
---
## Objetivo
Crear las funciones puras que parsean, validan y ordenan DAGs definidos en YAML. Estas funciones son el nucleo del sistema de orquestacion — todo lo demas depende de ellas.
## Contexto
- Dagu usa YAML con `steps`, `depends`, `env`, `schedule` — queremos compatibilidad con ese formato
- Las funciones deben ser puras: reciben datos, retornan datos, sin I/O
- Deben vivir en `functions/core/` (Go) para maxima composabilidad
- El formato YAML de Dagu existente en `~/dagu/dags/` debe poder parsearse sin cambios
## Arquitectura
```
functions/core/
├── dag_parse.go — NEW: YAML → DagDefinition
├── dag_parse.md — NEW: metadata
├── dag_validate.go — NEW: valida ciclos, refs rotas, campos requeridos
├── dag_validate.md — NEW: metadata
├── dag_topo_sort.go — NEW: ordena steps por dependencias (Kahn's algorithm)
├── dag_topo_sort.md — NEW: metadata
├── dag_resolve_env.go — NEW: sustituye variables ${VAR} en steps
├── dag_resolve_env.md — NEW: metadata
types/core/
├── dag_definition.md — NEW: tipo DagDefinition (product)
├── dag_step.md — NEW: tipo DagStep (product)
├── dag_schedule.md — NEW: tipo DagSchedule (product)
├── dag_result.md — NEW: tipo DagValidationResult (product)
```
### Patron pure core / impure shell
- `core/` — Todas las funciones de este issue son puras
- No hay shell/impure en este issue
- Los tipos usan nativos de Go en firmas, tipos del registry en `uses_types`
## Tareas
### Fase 1: Tipos
- [ ] **1.1** Definir `DagStep` — name, command, args, depends, env, timeout, retry, tags
- [ ] **1.2** Definir `DagSchedule` — cron expressions, timezone
- [ ] **1.3** Definir `DagDefinition` — name, description, steps, env, schedule, tags
- [ ] **1.4** Definir `DagValidationResult` — errors, warnings, step_order
### Fase 2: Parser
- [ ] **2.1** `dag_parse` — YAML bytes → DagDefinition. Soportar formato Dagu: steps con command/depends/env
- [ ] **2.2** Tests: parsear DAGs existentes de `~/dagu/dags/`, edge cases (YAML invalido, campos faltantes)
### Fase 3: Validacion
- [ ] **3.1** `dag_validate` — detectar ciclos (DFS), referencias rotas en depends, steps sin nombre, nombres duplicados
- [ ] **3.2** Tests: grafos ciclicos, DAGs validos, depends a steps inexistentes
### Fase 4: Topological sort
- [ ] **4.1** `dag_topo_sort` — Kahn's algorithm, retorna steps en orden de ejecucion con niveles de paralelismo
- [ ] **4.2** Tests: DAGs lineales, DAGs con ramas paralelas, diamond dependencies
### Fase 5: Resolucion de env
- [ ] **5.1** `dag_resolve_env` — sustituye `${VAR}` y `$VAR` en command/args de cada step usando env del DAG + env del step
- [ ] **5.2** Tests: variables anidadas, variables no definidas, escaping
### Fase 6: Cleanup
- [ ] `fn index` y verificar todos los IDs
- [ ] Verificar que todos los tipos son referenciados correctamente en uses_types
---
## Ejemplo de uso
```go
// Parsear un DAG
data, _ := os.ReadFile("dags/my_pipeline.yaml")
dag, err := dag_parse(data)
// Validar
result := dag_validate(dag)
if len(result.Errors) > 0 {
// ciclos, refs rotas...
}
// Ordenar
ordered := dag_topo_sort(dag.Steps)
// ordered = [[step_a], [step_b, step_c], [step_d]]
// nivel 0 nivel 1 (paralelo) nivel 2
// Resolver env
resolved := dag_resolve_env(dag, os.Environ())
```
## Decisiones de diseno
- **Kahn's algorithm sobre DFS topo sort**: Kahn's da niveles de paralelismo gratis — steps en el mismo nivel pueden ejecutarse en paralelo
- **Formato Dagu compatible**: no inventar formato nuevo, reutilizar el YAML que ya existe
- **Tipos nativos en firma**: `[]byte` entrada, structs con campos basicos, sin dependencias externas para parsear
## Criterios de aceptacion
- [ ] `dag_parse` parsea correctamente los DAGs existentes en `~/dagu/dags/`
- [ ] `dag_validate` detecta ciclos y referencias rotas
- [ ] `dag_topo_sort` retorna orden correcto con niveles de paralelismo
- [ ] Todas las funciones son puras (sin I/O, sin estado)
- [ ] Tests pasan con `go test -tags fts5 ./...`
- [ ] Indexado en registry.db
## Referencias
- Dagu YAML spec: `~/dagu/dags/example.yaml`
- Kahn's algorithm: topological sort con BFS que da niveles
+115
View File
@@ -0,0 +1,115 @@
# 0007b — Process manager: spawn, wait, kill, status
## Metadata
| Campo | Valor |
|-------|-------|
| **ID** | 0007b |
| **Estado** | pendiente |
| **Prioridad** | alta |
| **Tipo** | feature |
## Dependencias
| ID | Título | Estado | Requerido |
|----|--------|--------|-----------|
| 0007a | Funciones core del DAG engine | pendiente | Si |
**Bloqueada por:** `#0007a`
**Desbloquea:** `#0007e`
---
## Objetivo
Funciones impuras para gestionar procesos hijo: lanzar, esperar, matar, consultar estado. Son los bloques que el executor usara para correr cada step de un DAG.
## Contexto
- Cada step de un DAG se ejecuta como un proceso hijo (`os/exec`)
- Necesitamos captura de stdout/stderr, timeout, señales (SIGTERM/SIGKILL)
- Deben ser funciones atomicas — el executor las compone
- Dominio `infra` porque gestionan recursos del sistema
## Arquitectura
```
functions/infra/
├── process_spawn.go — NEW: lanza proceso, retorna PID + pipes
├── process_spawn.md
├── process_wait.go — NEW: espera proceso con timeout
├── process_wait.md
├── process_kill.go — NEW: envia señal a proceso (SIGTERM, SIGKILL)
├── process_kill.md
├── process_status.go — NEW: consulta estado de PID (running, exited, code)
├── process_status.md
types/infra/
├── process_handle.md — NEW: PID, stdin/stdout/stderr pipes, start_time
├── process_result.md — NEW: exit_code, stdout, stderr, duration_ms
```
### Patron pure core / impure shell
- `core/` — No aplica en este issue
- `infra/` — Todas impuras (spawn procesos, I/O con OS)
- `error_type`: `error_go_core` para todas
## Tareas
### Fase 1: Tipos
- [ ] **1.1** Definir `ProcessHandle` — pid, cmd, start_time, working_dir
- [ ] **1.2** Definir `ProcessResult` — exit_code, stdout, stderr, duration_ms, killed
### Fase 2: Funciones
- [ ] **2.1** `process_spawn` — ejecuta comando con args, env, working_dir. Retorna ProcessHandle. No bloquea.
- [ ] **2.2** `process_wait` — espera a que el proceso termine o timeout. Retorna ProcessResult.
- [ ] **2.3** `process_kill` — envia SIGTERM, espera grace period, luego SIGKILL si sigue vivo
- [ ] **2.4** `process_status` — consulta si el PID sigue corriendo, retorna estado
### Fase 3: Tests
- [ ] **3.1** Tests: spawn+wait de `echo hello`, timeout con `sleep 999`, kill de proceso largo
- [ ] **3.2** Tests: captura correcta de stdout/stderr, exit codes no-zero
### Fase 4: Cleanup
- [ ] `fn index` y verificar IDs
- [ ] Verificar error_type en todas las funciones impuras
---
## Ejemplo de uso
```go
handle, err := process_spawn(ProcessSpawnInput{
Command: "python3",
Args: []string{"script.py", "--flag"},
Env: []string{"API_KEY=xxx"},
WorkingDir: "/home/lucas/project",
})
result, err := process_wait(handle, 30*time.Second) // timeout 30s
// result.ExitCode == 0, result.Stdout == "output..."
// O matar si tarda demasiado
process_kill(handle, 5*time.Second) // SIGTERM, 5s grace, luego SIGKILL
```
## Decisiones de diseno
- **Spawn no bloquea**: retorna handle inmediatamente, wait es separado — permite al executor lanzar steps en paralelo
- **Kill con grace period**: SIGTERM primero, espera, SIGKILL si no murio — comportamiento estandar de process managers
- **Stdout/stderr como strings**: para steps cortos. Para steps con output grande, futuro: streaming a archivo
## Criterios de aceptacion
- [ ] Spawn y wait funcionan con comandos reales
- [ ] Timeout mata el proceso correctamente
- [ ] Kill con grace period funciona
- [ ] Exit codes se capturan correctamente
- [ ] Tests pasan
- [ ] Indexado en registry.db
+115
View File
@@ -0,0 +1,115 @@
# 0007c — Execution store: persistencia de estado
## Metadata
| Campo | Valor |
|-------|-------|
| **ID** | 0007c |
| **Estado** | pendiente |
| **Prioridad** | alta |
| **Tipo** | feature |
## Dependencias
| ID | Título | Estado | Requerido |
|----|--------|--------|-----------|
| 0007a | Funciones core del DAG engine | pendiente | Si |
**Bloqueada por:** `#0007a`
**Desbloquea:** `#0007e`
---
## Objetivo
Funciones para persistir el estado de ejecuciones de DAGs en SQLite: que DAG se ejecuto, cuando, que steps corrieron, resultado de cada step, logs. Permite historial, reintentos y debugging.
## Contexto
- Cada ejecucion de un DAG genera un `run` con multiples `step_results`
- Similar a `operations.db` pero especifico para el DAG engine
- La BD vive en el directorio de la app del scheduler (no en raiz)
- Debe soportar consultas tipo: "ultimas 10 ejecuciones de X", "steps fallidos"
## Arquitectura
```
functions/infra/
├── dag_store_init.go — NEW: crea schema SQLite para runs/steps
├── dag_store_init.md
├── dag_store_run.go — NEW: CRUD de runs (create, update status, list, get)
├── dag_store_run.md
├── dag_store_step.go — NEW: CRUD de step results dentro de un run
├── dag_store_step.md
types/infra/
├── dag_run.md — NEW: id, dag_name, status, started_at, finished_at, trigger
├── dag_step_result.md — NEW: run_id, step_name, status, exit_code, stdout, stderr, duration_ms
```
### Patron pure core / impure shell
- `infra/` — Todas impuras (SQLite I/O)
- Schema sencillo: `dag_runs` + `dag_step_results`
## Tareas
### Fase 1: Tipos y schema
- [ ] **1.1** Definir `DagRun` — id (ULID), dag_name, dag_path, status (pending/running/success/failed/cancelled), started_at, finished_at, trigger (manual/schedule/api)
- [ ] **1.2** Definir `DagStepResult` — id, run_id, step_name, status, exit_code, stdout, stderr, started_at, finished_at, duration_ms
- [ ] **1.3** Schema SQLite: `dag_runs`, `dag_step_results` con indices
### Fase 2: Funciones
- [ ] **2.1** `dag_store_init` — crea/migra la BD SQLite
- [ ] **2.2** `dag_store_run` — create_run, update_run_status, get_run, list_runs (con filtros)
- [ ] **2.3** `dag_store_step` — insert_step_result, list_steps_for_run
### Fase 3: Tests
- [ ] **3.1** Ciclo completo: init → create run → insert steps → update status → query
- [ ] **3.2** Queries: ultimas N ejecuciones, ejecuciones fallidas, steps de un run
### Fase 4: Cleanup
- [ ] `fn index` y verificar IDs
---
## Ejemplo de uso
```go
db := dag_store_init("/path/to/scheduler.db")
run := dag_store_create_run(db, "my_pipeline", "manual")
// run.ID = "01HXZ..."
dag_store_insert_step(db, run.ID, DagStepResult{
StepName: "fetch_data",
Status: "success",
ExitCode: 0,
Stdout: "fetched 1000 rows",
DurationMs: 1234,
})
dag_store_update_status(db, run.ID, "success")
// Consultar
runs := dag_store_list_runs(db, "my_pipeline", 10) // ultimas 10
```
## Decisiones de diseno
- **SQLite por DAG engine, no por DAG**: una sola BD para todas las ejecuciones, no una por cada DAG
- **ULID para run IDs**: ordenables por tiempo, unicos sin coordinacion
- **stdout/stderr en BD**: para steps cortos. Para output grande, guardar path a archivo de log
## Criterios de aceptacion
- [ ] Schema se crea correctamente
- [ ] CRUD completo funciona
- [ ] Queries con filtros funcionan
- [ ] Tests pasan
- [ ] Indexado en registry.db
+109
View File
@@ -0,0 +1,109 @@
# 0007d — Scheduler: cron parser y ticker
## Metadata
| Campo | Valor |
|-------|-------|
| **ID** | 0007d |
| **Estado** | pendiente |
| **Prioridad** | media |
| **Tipo** | feature |
## Dependencias
| ID | Título | Estado | Requerido |
|----|--------|--------|-----------|
| 0007a | Funciones core del DAG engine | pendiente | Si |
| 0007c | Execution store | pendiente | Si |
**Bloqueada por:** `#0007a, #0007c`
**Desbloquea:** `#0007e`
---
## Objetivo
Funciones para parsear expresiones cron, calcular proximas ejecuciones, y un ticker que dispara DAGs segun su schedule. Es lo que reemplaza el scheduler de Dagu.
## Contexto
- Las expresiones cron de Dagu son estandar (5 campos: min hour dom mon dow)
- El ticker es un loop infinito que cada minuto evalua que DAGs deben lanzarse
- Funciones puras para parseo y calculo, impura solo el ticker
## Arquitectura
```
functions/core/
├── cron_parse.go — NEW: string → CronExpression
├── cron_parse.md
├── cron_next.go — NEW: CronExpression + time → proxima ejecucion
├── cron_next.md
├── cron_match.go — NEW: CronExpression + time → bool (coincide?)
├── cron_match.md
functions/infra/
├── dag_ticker.go — NEW: loop que evalua schedules y lanza DAGs
├── dag_ticker.md
types/core/
├── cron_expression.md — NEW: minute, hour, dom, month, dow (cada uno []int o wildcard)
```
### Patron pure core / impure shell
- `core/``cron_parse`, `cron_next`, `cron_match` son puras
- `infra/``dag_ticker` es impuro (time.Sleep, lanza ejecuciones)
## Tareas
### Fase 1: Tipos
- [ ] **1.1** Definir `CronExpression` — campos parseados con soporte para *, ranges (1-5), lists (1,3,5), intervals (*/5)
### Fase 2: Funciones puras
- [ ] **2.1** `cron_parse` — "0 9 * * *" → CronExpression. Soportar: *, N, N-M, N/M, listas
- [ ] **2.2** `cron_next` — dada una CronExpression y un time.Time, retorna el proximo time.Time que coincide
- [ ] **2.3** `cron_match` — dada una CronExpression y un time.Time, retorna true si coincide (para el ticker)
- [ ] **2.4** Tests exhaustivos: wildcards, ranges, listas, intervalos, edge cases (fin de mes, febrero)
### Fase 3: Ticker
- [ ] **3.1** `dag_ticker` — recibe lista de (DagDefinition, path), cada minuto evalua cron_match para cada uno, lanza los que coinciden
- [ ] **3.2** Soporte para cancelacion (context.Context) y graceful shutdown
### Fase 4: Cleanup
- [ ] `fn index` y verificar IDs
---
## Ejemplo de uso
```go
// Puro
expr, _ := cron_parse("*/5 9-17 * * 1-5") // cada 5 min, 9-17h, lun-vie
next := cron_next(expr, time.Now()) // proxima ejecucion
matches := cron_match(expr, time.Now()) // true si ahora coincide
// Impuro (el ticker)
ctx, cancel := context.WithCancel(context.Background())
dag_ticker(ctx, dags, executor) // loop infinito hasta cancel
```
## Decisiones de diseno
- **No usar libreria cron externa**: las expresiones son simples, implementar desde cero es ~100 lineas y evita dependencias
- **Separar parse/next/match**: parse es costoso, match es barato — parsear una vez, match cada minuto
- **Ticker como funcion, no como goroutine**: el caller decide como lanzarlo
## Criterios de aceptacion
- [ ] Parsea todas las expresiones cron de los DAGs existentes en `~/dagu/dags/`
- [ ] `cron_next` calcula correctamente la proxima ejecucion
- [ ] `cron_match` coincide correctamente para el minuto actual
- [ ] Ticker lanza DAGs en el momento correcto
- [ ] Tests pasan
- [ ] Indexado en registry.db
+143
View File
@@ -0,0 +1,143 @@
# 0007e — DAG executor app: CLI/TUI que reemplaza Dagu
## Metadata
| Campo | Valor |
|-------|-------|
| **ID** | 0007e |
| **Estado** | pendiente |
| **Prioridad** | alta |
| **Tipo** | feature |
## Dependencias
| ID | Título | Estado | Requerido |
|----|--------|--------|-----------|
| 0007a | Funciones core del DAG engine | pendiente | Si |
| 0007b | Process manager | pendiente | Si |
| 0007c | Execution store | pendiente | Si |
| 0007d | Scheduler | pendiente | Si |
**Bloqueada por:** `#0007a, #0007b, #0007c, #0007d`
**Desbloquea:** ninguna
---
## Objetivo
App que compone todas las funciones de 0007a-d en un ejecutable unico que reemplaza a Dagu: lee DAGs YAML, los ejecuta con dependencias, persiste estado, y opcionalmente corre como daemon con scheduler.
## Contexto
- Vive en `apps/dag_engine/` (es una app, no una funcion reutilizable)
- Lee DAGs del directorio `~/dagu/dags/` (o configurable)
- El executor es el nucleo: toma un DagDefinition, lanza steps en orden topologico, gestiona paralelismo
- Modos: `run` (ejecuta un DAG), `start` (daemon con scheduler), `status` (consulta ejecuciones), `list` (lista DAGs)
## Arquitectura
```
apps/dag_engine/
├── app.md — metadata del registry
├── main.go — CLI: subcomandos run/start/status/list
├── executor.go — compone dag_topo_sort + process_spawn/wait + store
├── server.go — (futuro) HTTP API para trigger remoto
├── go.mod
├── .gitignore
```
### Patron pure core / impure shell
- `core/` — ya creadas en 0007a y 0007d (funciones puras del registry)
- `infra/` — ya creadas en 0007b y 0007c (funciones impuras del registry)
- `app/``executor.go` compone todo, `main.go` orquesta
## Tareas
### Fase 1: Executor
- [ ] **1.1** `executor.go` — funcion `ExecuteDAG(dag DagDefinition, store DB) DagRun`
- Crea run en store
- Resuelve env
- Ordena steps (topo sort)
- Ejecuta nivel por nivel: steps del mismo nivel van en paralelo (goroutines)
- Cada step: spawn → wait → guarda result en store
- Si un step falla: cancela dependientes, marca run como failed
- Retorna DagRun con resultado final
### Fase 2: CLI
- [ ] **2.1** `fn-dag run <path.yaml>` — parsea, valida, ejecuta, muestra resultado
- [ ] **2.2** `fn-dag list [dir]` — lista DAGs con su schedule y ultimo estado
- [ ] **2.3** `fn-dag status [dag_name]` — ultimas ejecuciones, detalle de steps
- [ ] **2.4** `fn-dag start [dir]` — daemon: carga todos los DAGs, arranca ticker
### Fase 3: Integracion
- [ ] **3.1** `app.md` con uses_functions referenciando todas las funciones de 0007a-d
- [ ] **3.2** `operations.db` inicializado (fn ops init)
- [ ] **3.3** Publicar en Gitea (dataforge/dag_engine)
### Fase 4: Tests e2e
- [ ] **4.1** Ejecutar DAGs existentes de `~/dagu/dags/` y comparar resultado con Dagu
- [ ] **4.2** Test: DAG con steps paralelos, DAG con fallo en medio, DAG con timeout
### Fase 5: Cleanup
- [ ] `fn index`
- [ ] Actualizar CLAUDE.md con documentacion del dag engine
---
## Ejemplo de uso
```bash
# Ejecutar un DAG
fn-dag run ~/dagu/dags/example.yaml
# Step hello... done (0.1s)
# Step list_files... done (0.2s)
# Step date... done (0.1s)
# Run completed: 3/3 steps succeeded (0.4s)
# Listar DAGs
fn-dag list ~/dagu/dags/
# NAME SCHEDULE LAST RUN STATUS
# example 0 9 * * * 2026-04-07 success
# example_lineage_tracking 0 */6 * * * 2026-04-08 failed
# Ver estado
fn-dag status example
# RUN_ID STARTED STATUS STEPS
# 01HXZ... 2026-04-08 09:00:01 success 3/3
# 01HXY... 2026-04-07 09:00:00 success 3/3
# Daemon con scheduler
fn-dag start ~/dagu/dags/
# [09:00] Scheduler started. Watching 5 DAGs.
# [09:00] Triggered: example (schedule match)
# ...
```
## Decisiones de diseno
- **Un binario, no un servicio**: `fn-dag run` es fire-and-forget. `fn-dag start` es el unico modo daemon.
- **Paralelismo por niveles**: steps en el mismo nivel topologico corren en goroutines, no hay limite de concurrencia (por ahora)
- **Compatible con DAGs de Dagu**: lee el mismo formato YAML, no requiere migracion
- **Sin web UI por ahora**: la TUI y/o web UI es un issue futuro, el CLI cubre el 80% del uso
## Riesgos
- **Riesgo**: DAGs complejos de Dagu usan features que no implementamos (preconditions, params, mail on failure). **Mitigacion**: empezar con el subset que usamos, documentar que no se soporta.
- **Riesgo**: Race conditions en el executor paralelo. **Mitigacion**: cada goroutine tiene su propio ProcessHandle, el store usa transacciones SQLite.
## Criterios de aceptacion
- [ ] `fn-dag run` ejecuta correctamente los DAGs existentes
- [ ] Steps paralelos se ejecutan concurrentemente
- [ ] Fallos en un step cancelan dependientes
- [ ] Estado se persiste en SQLite
- [ ] `fn-dag start` corre como daemon con scheduler
- [ ] App registrada en registry.db e indexada
- [ ] Publicada en Gitea
+15
View File
@@ -0,0 +1,15 @@
# Issues
| ID | Título | Estado | Prioridad | Tipo | Bloquea |
|----|--------|--------|-----------|------|---------|
| 0001 | Jupyter create notebook | completado | — | feature | — |
| 0002 | Jupyter discover root dir | completado | — | bugfix | — |
| 0003 | Jupyter tools documentation | completado | — | docs | — |
| 0004 | Jupyter discover multiple instances | completado | — | feature | — |
| 0005 | Jupyter write batch | completado | — | feature | — |
| 0006 | Jupyter exec outputs keyerror | completado | — | bugfix | — |
| **0007a** | **DAG engine: core (parse, validate, topo sort)** | pendiente | alta | feature | 0007b-e |
| **0007b** | **DAG engine: process manager (spawn, wait, kill)** | pendiente | alta | feature | 0007e |
| **0007c** | **DAG engine: execution store (SQLite)** | pendiente | alta | feature | 0007e |
| **0007d** | **DAG engine: scheduler (cron parser, ticker)** | pendiente | media | feature | 0007e |
| **0007e** | **DAG engine: app CLI que reemplaza Dagu** | pendiente | alta | feature | — |
+40 -3
View File
@@ -1,6 +1,6 @@
[Window][WindowOverViewport_11111111]
Pos=0,0
Size=1400,900
Size=1600,968
Collapsed=0
[Window][Debug##Default]
@@ -13,6 +13,43 @@ Pos=45,133
Size=1260,514
Collapsed=0
[Docking][Data]
DockSpace ID=0x08BD597D Window=0x1BBC0F80 Pos=0,0 Size=1400,900 CentralNode=1 Selected=0x2DADAD08
[Window][fn_registry Dashboard]
Pos=84,64
Size=1440,871
Collapsed=0
[Table][0x3D1838B3,7]
RefScale=13
Column 0 Width=126 Sort=0v
Column 1 Width=40
Column 2 Width=54
Column 3 Width=63
Column 4 Width=54
Column 5 Width=54
Column 6 Width=70
[Table][0x736EAF20,5]
RefScale=13
Column 0 Width=119 Sort=0v
Column 1 Width=40
Column 2 Width=63
Column 3 Width=75
Column 4 Width=1002
[Table][0xC80217F1,3]
RefScale=13
Column 0 Width=133 Sort=0v
Column 1 Width=54
Column 2 Width=477
[Table][0x393CD6C2,5]
RefScale=13
Column 0 Width=182 Sort=0v
Column 1 Width=40
Column 2 Width=91
Column 3 Width=75
Column 4 Width=2710
[Docking][Data]
DockSpace ID=0x08BD597D Window=0x1BBC0F80 Pos=0,0 Size=1600,968 CentralNode=1 Selected=0x22F01560
BIN
View File
Binary file not shown.