# Task Recon: Implement OutputFormatter

**Task ID:** 8
**Status:** pending
**Generated:** 2026-01-28T12:00:00Z
**Agent:** claude-opus-4-5-20251101

---

## Task Summary

Create the OutputFormatter component for the infrastructure layer that provides rich table display, JSON output mode, progress bars, and formatted error messages with remediation guidance. This is a foundational UI component that will be used by all CLI commands.

### Dependencies
- Depends on: Task 1 (Initialize Python Project Structure) - **DONE**

### Acceptance Criteria
- Create `src/teams_phone/infrastructure/output_formatter.py`
- Implement `OutputFormatter` class with the following methods:
  - `__init__(console, json_mode, no_color)` - Initialize with optional Rich Console
  - `table(data, columns, title)` - Display data as Rich table
  - `json(data)` - Output valid JSON to stdout
  - `success(message)` - Display green success message
  - `error(message, remediation)` - Display red error with optional remediation guidance
  - `warning(message)` - Display yellow warning message
  - `info(message)` - Display info message
  - `progress(total, description)` - Return Progress context manager
- JSON mode must output ONLY valid JSON to stdout (no markup/formatting)
- Color must respect `--no-color` flag AND `NO_COLOR` environment variable
- Error messages must include remediation guidance when provided
- Unit tests with stdout capture verifying all output modes

---

## Insights from Previous Tasks

### Dependency Task Notes
| Task ID | Title | Key Insight |
|---------|-------|-------------|
| 1 | Initialize Python Project Structure | rich>=13.0.0 is already in dependencies; mypy strict mode requires full type hints |

### Reusable Patterns from Dependencies
- Package structure established in Task 1 - infrastructure layer at `src/teams_phone/infrastructure/`
- Module exports via `__init__.py` - see `src/teams_phone/infrastructure/__init__.py:1-7`

### Warnings and Gotchas
- **mypy strict mode**: All functions need complete type annotations
- **pytest markers**: Use `@pytest.mark.unit` for unit tests

### Open Items Inherited
- None affecting this task

---

## Architecture Context

### Relevant Design Decisions
- **OutputFormatter is in infrastructure layer**: Part of the lowest-level components with no external dependencies
- **Rich library for terminal output**: Project uses Rich for tables, progress bars, and styled text
- **JSON mode for automation**: When `--json` flag is used, output ONLY valid JSON to stdout
- **NO_COLOR standard**: Must respect both `--no-color` CLI flag AND `NO_COLOR` environment variable

### Module Structure
The OutputFormatter fits into the infrastructure layer alongside:
- `ConfigManager` - Configuration file management
- `CacheManager` - Token and data caching
- `GraphClient` - HTTP client for Graph API

The CLI layer will depend on OutputFormatter for all user-facing output.

### Conventions to Follow
- Use Pydantic strict mode for any models (though OutputFormatter may not need models)
- All errors include remediation guidance
- `--json` output must be valid JSON only (no extra text)
- Python 3.10+ syntax (use `|` for Union types)

---

## Related Code References

### Primary References (Must Read)
| File | Lines | Purpose |
|------|-------|---------|
| `src/teams_phone/infrastructure/__init__.py` | 1-7 | How to export OutputFormatter from infrastructure module |
| `src/teams_phone/exceptions.py` | 1-138 | Exception hierarchy with `message` and `remediation` attributes |
| `.taskmaster/docs/cli-specification/output-formats.md` | 1-51 | Output format specification including JSON structure |
| `.taskmaster/docs/cli-specification/error-handling.md` | 1-69 | Error message format with remediation guidance |

### Secondary References (Helpful Context)
| File | Lines | Purpose |
|------|-------|---------|
| `src/teams_phone/constants.py` | 1-97 | ExitCode enum for reference (not directly used by OutputFormatter) |
| `.taskmaster/docs/architecture/5-components.md` | 168-183 | OutputFormatter component specification |
| `.taskmaster/docs/architecture/11-error-handling-strategy.md` | 54-70 | Error message format example |
| `tests/unit/test_exceptions.py` | 1-225 | Test patterns - how to structure unit tests |

### Code Patterns to Follow

```python
# Example infrastructure class pattern from config_manager.py
class ConfigManager:
    """Read/write TOML configuration file and tenant profiles."""

    def __init__(self, config_path: str | None = None) -> None:
        """Initialize ConfigManager with optional custom path.

        Args:
            config_path: Path to config file. Defaults to ~/.teams-phone/config.toml
        """
        # Implementation...
```

```python
# Example test pattern from test_exceptions.py:21-26
class TestTeamsPhoneError:
    """Tests for the base TeamsPhoneError class."""

    @pytest.mark.unit
    def test_base_exception_exit_code(self) -> None:
        """Base exception has GENERAL_ERROR exit code."""
        exc = TeamsPhoneError("Something went wrong")
        assert exc.exit_code == ExitCode.GENERAL_ERROR
```

---

## Dependencies

### Internal Modules
- `teams_phone.exceptions` - For exception types with `message` and `remediation` attributes (reference only)

### External Packages
- `rich>=13.0.0` - Already in pyproject.toml for Console, Table, Progress, and styling

### New Dependencies Needed
- None required - Rich is already available

---

## Testing Guidance

### Test File Location
`tests/unit/test_output_formatter.py`

### Test Patterns in Use

```python
# Example test structure from test_config_manager.py:20-27
class TestConfigManagerInit:
    """Tests for ConfigManager initialization."""

    @pytest.mark.unit
    def test_default_path(self) -> None:
        """ConfigManager uses default path when not specified."""
        manager = ConfigManager()
        assert manager.config_path == Path.home() / ".teams-phone" / "config.toml"
```

### Capturing Rich Console Output for Testing

From Rich documentation - use StringIO to capture output:
```python
from io import StringIO
from rich.console import Console

# Capture output for testing
console = Console(file=StringIO(), force_terminal=True)
console.print("[bold red]Hello[/] World")
str_output = console.file.getvalue()
```

### Fixtures Available
- `tmp_path` - pytest built-in for temporary directory (if needed for file output tests)

### Test Utilities
- `tests/conftest.py` - Contains pytest marker registration

### Key Test Scenarios
1. **Table output**: Verify table formatting with data and columns
2. **JSON output**: Verify valid JSON output with `json.loads()` parsing
3. **Color codes presence/absence**: Test with `no_color=True` vs default
4. **NO_COLOR env var**: Test that environment variable is respected
5. **Progress bar**: Verify Progress context manager is returned
6. **Error with remediation**: Verify remediation text is included
7. **Success/warning/info messages**: Verify correct styling and output

---

## Risks and Blockers

### Technical Risks
- [ ] **NO_COLOR env var handling**: Rich should handle `NO_COLOR` automatically, but need to verify behavior with tests

### Dependency Risks
- None - Task 1 is complete and Rich is available

### Open Questions
- [ ] Should `json()` method use `print_json()` from Rich (which syntax-highlights) or plain `json.dumps()` for pure JSON output?
  - **Recommendation**: Use `json.dumps()` directly for `--json` mode to ensure ONLY valid JSON is output (no ANSI codes)
- [ ] Should the Progress returned be a context manager or should OutputFormatter provide `start_progress()`/`update_progress()` methods?
  - **Recommendation**: Return Rich's Progress context manager directly for simplicity

---

## Implementation Recommendations

### Suggested Approach
1. Create `src/teams_phone/infrastructure/output_formatter.py` with the `OutputFormatter` class
2. Implement `__init__` with Console creation, respecting `json_mode`, `no_color`, and `NO_COLOR` env var
3. Implement `table()` method using `rich.table.Table`
4. Implement `json()` method using `json.dumps()` for pure JSON output
5. Implement message methods (`success`, `error`, `warning`, `info`) with appropriate Rich styling
6. Implement `progress()` method returning Rich Progress context manager
7. Export `OutputFormatter` from `infrastructure/__init__.py`
8. Write comprehensive unit tests in `tests/unit/test_output_formatter.py`

### Files to Create/Modify
| Action | File | Reason |
|--------|------|--------|
| Create | `src/teams_phone/infrastructure/output_formatter.py` | Main implementation |
| Modify | `src/teams_phone/infrastructure/__init__.py` | Export OutputFormatter |
| Create | `tests/unit/test_output_formatter.py` | Unit tests |

### Quick Wins
- Start with `__init__` and basic `info()` method to validate Console setup
- Add `json()` next as it's the simplest (just `json.dumps`)
- `table()` is the most complex - implement after simpler methods work

---

## Cheat Sheet

### Key Imports
```python
from __future__ import annotations

import json
import os
from typing import Any

from rich.console import Console
from rich.progress import Progress
from rich.table import Table
```

### Key Functions to Use
- `Console(force_terminal=True, no_color=True)` - Create console with color control
- `Table(title="Title")` - Create table with title
- `table.add_column("Name", style="cyan")` - Add styled column
- `table.add_row("value1", "value2")` - Add data row
- `console.print(table)` - Print table to console
- `console.print("[green]Success[/green]")` - Styled text with markup
- `Progress()` - Progress bar context manager
- `json.dumps(data, indent=2)` - JSON serialization

### NO_COLOR Handling
```python
# Check both flag and environment variable
no_color = no_color or os.environ.get("NO_COLOR") is not None
```

### Common Patterns
- Console instantiation: See Rich docs for `force_terminal`, `no_color` options
- JSON mode: When `json_mode=True`, use plain `print(json.dumps(data))` not Rich
- Error formatting: `"[red]Error:[/red] {message}\n\nTo fix:\n{remediation}"`
- Table styling: Use `style="cyan"` for columns, markup in cells

---

*Generated by task-recon (claude-opus-4-5-20251101) | Ready for implementation*
