Last Updated: 2026-01-04 Current Phase: PRODUCTION - Multi-manifest architecture with mise tools consolidation! ✅
Multi-Manifest Architecture - Completed 2026-01-04
- Split monolithic 438-line
packages.yamlinto 3 organized files - Created
common.yaml(cross-platform packages + consolidated mise tools) - Created
ubuntu.yaml(Ubuntu-specific packages) - Created
macos.yaml(macOS-specific packages) - Consolidated 35 mise packages from 140 lines → 50 lines (64% reduction)
- Reorganized from 4 categories → 9 specific categories
- Removed unused bulk_install_groups feature (191 lines of dead code)
- All 184 tests passing ✅
# Run all tests (184 tests)
make -f test.mk test
# View test coverage
make -f test.mk test-coverage
# Install packages (manifest is now the default)
./install.sh --minimal --dry-run # Preview minimal profile
./install.sh --dry-run # Preview dev profile
# Direct manifest usage (now uses directory)
bash install/packages-manifest.sh minimal --dry-run
bash install/packages-manifest.sh dev
# Test manifest queries (multi-manifest aware)
source install/lib/manifest-parser.sh
# Queries now work with merged manifests automatically
get_packages_for_profile_multi install/manifests minimal ubuntu
validate_manifest_schema install/manifests/common.yamlWhat was built:
-
Manifest Parser (
install/lib/manifest-parser.sh)- Full YAML querying library using yq
- Functions for filtering by profile, platform, category
- Priority chain resolution
- Package manager config extraction
- 22 passing tests
-
JSON Schema Validation (
install/schemas/package-manifest.schema.json)- Comprehensive schema definition
- Validates all manifest fields and constraints
- Enforces profile rules (packages XOR includes/excludes)
- Platform and priority value enums
- PPA format validation (must start with "ppa:")
- 27 passing tests
-
Package Manifest (
install/manifests/packages.yaml)- Defines all packages with metadata
- 4 installation profiles: full, dev, minimal, remote
- 4 categories: language_runtimes, general_tools, system_libraries, gui_applications
- ~70 packages defined with platform filters and priority chains
- Bulk install groups for optimization
-
Test Infrastructure
- BATS testing framework
- Test fixtures and helpers
- Makefile-based test runner (
test.mk) - 49/49 tests passing
Key Files:
install/lib/manifest-parser.sh- Parser with validationinstall/schemas/package-manifest.schema.json- JSON Schemainstall/manifests/packages.yaml- Package definitionsinstall/tests/test-manifest-parser.bats- Parser tests (22)install/tests/test-schema-validation.bats- Schema tests (27)test.mk- Test runnermise/config.toml- Added pipx:check-jsonschema
Dependencies Installed:
mise install yq@latest # YAML processor
mise install bats@latest # Test framework
mise install pipx:check-jsonschema@latest # Schema validatorWhat was built: All 4 package manager backends implemented with comprehensive test coverage using TDD methodology.
Backends Implemented:
Test Results: 22/22 tests passing
Functions:
apt_get_package_name- Extract package name(s) from manifestapt_check_installed- Check if package is installed via dpkgapt_install_package- Install single package with dry-run supportapt_install_bulk- Batch install multiple packages
Features:
- Handles single package or packages array
- Checks installation status before installing
- Comprehensive error handling and validation
- Dry-run mode for testing
- Installation summaries
Test Results: 27/27 tests passing Functions:
brew_get_package_name- Extract package name from manifestbrew_is_cask- Determine if package is a cask or formulabrew_check_installed- Check installation statusbrew_install_package- Install formula or cask with dry-run supportbrew_add_tap- Add custom Homebrew tapsbrew_install_bulk- Batch install multiple packages
Features:
- Automatically distinguishes formulas from casks
- Validates tap name format
- Handles mixed formula/cask installations
- Installation summaries
Test Results: 23/23 tests passing Functions:
ppa_get_repository- Extract PPA repository URL from manifestppa_get_package_name- Extract package name(s) from manifestppa_get_gpg_key- Get optional GPG key URLppa_check_added- Check if PPA is already addedppa_add_repository- Add PPA with optional GPG keyppa_install_package- Add PPA and install packages
Features:
- Validates PPA format (must start with "ppa:")
- Handles optional GPG keys
- Checks if PPA already added before adding
- Automatically runs apt-get update after adding PPA
- Supports multiple packages per PPA
Test Results: 22/22 tests passing Functions:
mise_is_managed- Check if package is managed by misemise_check_available- Check if mise command existsmise_check_tool_available- Check if tool exists in mise registrymise_check_installed- Check if tool is installed (not just listed)mise_get_version- Get version to install (defaults to "latest")mise_install_tool- Install tool with dry-run supportmise_sync_with_manifest- Sync all mise tools in a category
Features:
- Distinguishes between listed and actually installed tools
- Handles explicit version specifications
- Syncs entire categories of mise-managed tools
- Gracefully handles mise not being available
Test Files:
install/tests/test-backend-apt.bats- APT backend tests (22)install/tests/test-backend-homebrew.bats- Homebrew backend tests (27)install/tests/test-backend-ppa.bats- PPA backend tests (23)install/tests/test-backend-mise.bats- mise backend tests (22)
TDD Methodology Used: All backends were developed using strict Test-Driven Development:
- ✅ Write failing tests first (Red phase)
- ✅ Implement minimal code to pass tests (Green phase)
- ✅ Refactor while keeping tests green (Refactor phase)
- ✅ Verify no regressions with full test suite
Common Backend Interface: All backends follow a consistent pattern:
<backend>_install_package(manifest_file, package_name, [dry_run])
<backend>_check_installed(package_name)
<backend>_get_package_name(manifest_file, package_name)Phase 2 Test Coverage:
- Total: 94/94 tests passing
- All backends tested with dry-run mode
- Error handling and validation tested
- Edge cases covered (missing files, corrupted manifests, invalid packages)
What was built: Complete manifest-driven package installation orchestration system with CLI interface.
Implementation: install/packages-manifest.sh - Main orchestration layer
Features:
- Profile-based installation: Select from
full,dev,minimal, orremoteprofiles - Automatic backend resolution: Determines best package manager for each package based on priority chains
- Platform detection: Automatically filters packages by OS (ubuntu, macos, linux)
- Mixed backend orchestration: Handles apt, homebrew, PPA, and mise in single installation
- Dry-run mode: Test installations without making changes (
--dry-run) - CLI interface: User-friendly command-line tool with help documentation
- Comprehensive error handling: Graceful degradation and detailed reporting
- Installation summaries: Track succeeded, skipped, and failed packages
Functions Implemented:
detect_platform()- Detects current OS (ubuntu/macos/linux)get_profile_packages()- Returns all packages for a profileresolve_package_backend()- Determines best backend for a packageinstall_package_with_backend()- Installs package using specified backendinstall_from_manifest()- Main installation orchestratormanifest_install_main()- CLI entry point with argument parsing
Test Results: 30/30 integration tests passing
CLI Usage Examples:
# Show help
bash install/packages-manifest.sh --help
# Install with dry-run
bash install/packages-manifest.sh minimal --dry-run
# Install dev profile
bash install/packages-manifest.sh dev
# Custom manifest
bash install/packages-manifest.sh --manifest=custom.yaml fullWhat was done: Fully integrated manifest-based installation as the default and only package installation system.
Implementation Changes in install.sh:
- Removed legacy
install/packages.sh(683 lines removed) - Removed
USE_MANIFESTflag and--use-manifestoption - Manifest installation is now always used in Step 2
- Profile mapping:
--minimalor--no-languages→ "minimal" profile, default → "dev" profile
Usage:
# Install packages (manifest is the only way)
./install.sh --dry-run # Dev profile (59 packages)
./install.sh --minimal --dry-run # Minimal profile (6 packages)
./install.sh --minimal # Install minimal profile
# Direct manifest CLI
bash install/packages-manifest.sh dev --dry-run
bash install/packages-manifest.sh minimalProfile Mapping:
--minimalor--no-languages→ "minimal" profile (6 core CLI tools)- Default → "dev" profile (59 packages - all tools except GUI apps)
- Future: Could add
--fullflag for "full" profile (including GUI apps)
Benefits:
- ✅ Simplified codebase: No legacy code to maintain
- ✅ Declarative: All packages defined in YAML
- ✅ Testable: 173 passing tests
- ✅ Maintainable: Easy to add/remove packages
- ✅ Consistent: One installation path for all users
Migration Complete:
- Legacy
install/packages.shremoved - Manifest system is production-ready
- No backwards compatibility needed
What was built: Complete reorganization of package manifests into logical, platform-specific files with consolidated mise tools.
Key Changes:
-
File Structure Reorganization:
- Replaced single
packages.yaml(438 lines) with 3 focused files common.yaml(~200 lines) - Cross-platform packages and mise toolsubuntu.yaml(~120 lines) - Ubuntu-specific packagesmacos.yaml(~80 lines) - macOS-specific packages
- Replaced single
-
Consolidated mise Tools Format:
- New
mise_toolssection with compressed inline format - Reduced 35 mise packages from 140 lines → 50 lines (64% reduction)
- Explicit
global: trueflag for mise installations - Example format:
{ name: fzf, desc: "Fuzzy finder" }
- New
-
Category Reorganization:
- Split overly-broad
general_tools(39 packages) into 9 specific categories:core_utils- Essential POSIX utilities (7 packages)shell_tools- Shell enhancements (6 packages)dev_tools- Development utilities (8 packages)git_tools- Git ecosystem (6 packages)file_tools- File management (6 packages)monitoring_tools- System monitoring (5 packages)language_runtimes- Programming languages (8 packages)system_libraries- Build dependencies (12 packages)gui_applications- Desktop apps (1 package)
- Split overly-broad
-
Multi-Manifest Loading:
- New functions in
manifest-parser.sh:load_manifests_for_platform()- Loads common + platform-specificmerge_manifests()- Merges YAML files (later overrides earlier)expand_mise_tools()- Converts mise_tools to package format
- Automatic platform detection (ubuntu/macos/linux)
- Orchestration layer updated to use manifest directories
- New functions in
-
Schema Updates:
- Added
mise_toolssection support - Made profiles/categories/packages optional (for platform-specific files)
- Added new platforms: fedora, debian, arch, amazonlinux
- Added
Test Results: 184/184 tests passing ✅
- 14 new parser tests for multi-manifest functionality
- Updated 30 integration tests for directory-based loading
- Removed 3 schema validation tests for unused bulk_install_groups feature
Benefits:
- ✅ Multi-distro ready: Easy to add Fedora, Arch, Debian support
- ✅ Reduced duplication: 64% reduction in mise package definitions
- ✅ Logical organization: 9 specific categories vs 4 overly-broad
- ✅ Platform separation: Ubuntu/macOS packages cleanly separated
- ✅ Scalable: Each distro gets its own file, common packages shared
- ✅ Explicit mise global install:
mise_tools.global: true
Files Modified:
install/lib/manifest-parser.sh- Added multi-manifest support (lines 306-528)install/packages-manifest.sh- Updated to use manifest directoriesinstall.sh- Updated to pass manifest directoryinstall/schemas/package-manifest.schema.json- Added mise_tools sectioninstall/tests/test-manifest-parser.bats- Added 14 new testsinstall/tests/test-integration.bats- Updated for directory structureinstall/tests/test-schema-validation.bats- Updated for optional sections
Files Created:
install/manifests/common.yaml- Cross-platform packagesinstall/manifests/ubuntu.yaml- Ubuntu-specific packagesinstall/manifests/macos.yaml- macOS-specific packages
Files Removed:
install/manifests/packages.yaml- Replaced by multi-manifest structure
install/manifests/
├── common.yaml # Cross-platform packages + mise tools
├── ubuntu.yaml # Ubuntu-specific packages
├── macos.yaml # macOS-specific packages
└── schemas/
└── package-manifest.schema.json
Loading: Platform automatically detected, merges common.yaml + {platform}.yaml
- full: Complete dev environment (all categories)
- dev: Headless dev (all except GUI apps)
- minimal: Essential CLI only (git, curl, wget, tmux, tree, pinentry)
- remote: Lightweight server (minimal + htop + build-essential)
- core_utils: Essential POSIX utilities (priority: apt → homebrew)
- shell_tools: Shell enhancements (priority: mise → homebrew → apt)
- dev_tools: Development utilities (priority: mise → homebrew)
- git_tools: Git ecosystem tools (priority: mise → homebrew → apt)
- file_tools: File search/management (priority: mise → homebrew)
- monitoring_tools: System monitoring (priority: mise → homebrew → apt)
- language_runtimes: Programming languages (priority: mise)
- system_libraries: Build dependencies (priority: apt only)
- gui_applications: Desktop apps (priority: homebrew-cask → flatpak)
# In common.yaml or platform-specific file
packages:
git:
category: core_utils
description: "Version control system"
priority: ["apt", "homebrew"] # Override category default
platforms: ["ubuntu", "macos"] # Optional
apt:
package: git
homebrew:
package: git# In common.yaml
mise_tools:
global: true # Install globally (mise use -g)
categories:
shell_tools:
description: "Shell enhancement tools"
tools:
- { name: fzf, desc: "Fuzzy finder" }
- { name: zoxide, desc: "Smarter cd command" }
- { name: bat, desc: "Cat with syntax highlighting" }
dev_tools:
description: "Development utilities"
tools:
- { name: jq, desc: "JSON processor" }
- { name: yq, desc: "YAML processor" }
- { name: shellcheck, desc: "Shell script linter" }Note: mise_tools are automatically expanded into package format during manifest loading.
make -f test.mk help # Show all commands
make -f test.mk test # Run all tests
make -f test.mk test-parser # Parser tests only
make -f test.mk test-verbose # Verbose output
make -f test.mk test-watch # Watch mode
make -f test.mk test-coverage # Coverage summaryinstall/tests/
├── fixtures/
│ └── test-packages.yaml # Test data
├── test-helper.bash # Common utilities
├── test-manifest-parser.bats # Parser tests (36 - includes multi-manifest)
├── test-schema-validation.bats # Schema tests (27)
├── test-integration.bats # Integration tests (30)
├── test-backend-apt.bats # APT backend tests (22)
├── test-backend-homebrew.bats # Homebrew backend tests (27)
├── test-backend-ppa.bats # PPA backend tests (23)
└── test-backend-mise.bats # mise backend tests (22)
Total: 184 tests passing ✅
# Using function (validates individual files)
source install/lib/manifest-parser.sh
validate_manifest_schema install/manifests/common.yaml
validate_manifest_schema install/manifests/ubuntu.yaml
validate_manifest_schema install/manifests/macos.yaml
# Direct command
check-jsonschema --schemafile install/schemas/package-manifest.schema.json \
install/manifests/common.yamlsource install/lib/manifest-parser.sh
# Multi-manifest queries (recommended - auto-merges)
get_packages_for_profile_multi install/manifests minimal ubuntu
get_packages_for_profile_multi install/manifests dev macos
# Single-file queries (for testing individual manifests)
get_packages_for_profile install/manifests/common.yaml minimal
get_packages_for_platform install/manifests/common.yaml ubuntu
get_package_priority install/manifests/common.yaml git
is_managed_by_mise install/manifests/common.yaml ruby
# Load and merge manifests manually
manifests=($(load_manifests_for_platform install/manifests ubuntu))
merged=$(merge_manifests "${manifests[@]}")
echo "$merged" > /tmp/merged.yamlAll documentation is in AGENTS.md:
- Full refactor overview and architecture
- Phase 1, 2, 3 details
- Manifest schema reference
- Testing guide
- Adding packages and profiles
- Migration strategy
- Schema Validation: Using check-jsonschema via mise (not yq, which lacks schema support)
- Version Format: Accepts both string "1.0" and number 1.0
- Profile Types: Support both category-based (includes/excludes) and explicit (packages array)
- Priority Chain: Per-package overrides category defaults
- NO snap: Explicitly excluded from valid package managers
- Graceful Degradation: Schema validation falls back to basic yq validation if check-jsonschema unavailable
- Multi-Manifest Architecture: Common + platform-specific files for scalability (Phase 4)
- Consolidated mise Format: Inline
{ name, desc }format for 64% space reduction - Optional Schema Sections: Profiles/categories/packages optional to support platform-specific files
- 9 Categories: Replaced overly-broad general_tools with 9 specific categories
- None currently - All phases complete, 184/184 tests passing ✅
New files (ready to commit):
- install/manifests/common.yaml
- install/manifests/ubuntu.yaml
- install/manifests/macos.yaml
- REFACTOR_STATUS.md (this file - updated)
Modified:
- install/lib/manifest-parser.sh (added multi-manifest support)
- install/packages-manifest.sh (updated for manifest directories)
- install.sh (updated to pass manifest directory)
- install/schemas/package-manifest.schema.json (added mise_tools section)
- install/tests/test-manifest-parser.bats (added 14 tests)
- install/tests/test-integration.bats (updated for directory structure)
- install/tests/test-schema-validation.bats (updated for optional sections)
- test.mk (updated coverage count)
Removed:
- install/manifests/packages.yaml (replaced by multi-manifest)
Current Status:
- ✅ All 4 Phases Complete - 184/184 tests passing
- ✅ Multi-manifest architecture implemented and tested
- ✅ Manifest is the only installation system (no legacy code)
- ✅ Production-ready with comprehensive test coverage
- ✅ Dead code removed - 191 lines of unused code eliminated
Quick Reference:
- Run tests:
make -f test.mk test - Install packages:
./install.sh --dry-run - Manifests:
install/manifests/{common,ubuntu,macos}.yaml - Parser:
install/lib/manifest-parser.sh - Orchestration:
install/packages-manifest.sh
Future Enhancements:
- Add fedora.yaml, debian.yaml, arch.yaml for additional distro support
- Consider adding GUI tools categories
- Explore automatic manifest validation in CI/CD
Session 2026-01-04 - Phase 4 Complete + Dead Code Cleanup:
- Split packages.yaml into multi-manifest architecture
- Created common.yaml, ubuntu.yaml, macos.yaml
- Consolidated 35 mise packages (140 lines → 50 lines, 64% reduction)
- Reorganized into 9 specific categories (was 4 overly-broad)
- Added multi-manifest loading and merging functions
- Updated schema to support mise_tools section
- Removed legacy packages.yaml
- Removed unused bulk_install_groups feature (191 lines dead code)
- Updated all 184 tests to pass with new structure
- Updated all documentation
Session 2026-01-03 - Phases 1-3 Complete:
- Implemented all 4 backend modules (apt, homebrew, ppa, mise) using TDD
- Created integration layer with CLI (
packages-manifest.sh) - Removed legacy install/packages.sh (683 lines)
- Made manifest the default and only installation system
- Added comprehensive JSON Schema validation
- Built test infrastructure with 173 passing tests
- Complete migration from legacy to manifest-based system
Initial session:
- Implemented manifest parser with yq
- Created package manifest with profiles
- Built test infrastructure
- 22 parser tests passing