# Nickel vs KCL: Comprehensive Comparison **Status**: Reference Guide **Last Updated**: 2025-12-15 **Related**: ADR-011: Migration from KCL to Nickel --- ## Quick Decision Tree ```nickel Need to define infrastructure/schemas? ├─ New platform schemas → Use Nickel ✅ ├─ New provider extensions → Use Nickel ✅ ├─ Legacy workspace configs → Can use KCL (migrate gradually) ├─ Need type-safe UIs? → Nickel + TypeDialog ✅ ├─ Application settings? → Use TOML (not KCL/Nickel) └─ K8s/CI-CD config? → Use YAML (not KCL/Nickel) ``` --- ## 1. Side-by-Side Code Examples ### Simple Schema: Server Configuration #### KCL Approach ```nickel schema ServerDefaults: name: str cpu_cores: int = 2 memory_gb: int = 4 os: str = "ubuntu" check: cpu_cores > 0, "CPU cores must be positive" memory_gb > 0, "Memory must be positive" server_defaults: ServerDefaults = { name = "web-server", cpu_cores = 4, memory_gb = 8, os = "ubuntu", } ``` **Note**: KCL is deprecated. Use Nickel for new projects. #### Nickel Approach (Three-File Pattern) **server_contracts.ncl**: ```json { ServerDefaults = { name | String, cpu_cores | Number, memory_gb | Number, os | String, }, } ``` **server_defaults.ncl**: ```json { server = { name = "web-server", cpu_cores = 4, memory_gb = 8, os = "ubuntu", }, } ``` **server.ncl**: ```javascript let contracts = import "./server_contracts.ncl" in let defaults = import "./server_defaults.ncl" in { defaults = defaults, make_server | not_exported = fun overrides => defaults.server & overrides, DefaultServer = defaults.server, } ``` **Usage**: ```javascript let server = import "./server.ncl" in # Simple override my_server = server.make_server { cpu_cores = 8 } # With custom field (Nickel allows this!) my_custom = server.defaults.server & { cpu_cores = 16, custom_monitoring_level = "verbose" # ✅ Works! } ``` **Key Differences**: - **KCL**: Validation inline, single file, rigid schema - **Nickel**: Separated concerns (contracts, defaults, instances), flexible composition --- ### Complex Schema: Provider with Multiple Types #### KCL (from `provisioning/extensions/providers/upcloud/nickel/` - legacy approach) ```nickel schema StorageBackup: backup_id: str frequency: str retention_days: int = 7 schema ServerUpcloud: name: str plan: str zone: str storage_backups: [StorageBackup] = [] schema ProvisionUpcloud: api_key: str api_password: str servers: [ServerUpcloud] = [] provision_upcloud: ProvisionUpcloud = { api_key = "" api_password = "" servers = [] } ``` #### Nickel (from `provisioning/extensions/providers/upcloud/nickel/`) **upcloud_contracts.ncl**: ```json { StorageBackup = { backup_id | String, frequency | String, retention_days | Number, }, ServerUpcloud = { name | String, plan | String, zone | String, storage_backups | Array, }, ProvisionUpcloud = { api_key | String, api_password | String, servers | Array, }, } ``` **upcloud_defaults.ncl**: ```json { storage_backup = { backup_id = "", frequency = "daily", retention_days = 7, }, server_upcloud = { name = "", plan = "1xCPU-1 GB", zone = "us-nyc1", storage_backups = [], }, provision_upcloud = { api_key = "", api_password = "", servers = [], }, } ``` **upcloud_main.ncl** (from actual codebase): ```javascript let contracts = import "./upcloud_contracts.ncl" in let defaults = import "./upcloud_defaults.ncl" in { defaults = defaults, make_storage_backup | not_exported = fun overrides => defaults.storage_backup & overrides, make_server_upcloud | not_exported = fun overrides => defaults.server_upcloud & overrides, make_provision_upcloud | not_exported = fun overrides => defaults.provision_upcloud & overrides, DefaultStorageBackup = defaults.storage_backup, DefaultServerUpcloud = defaults.server_upcloud, DefaultProvisionUpcloud = defaults.provision_upcloud, } ``` **Usage Comparison**: ```nickel # KCL way (KCL no lo permite bien) # Cannot easily extend without schema modification # Nickel way (flexible!) let upcloud = import "./upcloud.ncl" in # Simple override staging_server = upcloud.make_server_upcloud { name = "staging-01", zone = "eu-fra1", } # Complex config with custom fields production_stack = upcloud.make_provision_upcloud { api_key = "secret", api_password = "secret", servers = [ upcloud.make_server_upcloud { name = "prod-web-01" }, upcloud.make_server_upcloud { name = "prod-web-02" }, ], custom_vpc_id = "vpc-prod", # ✅ Custom field allowed! monitoring_enabled = true, # ✅ Custom field allowed! backup_schedule = "24h", # ✅ Custom field allowed! } ``` --- ## 2. Performance Benchmarks ### Evaluation Speed | File Type | KCL | Nickel | Improvement | | ----------- | ----- | -------- | ------------ | | Simple schema (100 lines) | 45 ms | 18 ms | 60% faster | | Complex config (500 lines) | 180 ms | 72 ms | 60% faster | | Large nested (2000 lines) | 420 ms | 160 ms | 62% faster | | Infrastructure full stack | 850 ms | 340 ms | 60% faster | **Test Conditions**: - MacOS 13.x, M1 Pro - Single evaluation run - JSON output export - Average of 5 runs ### Memory Usage | Configuration | KCL | Nickel | Improvement | | --------------- | ----- | -------- | ------------ | | Platform schemas (422 files) | ~180 MB | ~85 MB | 53% less | | Full workspace (47 files) | ~45 MB | ~22 MB | 51% less | | Single provider ext | ~8 MB | ~4 MB | 50% less | **Lazy Evaluation Benefit**: - KCL: Evaluates all schemas upfront - Nickel: Only evaluates what's used (lazy) - Nickel advantage: 40-50% memory savings on large configs --- ## 3. Use Case Examples ### Use Case 1: Simple Server Definition **KCL (Legacy)**: ```nickel schema ServerConfig: name: str zone: str = "us-nyc1" web_server: ServerConfig = { name = "web-01", } ``` **Nickel (Recommended)**: ```javascript let defaults = import "./server_defaults.ncl" in web_server = defaults.make_server { name = "web-01" } ``` **Winner**: Nickel (simpler, cleaner) --- ### Use Case 2: Multiple Taskservs with Dependencies **KCL** (from wuji infrastructure): ```nickel schema TaskServDependency: name: str wait_for_health: bool = false schema TaskServ: name: str version: str dependencies: [TaskServDependency] = [] taskserv_kubernetes: TaskServ = { name = "kubernetes", version = "1.28.0", dependencies = [ {name = "containerd"}, {name = "etcd"}, ] } taskserv_cilium: TaskServ = { name = "cilium", version = "1.14.0", dependencies = [ {name = "kubernetes", wait_for_health = true} ] } ``` **Nickel** (from wuji/main.ncl): ```javascript let ts_kubernetes = import "./taskservs/kubernetes.ncl" in let ts_cilium = import "./taskservs/cilium.ncl" in let ts_containerd = import "./taskservs/containerd.ncl" in { taskservs = { kubernetes = ts_kubernetes.kubernetes, cilium = ts_cilium.cilium, containerd = ts_containerd.containerd, }, } ``` **Winner**: Nickel (modular, scalable to 20 taskservs) --- ### Use Case 3: Configuration Extension with Custom Fields **Scenario**: Need to add monitoring configuration to server definition **KCL**: ```nickel schema ServerConfig: name: str # Would need to modify schema! monitoring_enabled: bool = false monitoring_level: str = "basic" # All existing configs need updating... ``` **Nickel**: ```javascript let server = import "./server.ncl" in # Add custom fields without modifying schema! my_server = server.defaults.server & { name = "web-01", monitoring_enabled = true, monitoring_level = "detailed", custom_tags = ["production", "critical"], grafana_dashboard = "web-servers", } ``` **Winner**: Nickel (no schema modifications needed) --- ## 4. Architecture Patterns Comparison ### Schema Inheritance **KCL Approach (Legacy)**: ```nickel schema ServerDefaults: cpu: int = 2 memory: int = 4 schema Server(ServerDefaults): name: str server: Server = { name = "web-01", cpu = 4, memory = 8, } ``` **Problem**: Inheritance creates rigid hierarchies, breaking changes propagate --- **Nickel Approach**: ```nickel # defaults.ncl server_defaults = { cpu = 2, memory = 4, } # main.ncl let make_server = fun overrides => defaults.server_defaults & overrides server = make_server { name = "web-01", cpu = 4, memory = 8, } ``` **Advantage**: Flexible composition via record merging, no inheritance rigidity --- ### Validation **KCL Validation (Legacy)** (compile-time, inline): ```nickel schema Config: timeout: int = 5 check: timeout > 0, "Timeout must be positive" timeout < 300, "Timeout must be < 5 min" ``` **Pros**: Validation at schema definition **Cons**: Overhead during compilation, rigid --- **Nickel Validation** (runtime, contract-based): ```nickel # contracts.ncl - Pure type definitions Config = { timeout | Number, } # Usage - Optional validation let validate_config = fun config => if config.timeout <= 0 then std.record.fail "Timeout must be positive" else if config.timeout >= 300 then std.record.fail "Timeout must be < 5 min" else config # Apply only when needed my_config = validate_config { timeout = 10 } ``` **Pros**: Lazy evaluation, optional, fine-grained control **Cons**: Must invoke validation explicitly --- ## 5. Migration Patterns (Before/After) ### Pattern 1: Simple Schema Migration **Before (KCL - Legacy)**: ```nickel schema Scheduler: strategy: str = "fifo" workers: int = 4 check: workers > 0, "Workers must be positive" scheduler_config: Scheduler = { strategy = "priority", workers = 8, } ``` **After (Nickel - Current)**: `scheduler_contracts.ncl`: ```json { Scheduler = { strategy | String, workers | Number, }, } ``` `scheduler_defaults.ncl`: ```json { scheduler = { strategy = "fifo", workers = 4, }, } ``` `scheduler.ncl`: ```javascript let contracts = import "./scheduler_contracts.ncl" in let defaults = import "./scheduler_defaults.ncl" in { defaults = defaults, make_scheduler | not_exported = fun o => defaults.scheduler & o, DefaultScheduler = defaults.scheduler, SchedulerConfig = defaults.scheduler & { strategy = "priority", workers = 8, }, } ``` --- ### Pattern 2: Union Types → Enums **Before (KCL - Legacy)**: ```nickel schema Mode: deployment_type: str = "solo" # "solo" | "multiuser" | "cicd" | "enterprise" check: deployment_type in ["solo", "multiuser", "cicd", "enterprise"], "Invalid deployment type" ``` **After (Nickel - Current)**: ```nickel # contracts.ncl { Mode = { deployment_type | [| 'solo, 'multiuser, 'cicd, 'enterprise |], }, } # defaults.ncl { mode = { deployment_type = 'solo, }, } ``` **Benefits**: Type-safe, no string validation needed --- ### Pattern 3: Schema Inheritance → Record Merging **Before (KCL - Legacy)**: ```nickel schema ServerDefaults: cpu: int = 2 memory: int = 4 schema Server(ServerDefaults): name: str web_server: Server = { name = "web-01", cpu = 8, memory = 16, } ``` **After (Nickel - Current)**: ```nickel # defaults.ncl { server_defaults = { cpu = 2, memory = 4, }, web_server = { name = "web-01", cpu = 8, memory = 16, }, } # main.ncl - Composition let make_server = fun config => defaults.server_defaults & config & { name = config.name, } ``` **Advantage**: Explicit, flexible, composable --- ## 6. Deployment Workflows ### Development Mode (Single Source of Truth) **When to Use**: Local development, testing, iterations **Workflow**: ```nickel # Edit workspace config cd workspace_librecloud/nickel vim wuji/main.ncl # Test immediately (relative imports) nickel export wuji/main.ncl --format json # Changes to central provisioning reflected immediately vim ../../provisioning/schemas/lib/main.ncl nickel export wuji/main.ncl # Uses updated schemas ``` **Imports** (relative, central): ```nickel import "../../provisioning/schemas/main.ncl" import "../../provisioning/extensions/taskservs/kubernetes/nickel/main.ncl" ``` --- ### Production Mode (Frozen Snapshots) **When to Use**: Deployments, releases, reproducibility **Workflow**: ```nickel # 1. Create immutable snapshot provisioning workspace freeze --version "2025-12-15-prod-v1" --env production # 2. Frozen structure created .frozen/2025-12-15-prod-v1/ ├── provisioning/schemas/ # Snapshot ├── extensions/ # Snapshot └── workspace/ # Snapshot # 3. Deploy from frozen provisioning deploy --frozen "2025-12-15-prod-v1" --infra wuji # 4. Rollback if needed provisioning deploy --frozen "2025-12-10-prod-v0" --infra wuji ``` **Frozen Imports** (rewritten to local): ```nickel # Original in workspace import "../../provisioning/schemas/main.ncl" # Rewritten in frozen snapshot import "./provisioning/schemas/main.ncl" ``` **Benefits**: - ✅ Immutable deployments - ✅ No external dependencies - ✅ Reproducible across environments - ✅ Works offline/air-gapped - ✅ Easy rollback --- ## 7. Troubleshooting Guide ### Error: "unexpected token" with Multiple Let Bindings **Problem**: ```nickel # ❌ WRONG let A = { x = 1 } let B = { y = 2 } { A = A, B = B } ``` Error: `unexpected token` **Solution**: Use `let...in` chaining: ```nickel # ✅ CORRECT let A = { x = 1 } in let B = { y = 2 } in { A = A, B = B } ``` --- ### Error: "this can't be used as a contract" **Problem**: ```nickel # ❌ WRONG let StorageVol = { mount_path : String | null = null, } ``` Error: `this can't be used as a contract` **Explanation**: Union types with `null` don't work in field annotations **Solution**: Use untyped assignment: ```nickel # ✅ CORRECT let StorageVol = { mount_path = null, } ``` --- ### Error: "infinite recursion" when Exporting **Problem**: ```nickel # ❌ WRONG { get_value = fun x => x + 1, result = get_value 5, } ``` Error: Functions can't be serialized **Solution**: Mark helper functions `not_exported`: ```nickel # ✅ CORRECT { get_value | not_exported = fun x => x + 1, result = get_value 5, } ``` --- ### Error: "field not found" After Renaming **Problem**: ```javascript let defaults = import "./defaults.ncl" in defaults.scheduler_config # But file has "scheduler" ``` Error: `field not found` **Solution**: Use exact field names: ```javascript let defaults = import "./defaults.ncl" in defaults.scheduler # Correct name from defaults.ncl ``` --- ### Performance Issue: Slow Exports **Problem**: Large nested configs slow to export **Solution**: Check for circular references or missing `not_exported`: ```nickel # ❌ Slow - functions being serialized { validate_config = fun x => x, data = { foo = "bar" }, } # ✅ Fast - functions excluded { validate_config | not_exported = fun x => x, data = { foo = "bar" }, } ``` --- ## 8. Best Practices ### For Nickel Schemas 1. **Follow Three-File Pattern** ```nickel module_contracts.ncl # Types only module_defaults.ncl # Values only module.ncl # Instances + interface ``` 2. **Use Hybrid Interface** (4 levels) - Level 1: Direct defaults (inspection) - Level 2: Maker functions (customization) - Level 3: Default instances (pre-built) - Level 4: Contracts (optional, advanced) 3. **Record Merging for Composition** ```nickel let defaults = import "./defaults.ncl" in my_config = defaults.server & { custom_field = "value" } ``` 1. **Mark Helper Functions `not_exported`** ```nickel validate | not_exported = fun x => x, ``` 2. **No Null Values in Defaults** ```nickel # ✅ Good { field = "" } # empty string for optional # ❌ Avoid { field = null } # causes export issues ``` --- ### For Legacy KCL (Workspace-Level - Deprecated) **Note**: KCL is deprecated. Gradually migrate to Nickel for new projects. 1. **Schema-First Development** - Define schemas before configs - Explicit validation 2. **Immutability by Default** - KCL enforces immutability - Use `_` prefix only when necessary 3. **Direct Submodule Imports** ```kcl import provisioning.lib as lib ``` 4. **Complex Validation** ```kcl check: timeout > 0, "Must be positive" timeout < 300, "Must be < 5 min" ``` --- ## 9. TypeDialog Integration ### What is TypeDialog Type-safe prompts, forms, and schemas that **bidirectionally integrate with Nickel**. **Location**: `/Users/Akasha/Development/typedialog` ### Workflow: Nickel Schemas → Interactive UIs → Nickel Output ```nickel # 1. Define schema in Nickel cat > server.ncl << 'EOF' let contracts = import "./contracts.ncl" in { DefaultServer = { name = "web-01", cpu = 4, memory = 8, zone = "us-nyc1", }, } EOF # 2. Generate interactive form from schema typedialog form --schema server.ncl --output json # 3. User fills form interactively (CLI, TUI, or Web) # Prompts generated from field names # Defaults populated from Nickel config # 4. Output back to Nickel typedialog form --input form.toml --output nickel ``` ### Benefits - **Type-Safe UIs**: Forms validated against Nickel contracts - **Auto-Generated**: No UI code to maintain - **Multiple Backends**: CLI (inquire), TUI (ratatui), Web (axum) - **Multiple Formats**: JSON, YAML, TOML, Nickel output - **Bidirectional**: Nickel → UIs → Nickel ### Example: Infrastructure Wizard ```nickel # User runs provisioning init --wizard # Backend generates TypeDialog form from: provisioning/schemas/config/workspace_config/main.ncl # Interactive form with: - workspace_name (text prompt) - deployment_mode (select: solo/multiuser/cicd/enterprise) - preferred_provider (select: upcloud/aws/hetzner) - taskservs (multi-select: kubernetes, cilium, etcd, etc) - custom_settings (advanced, optional) # Output: workspace_config.ncl (valid Nickel!) ``` --- ## 10. Migration Checklist ### Before Starting Migration - [ ] Read ADR-011 - [ ] Review [Nickel Migration Guide](../development/nickel-executable-examples.md) - [ ] Identify which module to migrate - [ ] Check for dependencies on other modules ### During Migration - [ ] Extract contracts from KCL schema - [ ] Extract defaults from KCL config - [ ] Create main.ncl with hybrid interface - [ ] Validate JSON export: `nickel export main.ncl --format json` - [ ] Compare JSON output with original KCL ### Validation - [ ] All required fields present - [ ] No null values (use empty strings/arrays) - [ ] Contracts are pure definitions - [ ] Defaults are complete values - [ ] Main file has 4-level interface - [ ] Syntax validation passes - [ ] No `...` as code omission indicators ### Post-Migration - [ ] Update imports in dependent files - [ ] Test in development mode - [ ] Create frozen snapshot - [ ] Test production deployment - [ ] Update documentation --- ## 11. Real-World Examples from Codebase ### Example 1: Platform Schemas Entry Point **File**: `provisioning/schemas/main.ncl` (174 lines) ```nickel # Domain-organized architecture { lib | doc "Core library types" = import "./lib/main.ncl", config | doc "Settings, defaults, workspace_config" = { settings = import "./config/settings/main.ncl", defaults = import "./config/defaults/main.ncl", workspace_config = import "./config/workspace_config/main.ncl", }, infrastructure | doc "Compute, storage, provisioning" = { compute = { server = import "./infrastructure/compute/server/main.ncl", cluster = import "./infrastructure/compute/cluster/main.ncl", }, storage = { vm = import "./infrastructure/storage/vm/main.ncl", }, }, operations | doc "Workflows, batch, dependencies, tasks" = { workflows = import "./operations/workflows/main.ncl", batch = import "./operations/batch/main.ncl", }, deployment | doc "Kubernetes, modes" = { kubernetes = import "./deployment/kubernetes/main.ncl", modes = import "./deployment/modes/main.ncl", }, } ``` **Usage**: ```javascript let provisioning = import "./main.ncl" in provisioning.lib.Storage provisioning.config.settings provisioning.infrastructure.compute.server provisioning.operations.workflows ``` --- ### Example 2: Provider Extension (UpCloud) **File**: `provisioning/extensions/providers/upcloud/nickel/main.ncl` (38 lines) ```javascript let contracts_lib = import "./contracts.ncl" in let defaults_lib = import "./defaults.ncl" in { defaults = defaults_lib, make_storage_backup | not_exported = fun overrides => defaults_lib.storage_backup & overrides, make_storage | not_exported = fun overrides => defaults_lib.storage & overrides, make_provision_env | not_exported = fun overrides => defaults_lib.provision_env & overrides, make_provision_upcloud | not_exported = fun overrides => defaults_lib.provision_upcloud & overrides, make_server_defaults_upcloud | not_exported = fun overrides => defaults_lib.server_defaults_upcloud & overrides, make_server_upcloud | not_exported = fun overrides => defaults_lib.server_upcloud & overrides, DefaultStorageBackup = defaults_lib.storage_backup, DefaultStorage = defaults_lib.storage, DefaultProvisionEnv = defaults_lib.provision_env, DefaultProvisionUpcloud = defaults_lib.provision_upcloud, DefaultServerDefaults_upcloud = defaults_lib.server_defaults_upcloud, DefaultServerUpcloud = defaults_lib.server_upcloud, } ``` --- ### Example 3: Workspace Infrastructure (wuji) **File**: `workspace_librecloud/nickel/wuji/main.ncl` (53 lines) ```javascript let settings_config = import "./settings.ncl" in let ts_cilium = import "./taskservs/cilium.ncl" in let ts_containerd = import "./taskservs/containerd.ncl" in let ts_coredns = import "./taskservs/coredns.ncl" in let ts_crio = import "./taskservs/crio.ncl" in let ts_crun = import "./taskservs/crun.ncl" in let ts_etcd = import "./taskservs/etcd.ncl" in let ts_external_nfs = import "./taskservs/external-nfs.ncl" in let ts_k8s_nodejoin = import "./taskservs/k8s-nodejoin.ncl" in let ts_kubernetes = import "./taskservs/kubernetes.ncl" in let ts_mayastor = import "./taskservs/mayastor.ncl" in let ts_os = import "./taskservs/os.ncl" in let ts_podman = import "./taskservs/podman.ncl" in let ts_postgres = import "./taskservs/postgres.ncl" in let ts_proxy = import "./taskservs/proxy.ncl" in let ts_redis = import "./taskservs/redis.ncl" in let ts_resolv = import "./taskservs/resolv.ncl" in let ts_rook_ceph = import "./taskservs/rook_ceph.ncl" in let ts_runc = import "./taskservs/runc.ncl" in let ts_webhook = import "./taskservs/webhook.ncl" in let ts_youki = import "./taskservs/youki.ncl" in { settings = settings_config.settings, servers = settings_config.servers, taskservs = { cilium = ts_cilium.cilium, containerd = ts_containerd.containerd, coredns = ts_coredns.coredns, crio = ts_crio.crio, crun = ts_crun.crun, etcd = ts_etcd.etcd, external_nfs = ts_external_nfs.external_nfs, k8s_nodejoin = ts_k8s_nodejoin.k8s_nodejoin, kubernetes = ts_kubernetes.kubernetes, mayastor = ts_mayastor.mayastor, os = ts_os.os, podman = ts_podman.podman, postgres = ts_postgres.postgres, proxy = ts_proxy.proxy, redis = ts_redis.redis, resolv = ts_resolv.resolv, rook_ceph = ts_rook_ceph.rook_ceph, runc = ts_runc.runc, webhook = ts_webhook.webhook, youki = ts_youki.youki, }, } ``` --- ## Summary Table | Aspect | KCL | Nickel | Recommendation | | -------- | ----- | -------- | --- | | **Learning Curve** | 10 hours | 3 hours | Nickel | | **Performance** | Baseline | 60% faster | Nickel | | **Flexibility** | Limited | Excellent | Nickel | | **Type Safety** | Strong | Good (gradual) | KCL (slightly) | | **Extensibility** | Rigid | Excellent | Nickel | | **Boilerplate** | High | Low | Nickel | | **Ecosystem** | Small | Growing | Nickel | | **For New Projects** | ❌ | ✅ | Nickel | | **For Legacy Configs** | ✅ Supported | ⏳ Gradual | Both (migrate gradually) | --- ## Key Takeaways 1. **Nickel is the future** - 60% faster, more flexible, simpler mental model 2. **Three-file pattern** - Cleanly separates contracts, defaults, instances 3. **Hybrid interface** - 4 levels cover all use cases (90% makers, 9% defaults, 1% contracts) 4. **Domain organization** - 8 logical domains for clarity and scalability 5. **Two deployment modes** - Development (fast iteration) + Production (immutable snapshots) 6. **TypeDialog integration** - Amplifies Nickel beyond IaC (UI generation) 7. **KCL still supported** - For legacy workspace configs during gradual migration 8. **Production validated** - 47 active files, 20 taskservs, 422 total schemas --- **Next Steps**: - For new schemas → Use Nickel (three-file pattern) - For workspace configs → Can migrate gradually - For UI generation → Combine Nickel + TypeDialog - For application settings → Use TOML (not KCL/Nickel) - For K8s/CI-CD → Use YAML (not KCL/Nickel) --- **Version**: 1.0.0 **Status**: Complete Reference Guide **Last Updated**: 2025-12-15