Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

KCL Module Organization - Implementation Summary

Date: 2025-10-03 Status: ✅ Complete KCL Version: 0.11.3


Executive Summary

Successfully resolved KCL ImmutableError issues and established a clean, maintainable module organization pattern for the provisioning project. The root cause was re-export assignments in main.k that created immutable variables, causing E1001 errors when extensions imported schemas.

Solution: Direct submodule imports (no re-exports) - already implemented by the codebase, just needed cleanup and documentation.


Problem Analysis

Root Cause

The original main.k contained 100+ lines of re-export assignments:

# This pattern caused ImmutableError
Settings = settings.Settings
Server = server.Server
TaskServDef = lib.TaskServDef
# ... 100+ more

Why it failed:

  1. These assignments create immutable top-level variables in KCL
  2. When extensions import from provisioning, KCL attempts to re-assign these variables
  3. KCL’s immutability rules prevent this → ImmutableError E1001
  4. KCL 0.11.3 doesn’t support Python-style namespace re-exports

Discovery

  • Extensions were already using direct imports correctly: import provisioning.lib as lib
  • Commenting out re-exports in main.k immediately fixed all errors
  • kcl run provision_aws.k worked perfectly with cleaned-up main.k

Solution Implemented

1. Cleaned Up provisioning/kcl/main.k

Before (110 lines):

  • 100+ lines of re-export assignments (commented out)
  • Cluttered with non-functional code
  • Misleading documentation

After (54 lines):

  • Only import statements (no re-exports)
  • Clear documentation explaining the pattern
  • Examples of correct usage
  • Anti-pattern warnings

Key Changes:

# BEFORE (❌ Caused ImmutableError)
Settings = settings.Settings
Server = server.Server
# ... 100+ more

# AFTER (✅ Works correctly)
import .settings
import .defaults
import .lib
import .server
# ... just imports

2. Created Comprehensive Documentation

File: docs/architecture/kcl-import-patterns.md

Contents:

  • Module architecture overview
  • Correct import patterns with examples
  • Anti-patterns with explanations
  • Submodule reference (all 10 submodules documented)
  • Workspace integration guide
  • Best practices
  • Troubleshooting section
  • Version compatibility matrix

Architecture Pattern: Direct Submodule Imports

How It Works

Core Module (provisioning/kcl/main.k):

# Import submodules to make them discoverable
import .settings
import .lib
import .server
import .dependencies
# ... etc

# NO re-exports - just imports

Extensions Import Specific Submodules:

# Provider example
import provisioning.lib as lib
import provisioning.defaults as defaults

schema Storage_aws(lib.Storage):
    voltype: "gp2" | "gp3" = "gp2"
# Taskserv example
import provisioning.dependencies as schema

_deps = schema.TaskservDependencies {
    name = "kubernetes"
    requires = ["containerd"]
}

Why This Works

No ImmutableError - No variable assignments in main.k ✅ Explicit Dependencies - Clear what each extension needs ✅ Works with kcl run - Individual files can be executed ✅ No Circular Imports - Clean dependency hierarchy ✅ KCL-Idiomatic - Follows language design patterns ✅ Better Performance - Only loads needed submodules ✅ Already Implemented - Codebase was using this correctly!


Validation Results

All schemas validate successfully after cleanup:

TestCommandResult
Core modulekcl run provisioning/kcl/main.k✅ Pass
AWS providerkcl run provisioning/extensions/providers/aws/kcl/provision_aws.k✅ Pass
Kubernetes taskservkcl run provisioning/extensions/taskservs/kubernetes/kcl/kubernetes.k✅ Pass
Web clusterkcl run provisioning/extensions/clusters/web/kcl/web.k✅ Pass

Note: Minor type error in version.k:105 (unrelated to import pattern) - can be fixed separately.


Files Modified

1. /Users/Akasha/project-provisioning/provisioning/kcl/main.k

Changes:

  • Removed 82 lines of commented re-export assignments
  • Added comprehensive documentation (42 lines)
  • Kept only import statements (10 lines)
  • Added usage examples and anti-pattern warnings

Impact: Core module now clearly defines the import pattern

2. /Users/Akasha/project-provisioning/docs/architecture/kcl-import-patterns.md

Created: Complete reference guide for KCL module organization

Sections:

  • Module Architecture (core + extensions structure)
  • Import Patterns (correct usage, common patterns by type)
  • Submodule Reference (all 10 submodules documented)
  • Workspace Integration (how extensions are loaded)
  • Best Practices (5 key practices)
  • Troubleshooting (4 common issues with solutions)
  • Version Compatibility (KCL 0.11.x support)

Purpose: Single source of truth for extension developers


Submodule Reference

The core provisioning module provides 10 submodules:

SubmoduleSchemasPurpose
provisioning.settingsSettings, SecretProvider, SopsConfig, KmsConfig, AIProviderCore configuration
provisioning.defaultsServerDefaultsBase server defaults
provisioning.libStorage, TaskServDef, ClusterDef, ScaleDataCore library types
provisioning.serverServerServer definitions
provisioning.clusterClusterCluster management
provisioning.dependenciesTaskservDependencies, HealthCheck, ResourceRequirementDependency management
provisioning.workflowsBatchWorkflow, BatchOperation, RetryPolicyWorkflow definitions
provisioning.batchBatchScheduler, BatchExecutor, BatchMetricsBatch operations
provisioning.versionVersion, TaskservVersion, PackageMetadataVersion tracking
provisioning.k8s_deployK8s* (50+ K8s schemas)Kubernetes deployments

Best Practices Established

1. Direct Imports Only

✅ import provisioning.lib as lib
❌ Settings = settings.Settings

2. Meaningful Aliases

✅ import provisioning.dependencies as deps
❌ import provisioning.dependencies as d

3. Import What You Need

✅ import provisioning.version as v
❌ import provisioning.* (not even possible in KCL)
# Core schemas
import provisioning.settings
import provisioning.lib as lib

# Workflow schemas
import provisioning.workflows as wf
import provisioning.batch as batch

5. Document Dependencies

# Dependencies:
#   - provisioning.dependencies
#   - provisioning.version
import provisioning.dependencies as schema
import provisioning.version as v

Workspace Integration

Extensions can be loaded into workspaces and used in infrastructure definitions:

Structure:

workspace-librecloud/
├── .providers/          # Loaded providers (aws, upcloud, local)
├── .taskservs/          # Loaded taskservs (kubernetes, containerd, etc.)
└── infra/              # Infrastructure definitions
    └── production/
        ├── kcl.mod
        └── servers.k

Usage:

# workspace-librecloud/infra/production/servers.k
import provisioning.server as server
import provisioning.lib as lib
import aws_prov.defaults_aws as aws

_servers = [
    server.Server {
        hostname = "k8s-master-01"
        defaults = aws.ServerDefaults_aws {
            zone = "eu-west-1"
        }
    }
]

Troubleshooting Guide

ImmutableError (E1001)

  • Cause: Re-export assignments in modules
  • Solution: Use direct submodule imports

Schema Not Found

  • Cause: Importing from wrong submodule
  • Solution: Check submodule reference table

Circular Import

  • Cause: Module A imports B, B imports A
  • Solution: Extract shared schemas to separate module

Version Mismatch

  • Cause: Extension kcl.mod version conflict
  • Solution: Update kcl.mod to match core version

KCL Version Compatibility

VersionStatusNotes
0.11.3✅ CurrentDirect imports work perfectly
0.11.x✅ SupportedSame pattern applies
0.10.x⚠️ LimitedMay have import issues
Future🔄 TBDNamespace traversal planned (#1686)

Impact Assessment

Immediate Benefits

  • ✅ All ImmutableErrors resolved
  • ✅ Clear, documented import pattern
  • ✅ Cleaner, more maintainable codebase
  • ✅ Better onboarding for extension developers

Long-term Benefits

  • ✅ Scalable architecture (no central bottleneck)
  • ✅ Explicit dependencies (easier to track and update)
  • ✅ Better IDE support (submodule imports are clearer)
  • ✅ Future-proof (aligns with KCL evolution)

Performance Impact

  • ⚡ Faster compilation (only loads needed submodules)
  • ⚡ Better caching (submodules cached independently)
  • ⚡ Reduced memory usage (no unnecessary schema loading)

Next Steps (Optional Improvements)

1. Fix Minor Type Error

File: provisioning/kcl/version.k:105 Issue: Type mismatch in PackageMetadata Priority: Low (doesn’t affect imports)

2. Add Import Examples to Extension Templates

Location: Extension scaffolding tools Purpose: New extensions start with correct patterns Priority: Medium

3. Create IDE Snippets

Platforms: VS Code, Vim, Emacs Content: Common import patterns Priority: Low

4. Automated Validation

Tool: CI/CD check for anti-patterns Check: Ensure no re-exports in new code Priority: Medium


Conclusion

The KCL module organization is now clean, well-documented, and follows best practices. The direct submodule import pattern:

  • ✅ Resolves all ImmutableError issues
  • ✅ Aligns with KCL language design
  • ✅ Was already implemented by the codebase
  • ✅ Just needed cleanup and documentation

Status: Production-ready. No further changes required for basic functionality.


  • Import Patterns Guide: docs/architecture/kcl-import-patterns.md (comprehensive reference)
  • Core Module: provisioning/kcl/main.k (documented entry point)
  • KCL Official Docs: https://www.kcl-lang.io/docs/reference/lang/spec/

Support

For questions about KCL imports:

  1. Check docs/architecture/kcl-import-patterns.md
  2. Review provisioning/kcl/main.k documentation
  3. Examine working examples in provisioning/extensions/
  4. Consult KCL language specification

Last Updated: 2025-10-03 Maintained By: Architecture Team Review Cycle: Quarterly or when KCL version updates