# 🚀 PLAN DE IMPLEMENTACIÓN CORRECTO - KCL Modular en Syntaxis **Fecha**: 2025-11-20 **Estado**: Listo para implementación **Basado en**: ARCHITECTURE_CORRECT_FINAL.md --- ## 📋 Resumen Ejecutivo Crearemos 5 módulos KCL en `syntaxis/configs/` que definan completamente el proyecto: - Validación nativa - Defaults inteligentes - Herencia de presets - Sin generación almacenada --- ## 🔧 Fase 1: Módulos KCL (5 archivos) ### Paso 1.1: Crear `syntaxis/configs/schemas.k` **Archivo**: `/Users/Akasha/Development/syntaxis/configs/schemas.k` ```kcl # syntaxis/configs/schemas.k # Definiciones de tipos y esquemas con validación nativa import regex # Service Schema schema Service: """Define un servicio de syntaxis""" name: str type: "binary" | "service" | "database" # Validación: port requerido si type es service port?: int check: port is not None if type == "service", "Service debe tener port" check: 1 <= port <= 65535 if port is not None, "Port debe estar entre 1-65535" enabled: bool = False requires: [str] = [] # Configuración operativa restart_policy: "always" | "on-failure" | "never" = "on-failure" restart_delay_seconds: int = 5 # Health check health_check?: { type: "http" | "tcp" | "script" endpoint?: str interval_seconds: int = 10 timeout_seconds: int = 5 } # Database Schema schema Database: """Configuración de base de datos""" type: "sqlite" | "postgres" | "mysql" | "surrealdb" # Validación por tipo path?: str check: path is not None if type == "sqlite", "SQLite requiere path" host?: str = "localhost" check: host is not None if type != "sqlite", "BD remota requiere host" port?: int user?: str = "syntaxis" password?: str # Cache Schema schema Cache: """Configuración de caché""" enabled: bool = False type: "redis" | "memcached" = "redis" host: str = "localhost" port: int = 6379 # Preset Schema schema Preset: """Define un preset de deployment""" name: str description: str services_enabled: [str] # Overrides específicos del preset database_type?: "sqlite" | "postgres" | "mysql" | "surrealdb" cache_enabled?: bool # Manager para este preset manager: "manual" | "provctl" | "provisioning" = "manual" # Full Deployment Schema schema Deployment: """Configuración completa de deployment""" project: str = "syntaxis" version: str services: {str: Service} presets: {str: Preset} default_database: Database default_cache: Cache ``` **Checklist**: - [ ] Archivo creado en `/Users/Akasha/Development/syntaxis/configs/schemas.k` - [ ] KCL compila sin errores: `kcl run schemas.k` - [ ] Contiene 6 schemas (Service, Database, Cache, Preset, Deployment, más validaciones) --- ### Paso 1.2: Crear `syntaxis/configs/defaults.k` **Archivo**: `/Users/Akasha/Development/syntaxis/configs/defaults.k` ```kcl # syntaxis/configs/defaults.k # Valores por defecto y constraints reutilizables import .schemas # Servicios por defecto (basados en services-catalog.toml) default_services: {str: schemas.Service} = { cli = schemas.Service { name = "syntaxis-cli" display_name = "syntaxis CLI" description = "Command-line interface for project management" type = "cli" enabled = True platform_support = ["linux", "macos", "windows"] min_disk_space_mb = 50 min_memory_mb = 64 background_service = False config_location = "~/.config/syntaxis" database_required = True database_types = ["sqlite", "surrealdb"] } tui = schemas.Service { name = "syntaxis-tui" display_name = "syntaxis TUI" description = "Terminal UI for interactive project management" type = "tui" enabled = False requires = ["syntaxis-cli"] platform_support = ["linux", "macos"] min_disk_space_mb = 50 min_memory_mb = 128 background_service = False config_location = "~/.config/syntaxis" database_required = True database_types = ["sqlite", "surrealdb"] } api = schemas.Service { name = "syntaxis-api" display_name = "syntaxis REST API" description = "REST API server for programmatic access to syntaxis" type = "server" port = 3000 enabled = False requires = ["syntaxis-cli"] optional = ["surrealdb"] platform_support = ["linux", "macos", "windows"] min_disk_space_mb = 100 min_memory_mb = 256 background_service = True config_location = "~/.config/syntaxis" database_required = True database_types = ["sqlite", "surrealdb"] health_check = { type = "http" endpoint = "http://127.0.0.1:3000/health" method = "GET" expected_status = 200 interval_seconds = 10 timeout_seconds = 5 } } dashboard = schemas.Service { name = "syntaxis-dashboard" display_name = "syntaxis Dashboard" description = "Web-based dashboard UI for project management" type = "web" port = 8080 enabled = False requires = ["syntaxis-api"] platform_support = ["linux", "macos", "windows"] min_disk_space_mb = 50 min_memory_mb = 128 background_service = True config_location = "~/.config/syntaxis" database_required = False health_check = { type = "http" endpoint = "http://127.0.0.1:8080" method = "GET" expected_status = 200 interval_seconds = 10 timeout_seconds = 5 } } surrealdb = schemas.Service { name = "surrealdb" display_name = "SurrealDB" description = "Multi-model document database" type = "database" port = 8000 enabled = False platform_support = ["linux", "macos"] min_disk_space_mb = 100 min_memory_mb = 256 background_service = True config_location = "~/.config/syntaxis" database_required = False health_check = { type = "http" endpoint = "http://127.0.0.1:8000" method = "GET" expected_status = 200 interval_seconds = 10 timeout_seconds = 5 } } nats = schemas.Service { name = "nats" display_name = "NATS" description = "Cloud-native messaging platform" type = "messaging" port = 4222 enabled = False platform_support = ["linux", "macos"] min_disk_space_mb = 50 min_memory_mb = 256 background_service = True config_location = "~/.config/syntaxis" database_required = False health_check = { type = "http" endpoint = "http://127.0.0.1:8222/healthz" method = "GET" expected_status = 200 interval_seconds = 10 timeout_seconds = 5 } } } # Bases de datos por defecto default_databases: {str: schemas.Database} = { sqlite = schemas.Database { type = "sqlite" path = "/var/lib/syntaxis/db.sqlite" } postgres = schemas.Database { type = "postgres" host = "localhost" port = 5432 user = "syntaxis" } surrealdb = schemas.Database { type = "surrealdb" host = "localhost" port = 8000 } } # Caché por defecto default_cache: schemas.Cache = schemas.Cache { enabled = False type = "redis" host = "localhost" port = 6379 } # Presets por defecto (basados en patterns de services-catalog.toml) base_presets: {str: schemas.Preset} = { local = schemas.Preset { name = "local" description = "CLI Only - Command-line only, no services" services_enabled = ["cli"] manager = "manual" } dev = schemas.Preset { name = "dev" description = "Development with API - Full development stack" services_enabled = ["cli", "tui", "api", "dashboard", "surrealdb"] manager = "provctl" database_type = "sqlite" cache_enabled = False } staging = schemas.Preset { name = "staging" description = "Staging environment - Multi-machine deployment" services_enabled = ["cli", "api", "dashboard", "surrealdb", "nats"] manager = "provisioning" database_type = "postgres" cache_enabled = True } production = schemas.Preset { name = "production" description = "Production - Minimal production deployment" services_enabled = ["api", "surrealdb", "nats"] manager = "provisioning" database_type = "postgres" cache_enabled = True } } ``` **Checklist**: - [ ] Archivo creado en `/Users/Akasha/Development/syntaxis/configs/defaults.k` - [ ] KCL compila: `kcl run defaults.k` (import de schemas.k debe funcionar) - [ ] Contiene 5 servicios, 3 BDs, 1 caché, 4 presets --- ### Paso 1.3: Crear `syntaxis/configs/services.k` **Archivo**: `/Users/Akasha/Development/syntaxis/configs/services.k` ```kcl # syntaxis/configs/services.k # Definiciones de servicios (usa defaults, permite personalización) import .schemas import .defaults # Servicios con defaults aplicados services: {str: schemas.Service} = defaults.default_services ``` **Checklist**: - [ ] Archivo creado en `/Users/Akasha/Development/syntaxis/configs/services.k` - [ ] KCL compila sin errores **Nota**: Este archivo permite personalizar servicios en el futuro sin afectar defaults. --- ### Paso 1.4: Crear `syntaxis/configs/presets.k` **Archivo**: `/Users/Akasha/Development/syntaxis/configs/presets.k` ```kcl # syntaxis/configs/presets.k # Definiciones de presets con capacidad de override import .schemas import .defaults import .services # Presets con defaults presets: {str: schemas.Preset} = { local = defaults.base_presets.local dev = defaults.base_presets.dev { # Puede refinarse aquí si necesita overrides específicos } staging = defaults.base_presets.staging { # Overrides específicos si es necesario } production = defaults.base_presets.production { # Overrides específicos si es necesario } } ``` **Checklist**: - [ ] Archivo creado en `/Users/Akasha/Development/syntaxis/configs/presets.k` - [ ] KCL compila sin errores --- ### Paso 1.5: Crear `syntaxis/configs/deployment.k` **Archivo**: `/Users/Akasha/Development/syntaxis/configs/deployment.k` ```kcl # syntaxis/configs/deployment.k # Declaración principal que integra todo import .schemas import .services import .presets import .defaults # Deployment principal deployment: schemas.Deployment = schemas.Deployment { project = "syntaxis" version = "1.0.0" services = services presets = presets default_database = defaults.default_databases.sqlite default_cache = defaults.default_cache } # Export principales para consumidores export { deployment services presets default_databases = defaults.default_databases default_cache = defaults.default_cache } ``` **Checklist**: - [ ] Archivo creado en `/Users/Akasha/Development/syntaxis/configs/deployment.k` - [ ] KCL compila y exporta: `kcl run deployment.k` - [ ] Export contiene deployment, services, presets, databases, cache --- ## 🔄 Validación de Módulos KCL Después de crear los 5 archivos: ```bash # Test 1: Compilación de cada módulo kcl run syntaxis/configs/schemas.k kcl run syntaxis/configs/defaults.k kcl run syntaxis/configs/services.k kcl run syntaxis/configs/presets.k kcl run syntaxis/configs/deployment.k # Test 2: Export a JSON kcl run syntaxis/configs/deployment.k --format json > /tmp/deployment.json # Test 3: Validar output cat /tmp/deployment.json | jq '.deployment' ``` **Resultado esperado**: ```json { "deployment": { "project": "syntaxis", "version": "1.0.0", "services": { "cli": { "name": "syntaxis-cli", "type": "binary", "enabled": true }, "api": { "name": "syntaxis-api", "type": "service", "port": 3000, ... }, ... }, "presets": { "local": { "name": "local", ... }, "dev": { "name": "dev", ... }, ... } } } ``` --- ## 🔧 Fase 2: Templates Tera (3 archivos) ### Paso 2.1: Crear `syntaxis/templates/provisioning-config.j2` **Archivo**: `/Users/Akasha/Development/syntaxis/templates/provisioning-config.j2` ```jinja2 {# syntaxis/templates/provisioning-config.j2 #} {# Genera configuración para provisioning desde KCL #} # Configuración Syntaxis para provisioning # Generado desde syntaxis/configs/deployment.k project: name = "{{ deployment.project }}" version = "{{ deployment.version }}" services: {% for name, service in deployment.services %} {% if service.enabled %} "{{ name }}": name = "{{ service.name }}" type = "{{ service.type }}" {% if service.port %} port = {{ service.port }} {% endif %} restart_policy = "{{ service.restart_policy }}" requires = [ {% for req in service.requires %}"{{ req }}"{{ "," if not loop.last else "" }}{% endfor %} ] {% if service.health_check %} health_check = { type = "{{ service.health_check.type }}" {% if service.health_check.endpoint %} endpoint = "{{ service.health_check.endpoint }}" {% endif %} interval_seconds = {{ service.health_check.interval_seconds }} timeout_seconds = {{ service.health_check.timeout_seconds }} } {% endif %} {% endif %} {% endfor %} database: type = "{{ deployment.default_database.type }}" {% if deployment.default_database.host %} host = "{{ deployment.default_database.host }}" {% endif %} {% if deployment.default_database.port %} port = {{ deployment.default_database.port }} {% endif %} {% if deployment.default_database.path %} path = "{{ deployment.default_database.path }}" {% endif %} cache: enabled = {{ deployment.default_cache.enabled | lower }} {% if deployment.default_cache.enabled %} type = "{{ deployment.default_cache.type }}" host = "{{ deployment.default_cache.host }}" port = {{ deployment.default_cache.port }} {% endif %} ``` **Checklist**: - [ ] Archivo creado en `/Users/Akasha/Development/syntaxis/templates/provisioning-config.j2` - [ ] Contiene loop sobre servicios, databases, cache --- ### Paso 2.2: Crear `syntaxis/templates/provctl-config.j2` **Archivo**: `/Users/Akasha/Development/syntaxis/templates/provctl-config.j2` ```jinja2 {# syntaxis/templates/provctl-config.j2 #} {# Genera TOML para provctl #} # Configuración Syntaxis para provctl # Generado desde syntaxis/configs/deployment.k {% for name, service in deployment.services %} {% if service.enabled %} [[services]] name = "{{ service.name }}" type = "{{ service.type }}" {% if service.port %} port = {{ service.port }} {% endif %} restart_policy = "{{ service.restart_policy }}" {% if service.health_check %} [services.health_check] type = "{{ service.health_check.type }}" {% if service.health_check.endpoint %} endpoint = "{{ service.health_check.endpoint }}" {% endif %} interval_seconds = {{ service.health_check.interval_seconds }} timeout_seconds = {{ service.health_check.timeout_seconds }} {% endif %} {% endif %} {% endfor %} [database] type = "{{ deployment.default_database.type }}" {% if deployment.default_database.host %} host = "{{ deployment.default_database.host }}" {% endif %} {% if deployment.default_database.port %} port = {{ deployment.default_database.port }} {% endif %} {% if deployment.default_database.path %} path = "{{ deployment.default_database.path }}" {% endif %} [cache] enabled = {{ deployment.default_cache.enabled | lower }} {% if deployment.default_cache.enabled %} type = "{{ deployment.default_cache.type }}" host = "{{ deployment.default_cache.host }}" port = {{ deployment.default_cache.port }} {% endif %} ``` **Checklist**: - [ ] Archivo creado en `/Users/Akasha/Development/syntaxis/templates/provctl-config.j2` - [ ] Genera TOML válido --- ### Paso 2.3: Crear `syntaxis/templates/installer-settings.j2` **Archivo**: `/Users/Akasha/Development/syntaxis/templates/installer-settings.j2` ```jinja2 {# syntaxis/templates/installer-settings.j2 #} {# Genera JSON/YAML para installer interactivo #} { "preset": "{{ preset }}", "description": "{{ preset_description }}", "services": [ {% for service in services_enabled %} "{{ service }}"{{ "," if not loop.last else "" }} {% endfor %} ], "database": { "type": "{{ database_type }}" {% if database_type == "postgres" %} ,"host": "{{ database_host | default("localhost") }}", "port": {{ database_port | default(5432) }}, "user": "{{ database_user | default("syntaxis") }}" {% elif database_type == "sqlite" %} ,"path": "{{ database_path | default("/var/lib/syntaxis/db.sqlite") }}" {% elif database_type == "surrealdb" %} ,"host": "{{ database_host | default("localhost") }}", "port": {{ database_port | default(8000) }} {% endif %} }, "cache": { "enabled": {{ cache_enabled | lower }} {% if cache_enabled %} ,"type": "{{ cache_type | default("redis") }}", "host": "{{ cache_host | default("localhost") }}", "port": {{ cache_port | default(6379) }} {% endif %} } } ``` **Checklist**: - [ ] Archivo creado en `/Users/Akasha/Development/syntaxis/templates/installer-settings.j2` - [ ] Genera JSON válido --- ## 🔧 Fase 3: Script NuShell (1 archivo) ### Paso 3.1: Crear `syntaxis/scripts/export-config.nu` **Archivo**: `/Users/Akasha/Development/syntaxis/scripts/export-config.nu` ```nushell # syntaxis/scripts/export-config.nu # Exporta KCL a YAML/JSON on-demand (sin guardar) # Función principal: exportar deployment def export-deployment [format: string = "yaml"] -> string { """ Exporta deployment.k a YAML o JSON Ejemplos: export-deployment # YAML por defecto export-deployment --format json # Exporta como JSON """ # 1. Llamar kcl compile para obtener resultado let kcl_output = ( kcl run ([$env.PWD, "configs", "deployment.k"] | path join) --format json | from json ) # 2. Convertir según formato requerido match $format { "json" => { $kcl_output | to json } "yaml" => { # Convertir JSON a YAML $kcl_output | to json | from json | to yaml } _ => { error make { msg: "Formato no soportado: $format (usa 'json' o 'yaml')" } } } } # Función: obtener preset específico def get-preset [preset: string] -> string { """ Obtiene un preset específico en formato YAML Ejemplo: get-preset dev """ let deployment = (export-deployment yaml | from yaml) if ($preset not-in $deployment.deployment.presets) { error make { msg: "Preset '$preset' no existe" } } $deployment.deployment.presets | get $preset | to yaml } # Función: listar servicios de preset def get-services [preset: string] -> list { """ Lista servicios habilitados en un preset Ejemplo: get-services dev """ let deployment = (export-deployment yaml | from yaml) $deployment.deployment.presets | get $preset | get services_enabled } # Exportar comandos export def "config export" [format: string = "yaml"] { """Export deployment configuration to YAML or JSON""" export-deployment $format } export def "config preset" [preset: string] { """Get a specific preset configuration""" get-preset $preset } export def "config services" [preset: string] { """List services enabled for a preset""" get-services $preset } ``` **Checklist**: - [ ] Archivo creado en `/Users/Akasha/Development/syntaxis/scripts/export-config.nu` - [ ] Script es ejecutable: `chmod +x syntaxis/scripts/export-config.nu` --- ## ✅ Validación Completa ### Test Suite ```bash #!/bin/bash set -e echo "=== Testing KCL Modules ===" # Test 1: Compilación individual echo "1. Testing schemas.k..." kcl run syntaxis/configs/schemas.k > /dev/null echo " ✅ schemas.k compila" echo "2. Testing defaults.k..." kcl run syntaxis/configs/defaults.k > /dev/null echo " ✅ defaults.k compila" echo "3. Testing services.k..." kcl run syntaxis/configs/services.k > /dev/null echo " ✅ services.k compila" echo "4. Testing presets.k..." kcl run syntaxis/configs/presets.k > /dev/null echo " ✅ presets.k compila" echo "5. Testing deployment.k..." kcl run syntaxis/configs/deployment.k > /dev/null echo " ✅ deployment.k compila" # Test 2: Export echo "" echo "=== Testing Exports ===" echo "6. Testing JSON export..." kcl run syntaxis/configs/deployment.k --format json > /tmp/deployment.json echo " ✅ JSON export successful" echo "7. Testing YAML export..." nu syntaxis/scripts/export-config.nu yaml > /tmp/deployment.yaml echo " ✅ YAML export successful" # Test 3: Validación de estructura echo "" echo "=== Validating Structure ===" echo "8. Checking deployment structure..." jq '.deployment | keys' /tmp/deployment.json | grep -q "project\|version\|services\|presets" echo " ✅ Deployment has correct keys" echo "9. Checking services..." jq '.deployment.services | keys' /tmp/deployment.json | grep -q "cli\|api" echo " ✅ Services defined" echo "10. Checking presets..." jq '.deployment.presets | keys' /tmp/deployment.json | grep -q "local\|dev\|staging\|production" echo " ✅ All presets present" echo "" echo "=== ✅ ALL TESTS PASSED ===" ``` **Checklist**: - [ ] Todos los .k compilan sin errores - [ ] JSON export generado correctamente - [ ] YAML export generado correctamente - [ ] Estructura contiene keys esperadas --- ## 🎯 Próximos Pasos Después de Implementación ### Paso 4: Integración con syntaxis-installer ```rust // En syntaxis-installer fn load_deployment() -> Result { // Cargar deployment.k directamente let kcl_output = Command::new("kcl") .arg("run") .arg("configs/deployment.k") .arg("--format") .arg("json") .output()?; let json: Value = serde_json::from_slice(&kcl_output.stdout)?; Ok(serde_json::from_value(json["deployment"].clone())?) } fn get_preset_for_user() -> Result { println!("¿Cómo deseas instalar Syntaxis?"); println!("1. local - CLI only"); println!("2. dev - Full stack + provctl"); println!("3. staging - Multi-machine"); println!("4. production - HA"); // User selecciona... // Si dev: genera config con NuShell + Tera // Si provisioning: guía al usuario } ``` ### Paso 5: Integración con provisioning ```kcl // En provisioning/kcl/workspace.k import /path/to/syntaxis/configs/deployment as sx // Usar declaración de syntaxis syntaxis_deployment = sx.deployment // Validar y adaptar a provisioning // (provisioning puede leer KCL directamente) ``` ### Paso 6: Integración con provctl (via NuShell) ```bash # Durante instalación en modo "dev": # 1. Export KCL a YAML (temporal) nu syntaxis/scripts/export-config.nu yaml > /tmp/deployment.yaml # 2. Usar Tera para adaptar a provctl tera --template syntaxis/templates/provctl-config.j2 \ --input /tmp/deployment.yaml \ > /tmp/provctl-config.toml # 3. Aplicar con provctl provctl config apply /tmp/provctl-config.toml # 4. No guardar los archivos temporales rm /tmp/deployment.yaml /tmp/provctl-config.toml ``` --- ## 📊 Resumen de Archivos a Crear | Archivo | Ubicación | Tipo | Contenido | |---------|-----------|------|----------| | schemas.k | `syntaxis/configs/` | KCL | 6 schemas con validación | | defaults.k | `syntaxis/configs/` | KCL | 5 servicios, 3 BDs, 1 caché, 4 presets | | services.k | `syntaxis/configs/` | KCL | Referencia a defaults | | presets.k | `syntaxis/configs/` | KCL | Override-capable presets | | deployment.k | `syntaxis/configs/` | KCL | Declaración principal + exports | | provisioning-config.j2 | `syntaxis/templates/` | Tera | Config para provisioning | | provctl-config.j2 | `syntaxis/templates/` | Tera | TOML para provctl | | installer-settings.j2 | `syntaxis/templates/` | Tera | JSON para installer | | export-config.nu | `syntaxis/scripts/` | NuShell | On-demand export a YAML/JSON | **Total**: 9 archivos nuevos --- ## ✅ Checklist Final - [ ] Fase 1: 5 módulos KCL creados y compilables - [ ] Fase 2: 3 templates Tera creados - [ ] Fase 3: 1 script NuShell creado - [ ] Validación: Todos los tests pasan - [ ] Integración: syntaxis-installer usa presets - [ ] Integración: provisioning puede leer deployment.k - [ ] Integración: provctl recibe config via NuShell + Tera --- ## 🎓 Conceptos Clave ### KCL como Fuente de Verdad - Módulos independientes pero conectados - Validación compile-time - Defaults inteligentes - Herencia de presets - Versionado con código ### Generación On-Demand - `kcl run deployment.k` genera JSON - NuShell convierte a YAML si es necesario - Tera templates adaptan al consumidor - Archivos temporales, no almacenados ### Consumo Adaptativo - provisioning: lee .k directamente - provctl: recibe YAML via NuShell - installer: parametrizado via templates - Cada uno adapta según necesidad --- **Siguiente paso**: Ejecutar Fase 1 (crear 5 módulos KCL) y validar compilación.