# ADR-016: Schema-Driven Accessor Generation Pattern **Status**: Proposed **Date**: 2026-01-13 **Author**: Architecture Team **Supersedes**: Manual accessor maintenance in `lib_provisioning/config/accessor.nu` ## Context The `lib_provisioning/config/accessor.nu` file contains 1567 lines across 187 accessor functions. Analysis reveals that 95% of these functions follow an identical mechanical pattern: ```javascript export def get-{field-name} [--config: record] { config-get "{path.to.field}" {default_value} --config $config } ``` This represents significant technical debt: 1. **Manual Maintenance Burden**: Adding a new config field requires manually writing a new accessor function 2. **Schema Drift Risk**: No automated validation that accessor matches the actual Nickel schema 3. **Code Duplication**: Nearly identical functions across 187 definitions 4. **Testing Complexity**: Each accessor requires manual testing ## Problem Statement **Current Architecture**: - Nickel schemas define configuration structure (source of truth) - Accessor functions manually mirror the schema structure - No automated synchronization between schema and accessors - High risk of accessor-schema mismatch **Key Metrics**: - 1567 lines of accessor code - 187 repetitive functions - ~95% code similarity ## Decision Implement **Schema-Driven Accessor Generation**: automatically generate accessor functions from Nickel schema definitions. ### Architecture ```bash Nickel Schema (contracts.ncl) ↓ [Parse & Extract Schema Structure] ↓ [Generate Nushell Functions] ↓ accessor_generated.nu (800 lines) ↓ [Validation & Integration] ↓ CI/CD enforces: schema hash == generated code ``` ### Generation Process 1. **Schema Parsing**: Extract field paths, types, and defaults from Nickel contracts 2. **Code Generation**: Create accessor functions with Nushell 0.109 compliance 3. **Validation**: Verify generated code against schema 4. **CI Integration**: Detect schema changes, validate generated code matches ### Compliance Requirements **Nushell 0.109 Guidelines**: - No `try-catch` blocks (use `do-complete` pattern) - No `reduce --init` (use `reduce --fold`) - No mutable variables (use immutable bindings) - No type annotations on boolean flags - Use `each` not `map`, `is-not-empty` not `length` **Nickel Compliance**: - Schema-first design (schema is source of truth) - Type contracts enforce structure - `| doc` before `| default` ordering ## Consequences ### Positive - **Elimination of Manual Maintenance**: New config fields automatically get accessors - **Zero Schema Drift**: Automatic validation ensures accessors match schema - **Reduced Code Size**: 1567 lines → ~400 lines (manual core) + ~800 lines (generated) - **Type Safety**: Generated code guarantees type correctness - **Consistency**: All 187 functions use identical pattern ### Negative - **Tool Complexity**: Generator must parse Nickel and emit valid Nushell - **CI/CD Changes**: Build must validate schema hash - **Initial Migration**: One-time effort to verify generated code matches manual versions ## Implementation Strategy 1. **Create Generator** (`tools/codegen/accessor_generator.nu`) - Parse Nickel schema files - Extract paths, types, defaults - Generate valid Nushell code - Emit with proper formatting 2. **Generate Accessors** (`lib_provisioning/config/accessor_generated.nu`) - Run generator on `provisioning/schemas/config/settings/contracts.ncl` - Output 187 accessor functions - Verify compatibility with existing code 3. **Validation** - Integration tests comparing manual vs generated output - Signature validator ensuring generated functions match patterns - CI check for schema hash validity 4. **Gradual Adoption** - Keep manual accessors temporarily - Feature flag to switch between manual and generated - Gradual migration of dependent code ## Testing Strategy 1. **Unit Tests** - Each generated accessor returns correct type - Default values applied correctly - Path resolution handles nested fields 2. **Integration Tests** - Generated accessors produce identical output to manual versions - Config loading pipeline works with generated accessors - Fallback behavior preserved 3. **Regression Tests** - All existing config access patterns work - Performance within 5% of manual version - No breaking changes to public API ## Related ADRs - **ADR-010**: Configuration Format Strategy (TOML/YAML/Nickel) - **ADR-011**: Nickel Migration (schema-first architecture) ## Open Questions 1. Should accessors be regenerated on every build or only on schema changes? 2. How do we handle conditional fields (if X then Y)? 3. What's the fallback strategy if generator fails? ## Timeline - **Phase 1**: Generator implementation (foundation) - **Phase 2**: Generate and validate accessor functions - **Phase 3**: Integration tests and feature flags - **Phase 4**: Full migration and manual code removal ## References - Nickel Language: [https://nickel-lang.org/](https://nickel-lang.org/) - Nushell 0.109 Guidelines: `.claude/guidelines/nushell.md` - Current Accessor Implementation: `provisioning/core/nulib/lib_provisioning/config/accessor.nu` - Schema Source: `provisioning/schemas/config/settings/contracts.ncl`