6.1 KiB
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:
- auth.nu (1066 lines) - Authentication plugin wrapper
- orchestrator.nu (~500 lines) - Orchestrator plugin wrapper
- secretumvault.nu (~500 lines) - Secrets vault plugin wrapper
- kms.nu (~500 lines) - Key management service plugin wrapper
Analysis reveals ~90% code duplication across these wrappers:
# 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
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:
- Check Availability: Is plugin installed and running?
- Try Plugin Call: Execute plugin method with timeout
- On Failure: Fall back to HTTP endpoint
- Error Handling: Unified error response format
- Retry Logic: Configurable retry with exponential backoff
Error Handling Pattern
Nushell 0.109 Compliant (do-complete pattern, no try-catch):
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
-
Create Generic Proxy (
lib_provisioning/plugins/proxy.nu)- Plugin availability detection
- Call execution with error handling
- HTTP fallback mechanism
- Retry logic with backoff
-
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)
-
Plugin Definitions (
lib_provisioning/plugins/definitions/)auth.yaml- Authentication pluginorchestrator.yaml- Orchestrator pluginsecretumvault.yaml- Secrets vault pluginkms.yaml- Key management service plugin
-
Code Generator (
tools/codegen/plugin_wrapper_generator.nu)- Parse plugin YAML definitions
- Generate wrapper functions
- Ensure Nushell 0.109 compliance
-
Integration
- Feature flag:
$env.PROVISIONING_USE_GENERATED_PLUGINS - Gradual migration from manual to generated wrappers
- Full compatibility with existing code
- Feature flag:
Testing Strategy
-
Unit Tests
- Plugin availability detection
- Successful plugin calls
- HTTP fallback on plugin failure
- Error handling and retry logic
-
Integration Tests
- Real plugin calls with actual plugins
- Mock HTTP server for fallback testing
- Timeout handling
- Retry with backoff
-
Contract Tests
- Plugin method signatures match definitions
- Return values have expected structure
- Error responses consistent
Plugin Definitions
auth.yaml Example
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:
# 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
- Should plugin definitions be YAML or Nickel?
- How do we handle plugin discovery automatically?
- What's the expected HTTP endpoint format for all plugins?
- 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/