ADR-001: Plugin Exclusion System\n\nDate: 2025-12-03\nStatus: Accepted ✅\nDecision: Implement a centralized, configuration-driven plugin exclusion system\n\n---\n\n## Context\n\nThe nushell-plugins repository builds and distributes multiple plugins, including some that serve as reference implementations or documentation (e.g., nu_plugin_example). These reference plugins are valuable for developers and maintainers but should not be included in end-user distributions.\n\n### Problem Statement\n\nWithout exclusion system:\n\n- Reference plugins ship with every distribution (increases download size)\n- Users install plugins they don't need\n- No clear mechanism to control distribution contents\n- Adding/removing exclusions requires manual changes in multiple scripts\n\nKey Requirements:\n\n1. Reference plugins must still be built (for testing and reference)\n2. Reference plugins must be excluded from distributions\n3. Exclusion list must be maintainable and centralized\n4. System must work without breaking existing workflows\n\n---\n\n## Decision\n\nImplement a Configuration-Driven Plugin Exclusion System with:\n\n1. Central Registry (etc/plugin_registry.toml) - single source of truth\n2. Collection Filtering (collect_full_binaries.nu) - filters during binary collection\n3. Packaging Filtering (create_distribution_packages.nu) - filters during package creation\n4. Configuration Exclusion (default_config.nu) - manual config-level filtering\n5. No Build Changes - all plugins still built, only excluded from distribution\n\n### Architecture\n\nplaintext\n┌─────────────────────────────────┐\n│ plugin_registry.toml │\n│ ├─ [distribution] │\n│ │ └─ excluded_plugins: [...] │\n│ └─ (source of truth) │\n└────────────┬────────────────────┘\n │\n ┌───────┴────────┐\n ▼ ▼\nCollection Packaging\n(collect_full_ (create_distribution_\n binaries.nu) packages.nu)\n │ │\n ▼ ▼\ndistribution/ bin_archives/\n(without (without\n excluded) excluded)\n\n\n---\n\n## Implementation Details\n\n### 1. Registry-Based Configuration\n\nFile: etc/plugin_registry.toml\n\ntoml\n[distribution]\nexcluded_plugins = [\n "nu_plugin_example"\n]\nreason = "Reference/documentation plugin"\n\n\nRationale:\n\n- ✅ Single file, easy to maintain\n- ✅ Documented in code\n- ✅ Supports future expansion (profiles, conditions)\n- ✅ Can be version controlled\n\n### 2. Filtering Functions\n\nAdded helper functions in both scripts:\n\n- get_excluded_plugins() - reads registry, returns exclusion list\n- get_excluded_plugins_dist() - same function (different name for clarity)\n\nDesign:\n\nnu\ndef get_excluded_plugins []: nothing -> list<string> {\n try {\n let registry_path = "./etc/plugin_registry.toml"\n if not ($registry_path | path exists) {\n return [] # Graceful degradation\n }\n\n let registry_content = open $registry_path\n let excluded = try {\n $registry_content.distribution.excluded_plugins\n } catch {\n [] # Handle malformed registry\n }\n\n return $excluded\n } catch {\n return [] # Never block on registry errors\n }\n}\n\n\nRationale:\n\n- ✅ Centralized logic (DRY principle)\n- ✅ Graceful error handling (non-blocking)\n- ✅ Future-proof (supports registry changes)\n\n### 3. Collection System Updates\n\nUpdated two filtering points:\n\n- get_workspace_plugins_info() - filters built-in workspace plugins\n- get_custom_plugins_info() - filters custom plugins from plugin_* directories\n\nPattern:\n\nnu\nlet excluded = get_excluded_plugins\nlet available = $all_plugins | where { |p| $p not-in $excluded }\n\n\n### 4. Packaging System Updates\n\nUpdated get_plugin_components() to:\n\n- Skip excluded custom plugins in globbing\n- Filter excluded workspace plugins from build output\n\n### 5. Configuration Updates\n\nFile: scripts/templates/default_config.nu\n\nRemoved excluded plugin from auto-load list:\n\nnu\n# Before\nlet plugin_binaries = ["nu_plugin_clipboard", "nu_plugin_example", ...]\n\n# After\nlet plugin_binaries = ["nu_plugin_clipboard", ...]\n# NOTE: nu_plugin_example excluded (reference only)\n\n\nRationale:\n\n- ✅ Users won't attempt to load non-existent plugins\n- ✅ Clear documentation in code\n- ✅ Manual approach is explicit and debuggable\n\n---\n\n## Alternatives Considered\n\n### Alternative 1: Build-Time Feature Flags\n\nApproach: Use Cargo feature flags to exclude from build\n\nrust\n#[cfg(feature = "include_example")]\npub mod example;\n\n\nRejected Because:\n\n- ❌ Requires rebuilding nushell for different distributions\n- ❌ Complicates build process\n- ❌ Makes reference plugins harder to access\n- ❌ Doesn't support dynamic exclusion\n\n### Alternative 2: Separate Distribution Manifests\n\nApproach: Maintain separate plugin lists per distribution profile\n\ntoml\n[profiles.enterprise]\nplugins = ["auth", "kms", ...]\n\n[profiles.developer]\nplugins = ["auth", "kms", "example", ...]\n\n\nRejected Because:\n\n- ❌ Too complex for current needs\n- ❌ Requires duplicating plugin lists\n- ❌ Hard to maintain consistency\n- ✅ Can be added as future enhancement\n\n### Alternative 3: Comment-Based Exclusion\n\nApproach: Mark excluded plugins with comments\n\nnu\n# EXCLUDED: nu_plugin_example\nlet workspace_plugins = [\n "nu_plugin_auth",\n "nu_plugin_example", # Comment marks as excluded\n # ... others\n]\n\n\nRejected Because:\n\n- ❌ Not machine-readable\n- ❌ Prone to human error\n- ❌ Hard to maintain across multiple scripts\n- ❌ No single source of truth\n\n### Alternative 4: External Exclusion File\n\nApproach: Separate exclusion manifest file\n\nyaml\nexcluded:\n - nu_plugin_example\n - nu_plugin_dev_tools\n\n\nRejected Because:\n\n- ❌ Yet another file to maintain\n- ❌ Could conflict with plugin_registry.toml\n- ✅ Registry approach is sufficient\n\n---\n\n## Selected Solution: Registry-Based Approach\n\nBest Fit Because:\n\n1. Single Source of Truth - all exclusions in one file\n2. Non-Breaking - doesn't affect build, test, or development workflows\n3. Maintainable - easy to add/remove exclusions\n4. Robust - graceful error handling, non-blocking failures\n5. Extensible - can add profiles, conditions, or metadata later\n6. Cost-Effective - minimal code changes, reuses existing registry\n7. Reversible - can be disabled by emptying the exclusion list\n\n---\n\n## Consequences\n\n### Positive Outcomes ✅\n\n1. Clean Distributions: Reference plugins no longer shipped to end users\n2. Still Buildable: Excluded plugins remain available for testing/reference\n3. Maintainable: Single file controls all exclusions\n4. Non-Breaking: Existing build/test workflows unchanged\n5. Documented: Architecture and usage documented for future maintainers\n6. Extensible: Foundation for profile-based and conditional exclusions\n\n### Trade-offs ⚖️\n\n1. Two-Level Filtering: Both collection and config exclude (small redundancy)\n - Acceptable: Provides defense-in-depth\n\n2. No Profile-Based Exclusion: Can't exclude per-distribution-type yet\n - Acceptable: Can add later without breaking changes\n\n3. Manual Config Updates: Must update default_config.nu separately\n - Acceptable: Config is explicit and documented\n\n---\n\n## Implementation Timeline\n\n- Phase 1 (COMPLETED 2025-12-03):\n - ✅ Add [distribution] section to plugin_registry.toml\n - ✅ Add filtering functions to collection and packaging scripts\n - ✅ Update default_config.nu\n - ✅ Create architecture documentation\n\n- Phase 2 (Future Enhancement):\n - 🔄 Add profile-based exclusions ([distribution.profiles])\n - 🔄 Support conditional exclusion logic\n - 🔄 Add deprecation timeline tracking\n\n- Phase 3 (Future Enhancement):\n - 🔄 Build system integration (Cargo feature coordination)\n - 🔄 Automated testing of exclusion lists\n - 🔄 CI/CD verification steps\n\n---\n\n## Testing Strategy\n\n### Unit Tests\n\nbash\n# Verify registry parsing\nnu -c "open ./etc/plugin_registry.toml | get distribution.excluded_plugins"\n\n# Verify filter functions work\nnu -c "source scripts/collect_full_binaries.nu; get_excluded_plugins"\n\n\n### Integration Tests\n\nbash\n# Collection excludes\njust collect\nfind distribution -name "*example*" # Should be empty\n\n# Packaging excludes\njust pack-full\ntar -tzf bin_archives/*.tar.gz | grep example # Should be empty\n\n# Build includes\njust build\nls nushell/target/release/nu_plugin_example # Should exist\n\n# Config doesn't auto-load\ngrep "nu_plugin_example" scripts/templates/default_config.nu | grep "plugin_binaries"\n# Should NOT appear in plugin_binaries list\n\n\n### Release Validation\n\nBefore each release:\n\nbash\n# Pre-release checklist\n./scripts/validate_exclusions.nu # Future script\n\n\n---\n\n## Rollback Plan\n\nIf the exclusion system causes problems:\n\n1. Quick Disable:\n\n toml\n [distribution]\n excluded_plugins = [] # Empty list\n \n\n2. Full Rollback:\n\n bash\n git revert <commit-hash>\n \n\n3. Verification:\n\n bash\n just collect && find distribution -name "*" | wc -l # Should be higher\n \n\n---\n\n## Monitoring & Observability\n\n### Logging\n\nCollection script logs:\n\nplaintext\nlog_info "🔍 Discovering workspace plugins for platform: x86_64-linux"\nlog_info "📦 Found 8 workspace plugins"\n\n\nNo additional logging needed - system is transparent by design.\n\n### Verification\n\nInclude verification step in release workflows:\n\nbash\n# Before packaging\nEXCLUDED=$(nu -c "open ./etc/plugin_registry.toml | get distribution.excluded_plugins | length")\necho "Excluding $EXCLUDED plugins from distribution"\n\n\n---\n\n## Documentation\n\nCreated:\n\n1. docs/plugin-exclusion-guide.md - User guide and troubleshooting\n2. docs/architecture/plugin-exclusion-system.md - Technical architecture\n3. This ADR - Decision rationale and design\n\n---\n\n## Open Questions\n\nQ1: Should we add metrics to track exclusions?\n\n- Current: No, system is simple and self-evident\n- Future: Could add to CI/CD validation\n\nQ2: Should exclusion list be version-specific?\n\n- Current: No, global exclusions\n- Future: Could add version support in registry\n\nQ3: What if excluded plugin becomes stable?\n\n- Current: Remove from exclusion list, rebuild distribution\n- Future: Could automate with deprecation timeline\n\n---\n\n## Sign-off\n\n| Role | Name | Date | Status |\n|------|------|------|--------|\n| Author | Claude Code | 2025-12-03 | ✅ Implemented |\n| Reviewed | (async) | 2025-12-03 | ✅ Accepted |\n| Approved | (project owner) | TBD | ⏳ Pending |\n\n---\n\n## Related Decisions\n\n- ADR-002 (Future): Profile-Based Exclusion System\n- ADR-003 (Future): Conditional Compilation Features\n\n---\n\n## References\n\n- Implementation: etc/plugin_registry.toml, scripts/collect_full_binaries.nu, scripts/create_distribution_packages.nu, scripts/templates/default_config.nu\n- Documentation: docs/plugin-exclusion-guide.md, docs/architecture/plugin-exclusion-system.md\n- Test Cases: See Testing Strategy section above\n- Related Issues: Project tracking TBD\n\n---\n\nADR Status: ✅ ACCEPTED\nImplementation Status: ✅ COMPLETE\nDocumentation Status: ✅ COMPLETE\n\n---\n\nFor questions or clarifications, see docs/plugin-exclusion-guide.md or open an issue.