Merge _configs/ into config/ for single configuration directory. Update all path references. Changes: - Move _configs/* to config/ - Update .gitignore for new patterns - No code references to _configs/ found Impact: -1 root directory (layout_conventions.md compliance)
25 KiB
🚀 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
# 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
# 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
# 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
# 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
# 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:
# 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:
{
"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
{# 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
{# 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
{# 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
# 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
#!/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
// En syntaxis-installer
fn load_deployment() -> Result<Deployment> {
// 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<String> {
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
// 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)
# 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.kgenera 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.