syntaxis/docs/adr/adr-002-bundle-wrapper-deployment.md
Jesús Pérez 9cef9b8d57 refactor: consolidate configuration directories
Merge _configs/ into config/ for single configuration directory.
Update all path references.

Changes:
- Move _configs/* to config/
- Update .gitignore for new patterns
- No code references to _configs/ found

Impact: -1 root directory (layout_conventions.md compliance)
2025-12-26 18:36:23 +00:00

11 KiB

ADR-002: Bundle Wrapper Deployment Architecture

Date: 2025-11-17 Status: ACCEPTED Version: 2.0 (Updated with hybrid NuShell + Bash fallback) Author: Architecture Review Scope: Hybrid runtime wrapper deployment for bundle installations


Problem Statement

Question: Should bundle installations create the same three-layer wrapper structure that development installations create?

Current State:

  • Development users (scripts/install-syntaxis.nu): Get full wrapper infrastructure

    • Binaries renamed to .real
    • Bash wrappers at original locations
    • NuShell wrappers deployed to ~/.config/syntaxis/scripts/
    • Result: Auto-config injection → Users type: syntaxis-cli status
  • Bundle users (scripts/provisioning/install.sh): Get direct binaries only

    • Binaries installed directly
    • No wrapper infrastructure
    • No config auto-injection
    • Result: Manual config flags → Users must type: syntaxis-cli --config path status

The Issue: Inconsistent user experience between installation methods.


Wrapper Architecture: Hybrid Design (NuShell + Bash Fallback)

With NuShell Installed (Preferred): Three Layers

Layer 1: Bash Wrapper

~/.local/bin/syntaxis-cli  (300 bytes)
  ├─ Checks: Is NuShell available?
  ├─ If YES: Sets NU_LIB_DIRS="$HOME/.config/syntaxis/scripts"
  └─ Calls: nu "$HOME/.config/syntaxis/scripts/syntaxis-cli.nu" "$@"

Layer 2: NuShell Wrapper

~/.config/syntaxis/scripts/syntaxis-cli.nu  (2.2 KB)
  ├─ Searches: Config file locations
  ├─ Discovers: ~/.config/syntaxis/syntaxis-cli.toml
  ├─ Injects: --config ~/.config/syntaxis/syntaxis-cli.toml
  └─ Calls: ~/.local/bin/syntaxis-cli.real --config ... status

Layer 3: Real Binary

~/.local/bin/syntaxis-cli.real  (7.2 MB)
  └─ Actual compiled Rust binary

Without NuShell: Two Layers (Simplified)

Layer 1: Bash Wrapper (with inline config discovery)

~/.local/bin/syntaxis-cli  (300 bytes)
  ├─ Checks: Is NuShell available?
  ├─ If NO: Use bash-only config discovery
  ├─ Searches: Config in ~/.config/syntaxis/*, .syntaxis/, .project/, .coder/
  ├─ If found: Injects --config flag
  └─ Calls: ~/.local/bin/syntaxis-cli.real --config <found> status

Layer 2: Real Binary

~/.local/bin/syntaxis-cli.real  (7.2 MB)
  └─ Actual compiled Rust binary

User Experience

$ syntaxis-cli status
# Wrapper automatically injects --config
# Clean, simple, no typing burden

Decision: ADD HYBRID WRAPPERS TO BUNDLE

Recommendation: Bundle installations should create hybrid wrappers that work with or without NuShell.

Rationale

  1. Self-Contained Bundle

    • No hard dependency on NuShell
    • Works offline (no downloads required)
    • Fallback ensures functionality
  2. User Flexibility

    • NuShell users get full features immediately
    • Non-NuShell users still get auto-config
    • User prompted to install NuShell (optional upgrade)
  3. Graceful Degradation

    • With NuShell: Full three-layer architecture (best UX)
    • Without NuShell: Simple bash discovery (good UX)
    • Both modes provide auto-config injection
  4. Minimal Maintenance

    • Only ~15 lines of bash fallback code
    • No library translation needed
    • NuShell libraries only used when available
  5. Daily UX Impact

    • Users invoke 100s of times per day/week
    • Auto-config saves significant typing burden
    • Works regardless of NuShell availability

Implementation Plan

1. Update scripts/provisioning/install.sh

Key functions added:

a) Detect NuShell:

check_nushell() {
    if command -v nu &> /dev/null; then
        NUSHELL_VERSION=$(nu --version 2>/dev/null | head -1)
        return 0
    else
        return 1
    fi
}

b) Prompt user about NuShell:

prompt_nushell() {
    # Shows installation guide if NuShell not found
    # Offers user choice: continue or install NuShell
    # Non-blocking (continues with fallback if needed)
}

c) Create hybrid bash wrapper:

create_bash_wrapper() {
    local dest="$1"
    local binary_name=$(basename "$dest")

    # Create wrapper that:
    # 1. Checks if NuShell is available
    # 2a. If YES: use full three-layer architecture
    # 2b. If NO: use bash-only config discovery
}

d) Wrapper logic:

#!/bin/bash
# Hybrid wrapper - tries NuShell first

if command -v nu &>/dev/null; then
    # Full mode: NuShell + libraries
    export NU_LIB_DIRS="$HOME/.config/syntaxis/scripts"
    exec nu "$HOME/.config/syntaxis/scripts/${BINARY_NAME}.nu" "$@"
fi

# Fallback: Bash-only config discovery
for cfg_path in \
    "$HOME/.config/syntaxis/${BINARY_NAME}.toml" \
    "$HOME/.config/syntaxis/config.toml" \
    ".syntaxis/${BINARY_NAME}.toml"; do

    if [[ -f "$cfg_path" ]]; then
        exec "$REAL_BINARY" --config "$cfg_path" "$@"
    fi
done

# No config found
exec "$REAL_BINARY" "$@"

2. Deploy NuShell Wrapper Scripts

Add to install.sh:

deploy_wrapper_scripts() {
    local scripts_dir="./scripts"
    local config_scripts_dir="$HOME/.config/syntaxis/scripts"

    mkdir -p "$config_scripts_dir"

    # Deploy shared library
    cp "$scripts_dir/syntaxis-lib.nu" "$config_scripts_dir/"

    # Deploy binary-specific wrappers
    for binary in syntaxis-cli syntaxis-tui syntaxis-api; do
        cp "$scripts_dir/${binary}.nu" "$config_scripts_dir/" 2>/dev/null || true
        cp "$scripts_dir/${binary}-lib.nu" "$config_scripts_dir/" 2>/dev/null || true
    done

    chmod +x "$config_scripts_dir"/*.nu
}

3. Update provisioning.toml

Add wrapper scripts to artifacts:

[artifacts]
# ... existing config ...

# Wrapper scripts for auto-config injection
wrapper_scripts = [
    "scripts/syntaxis-lib.nu",
    "scripts/syntaxis-cli.nu",
    "scripts/syntaxis-cli-lib.nu",
    "scripts/syntaxis-tui.nu",
    "scripts/syntaxis-tui-lib.nu",
    "scripts/syntaxis-api.nu",
    "scripts/syntaxis-api-lib.nu",
]

4. Update Bundle Structure

Result after installation:

User's system after bundle installation:
~/.cargo/bin/
├── syntaxis-cli          ← Bash wrapper
├── syntaxis-cli.real     ← Actual binary
├── syntaxis-tui          ← Bash wrapper
├── syntaxis-tui.real     ← Actual binary
└── syntaxis-api          ← Bash wrapper
└── syntaxis-api.real     ← Actual binary

~/.config/syntaxis/scripts/
├── syntaxis-lib.nu       ← Shared library
├── syntaxis-cli.nu       ← NuShell wrapper
├── syntaxis-cli-lib.nu   ← CLI utilities
├── syntaxis-tui.nu       ← NuShell wrapper
├── syntaxis-tui-lib.nu   ← TUI utilities
├── syntaxis-api.nu       ← NuShell wrapper
└── syntaxis-api-lib.nu   ← API utilities

Technical Specifications

Wrapper Scripts to Include

Script Size Purpose
syntaxis-lib.nu 1.1 KB Shared library (config discovery)
syntaxis-cli.nu 2.2 KB CLI wrapper (auto-config injection)
syntaxis-cli-lib.nu 1.5 KB CLI utilities
syntaxis-tui.nu 2.2 KB TUI wrapper
syntaxis-tui-lib.nu 743 B TUI utilities
syntaxis-api.nu 3.6 KB API wrapper
syntaxis-api-lib.nu 2.2 KB API utilities
Total ~13.5 KB All wrapper infrastructure

Bundle Size Impact

  • Before: Binaries + configs + docs = ~9.2 MB
  • After: + wrapper scripts (~13.5 KB) = ~9.2 MB (negligible)

Installation Steps

  1. Extract bundle
  2. Run bash install.sh --interactive
    • Installs binaries to ~/.local/bin/ (or custom prefix)
    • Deploys wrapper scripts to ~/.config/syntaxis/scripts/
    • Renames binaries to .real
    • Creates bash wrappers at original locations
  3. Run bash setup-config.sh --interactive
    • Creates configuration files

Benefits

Users Don't Type --config

  • Every invocation automatically injects config path
  • Saves millions of characters per year

Transparent

  • Users see no difference from direct binary
  • Wrapper is invisible to end users

Flexible

  • Config files can be in multiple locations
  • Wrapper discovers automatically

Consistent

  • Same experience as development installation
  • Feature parity across installation methods

Maintainable

  • Clear separation: wrapper scripts vs binaries
  • Easy to update wrappers without rebuilding binaries

Trade-offs

With NuShell (Full Mode)

Pros:

  • Excellent user experience (no --config needed)
  • Advanced config discovery (multiple paths, precedence)
  • Full feature set available
  • Consistent with development installation

Cons:

  • ⚠️ Startup overhead (~50ms from NuShell)
  • ⚠️ Requires NuShell to be installed
  • ⚠️ Slightly more complex

Without NuShell (Simplified Mode)

Pros:

  • No external dependencies
  • Works offline (self-contained)
  • Still provides auto-config injection
  • Simple bash-only fallback

Cons:

  • ⚠️ Less advanced config discovery
  • ⚠️ Simplified version of full features
  • ⚠️ User prompted to install NuShell (optional)

Overall Hybrid Approach

Pros:

  • Self-contained bundle (no requirements)
  • Works for all users (NuShell or not)
  • Graceful degradation (best effort)
  • Minimal maintenance (only ~15 lines bash fallback)
  • User-aware (prompted to upgrade if desired)

Cons:

  • ⚠️ Two code paths to test
  • ⚠️ Slightly more complexity in wrapper
  • ⚠️ NuShell libraries deployment (optional when not available)

Risk Assessment

Low Risk

  • Exact same code as development installation
  • NuShell is already required for development
  • Startup overhead is acceptable for CLI tools
  • Can always use .real binaries if needed

Implementation Checklist

  • Modify scripts/provisioning/install.sh to create wrappers
  • Add deploy_wrapper_scripts() function to install.sh
  • Update configs/provisioning.toml to include wrapper scripts
  • Update scripts/provisioning/pack.nu to package wrapper scripts
  • Test bundle installation creates complete wrapper structure
  • Update documentation (docs/installation.md, README.md)
  • Rebuild bundle with wrapper support
  • Verify wrapper functionality post-install

Comparison: With vs Without Wrappers

Without Wrappers (Current Bundle)

$ syntaxis-cli project list
# User must provide --config flag every time
$ syntaxis-cli --config ~/.config/syntaxis/syntaxis-cli.toml project list

# Burden: 50+ characters per invocation
# 100 invocations = 5,000+ characters of typing

With Wrappers (Proposed)

$ syntaxis-cli project list
# Wrapper injects --config automatically
# Clean, simple, Unix-like experience

# Burden: 0 additional typing
# Users just type normal commands

  • docs/installation/WRAPPER_DESIGN.md - Technical wrapper design
  • scripts/install-syntaxis.nu - Development installation with wrappers
  • scripts/provisioning/install.sh - Bundle installation (to be updated)

Conclusion

Decision: Implement full wrapper deployment in bundle installations.

Rationale:

  • High value (users don't type --config)
  • Low complexity (existing code, ~14 KB)
  • Better user experience
  • Feature parity with development installation

This approach aligns with SYNTAXIS philosophy of providing excellent, transparent tooling for developers and teams.


Status: ACCEPTED - Ready for implementation