provisioning/docs/src/architecture/adr/adr-016-schema-driven-accessor-generation.md
2026-01-14 04:53:58 +00:00

5.2 KiB

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:

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

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
  • 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/
  • 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