syntaxis/docs/adr/adr-001-wrapper-architecture-strategy.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

14 KiB
Raw Permalink Blame History

ADR-001: Wrapper Architecture Strategy

Date: 2025-11-17 Status: PROPOSED Author: Architecture Review Scope: Installation and execution wrappers in SYNTAXIS


Problem Statement

SYNTAXIS uses two different wrapper patterns:

  1. Binary Execution Wrappers (existing install-syntaxis):

    • Bash → NuShell → Rust (3 layers)
    • Handles config discovery and injection
    • Used by: syntaxis-cli, syntaxis-tui, syntaxis-api
  2. Installation Wrapper (new provision.sh):

    • Bash orchestrator
    • Wraps install.sh + setup-config.sh
    • Used by: Bundle users during first-time installation

Question: Should both patterns be used? Or should they follow different strategies?


Analysis Framework

Cost-Benefit Model for Wrappers

Wrapper Value = Frequency × (User Burden Saved) × Maintenance Complexity

Where:
- Frequency: How often is this called? (per session, per day, per lifetime)
- User Burden: What pain does it eliminate? (typing, remembering steps, errors)
- Complexity: How many layers/moving parts? (1 layer vs 3 layers)

Pattern 1: Binary Execution Wrappers (install-syntaxis)

Current Implementation

Shell Invocation: syntaxis-cli status
        ↓
[Layer 1] Bash Wrapper (~212 bytes)
  └─ Sets: NU_LIB_DIRS="$HOME/.config/syntaxis/scripts"
  └─ Calls: nu "$HOME/.config/syntaxis/scripts/syntaxis-cli.nu" "status"
        ↓
[Layer 2] NuShell Wrapper (~2.2 KB)
  └─ Imports: syntaxis-lib.nu, syntaxis-cli-lib.nu
  └─ Discovers: Config file location
  └─ Injects: --config flag automatically
  └─ Calls: ~/.cargo/bin/syntaxis-cli.real --config ~/.config/syntaxis/config.toml status
        ↓
[Layer 3] Real Binary (7.2 MB)
  └─ Executes: With config path already provided

Why This Pattern?

Without wrapper:

# Every single invocation requires:
$ syntaxis-cli --config ~/.config/syntaxis/config.toml status
$ syntaxis-cli --config ~/.config/syntaxis/config.toml project list
$ syntaxis-cli --config ~/.config/syntaxis/config.toml task create "name"
# ... repeated 100s of times per day

User Experience Problem:

  • Users must remember config path
  • Config path is long and error-prone
  • Repetitive typing (every invocation)
  • Non-obvious that --config is required

With wrapper:

# Clean, simple invocations:
$ syntaxis-cli status
$ syntaxis-cli project list
$ syntaxis-cli task create "name"
# Config is automatic!

User Experience Solution:

  • Config path is invisible
  • Users follow Unix conventions
  • No typing overhead
  • Feels like a normal CLI tool

Wrapper Value Calculation

Frequency: ~100 invocations per active session
           = Multiple times per hour, thousands per week

User Burden Saved:
  - Per invocation: ~50 characters of typing
  - Per session: ~5,000 characters typed
  - Per year: ~2.6 million characters saved
  - Mental load: No need to remember config path

Maintenance Complexity: 3 layers
  - But: Stable (rarely changed)
  - And: All in scripts (easy to modify)
  - Risk: Moderate (can bypass via .real)

VERDICT: Value >> Complexity Cost
RECOMMENDATION: KEEP THIS PATTERN ✅

Existing File Locations

Installation Script:
  scripts/install-syntaxis              (bash wrapper)
  scripts/install-syntaxis.nu           (NuShell orchestrator)

Deployed Wrappers:
  ~/.cargo/bin/syntaxis-cli             (bash wrapper)
  ~/.cargo/bin/syntaxis-tui             (bash wrapper)
  ~/.cargo/bin/syntaxis-api             (bash wrapper)

NuShell Wrappers:
  ~/.config/syntaxis/scripts/syntaxis-cli.nu    (2.2 KB)
  ~/.config/syntaxis/scripts/syntaxis-tui.nu    (2.2 KB)
  ~/.config/syntaxis/scripts/syntaxis-api.nu    (3.6 KB)

Shared Libraries:
  ~/.config/syntaxis/scripts/syntaxis-lib.nu    (1.1 KB)
  ~/.config/syntaxis/scripts/syntaxis-cli-lib.nu (1.5 KB)
  ~/.config/syntaxis/scripts/syntaxis-tui-lib.nu (743 B)
  ~/.config/syntaxis/scripts/syntaxis-api-lib.nu (2.2 KB)

Real Binaries:
  ~/.cargo/bin/syntaxis-cli.real        (7.2 MB)
  ~/.cargo/bin/syntaxis-tui.real        (3.9 MB)
  ~/.cargo/bin/syntaxis-api.real        (8.3 MB)

Pattern 2: Installation Wrapper (provision.sh)

Current Implementation

User: ./provision full
        ↓
[Layer 1] Bash Wrapper (provision.sh)
  └─ Detects: Shell availability (bash/zsh/NuShell)
  └─ Offers: Interactive menu OR one-step install
  └─ Orchestrates: install.sh → setup-config.sh
  └─ Calls: Either install or install + config
        ↓
[Layer 2A] Install Script (install.sh)
  └─ Installs: Binaries to ~/.local/bin/
  └─ Updates: Shell PATH
  └─ Creates: Data directories
        ↓
[Layer 2B] Config Script (setup-config.sh)
  └─ Creates: TOML configs
  └─ Interactive: Prompts user for options
  └─ Deploys: Configs to ~/.config/syntaxis/

Why This Pattern Was Created?

Intended Value:

  1. Make installation easier for beginners
  2. One command instead of two
  3. Guided interactive experience

Reality Check:

  • Users run installer: Once (during first installation)
  • Users run binaries: 100s of times (during normal use)

Wrapper Value Calculation

Frequency: ~1 invocation per user
           = Only once during initial setup
           = Not multiple times per session

User Burden Saved:
  - Per user: Saves 1 command typing (~50 characters)
  - Per year: ~50 characters saved (one time!)
  - Mental load: Not a problem (one-time activity)

Maintenance Complexity: 2+ layers
  - Scripts call other scripts
  - PATH updates, permission handling, error recovery
  - Three different code paths (install only, config only, both)
  - Need to handle shell detection and options

User Confusion:
  - "Which command should I use?"
  - install.sh or provision.sh?
  - provision install vs provision config vs provision full?
  - Documentation burden (explain 3 paths)

VERDICT: Value << Complexity Cost
RECOMMENDATION: REMOVE THIS PATTERN ❌

Why This Pattern Is Problematic

1. One-Time Usage

  • Wrapper is called once during entire user lifetime
  • Binary wrappers are called 100s of times per day
  • One-time benefit doesn't justify ongoing complexity

2. Confusion

Bundle contains:
  provision.sh         ← Is this the way?
  install.sh           ← Or this?
  setup-config.sh      ← Or this?

Users think: "Which command do I run?"
             "What's the difference?"
             "Which one is recommended?"

Documentation Burden: Must explain 3 approaches instead of 1

3. Not Optional = Misleading

Current Status:
  provision.sh is marked "optional"
  BUT it's included in bundle
  BUT its necessity is unclear
  BUT install.sh works independently

Result: Confusing, not truly optional

4. Maintenance Burden

Code Paths to Test:
  Path 1: provision full              (both steps)
  Path 2: provision install           (binaries only)
  Path 3: provision config            (config only)
  Path 4: bash install.sh             (direct script)
  Path 5: bash setup-config.sh        (direct script)
  Path 6: Manual (copy files)         (user DIY)

Testing Matrix:
  × 3 shells (bash, zsh, NuShell)
  × 4 platforms (Linux, macOS x2, Windows)
  × Multiple scenarios
  = Significant test burden for one-time use

5. Violates SYNTAXIS Philosophy

From CLAUDE.md:

  • "Modular with features to add or avoid"
  • "No unnecessary complexity"
  • "Users understand what's happening"

Wrapper violates:

  • Not modular (orchestrates other scripts)
  • Unnecessary complexity (for one-time use)
  • Less transparent (abstraction not needed)

Comparison: Binary Wrappers vs Installation Wrapper

Aspect Binary Wrappers Installation Wrapper
Frequency 100s per day Once per lifetime
User Pain Config path typing (repetitive) Two commands to type (one-time)
Value/Complexity High Low
Justification Strong Weak
Maintenance Stable/low Medium/scattered
Transparency Can bypass via .real No escape hatch
Philosophy Transparent execution Abstraction

Decision Criteria

When Wrappers Are Justified

✅ Wrappers are justified when:
   Frequency × User Burden > Maintenance Complexity Cost

   Example: Binary wrappers
   - Frequency: 100s/day
   - User Burden: 50 chars/invocation
   - Result: Worth the 3 layers

When Wrappers Are NOT Justified

❌ Wrappers are NOT justified when:
   Frequency × User Burden < Maintenance Complexity Cost

   Example: Installation wrapper
   - Frequency: 1x lifetime
   - User Burden: 50 chars once
   - Result: Not worth the orchestration

Recommendation

For Binary Execution: KEEP Wrapper Pattern

Decision: Keep install-syntaxis wrapper pattern (Bash → NuShell → Rust)

Rationale:

  • Users don't want to type --config path 100s of times per day
  • Config discovery is complex (multiple locations, precedence)
  • Pattern is battle-tested and stable
  • Complexity is justified by frequency of use
  • Transparent to end users (no perceived overhead)

Action Items: None - keep as-is


For Installation: REMOVE Wrapper Pattern

Decision: Remove provision.sh wrapper, use direct scripts only

Rationale:

  • Users call installer once, not repeatedly
  • Wrapper adds unnecessary orchestration
  • Two clear scripts are simpler than three options
  • Violates "no unnecessary complexity" principle
  • Maintenance burden not justified for one-time use

Better Approach:

# Bundle contains only:
install.sh              ← Install binaries
setup-config.sh         ← Configure

# Users follow two-step process:
bash install.sh --interactive
bash setup-config.sh --interactive

# Simple, clear, transparent, Unix-way

Benefits:

  • No confusion (only 1 recommended path)
  • Less code to maintain
  • Clear sequence (install → config)
  • User understands each step
  • Easy to debug (direct scripts)
  • Follows Unix philosophy (one tool per job)

Action Items:

  1. Remove scripts/provisioning/provision.sh
  2. Remove from configs/provisioning.toml artifacts.scripts array
  3. Update docs/installation.md to show two-step process
  4. Update BUNDLE_README.md to show simple sequence

Trade-offs

Keeping Binary Wrappers

Pros:

  • Excellent user experience (no --config needed)
  • Justified by frequency (100s per day)
  • Config discovery is complex (needs orchestration)
  • Transparent (users don't notice)

Cons:

  • More complex architecture
  • Requires both bash and NuShell
  • Adds startup overhead (~50ms)
  • More moving parts to understand

Removing Installation Wrapper

Pros:

  • Simpler (fewer scripts)
  • Clearer (no menu confusion)
  • More transparent (users understand each step)
  • Lower maintenance burden

Cons:

  • Two commands instead of one
  • Users might forget second step (but unlikely - one-time setup)
  • No guided experience (but not needed - simple process)

Implementation

Phase 1: Decision

Status: APPROVED

Phase 2: Code Cleanup

# Remove wrapper
rm scripts/provisioning/provision.sh

# Update config
# Edit: configs/provisioning.toml
# Remove: "scripts/provisioning/provision.sh" from artifacts.scripts array

# Update documentation
# Edit: docs/docs/installation.md - show two-step process
# Edit: docs/BUNDLE_README.md - update quick start
# Delete: docs/BUNDLE_DOCS_GUIDE.md references to provision

Phase 3: Update Bundle

# Rebuild bundle
nu scripts/provisioning/pack.nu

# Verify changes
tar -tzf dist/syntaxis-v0.1.0-*.tar.gz | grep -E '(install|setup|provision)'
# Should show only: install.sh, install.nu, setup-config.sh
# Should NOT show: provision.sh

Phase 4: Verification

# Test installation workflow
tar -xzf dist/syntaxis-v0.1.0-*.tar.gz
cd syntaxis-v0.1.0-*

# Step 1: Install
bash install.sh --interactive

# Step 2: Configure
bash setup-config.sh --interactive

# Verify
syntaxis-cli --version

Risk Assessment

Low Risk

  • Removing optional wrapper
  • Direct scripts are proven to work
  • No impact on binary wrappers
  • Clear documentation available

Mitigation

  • Document two-step process clearly
  • Include both scripts in bundle (proven, working)
  • Test complete installation flow
  • Update all user-facing documentation

Future Considerations

If Installation Complexity Grows

If future bundle setups become complex:

  • Consider wrapper again
  • But only if frequency or complexity increases
  • Apply same cost-benefit analysis

Wrapper Pattern Elsewhere

If other tools need wrappers:

  • Apply same framework
  • Justify by frequency × burden vs complexity
  • Transparency is key

Appendix: Real-World Example

Scenario: New User Installs SYNTAXIS

With Wrapper (Current):

$ cd /tmp/bundle
$ ./provision full

# Menu appears, user selects options
# Both install and configure run
# Done!

Without Wrapper (Proposed):

$ cd /tmp/bundle
$ bash install.sh --interactive

# User prompted for installation options
# Binaries installed
# Done with step 1

$ bash setup-config.sh --interactive

# User prompted for configuration
# Configs created
# Done with step 2

Difference:

  • Wrapper: ~5 seconds (one command, orchestrated)
  • Direct: ~10 seconds (two commands, sequential)
  • Complexity added: ~5 seconds saved, once per lifetime
  • Complexity removed: Eliminates 100+ lines of code, maintenance

Clear winner: Remove wrapper


Conclusion

Binary Execution Wrappers: KEEP

  • Value: Users invoke 100s of times per day
  • Problem Solved: Automatic config injection (major UX improvement)
  • Complexity: Justified and battle-tested

Installation Wrapper: REMOVE

  • Value: Users invoke once per lifetime
  • Problem Solved: Saves one command (minor UX improvement)
  • Complexity: Not justified by benefit

Philosophy: Wrappers should enable frequent workflows, not orchestrate one-time procedures.


Recommendation: Implement Phase 2-4 cleanup to simplify bundle provisioning system.


Status: READY FOR DECISION