syntaxis/docs/provision/validation-registry-nushell.md
Jesús Pérez 9cef9b8d57 refactor: consolidate configuration directories
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)
2025-12-26 18:36:23 +00:00

1003 lines
25 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 🔍 Validación y Registro con NuShell + KCL
**Fecha**: 2025-11-20
**Clasificación**: Implementación operacional
**Enfoque**: Automatización via NuShell + Leveraging KCL native capabilities
---
## 📋 Tabla de Contenidos
1. [Visión General](#visión-general)
2. [Validación Automática (KCL nativa)](#validación-automática-kcl-nativa)
3. [Scripts NuShell para Validación](#scripts-nushell-para-validación)
4. [Sistema de Registro (Service Discovery)](#sistema-de-registro-service-discovery)
5. [Integración en CI/CD](#integración-en-cicd)
6. [Monitoreo y Alertas](#monitoreo-y-alertas)
---
## 🎯 Visión General
```
KCL proporciona:
├─ Validación de esquemas (tipos)
├─ Detección de ciclos de dependencias
├─ Validación de referencias de servicios
├─ Detección de conflictos de puertos
└─ Validación de recursos (CPU/Memory)
→ TODO esto "on the fly" en compilación
NuShell proporciona:
├─ Orquestación de comandos
├─ Parsing de salidas
├─ CI/CD integration
├─ Generación de reportes
└─ Alertas y notificaciones
```
### Flujo Completo
```
┌──────────────────────────────────────────────┐
│ Cambio en services.k (developer comitea) │
└──────────────────────────────────────────────┘
↓ (git push)
┌──────────────────────────────────────────────┐
│ GitHub Actions / GitLab CI │
└──────────────────────────────────────────────┘
validate-catalog.nu
┌─────────────────────────────────────────┐
│ 1. KCL type-check │
│ └─ Detecta errores de esquema │
│ │
│ 2. Ciclos de dependencias │
│ └─ KCL políticas (policies.k) │
│ │
│ 3. Conflictos de puertos │
│ └─ Script NuShell analiza │
│ │
│ 4. Referencias válidas │
│ └─ KCL valida existencia │
│ │
│ 5. Cambios compatibles │
│ └─ Script compara con versión anterior
└─────────────────────────────────────────┘
¿VÁLIDO?
↙ ↘
SÍ NO
↓ ↓
✅ ❌ Reject + Notificar
Deploy
```
---
## ⚡ Validación Automática (KCL nativa)
### 1. Qué Valida KCL Automáticamente
```kcl
# El compilador de KCL RECHAZA automáticamente:
# ❌ Tipo incorrecto
service = {
id: 123 # ERROR: Esperaba str, recibió int
}
# ❌ Campo requerido faltante
service = {
id: "api"
# ERROR: name es requerido
}
# ❌ Referencia a servicio inexistente
depends_on: ["service-que-no-existe"] # ERROR en validación
# ❌ Puerto duplicado en mismo host
ports: [
{port: 3000},
{port: 3000} # ERROR: Puerto duplicado
]
# ❌ Ciclo de dependencias
service_a = { depends_on: ["service_b"] }
service_b = { depends_on: ["service_a"] } # ERROR: Ciclo detectado
```
### 2. Política de Validación: No Ciclos
```kcl
# policies.k - Validación nativa
# Schema base que define estructura y tipos
Service = {
id: str
name: str
depends_on: [str]
port: int
# ... más campos
}
# Función para detectar ciclos
validate_no_dependency_cycles = lambda services {
"""
KCL aplica esta validación automáticamente
al compilar las definiciones de servicios
"""
def has_cycle(service_id, visited, rec_stack, graph):
visited[service_id] = True
rec_stack[service_id] = True
for dep in graph.get(service_id, []):
if dep not in visited:
if has_cycle(dep, visited, rec_stack, graph):
return True
elif rec_stack.get(dep):
return True
rec_stack[service_id] = False
return False
# Construir grafo de dependencias
graph = {s.id: s.depends_on for s in services}
# Validar cada servicio
for service_id in graph:
assert not has_cycle(service_id, {}, {}, graph), \
"Cyclic dependency detected in service: {}".format(service_id)
}
# Validar que referencias existen
validate_all_dependencies_exist = lambda services {
service_ids = {s.id for s in services}
for service in services:
for dep_id in service.depends_on:
assert dep_id in service_ids, \
"Service '{}' depends on non-existent service '{}'".format(
service.id, dep_id
)
}
# Validar puertos sin conflicto
validate_no_port_conflicts = lambda services {
port_to_service = {}
for service in services:
for port in service.ports:
port_num = port.port
assert port_num not in port_to_service, \
"Port {} used by both '{}' and '{}'".format(
port_num,
port_to_service[port_num],
service.id
)
port_to_service[port_num] = service.id
}
# Validar restricciones de recursos
validate_resource_constraints = lambda services {
# CPU/Memory deben cumplir formato y restricciones
for service in services:
assert is_valid_cpu_format(service.resources.cpu), \
"Invalid CPU format: {}".format(service.resources.cpu)
assert is_valid_memory_format(service.resources.memory), \
"Invalid memory format: {}".format(service.resources.memory)
}
```
### 3. Ejecución: KCL Valida en Compilación
```bash
# KCL compila y valida automáticamente
$ kcl compile services.k
# Compila exitosamente SOLO si:
# ✅ Todos los tipos son correctos
# ✅ No hay ciclos de dependencias
# ✅ Todas las referencias existen
# ✅ No hay conflictos de puertos
# ✅ Recursos tienen formato válido
# Si hay error:
$ kcl compile services.k
Error: Service 'api' depends on non-existent service 'db'
at services.k:42:15
# El error previene deployment automáticamente
```
---
## 🔧 Scripts NuShell para Validación
### 1. validate-catalog.nu - Script Principal
```nushell
#!/usr/bin/env nu
# Script de validación completa del catálogo
# Uso: nu validate-catalog.nu [--check-only] [--verbose]
# Configuración
const KCL_PATH = "provisioning/services.k"
const PATTERNS_PATH = "provisioning/patterns.k"
const POLICIES_PATH = "provisioning/policies.k"
const REPORT_FILE = "validation-report.json"
# Colores
const GREEN = "[32m"
const RED = "[91m"
const YELLOW = "[33m"
const BLUE = "[34m"
const RESET = "[0m"
# Parsear argumentos
let check_only = ($env.COMMAND_LINE? | str contains "--check-only")
let verbose = ($env.COMMAND_LINE? | str contains "--verbose")
print $"($BLUE)🔍 Iniciando validación del catálogo($RESET)"
print ""
# 1. VALIDACIÓN KCL: Type-checking
print $"($BLUE)1⃣ Validación de tipos KCL($RESET)"
try {
let kcl_output = (kcl compile $KCL_PATH 2>&1 | str trim)
if ($kcl_output =~ "Error:") {
print $"($RED)✗ Error de compilación KCL($RESET)"
print $kcl_output
exit 1
} else {
print $"($GREEN)✓ Tipos KCL válidos($RESET)"
}
} catch {
print $"($RED)✗ KCL no está instalado o error en compilación($RESET)"
exit 1
}
print ""
# 2. VALIDACIÓN: Ciclos de Dependencias
print $"($BLUE)2⃣ Validación: Ciclos de dependencias($RESET)"
let dep_check = (nu scripts/check-dependencies.nu $KCL_PATH)
if ($dep_check.has_cycles) {
print $"($RED)✗ Ciclos detectados en dependencias($RESET)"
print $dep_check.cycles
exit 1
} else {
print $"($GREEN)✓ Sin ciclos de dependencias($RESET)"
if $verbose {
print $" Servicios validados: ($dep_check.total_services)"
print $" Dependencias únicas: ($dep_check.total_dependencies)"
}
}
print ""
# 3. VALIDACIÓN: Conflictos de Puertos
print $"($BLUE)3⃣ Validación: Conflictos de puertos($RESET)"
let port_check = (nu scripts/check-ports.nu $KCL_PATH)
if ($port_check.has_conflicts) {
print $"($RED)✗ Conflictos de puertos detectados($RESET)"
print $port_check.conflicts
exit 1
} else {
print $"($GREEN)✓ Sin conflictos de puertos($RESET)"
if $verbose {
print $" Puertos mapeados: ($port_check.total_ports)"
let unique_ports = ($port_check.ports | length)
print $" Puertos únicos: $unique_ports"
}
}
print ""
# 4. VALIDACIÓN: Compatibilidad hacia Atrás
print $"($BLUE)4⃣ Validación: Compatibilidad$(RESET)"
let compat_check = (nu scripts/check-compatibility.nu $KCL_PATH)
if ($compat_check.breaking_changes) {
print $"($YELLOW)⚠️ Breaking changes detectados($RESET)"
print $compat_check.changes
if not $check_only {
print $"($YELLOW)Continuar? (s/n)($RESET)"
let response = (input)
if $response != "s" {
exit 1
}
}
} else {
print $"($GREEN)✓ Compatible con versión anterior($RESET)"
}
print ""
# 5. VALIDACIÓN: Integridad de Patrones
print $"($BLUE)5⃣ Validación: Integridad de patrones($RESET)"
let pattern_check = (nu scripts/check-patterns.nu $PATTERNS_PATH $KCL_PATH)
if ($pattern_check.invalid_patterns | length) > 0 {
print $"($RED)✗ Patrones inválidos($RESET)"
for pattern in $pattern_check.invalid_patterns {
print $" - ($pattern.id): ($pattern.error)"
}
exit 1
} else {
print $"($GREEN)✓ Todos los patrones válidos($RESET)"
if $verbose {
print $" Total de patrones: ($pattern_check.total_patterns)"
}
}
print ""
# 6. GENERACIÓN DE REPORTE
print $"($BLUE)6⃣ Generando reporte($RESET)"
let report = {
timestamp: (date now),
status: "passed",
validations: {
kcl_types: {status: "passed", message: "Tipos KCL válidos"}
dependencies: {status: "passed", cycles: 0, total: $dep_check.total_dependencies}
ports: {status: "passed", conflicts: 0, total: $port_check.total_ports}
compatibility: {status: ($compat_check.breaking_changes | if . then "warning" else "passed" end)}
patterns: {status: "passed", total: $pattern_check.total_patterns}
}
}
$report | to json | save --force $REPORT_FILE
print $"($GREEN)✓ Reporte guardado en ($REPORT_FILE)($RESET)"
print ""
print $"($GREEN)═══════════════════════════════════════($RESET)"
print $"($GREEN)✅ Validación completada exitosamente($RESET)"
print $"($GREEN)═══════════════════════════════════════($RESET)"
print ""
# Salir con código de éxito
exit 0
```
### 2. check-dependencies.nu - Análisis de Dependencias
```nushell
#!/usr/bin/env nu
# Analizar ciclos de dependencias en servicios
# Uso: nu check-dependencies.nu services.k
let kcl_file = $in
# Extraer servicios y dependencias de KCL
# Esto analiza la salida compilada de KCL
let services = (
kcl compile $kcl_file 2>&1
| grep "service"
| each { |line|
let parts = ($line | split column "=" | transpose)
{
id: ($parts.0.column1 | str trim)
depends_on: ($parts.1.column1 | str trim | split row "," | each { |d| $d | str trim })
}
}
)
# Implementar detección de ciclos (algoritmo DFS)
def has_cycle [graph, node, visited, rec_stack] {
$visited | upsert ($node | tostring) true
$rec_stack | upsert ($node | tostring) true
let neighbors = ($graph | get $node)
for neighbor in $neighbors {
if not ($visited | has ($neighbor | tostring)) {
if (has_cycle $graph $neighbor $visited $rec_stack) {
return true
}
} else if ($rec_stack | get ($neighbor | tostring)) {
return true
}
}
$rec_stack | upsert ($node | tostring) false
false
}
# Construir grafo y detectar ciclos
let graph = (
$services
| reduce .[] as $service (
{};
. + {($service.id): $service.depends_on}
)
)
let cycles = ($services | filter { |s|
has_cycle $graph $s.id {} {}
})
{
has_cycles: ($cycles | length) > 0
cycles: $cycles
total_services: ($services | length)
total_dependencies: (
$services
| map { |s| $s.depends_on | length }
| math sum
)
}
```
### 3. check-ports.nu - Análisis de Puertos
```nushell
#!/usr/bin/env nu
# Detectar conflictos de puertos
# Uso: nu check-ports.nu services.k
let kcl_file = $in
# Extraer puertos de KCL compilado
let ports = (
kcl compile $kcl_file 2>&1
| grep -E "port:\s*[0-9]+"
| parse "{service}: {port}"
| each {
let port_num = ($in.port | into int)
{
service: $in.service
port: $port_num
}
}
)
# Detectar conflictos
let grouped = (
$ports
| group-by { |p| $p.port }
)
let conflicts = (
$grouped
| filter { |group| ($group | length) > 1 }
)
{
has_conflicts: ($conflicts | length) > 0
conflicts: (
$conflicts
| map { |group|
{
port: ($group.0.port)
services: ($group | map { |p| $p.service })
}
}
)
total_ports: ($ports | length)
ports: $ports
}
```
### 4. check-patterns.nu - Validación de Patrones
```nushell
#!/usr/bin/env nu
# Validar que patrones referencian servicios existentes
# Uso: nu check-patterns.nu patterns.k services.k
let patterns_file = $in.0
let services_file = $in.1
# Obtener lista de servicios
let valid_services = (
kcl compile $services_file 2>&1
| grep "id:"
| parse '{id: "{service}"}'
| map { |row| $row.service }
)
# Validar cada patrón
let patterns = (
kcl compile $patterns_file 2>&1
| parse '{id: "{pattern_id}", services: [{services}]}'
)
let invalid = (
$patterns
| filter { |pattern|
let pattern_services = (
$pattern.services
| split row ","
| each { |s| $s | str trim }
)
let invalid_services = (
$pattern_services
| filter { |svc| not ($svc in $valid_services) }
)
($invalid_services | length) > 0
}
| map { |pattern|
{
id: $pattern.pattern_id
error: "Contiene referencias a servicios inexistentes"
}
}
)
{
total_patterns: ($patterns | length)
invalid_patterns: $invalid
status: (if ($invalid | length) == 0 { "passed" } else { "failed" })
}
```
### 5. check-compatibility.nu - Cambios hacia Atrás
```nushell
#!/usr/bin/env nu
# Detectar breaking changes respecto a versión anterior
# Uso: nu check-compatibility.nu services.k
let kcl_file = $in
# Obtener versión actual desde git
let current = (kcl compile $kcl_file 2>&1)
# Obtener versión anterior
let previous = (git show HEAD:$kcl_file | kcl compile - 2>&1)
# Detectar cambios
let added_services = (
$current
| grep "id:"
| split row ","
| map { |s| $s | str trim }
)
let removed_services = (
$previous
| grep "id:"
| split row ","
| map { |s| $s | str trim }
| filter { |s| not ($s in $added_services) }
)
let breaking = ($removed_services | length) > 0
{
breaking_changes: $breaking
changes: {
removed_services: $removed_services
summary: if $breaking {
"WARNING: Servicios removidos (breaking change)"
} else {
"No breaking changes"
}
}
}
```
---
## 📦 Sistema de Registro (Service Discovery)
### 1. service-catalog.nu - Registro Principal
```nushell
#!/usr/bin/env nu
# Gestor de catálogo de servicios
# Uso: nu service-catalog.nu [list|ports|groups|patterns]
const KCL_SERVICES_FILE = "provisioning/services.k"
const KCL_PATTERNS_FILE = "provisioning/patterns.k"
def list [] {
"""
Listar todos los servicios disponibles
"""
print "📋 Servicios disponibles:"
print ""
let services = (
kcl compile $KCL_SERVICES_FILE 2>&1
| parse '{id: "{id}", name: "{name}", image: "{image}"}'
)
$services
| each { |s|
print $" • ($s.id) - ($s.name)"
print $" imagen: ($s.image)"
}
}
def ports [] {
"""
Mostrar mapeo de puertos
"""
print "🔌 Mapeo de puertos:"
print ""
let ports = (
kcl compile $KCL_SERVICES_FILE 2>&1
| parse '{service: "{service}", port: {port}}'
)
let sorted = ($ports | sort-by port)
$sorted
| each { |p|
print $" ($p.port) → ($p.service)"
}
}
def groups [] {
"""
Listar grupos de servicios
"""
print "📦 Grupos de servicios:"
print ""
let groups = (
kcl compile $KCL_SERVICES_FILE 2>&1
| grep "group:"
| parse '{group: "{group}", services: [{services}]}'
)
$groups
| each { |g|
print $" ($g.group):"
print $" servicios: ($g.services | str trim)"
}
}
def patterns [] {
"""
Listar patrones de despliegue disponibles
"""
print "🎯 Patrones de despliegue:"
print ""
let patterns = (
kcl compile $KCL_PATTERNS_FILE 2>&1
| parse '{id: "{id}", name: "{name}", environment: "{environment}"}'
)
$patterns
| each { |p|
print $" ($p.id) - ($p.name) [($p.environment)]"
}
}
def search [query: string] {
"""
Buscar servicios por nombre o tag
"""
print $"🔍 Buscando: ($query)"
print ""
let services = (
kcl compile $KCL_SERVICES_FILE 2>&1
| parse '{id: "{id}", name: "{name}", tags: [{tags}]}'
| filter { |s|
($s.id | str contains $query) or
($s.name | str contains $query) or
($s.tags | str contains $query)
}
)
if ($services | length) == 0 {
print " ❌ No se encontraron servicios"
} else {
$services | each { |s|
print $" • ($s.id)"
}
}
}
def dependencies [service: string] {
"""
Mostrar dependencias de un servicio
"""
print $"📍 Dependencias de ($service):"
print ""
let deps = (
kcl compile $KCL_SERVICES_FILE 2>&1
| parse '{id: "{id}", depends_on: [{depends}]}'
| filter { |s| $s.id == $service }
)
if ($deps | length) == 0 {
print " ❌ Servicio no encontrado"
} else {
let depends = ($deps.0.depends | split row "," | each { |d| $d | str trim })
if ($depends | length) == 0 {
print " ✓ Sin dependencias"
} else {
$depends | each { |d| print $" ← ($d)" }
}
}
}
# Parsear argumentos
let action = ($nu.env.ARGS? | get 0 | default "list")
match $action {
"list" => { list }
"ports" => { ports }
"groups" => { groups }
"patterns" => { patterns }
"search" => { search ($nu.env.ARGS?.1 | default "") }
"dependencies" => { dependencies ($nu.env.ARGS?.1 | default "") }
_ => {
print "Uso: nu service-catalog.nu [list|ports|groups|patterns|search|dependencies]"
}
}
```
### 2. Auto-Discovery: Registro Automático
```nushell
#!/usr/bin/env nu
# Auto-discover servicios y registrarlos en cache
# Uso: nu discover-services.nu
const CACHE_FILE = ".syntaxis/service-registry-cache.json"
print "🔍 Auto-discovering servicios..."
# 1. Compilar KCL
let kcl_services = (
kcl compile provisioning/services.k 2>&1
)
# 2. Extraer información
let services = (
$kcl_services
| grep "service:"
| parse '{id: "{id}", port: {port}}'
)
# 3. Enrichir con información adicional
let enriched = (
$services
| each { |service|
let details = (
$kcl_services
| grep $service.id
| parse '{status: "active"}'
)
{
id: $service.id
port: $service.port
status: "active"
discovered_at: (date now)
}
}
)
# 4. Guardar en cache
mkdir -p (.syntaxis)
$enriched | to json | save --force $CACHE_FILE
print $"✅ ($enriched | length) servicios registrados"
print $"📁 Cache guardado en ($CACHE_FILE)"
```
---
## 🔄 Integración en CI/CD
### 1. GitHub Actions Workflow
```yaml
# .github/workflows/validate-catalog.yml
name: Validate Service Catalog
on:
push:
paths:
- 'provisioning/services.k'
- 'provisioning/patterns.k'
- 'provisioning/policies.k'
pull_request:
paths:
- 'provisioning/services.k'
- 'provisioning/patterns.k'
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install NuShell
run: cargo install nushell
- name: Install KCL
run: |
wget https://github.com/kcl-lang/kcl/releases/download/v0.8.0/kcl-v0.8.0-linux-x86_64.tar.gz
tar xzf kcl-v0.8.0-linux-x86_64.tar.gz
sudo mv kcl-v0.8.0/bin/kcl /usr/local/bin/
- name: Run validation
run: nu validate-catalog.nu --verbose
- name: Upload report
if: always()
uses: actions/upload-artifact@v3
with:
name: validation-report
path: validation-report.json
- name: Comment on PR
if: failure()
uses: actions/github-script@v6
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: '❌ Validación de catálogo fallida. Ver detalles en los artifacts.'
})
```
### 2. Pre-Commit Hook
```bash
# .git/hooks/pre-commit
#!/bin/bash
echo "🔍 Validando catálogo antes de commit..."
if nu validate-catalog.nu --check-only; then
echo "✅ Catálogo válido"
exit 0
else
echo "❌ Validación fallida. Resuelve los errores antes de commitear."
exit 1
fi
```
---
## 📊 Monitoreo y Alertas
### 1. monitor-catalog.nu - Monitoreo Continuo
```nushell
#!/usr/bin/env nu
# Monitorear cambios en el catálogo
# Uso: nu monitor-catalog.nu
const WATCH_FILE = "provisioning/services.k"
const CHECK_INTERVAL = 5 # segundos
print "👁️ Monitoreando catálogo de servicios..."
print ""
let last_hash = (
open $WATCH_FILE
| hash sha256
)
loop {
let current_hash = (
open $WATCH_FILE
| hash sha256
)
if $current_hash != $last_hash {
print ""
print "📝 Cambio detectado en ($WATCH_FILE)"
print $"⏰ (date now | format date '%H:%M:%S')"
# Ejecutar validación
let validation = (nu validate-catalog.nu 2>&1)
if ($validation | str contains "✅") {
print "✅ Validación pasada"
} else {
print "❌ Validación fallida"
# Enviar alerta
# slack-notify "Validación de catálogo fallida"
}
# Actualizar hash
let last_hash = $current_hash
}
sleep 5sec
}
```
### 2. Alertas por Slack
```nushell
#!/usr/bin/env nu
# Enviar alerta a Slack
# Uso: nu alert.nu "mensaje"
def slack_notify [message: string] {
let webhook_url = $env.SLACK_WEBHOOK_URL?
if ($webhook_url | is-empty) {
print "⚠️ SLACK_WEBHOOK_URL no configurado"
return
}
let payload = {
text: "⚠️ Catalog Validation Alert"
blocks: [
{
type: "section"
text: {
type: "mrkdwn"
text: $message
}
}
]
}
http post $webhook_url ($payload | to json)
print "✅ Alerta enviada a Slack"
}
# Uso
let message = "❌ Validación fallida\nVerifica el reporte: https://..."
slack_notify $message
```
---
## 🎯 Resumen: KCL vs NuShell
| Responsabilidad | KCL | NuShell |
|-----------------|-----|---------|
| **Validación de tipos** | ✅ Automática en compilación | - |
| **Ciclos de dependencias** | ✅ Automática en compilación | ✅ Verificación adicional |
| **Conflictos de puertos** | ✅ Esquema KCL | ✅ Análisis detallado |
| **Referencias válidas** | ✅ Type system | ✅ Cross-check |
| **Registro/Discovery** | - | ✅ Auto-discovery + cache |
| **Orquestación** | - | ✅ Scripts y workflows |
| **CI/CD Integration** | - | ✅ Pipelines GitHub/GitLab |
| **Reportes** | - | ✅ JSON + alertas |
---
## 📋 Checklist de Implementación
- [ ] Crear `policies.k` con validaciones
- [ ] Crear `validate-catalog.nu`
- [ ] Crear `check-dependencies.nu`
- [ ] Crear `check-ports.nu`
- [ ] Crear `check-patterns.nu`
- [ ] Crear `service-catalog.nu` (registro)
- [ ] Crear `.git/hooks/pre-commit`
- [ ] Crear `.github/workflows/validate-catalog.yml`
- [ ] Crear `monitor-catalog.nu`
- [ ] Setup alertas Slack
- [ ] Documentar CLI commands para DevOps
- [ ] Test con cambios (breaking y non-breaking)
---
**Conclusión**: KCL proporciona validación automática "on the fly" en compilación. NuShell orquesta la validación, gestiona el registro, e integra con CI/CD. Juntos proporcionan un sistema robusto sin necesidad de recodificar lógica de validación en Rust.