provisioning/docs/src/architecture/adr/adr-017-plugin-wrapper-abstraction-framework.md
2026-01-14 04:59:11 +00:00

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:

  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:

# 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:

  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):

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

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.

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