# ADR-017: Plugin Wrapper Abstraction Framework **Status**: Proposed **Date**: 2026-01-13 **Author**: Architecture Team **Supersedes**: Manual plugin wrapper implementations in `lib_provisioning/plugins/` ## Context The provisioning system integrates with four critical plugins, each with its own wrapper layer: 1. **auth.nu** (1066 lines) - Authentication plugin wrapper 2. **orchestrator.nu** (~500 lines) - Orchestrator plugin wrapper 3. **secretumvault.nu** (~500 lines) - Secrets vault plugin wrapper 4. **kms.nu** (~500 lines) - Key management service plugin wrapper Analysis reveals ~90% code duplication across these wrappers: ```bash # Pattern repeated 4 times with minor variations: export def plugin-available? [] { # Check if plugin is installed } export def try-plugin-call [method args] { # Try to call the plugin # On failure, fallback to HTTP } export def http-fallback-call [endpoint method args] { # HTTP endpoint fallback } ``` ## Problem Statement **Current Architecture**: - Each plugin has manual wrapper implementation - ~3000 total lines across 4 files - Boilerplate code repeated for each plugin method - HTTP fallback logic duplicated - Error handling inconsistent - Testing each wrapper requires custom setup **Key Metrics**: - 3000 lines of plugin wrapper code - 90% code similarity - 85% reduction opportunity ## Decision Implement **Plugin Wrapper Abstraction Framework**: replace manual plugin wrappers with a generic proxy framework + declarative YAML definitions. ### Architecture ```bash Plugin Definition (YAML) ├─ plugin: auth ├─ methods: │ ├─ login(username, password) │ ├─ logout() │ └─ status() └─ http_endpoint: http://localhost:8001 Generic Plugin Proxy Framework ├─ availability() - Check if plugin installed ├─ call() - Try plugin, fallback to HTTP ├─ http_fallback() - HTTP call with retry └─ error_handler() - Consistent error handling Generated Wrappers ├─ auth_wrapper.nu (150 lines, autogenerated) ├─ orchestrator_wrapper.nu (150 lines) ├─ vault_wrapper.nu (150 lines) └─ kms_wrapper.nu (150 lines) ``` ### Mechanism **Plugin Call Flow**: 1. **Check Availability**: Is plugin installed and running? 2. **Try Plugin Call**: Execute plugin method with timeout 3. **On Failure**: Fall back to HTTP endpoint 4. **Error Handling**: Unified error response format 5. **Retry Logic**: Configurable retry with exponential backoff ### Error Handling Pattern **Nushell 0.109 Compliant** (do-complete pattern, no try-catch): ```python def call-plugin-with-fallback [method: string args: record] { let plugin_result = ( do { # Try plugin call call-plugin $method $args } | complete ) if $plugin_result.exit_code != 0 { # Fall back to HTTP call-http-endpoint $method $args } else { $plugin_result.stdout | from json } } ``` ## Consequences ### Positive - **85% Code Reduction**: 3000 lines → 200 (proxy) + 600 (generated) - **Consistency**: All plugins use identical call pattern - **Maintainability**: Single proxy implementation vs 4 wrapper files - **Testability**: Mock proxy for testing, no plugin-specific setup needed - **Extensibility**: New plugins require only YAML definition ### Negative - **Abstraction Overhead**: Proxy layer adds indirection - **YAML Schema**: Must maintain schema for plugin definitions - **Migration Risk**: Replacing working code requires careful testing ## Implementation Strategy 1. **Create Generic Proxy** (`lib_provisioning/plugins/proxy.nu`) - Plugin availability detection - Call execution with error handling - HTTP fallback mechanism - Retry logic with backoff 2. **Define Plugin Schema** (`lib_provisioning/plugins/definitions/plugin.schema.yaml`) - Plugin metadata (name, http_endpoint) - Method definitions (parameters, return types) - Fallback configuration (retry count, timeout) 3. **Plugin Definitions** (`lib_provisioning/plugins/definitions/`) - `auth.yaml` - Authentication plugin - `orchestrator.yaml` - Orchestrator plugin - `secretumvault.yaml` - Secrets vault plugin - `kms.yaml` - Key management service plugin 4. **Code Generator** (`tools/codegen/plugin_wrapper_generator.nu`) - Parse plugin YAML definitions - Generate wrapper functions - Ensure Nushell 0.109 compliance 5. **Integration** - Feature flag: `$env.PROVISIONING_USE_GENERATED_PLUGINS` - Gradual migration from manual to generated wrappers - Full compatibility with existing code ## Testing Strategy 1. **Unit Tests** - Plugin availability detection - Successful plugin calls - HTTP fallback on plugin failure - Error handling and retry logic 2. **Integration Tests** - Real plugin calls with actual plugins - Mock HTTP server for fallback testing - Timeout handling - Retry with backoff 3. **Contract Tests** - Plugin method signatures match definitions - Return values have expected structure - Error responses consistent ## Plugin Definitions ### auth.yaml Example ```yaml plugin: auth http_endpoint: http://localhost:8001 methods: login: params: username: string password: string returns: {token: string} logout: params: {} returns: {status: string} status: params: {} returns: {authenticated: bool} ``` ## Rollback Strategy **Feature Flag Approach**: ```bash # Use original manual wrappers export PROVISIONING_USE_GENERATED_PLUGINS=false # Use new generated proxy framework export PROVISIONING_USE_GENERATED_PLUGINS=true ``` Allows parallel operation and gradual migration. ## Related ADRs - **ADR-012**: Nushell/Nickel Plugin CLI Wrapper - **ADR-013**: TypeDialog Integration (forms for plugin configuration) ## Open Questions 1. Should plugin definitions be YAML or Nickel? 2. How do we handle plugin discovery automatically? 3. What's the expected HTTP endpoint format for all plugins? 4. Should retry logic be configurable per plugin? ## References - Nushell 0.109 Guidelines: `.claude/guidelines/nushell.md` - Do-Complete Pattern: Error handling without try-catch - Plugin Framework: `provisioning/core/nulib/lib_provisioning/plugins/`