Skip to content

feat: Add verbosity levels to provision command (-v, -vv, -vvv)#361

Merged
josecelano merged 15 commits intomainfrom
explore-verbosity-levels-provision
Feb 17, 2026
Merged

feat: Add verbosity levels to provision command (-v, -vv, -vvv)#361
josecelano merged 15 commits intomainfrom
explore-verbosity-levels-provision

Conversation

@josecelano
Copy link
Copy Markdown
Member

Overview

This PR adds graduated verbosity levels (-v, -vv, -vvv) to the provision command, giving users control over the amount of progress detail displayed during infrastructure provisioning operations.

Important: This feature controls only user-facing output (via UserOutput), not internal logging (which remains controlled by RUST_LOG environment variable). The two systems are intentionally kept separate.

Implemented Features

Four Verbosity Levels

  1. Normal (default): Essential progress and results

    • Shows main operation phases: Template building, provisioning, configuration
    • Minimal, production-friendly output
  2. Verbose (-v): Detailed progress with intermediate steps

    • Shows all 9 provisioning steps (template rendering, OpenTofu operations, SSH setup, cloud-init wait)
    • Useful for understanding where time is spent
  3. Very Verbose (-vv): Contextual details

    • Shows working directories, file paths, validation results, retry counts
    • Helps understand what the command is doing without excessive detail
  4. Debug (-vvv): Maximum technical detail

    • Shows executed commands (tofu init/validate/plan/apply, ansible-playbook)
    • Shows command parameters, working directories, exit codes
    • Essential for troubleshooting issues

Architecture

  • New Trait: CommandProgressListener in src/application/traits/command_progress_listener.rs

    • Enables application-layer progress reporting without presentation dependencies
    • Four event types: on_step_started(), on_detail(), on_debug(), on_success()
    • View implementation in UserOutputProgressListener converts events to user messages
  • DDD Compliance:

    • Application layer (handlers/steps) reports progress via trait
    • Presentation layer (views) renders progress based on verbosity level
    • Infrastructure layer remains unaware (no listener access due to dependency rules)
  • Documentation: New ADR documenting the architectural decision and nested progress reporting analysis

Implementation Phases

✅ Phase 1: CLI Flags and Wiring

  • Added counted verbosity flags (-v, -vv, -vvv) to GlobalArgs
  • Wired verbosity level through ExecutionContext to controllers
  • Comprehensive help text with examples

✅ Phase 2A: Verbose Level (-v)

  • Added step-level progress for all 9 provisioning steps
  • Handler emits on_step_started() events

✅ Phase 2B: Very Verbose Level (-vv)

  • Enhanced steps to report contextual details
  • Template paths, validation results, resource counts, retry status

✅ Phase 2C: Debug Level (-vvv)

  • Added technical implementation details
  • Executed commands, working directories, command outcomes

✅ Phase 3: Testing & Documentation

  • Manual testing with all verbosity levels
  • Readability verification at all levels
  • User guide documentation (168 lines added to provision.md)
  • Help text examples included in CLI args

Example Output

Normal Level (default)

⏳ [1/3] Building templates...
⏳ [2/3] Creating environment...
⏳ [3/3] Provisioning infrastructure...
⏳   ✓ Infrastructure provisioned (took 25.8s)

Verbose Level (-v)

⏳ [3/3] Provisioning infrastructure...
📋   [Step 1/9] Rendering OpenTofu templates...
📋   [Step 2/9] Initializing OpenTofu...
📋   [Step 3/9] Validating infrastructure configuration...
...
📋   [Step 9/9] Waiting for cloud-init completion...
⏳   ✓ Infrastructure provisioned (took 25.8s)

Debug Level (-vvv)

📋   [Step 2/9] Initializing OpenTofu...
🔍      → Working directory: ./build/my-env/tofu/lxd
🔍      → Executing: tofu init
🔍      → Command completed successfully

Testing

  • ✅ All 2292 tests passing
  • ✅ All 7 linters passing (markdown, yaml, toml, cspell, clippy, rustfmt, shellcheck)
  • ✅ No unused dependencies (cargo machete)
  • ✅ Manual testing verified all verbosity levels work correctly
  • ✅ Output formatting is clean and readable at all levels

Documentation

  • Issue Spec: docs/issues/drafts/add-verbosity-levels-to-provision-command.md (806 lines)
  • User Guide: docs/user-guide/commands/provision.md (+168 lines)
  • ADR: docs/issues/drafts/generic-command-progress-listener-for-verbosity.md
  • Feature Doc: docs/features/progress-reporting-in-application-layer/

Breaking Changes

None. The default behavior (no verbosity flags) remains unchanged. This is a backward-compatible enhancement.

Future Work (Out of Scope)

  • Quiet mode (-q): May be added if there's user demand
  • Extension to other commands: create, configure, deploy, etc.
  • These are deferred to future PRs to validate the pattern with provision first

Related

  • Roadmap Section 8: Add levels of verbosity
  • UX Research: Console Output & Logging Strategy
  • UX Research: User Output vs Logging Separation

Checklist

  • Pre-commit checks pass
  • No unused dependencies
  • All tests pass
  • No clippy warnings
  • Documentation updated
  • Help text includes examples
  • Backward compatible (default unchanged)
  • User output separated from logging

- Add verbosity field to GlobalArgs with ArgAction::Count
- Add verbosity_level() method to convert count to VerbosityLevel enum
- Wire CLI verbosity to Container::new() in bootstrap
- Add comprehensive unit tests for verbosity conversion
- Update doc examples to include verbosity field

This implements Phase 1 of the verbosity feature: CLI flags and wiring.
Users can now specify -v (Verbose), -vv (VeryVerbose), or -vvv (Debug)
to control output detail. Default remains Normal level.

Phase 2 will add progressive detail messages to command handlers.
…rbosity

- Add generic-command-progress-listener-for-verbosity.md with full
  architectural design including trait definition, DDD layer placement,
  nesting analysis, and anti-patterns
- Update verbosity spec with Phase 2 sub-phases (2.0/2A/2B/2C) using
  the CommandProgressListener pattern
- Fix clippy doc-markdown warnings (backtick RUST_LOG, VerbosityLevel,
  VeryVerbose)
- Fix markdown MD036 (emphasis as heading) in both draft documents
- Add 'substep' to project dictionary
…level reporting

Phase 2.0 - Infrastructure:
- Add CommandProgressListener trait in application/ports (DDD port)
- Add NullProgressListener no-op implementation
- Add DetailMessage (Verbose/📋) and DebugDetailMessage (Debug/🔍) types
- Extend Theme with detail_symbol and debug_symbol fields
- Add .detail() and .debug_detail() methods to UserOutput
- Create VerboseProgressListener (presentation layer implementation)

Phase 2A - Step-level reporting for provision command:
- Refactor ProvisionCommandHandler to accept Option<&dyn CommandProgressListener>
- Break create_instance() and wait_for_readiness() into individual steps
- Add on_step_started() calls for all 9 provisioning steps
- Wire VerboseProgressListener in provision controller
- Create RecordingProgressListener test double for assertions
- Add unit tests for notify_step_started helper and step descriptions
The term 'ports' was confusing in an infrastructure project that deals
with network ports (SSH ports, socket addresses). The new name 'traits'
is Rust-idiomatic and immediately clear to developers.

Changes:
- Renamed src/application/ports/ to src/application/traits/
- Updated all imports from application::ports to application::traits
- Updated documentation references in draft design documents
- All 2272 tests pass
Document the architectural decision to use trait-based progress reporting
with Dependency Inversion Principle for command handlers.

Key decisions:
- CommandProgressListener trait in application/traits (not 'ports')
- Optional listener parameter for backward compatibility
- VerboseProgressListener implementation in presentation layer
- RecordingProgressListener test double for verification
- Verbosity level mapping (step/detail/debug)

Related:
- Feature: docs/features/progress-reporting-in-application-layer/
- Design: docs/issues/drafts/generic-command-progress-listener-for-verbosity.md

Added to project dictionary: goroutines, inspectable
Added listener parameter to all Step execute() methods and implemented
on_detail() calls to report intermediate results and contextual information.

Changes:
- InitializeInfrastructureStep: Reports OpenTofu backend initialization
- ValidateInfrastructureStep: Reports configuration validation result
- PlanInfrastructureStep: Parses and reports plan summary (add/change/destroy counts)
- ApplyInfrastructureStep: Reports infrastructure resource creation status
- GetInstanceInfoStep: Reports retrieved instance IP address
- RenderOpenTofuTemplatesStep: Reports OpenTofu file generation
- WaitForSSHConnectivityStep: Reports connection testing and establishment
- WaitForCloudInitStep: Reports cloud-init completion status
- Handler: Added on_detail() calls for Ansible template rendering (step 7)

All Steps now accept Optional<&dyn CommandProgressListener> for backward
compatibility. The listener parameter flows from handler through all Steps.

Detail messages map to VeryVerbose level (-vv) in the presentation layer.

All 2288 library tests pass.
Added 'Implementation Learnings' section to the application-layer progress
reporting ADR based on Phase 2B implementation experience.

New section documents 6 key patterns:
- Report After Infrastructure Calls (respecting DDD boundaries)
- Avoid Private Field Access (use public APIs only)
- Helper Methods Reduce Boilerplate (pattern for if-let-Some checks)
- Message Granularity Guidelines (detail vs debug vs step)
- Listener Threading Through Layers (Option flow)
- Backward Compatibility Validation (2288 tests passed unchanged)

Includes concrete code examples showing correct vs incorrect patterns
(like the build_dir() private field access issue).

Also fixed clippy doc_markdown warnings (missing backticks in OpenTofu docs)
and applied rustfmt formatting to function signatures.
Created StepProgressMessage for Verbose-level step headers and changed
DetailMessage to require VeryVerbose level, ensuring proper progressive
disclosure across verbosity levels.

Changes:
- Created StepProgressMessage with VerbosityLevel::Verbose for step headers
- Changed DetailMessage.required_verbosity() from Verbose to VeryVerbose
- Added UserOutput.step_progress() method for step-level boundaries
- Updated VerboseProgressListener.on_step_started() to use step_progress()
- Updated documentation with actual output examples from manual testing

Verified:
- All 2292 library tests pass
- All linters pass (markdown, yaml, toml, clippy, rustfmt, shellcheck)
- Manual E2E test confirms correct filtering:
  * -v shows only 9 step headers
  * -vv shows step headers + detail messages
Added on_debug() calls to all 9 provisioning steps with technical details
for deep troubleshooting. Debug level provides infrastructure command
execution details, working directories, and runtime parameters.

Changes:
- Added debug calls in Steps 1-6 (infrastructure steps):
  * Working directories for OpenTofu commands
  * Commands executed (tofu init, validate, plan, apply, output)
  * Validation output and plan results
  * Instance name from OpenTofu outputs
- Added debug calls in Steps 7-9 (configuration and readiness):
  * Template and build directories for Ansible
  * Instance IP address injected into templates
  * SSH target configuration (user@host:port, private key path)
  * Ansible working directory and playbook execution
- Updated provision handler with debug info for inline Ansible rendering
- Added actual debug output example in implementation plan

Debug details follow Infrastructure boundaries:
- Steps report around Infrastructure calls using input/output
- Infrastructure layer remains opaque (no listener access per DDD)
- Debug info extracted from command outputs and configuration

Manual testing verified:
- Normal: Essential progress only (3 main steps)
- Verbose (-v): + Step headers (9 steps)
- VeryVerbose (-vv): + Detail messages
- Debug (-vvv): + Technical details (commands, paths, configs)

All 2292 library tests pass
All linters pass (markdown, yaml, toml, clippy, rustfmt, shellcheck)
@josecelano josecelano self-assigned this Feb 17, 2026
@josecelano
Copy link
Copy Markdown
Member Author

ACK dbdbfff

@josecelano josecelano merged commit 0e3dc99 into main Feb 17, 2026
39 checks passed
@josecelano josecelano deleted the explore-verbosity-levels-provision branch February 17, 2026 20:02
josecelano added a commit that referenced this pull request Feb 18, 2026
8c80ff2 docs: [#363] add verbosity documentation to configure command user guide (Jose Celano)
1ab53fd docs: [#363] update issue spec with Phase 2 completion status (Jose Celano)
206d613 feat: [#363] add step-level verbosity (VeryVerbose and Debug levels) (Jose Celano)
e83e50d docs: [#363] update issue spec with Phase 1 completion status (Jose Celano)
666ca36 feat: [#363] add verbosity levels to configure command (Jose Celano)

Pull request description:

  ## Summary

  Implements graduated verbosity levels (`-v`, `-vv`, `-vvv`) for the `configure` command, following the same pattern established in the provision command (PR #361). This gives users control over the amount of progress detail displayed during configuration operations.

  **Related**: Closes #363

  ## What's Changed

  ### Phase 1: Handler Integration (commit `666ca363`)
  - ✅ Updated `ConfigureCommandHandler` to accept `CommandProgressListener` parameter
  - ✅ Emits step progress for all 4 configuration steps at Verbose level
  - ✅ Controller passes listener from `UserOutput` to handler
  - ✅ Backward compatible - default behavior unchanged

  ### Phase 2: Step-Level Progress (commit `206d6137`)
  - ✅ All 4 configuration steps accept optional listener parameter:
    - `InstallDockerStep`
    - `InstallDockerComposeStep`
    - `ConfigureSecurityUpdatesStep`
    - `ConfigureFirewallStep`
  - ✅ Steps emit detail messages (versions, configurations, status)
  - ✅ Steps emit debug messages (Ansible commands, working directories)
  - ✅ Handler passes listener through to all steps

  ### Phase 3: Documentation (commit `8c80ff2b`)
  - ✅ Added comprehensive verbosity section to `docs/user-guide/commands/configure.md`
  - ✅ Included examples for all 4 verbosity levels (Normal, Verbose, VeryVerbose, Debug)
  - ✅ Pattern matches provision command documentation
  - ✅ **Verified with live testing** - all documented output matches actual behavior

  ## Verbosity Levels

  | Level           | Flag      | Shows                                        |
  | --------------- | --------- | -------------------------------------------- |
  | **Normal**      | (default) | Essential progress and results               |
  | **Verbose**     | `-v`      | + Detailed progress (4 configuration steps)  |
  | **VeryVerbose** | `-vv`     | + Context details (versions, status)         |
  | **Debug**       | `-vvv`    | + Technical details (Ansible commands)       |

  ## Example Output

  ### Normal (Default)
  ```text
  ⏳ [1/3] Validating environment...
  ⏳   ✓ Environment name validated: my-env (took 0ms)
  ⏳ [2/3] Creating command handler...
  ⏳   ✓ Done (took 0ms)
  ⏳ [3/3] Configuring infrastructure...
  ⏳   ✓ Infrastructure configured (took 37.3s)
  ✅ Environment 'my-env' configured successfully
  ```

  ### Verbose (-v)
  ```text
  ⏳ [3/3] Configuring infrastructure...
  📋   [Step 1/4] Installing Docker...
  📋   [Step 2/4] Installing Docker Compose...
  📋   [Step 3/4] Configuring automatic security updates...
  📋   [Step 4/4] Configuring firewall (UFW)...
  ⏳   ✓ Infrastructure configured (took 34.1s)
  ```

  ### VeryVerbose (-vv)
  ```text
  📋   [Step 1/4] Installing Docker...
  📋      → Installing Docker Engine from official repository
  📋      → Docker version: 24.0.7
  📋   [Step 2/4] Installing Docker Compose...
  📋      → Installing Docker Compose plugin
  📋      → Compose version: 2.23.3
  ```

  ### Debug (-vvv)
  ```text
  📋   [Step 1/4] Installing Docker...
  🔍      → Ansible working directory: ./build/my-env/ansible
  🔍      → Executing playbook: ansible-playbook install-docker.yml -i inventory.ini
  📋      → Installing Docker Engine from official repository
  📋      → Docker version: 24.0.7
  ```

  ## Testing

  ✅ **All tests pass**: 2292 library tests
  ✅ **Pre-commit checks**: All linters pass (markdown, YAML, TOML, cspell, clippy, rustfmt, shellcheck)
  ✅ **No unused dependencies**: cargo machete clean
  ✅ **Live testing**: Created 4 test environments and verified actual output matches documentation at all verbosity levels

  ## Acceptance Criteria

  **22/22 criteria complete (100%)** ✅

  **Quality Checks** (4/4):
  - ✅ Pre-commit checks pass
  - ✅ No unused dependencies
  - ✅ All tests pass
  - ✅ No clippy warnings

  **Task-Specific Criteria** (11/11):
  - ✅ Configure command accepts `-v`, `-vv`, `-vvv` flags
  - ✅ Default behavior unchanged
  - ✅ Verbose shows 4 configuration steps
  - ✅ VeryVerbose shows detail messages
  - ✅ Debug shows Ansible commands
  - ✅ User output separate from logs
  - ✅ RUST_LOG controls logging independently
  - ✅ Help text explains verbosity levels
  - ✅ Output clean and readable at all levels
  - ✅ Channel separation maintained
  - ✅ Pattern matches provision command

  ## Architecture

  - **DDD Layer**: Application (`src/application/command_handlers/configure/`)
  - **Pattern**: Reuses `CommandProgressListener` trait from provision command
  - **Consistency**: Follows same four verbosity levels as provision command
  - **Separation of Concerns**: User output (verbosity) completely separate from internal logging (`RUST_LOG`)

  ## Files Modified

  ### Source Code (5 files)
  - `src/application/command_handlers/configure/handler.rs` - Handler integration and step coordination
  - `src/application/steps/software/docker.rs` - Docker installation step with listener
  - `src/application/steps/software/docker_compose.rs` - Docker Compose installation step with listener
  - `src/application/steps/system/configure_security_updates.rs` - Security updates step with listener
  - `src/application/steps/system/configure_firewall.rs` - Firewall configuration step with listener

  ### Documentation (2 files)
  - `docs/user-guide/commands/configure.md` - Added comprehensive verbosity section with examples
  - `docs/issues/363-add-verbosity-levels-to-configure-command.md` - Issue specification and tracking

  ## Related PRs

  - #361 - Add verbosity levels to provision command (reference implementation)

  ## Breaking Changes

  None. This is a backward-compatible addition - default behavior remains unchanged.

  ## Notes

  - Verbosity controls **only user-facing progress messages** (via `UserOutput`)
  - Internal diagnostic logs controlled by `RUST_LOG` environment variable
  - Symbol legend: ⏳ (progress), ✅ (success), 📋 (details), 🔍 (debug)
  - Documentation verified with live testing - all examples match actual output

ACKs for top commit:
  josecelano:
    ACK 8c80ff2

Tree-SHA512: aea35dc0bcb2a9871fa7098deec0a29a8c47a74b25a2dd70f96938fc134d423a1a30733fe9b7aba032d3a76d7164ef74478ed9b5c4894b9c1b236bed7b183826
This was referenced Feb 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant