---
phase: 03-registry-compatibility
plan: 02
type: execute
wave: 1
depends_on: []
files_modified:
  - src/clawrium/core/hardware.py
  - tests/test_hardware.py
autonomous: true
requirements: [REG-03]

must_haves:
  truths:
    - "HardwareInfo includes OS distribution and version detected via Ansible facts"
    - "OS info (os, os_version) is gathered alongside existing hardware facts"
    - "Hosts added via clm host add now have os and os_version in hardware dict"
  artifacts:
    - path: "src/clawrium/core/hardware.py"
      provides: "Extended HardwareInfo with OS fields"
      contains: "os:"
    - path: "tests/test_hardware.py"
      provides: "Tests for OS detection"
      min_tests: 2
  key_links:
    - from: "src/clawrium/core/hardware.py"
      to: "ansible_distribution facts"
      via: "extract_hardware_from_facts"
      pattern: "ansible_distribution"
---

<objective>
Extend HardwareInfo TypedDict to include OS detection fields.

Purpose: Claw compatibility checking requires knowing the host's OS distribution and version. This data comes from Ansible facts (ansible_distribution, ansible_distribution_version) which are already gathered during hardware detection. We need to extract and store these fields.

Output: Extended HardwareInfo type with os and os_version fields, updated extraction logic, and tests.
</objective>

<execution_context>
@/home/devashish/workspace/ric03uec/clawrium/.claude/get-shit-done/workflows/execute-plan.md
@/home/devashish/workspace/ric03uec/clawrium/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/03-registry-compatibility/03-CONTEXT.md

<interfaces>
<!-- Current HardwareInfo from hardware.py -->

```python
class GpuInfo(TypedDict):
    """GPU detection result."""
    present: bool | None
    vendor: str | None
    error: str | None

class HardwareInfo(TypedDict):
    """Hardware capability information from remote host."""
    architecture: str
    processor_cores: int
    processor_count: int
    memtotal_mb: int
    mounts: list[dict]
    gpu: GpuInfo
```

Target HardwareInfo (add os and os_version):
```python
class HardwareInfo(TypedDict):
    """Hardware capability information from remote host."""
    architecture: str
    processor_cores: int
    processor_count: int
    memtotal_mb: int
    mounts: list[dict]
    gpu: GpuInfo
    os: str           # NEW: e.g., "ubuntu", "debian"
    os_version: str   # NEW: e.g., "24.04", "12"
```

Ansible facts to extract:
- ansible_distribution -> lowercase for os field (e.g., "Ubuntu" -> "ubuntu")
- ansible_distribution_version -> os_version field (e.g., "24.04")
</interfaces>
</context>

<tasks>

<task type="auto" tdd="true">
  <name>Task 1: Extend HardwareInfo with OS detection</name>
  <files>
    src/clawrium/core/hardware.py
    tests/test_hardware.py
  </files>
  <read_first>
    - /home/devashish/workspace/ric03uec/clawrium/src/clawrium/core/hardware.py (current HardwareInfo, extract_hardware_from_facts)
    - /home/devashish/workspace/ric03uec/clawrium/tests/test_hardware.py (existing test patterns)
    - /home/devashish/workspace/ric03uec/clawrium/.planning/phases/03-registry-compatibility/03-CONTEXT.md (D-07, D-08 decisions)
  </read_first>
  <behavior>
    - Test: extract_hardware_from_facts with ansible_distribution="Ubuntu", ansible_distribution_version="24.04" returns hardware dict with os="ubuntu", os_version="24.04"
    - Test: extract_hardware_from_facts with missing OS facts returns os="unknown", os_version="unknown"
    - Test: gather_hardware full flow includes os and os_version in returned dict
  </behavior>
  <action>
    1. Update HardwareInfo TypedDict in hardware.py:
       ```python
       class HardwareInfo(TypedDict):
           """Hardware capability information from remote host."""
           architecture: str
           processor_cores: int
           processor_count: int
           memtotal_mb: int
           mounts: list[dict]
           gpu: GpuInfo
           os: str           # lowercase distribution name
           os_version: str   # distribution version
       ```

    2. Update extract_hardware_from_facts() to include OS fields:
       ```python
       hardware = {
           "architecture": facts.get("ansible_architecture", "unknown"),
           "processor_cores": facts.get("ansible_processor_cores", 0),
           "processor_count": facts.get("ansible_processor_count", 0),
           "memtotal_mb": facts.get("ansible_memtotal_mb", 0),
           "mounts": [],
           "os": facts.get("ansible_distribution", "unknown").lower(),
           "os_version": str(facts.get("ansible_distribution_version", "unknown")),
       }
       ```

    3. Add tests to test_hardware.py:
       - test_parse_ansible_facts_os_detection: Pass facts with ansible_distribution="Ubuntu", ansible_distribution_version="24.04", verify os="ubuntu", os_version="24.04"
       - test_parse_ansible_facts_os_missing: Pass facts without OS fields, verify os="unknown", os_version="unknown"
       - Update test_gather_hardware_full to verify os and os_version in result (add to existing SetupResult mock)

    4. Update existing tests that check hardware dict to include os/os_version expectations.
  </action>
  <verify>
    <automated>uv run pytest tests/test_hardware.py -v</automated>
  </verify>
  <acceptance_criteria>
    - src/clawrium/core/hardware.py HardwareInfo contains "os: str"
    - src/clawrium/core/hardware.py HardwareInfo contains "os_version: str"
    - src/clawrium/core/hardware.py extract_hardware_from_facts contains "ansible_distribution"
    - tests/test_hardware.py contains "test_parse_ansible_facts_os_detection"
    - tests/test_hardware.py contains "test_parse_ansible_facts_os_missing"
    - uv run pytest tests/test_hardware.py exits 0
  </acceptance_criteria>
  <done>
    - HardwareInfo TypedDict includes os and os_version fields
    - extract_hardware_from_facts extracts ansible_distribution -> os (lowercase)
    - extract_hardware_from_facts extracts ansible_distribution_version -> os_version
    - Missing OS facts default to "unknown"
    - All hardware tests pass including new OS detection tests
  </done>
</task>

</tasks>

<verification>
```bash
# Verify HardwareInfo has os fields
grep -n "os:" src/clawrium/core/hardware.py
grep -n "os_version:" src/clawrium/core/hardware.py

# Verify extraction uses ansible_distribution
grep -n "ansible_distribution" src/clawrium/core/hardware.py

# Run all hardware tests
uv run pytest tests/test_hardware.py -v

# Verify OS detection in Python
uv run python -c "
from clawrium.core.hardware import extract_hardware_from_facts
facts = {'ansible_distribution': 'Ubuntu', 'ansible_distribution_version': '24.04'}
hw = extract_hardware_from_facts(facts)
assert hw['os'] == 'ubuntu', f\"Expected 'ubuntu', got {hw['os']}\"
assert hw['os_version'] == '24.04', f\"Expected '24.04', got {hw['os_version']}\"
print('OS detection works correctly')
"
```
</verification>

<success_criteria>
- HardwareInfo TypedDict includes os: str and os_version: str fields
- extract_hardware_from_facts extracts ansible_distribution (lowercased) to os
- extract_hardware_from_facts extracts ansible_distribution_version to os_version
- Missing OS facts default to "unknown"
- All existing hardware tests still pass
- New OS detection tests pass
</success_criteria>

<output>
After completion, create `.planning/phases/03-registry-compatibility/03-02-SUMMARY.md`
</output>
