---
phase: 10-nyquist-validation-coverage
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
  - .planning/phases/01-foundation/01-VALIDATION.md
  - .planning/phases/02-core-vm-lifecycle/02-VALIDATION.md
  - .planning/phases/03-ssh-execution/03-VALIDATION.md
autonomous: true
requirements:
  - FOUND-01
  - FOUND-02
  - FOUND-03
  - FOUND-04
  - CLUS-01
  - CLUS-02
  - CLUS-03
  - CLUS-04
  - CLUS-05
  - CLUS-06
  - CLUS-07
  - EXEC-01
  - EXEC-02
  - EXEC-03

must_haves:
  truths:
    - "01-VALIDATION.md exists and maps all 4 FOUND requirements to specific passing test names"
    - "02-VALIDATION.md exists and maps all 7 CLUS requirements to specific passing test names"
    - "03-VALIDATION.md exists and maps all 3 EXEC requirements to specific passing test names"
    - "All three VALIDATION.md files have nyquist_compliant: true in frontmatter"
  artifacts:
    - path: ".planning/phases/01-foundation/01-VALIDATION.md"
      provides: "Test-evidence mapping for FOUND-01..04"
      contains: "nyquist_compliant: true"
    - path: ".planning/phases/02-core-vm-lifecycle/02-VALIDATION.md"
      provides: "Test-evidence mapping for CLUS-01..07"
      contains: "nyquist_compliant: true"
    - path: ".planning/phases/03-ssh-execution/03-VALIDATION.md"
      provides: "Test-evidence mapping for EXEC-01..03"
      contains: "nyquist_compliant: true"
  key_links:
    - from: "02-VALIDATION.md"
      to: "tests/test_cluster_define.py, test_cluster_lifecycle.py, test_cluster_status.py"
      via: "per-task verification map rows"
      pattern: "CLUS-0[1-7]"
    - from: "03-VALIDATION.md"
      to: "tests/test_ssh_client.py, test_node_exec.py, test_node_exec_all.py"
      via: "per-task verification map rows"
      pattern: "EXEC-0[1-3]"
---

<objective>
Create VALIDATION.md artifacts for Phases 01, 02, and 03 — mapping each requirement to the test functions that verify it, with nyquist_compliant: true set in each file.

Purpose: Provide structured test-evidence for all Foundation, Cluster Lifecycle, and SSH Execution requirements so the Nyquist audit finds no missing phases for 01–03.
Output: Three filled-out VALIDATION.md files in the respective phase directories.
</objective>

<execution_context>
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
@$HOME/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@$HOME/.claude/get-shit-done/templates/VALIDATION.md
</context>

<tasks>

<task type="auto">
  <name>Task 1: Create 01-VALIDATION.md for Phase 1 (Foundation)</name>
  <read_first>
    - /home/hornc/.claude/get-shit-done/templates/VALIDATION.md (template to follow exactly)
    - .planning/phases/01-foundation/01-01-PLAN.md
    - .planning/phases/01-foundation/01-02-PLAN.md
    - .planning/phases/01-foundation/01-03-PLAN.md
    - .planning/phases/01-foundation/01-01-SUMMARY.md
    - .planning/phases/01-foundation/01-02-SUMMARY.md
    - .planning/phases/01-foundation/01-03-SUMMARY.md
    - tests/test_config.py (FOUND-03 evidence: test_defaults_no_file_no_env, test_env_var_overrides_default, test_loads_from_yaml_file, test_vmcluster_config_env_specifies_path)
    - tests/test_models.py (FOUND-02 evidence: TestToolResult tests)
    - tests/test_server.py (FOUND-01 + FOUND-02 evidence: TestStructuredToolHandler tests)
    - tests/test_topology.py (FOUND-04 evidence: test_full_topology_validates, test_rejects_*, test_invalid_field_raises_value_error, test_wrong_type_raises_value_error)
  </read_first>
  <files>.planning/phases/01-foundation/01-VALIDATION.md</files>
  <action>
    Create `.planning/phases/01-foundation/01-VALIDATION.md` using the template structure exactly. Fill in all sections with accurate data for Phase 1.

    **Frontmatter:**
    ```yaml
    ---
    phase: 1
    slug: foundation
    status: complete
    nyquist_compliant: true
    wave_0_complete: true
    created: 2026-03-24
    ---
    ```

    **Test Infrastructure section:**
    - Framework: pytest 8.x
    - Config file: pyproject.toml
    - Quick run command: `.venv/bin/pytest tests/test_config.py tests/test_models.py tests/test_server.py tests/test_topology.py -x -q`
    - Full suite command: `.venv/bin/pytest tests/ -q`
    - Estimated runtime: ~5 seconds

    **Sampling Rate section:** Fill in with the quick and full suite commands above. Max feedback latency: 10 seconds.

    **Per-Task Verification Map** — use these EXACT mappings:

    | Task ID | Plan | Wave | Requirement | Test Type | Automated Command | File Exists | Status |
    |---------|------|------|-------------|-----------|-------------------|-------------|--------|
    | 01-01-01 | 01 | 1 | FOUND-01 | unit | `.venv/bin/pytest tests/test_server.py -x -q` | ✅ | ✅ green |
    | 01-02-01 | 02 | 1 | FOUND-02 | unit | `.venv/bin/pytest tests/test_models.py tests/test_server.py -x -q` | ✅ | ✅ green |
    | 01-02-02 | 02 | 1 | FOUND-02 | unit | `.venv/bin/pytest tests/test_server.py::TestStructuredToolHandler -x -q` | ✅ | ✅ green |
    | 01-03-01 | 03 | 1 | FOUND-03 | unit | `.venv/bin/pytest tests/test_config.py -x -q` | ✅ | ✅ green |
    | 01-03-02 | 03 | 1 | FOUND-04 | unit | `.venv/bin/pytest tests/test_topology.py -x -q` | ✅ | ✅ green |

    **Wave 0 Requirements section:** Write: "Existing infrastructure covers all phase requirements."

    **Manual-Only Verifications section:** Write: "All phase behaviors have automated verification."

    **Requirement → Test Evidence mapping** — add this as a section titled "## Requirement Coverage":

    | Requirement | Description | Covered By | Test Names |
    |-------------|-------------|------------|------------|
    | FOUND-01 | Server starts via `python -m vmcluster_mcp.server` and accepts MCP stdio connections | tests/test_server.py | `TestStructuredToolHandler::test_catches_value_error`, `test_catches_runtime_error`, `test_passthrough_normal_return`, `test_preserves_functools_wraps`, `test_error_node_is_none` |
    | FOUND-02 | All tool results returned as `ToolResult[T]` — no raw exceptions reach agent | tests/test_models.py, tests/test_server.py | `TestToolResult::test_success_with_string`, `test_failure_with_error_detail`, `test_serialization_round_trip`; `TestStructuredToolHandler::test_catches_value_error`, `test_catches_runtime_error` |
    | FOUND-03 | Config loadable from `/etc/vmcluster/config.yaml` or env vars | tests/test_config.py | `test_defaults_no_file_no_env`, `test_env_var_overrides_default`, `test_env_var_overrides_file`, `test_loads_from_yaml_file`, `test_vmcluster_config_env_specifies_path`, `test_empty_file_returns_defaults` |
    | FOUND-04 | Topology YAML parsed and validated with clear schema errors | tests/test_topology.py | `test_full_topology_validates`, `test_rejects_missing_cluster_name`, `test_rejects_missing_nodes`, `test_rejects_node_missing_ip`, `test_rejects_invalid_memory_mb_type`, `test_loads_valid_yaml`, `test_file_not_found`, `test_invalid_field_raises_value_error`, `test_wrong_type_raises_value_error`, `test_non_dict_yaml_raises_value_error` |

    **Validation Sign-Off section:** All checkboxes checked. Set "Approval: approved 2026-03-24".
  </action>
  <verify>
    <automated>.venv/bin/python -c "import yaml; d=yaml.safe_load(open('.planning/phases/01-foundation/01-VALIDATION.md')); assert d['nyquist_compliant'] is True, 'nyquist_compliant must be true'; print('OK')"</automated>
  </verify>
  <acceptance_criteria>
    - File exists: `.planning/phases/01-foundation/01-VALIDATION.md`
    - `grep "nyquist_compliant: true" .planning/phases/01-foundation/01-VALIDATION.md` returns a match
    - `grep "FOUND-01" .planning/phases/01-foundation/01-VALIDATION.md` returns at least 1 match
    - `grep "FOUND-02" .planning/phases/01-foundation/01-VALIDATION.md` returns at least 1 match
    - `grep "FOUND-03" .planning/phases/01-foundation/01-VALIDATION.md` returns at least 1 match
    - `grep "FOUND-04" .planning/phases/01-foundation/01-VALIDATION.md` returns at least 1 match
    - `grep "test_catches_value_error" .planning/phases/01-foundation/01-VALIDATION.md` returns a match
    - `grep "test_loads_from_yaml_file" .planning/phases/01-foundation/01-VALIDATION.md` returns a match
    - `grep "test_full_topology_validates" .planning/phases/01-foundation/01-VALIDATION.md` returns a match
    - `grep "wave_0_complete: true" .planning/phases/01-foundation/01-VALIDATION.md` returns a match
  </acceptance_criteria>
  <done>01-VALIDATION.md exists, maps all 4 FOUND requirements to their test functions, nyquist_compliant is true.</done>
</task>

<task type="auto">
  <name>Task 2: Create 02-VALIDATION.md for Phase 2 (Core VM Lifecycle)</name>
  <read_first>
    - /home/hornc/.claude/get-shit-done/templates/VALIDATION.md (template to follow exactly)
    - .planning/phases/02-core-vm-lifecycle/02-01-PLAN.md
    - .planning/phases/02-core-vm-lifecycle/02-02-PLAN.md
    - .planning/phases/02-core-vm-lifecycle/02-03-PLAN.md
    - .planning/phases/02-core-vm-lifecycle/02-04-PLAN.md
    - .planning/phases/02-core-vm-lifecycle/02-01-SUMMARY.md
    - .planning/phases/02-core-vm-lifecycle/02-04-SUMMARY.md
    - tests/test_libvirt_client.py (CLUS-01/02/03/04 infrastructure evidence)
    - tests/test_builders.py (CLUS-01 evidence: domain/network XML builders)
    - tests/test_cloud_init.py (CLUS-01 evidence: cloud-init ISO generation)
    - tests/test_cluster_define.py (CLUS-01, CLUS-07 evidence)
    - tests/test_cluster_lifecycle.py (CLUS-02, CLUS-03, CLUS-04 evidence)
    - tests/test_cluster_status.py (CLUS-05, CLUS-06 evidence)
  </read_first>
  <files>.planning/phases/02-core-vm-lifecycle/02-VALIDATION.md</files>
  <action>
    Create `.planning/phases/02-core-vm-lifecycle/02-VALIDATION.md` using the template structure exactly.

    **Frontmatter:**
    ```yaml
    ---
    phase: 2
    slug: core-vm-lifecycle
    status: complete
    nyquist_compliant: true
    wave_0_complete: true
    created: 2026-03-24
    ---
    ```

    **Test Infrastructure section:**
    - Framework: pytest 8.x
    - Config file: pyproject.toml
    - Quick run command: `.venv/bin/pytest tests/test_cluster_define.py tests/test_cluster_lifecycle.py tests/test_cluster_status.py -x -q`
    - Full suite command: `.venv/bin/pytest tests/ -q`
    - Estimated runtime: ~8 seconds

    **Per-Task Verification Map** — use these EXACT mappings:

    | Task ID | Plan | Wave | Requirement | Test Type | Automated Command | File Exists | Status |
    |---------|------|------|-------------|-----------|-------------------|-------------|--------|
    | 02-01-01 | 01 | 1 | CLUS-01 | unit | `.venv/bin/pytest tests/test_libvirt_client.py tests/test_builders.py -x -q` | ✅ | ✅ green |
    | 02-02-01 | 02 | 1 | CLUS-01 | unit | `.venv/bin/pytest tests/test_cluster_define.py tests/test_cloud_init.py -x -q` | ✅ | ✅ green |
    | 02-02-02 | 02 | 1 | CLUS-07 | unit | `.venv/bin/pytest tests/test_cluster_define.py::test_cluster_define_idempotent -x -q` | ✅ | ✅ green |
    | 02-03-01 | 03 | 1 | CLUS-02 | unit | `.venv/bin/pytest tests/test_cluster_lifecycle.py -k "start" -x -q` | ✅ | ✅ green |
    | 02-03-02 | 03 | 1 | CLUS-03 | unit | `.venv/bin/pytest tests/test_cluster_lifecycle.py -k "stop" -x -q` | ✅ | ✅ green |
    | 02-03-03 | 03 | 1 | CLUS-04 | unit | `.venv/bin/pytest tests/test_cluster_lifecycle.py -k "destroy" -x -q` | ✅ | ✅ green |
    | 02-04-01 | 04 | 1 | CLUS-05 | unit | `.venv/bin/pytest tests/test_cluster_status.py -k "status" -x -q` | ✅ | ✅ green |
    | 02-04-02 | 04 | 1 | CLUS-06 | unit | `.venv/bin/pytest tests/test_cluster_status.py -k "handle" -x -q` | ✅ | ✅ green |

    **Wave 0 Requirements section:** Write: "Existing infrastructure covers all phase requirements."

    **Manual-Only Verifications section:** Write: "All phase behaviors have automated verification."

    **Add a "## Requirement Coverage" section:**

    | Requirement | Description | Covered By | Test Names |
    |-------------|-------------|------------|------------|
    | CLUS-01 | Define cluster from topology — creates libvirt domain definitions and overlay disks | tests/test_cluster_define.py, tests/test_builders.py, tests/test_cloud_init.py | `test_cluster_define_creates_network_and_domains`, `test_cluster_define_creates_overlay_dir`, `test_cluster_define_calls_qemu_img`, `test_build_domain_xml_overlay_disk`, `test_build_network_xml_name`, `test_generate_cloud_init_iso_creates_iso` |
    | CLUS-02 | Start all nodes and optionally wait for SSH reachability | tests/test_cluster_lifecycle.py | `test_cluster_start_boots_all_domains`, `test_cluster_start_skips_already_active_domain`, `test_cluster_start_no_wait_returns_immediately`, `test_cluster_start_ssh_success_sets_reachable`, `test_cluster_start_ssh_timeout_returns_failure` |
    | CLUS-03 | Stop all nodes gracefully (shutdown) or forcibly (destroy) | tests/test_cluster_lifecycle.py | `test_cluster_stop_shutdown_mode`, `test_cluster_stop_destroy_mode`, `test_cluster_stop_skips_inactive_domains` |
    | CLUS-04 | Destroy cluster and optionally remove overlay disk images | tests/test_cluster_lifecycle.py | `test_cluster_destroy_undefines_domains`, `test_cluster_destroy_force_stops_active_domains`, `test_cluster_destroy_always_removes_network`, `test_cluster_destroy_remove_overlays_true`, `test_cluster_destroy_remove_overlays_false` |
    | CLUS-05 | Query current status of all nodes (domain state, SSH reachability) | tests/test_cluster_status.py | `test_cluster_status_returns_cluster_name`, `test_cluster_status_network_active_true`, `test_cluster_status_per_node_domain_state`, `test_cluster_status_ssh_checked_only_for_running_nodes`, `test_cluster_status_mixed_states` |
    | CLUS-06 | Obtain ClusterHandle descriptor for downstream direct-SSH consumers | tests/test_cluster_status.py | `test_cluster_handle_returns_cluster_name`, `test_cluster_handle_node_descriptors`, `test_cluster_handle_artifact_path`, `test_cluster_handle_kernel_version_from_ssh`, `test_cluster_handle_ssh_key_path_from_topology` |
    | CLUS-07 | `cluster_define` is idempotent — safe to call on already-defined cluster | tests/test_cluster_define.py | `test_cluster_define_idempotent` |

    **Validation Sign-Off:** All checkboxes checked. "Approval: approved 2026-03-24".
  </action>
  <verify>
    <automated>.venv/bin/python -c "import yaml; d=yaml.safe_load(open('.planning/phases/02-core-vm-lifecycle/02-VALIDATION.md')); assert d['nyquist_compliant'] is True; print('OK')"</automated>
  </verify>
  <acceptance_criteria>
    - File exists: `.planning/phases/02-core-vm-lifecycle/02-VALIDATION.md`
    - `grep "nyquist_compliant: true" .planning/phases/02-core-vm-lifecycle/02-VALIDATION.md` returns a match
    - `grep "CLUS-01" .planning/phases/02-core-vm-lifecycle/02-VALIDATION.md` returns at least 1 match
    - `grep "CLUS-07" .planning/phases/02-core-vm-lifecycle/02-VALIDATION.md` returns at least 1 match
    - `grep "test_cluster_define_idempotent" .planning/phases/02-core-vm-lifecycle/02-VALIDATION.md` returns a match
    - `grep "test_cluster_destroy_remove_overlays_true" .planning/phases/02-core-vm-lifecycle/02-VALIDATION.md` returns a match
    - `grep "test_cluster_handle_node_descriptors" .planning/phases/02-core-vm-lifecycle/02-VALIDATION.md` returns a match
    - `grep "wave_0_complete: true" .planning/phases/02-core-vm-lifecycle/02-VALIDATION.md` returns a match
  </acceptance_criteria>
  <done>02-VALIDATION.md exists, maps all 7 CLUS requirements to their test functions, nyquist_compliant is true.</done>
</task>

<task type="auto">
  <name>Task 3: Create 03-VALIDATION.md for Phase 3 (SSH Execution)</name>
  <read_first>
    - /home/hornc/.claude/get-shit-done/templates/VALIDATION.md (template to follow exactly)
    - .planning/phases/03-ssh-execution/03-01-PLAN.md
    - .planning/phases/03-ssh-execution/03-02-PLAN.md
    - .planning/phases/03-ssh-execution/03-03-PLAN.md
    - .planning/phases/03-ssh-execution/03-01-SUMMARY.md
    - .planning/phases/03-ssh-execution/03-03-SUMMARY.md
    - tests/test_ssh_client.py (EXEC-01/03 evidence: SSHConnectionManager tests)
    - tests/test_node_exec.py (EXEC-01 evidence: node_exec tool tests)
    - tests/test_node_exec_all.py (EXEC-02 evidence: node_exec_all tool tests)
  </read_first>
  <files>.planning/phases/03-ssh-execution/03-VALIDATION.md</files>
  <action>
    Create `.planning/phases/03-ssh-execution/03-VALIDATION.md` using the template structure exactly.

    **Frontmatter:**
    ```yaml
    ---
    phase: 3
    slug: ssh-execution
    status: complete
    nyquist_compliant: true
    wave_0_complete: true
    created: 2026-03-24
    ---
    ```

    **Test Infrastructure section:**
    - Framework: pytest 8.x (with pytest-asyncio)
    - Config file: pyproject.toml
    - Quick run command: `.venv/bin/pytest tests/test_ssh_client.py tests/test_node_exec.py tests/test_node_exec_all.py -x -q`
    - Full suite command: `.venv/bin/pytest tests/ -q`
    - Estimated runtime: ~6 seconds

    **Per-Task Verification Map:**

    | Task ID | Plan | Wave | Requirement | Test Type | Automated Command | File Exists | Status |
    |---------|------|------|-------------|-----------|-------------------|-------------|--------|
    | 03-01-01 | 01 | 1 | EXEC-01 | unit | `.venv/bin/pytest tests/test_ssh_client.py -x -q` | ✅ | ✅ green |
    | 03-01-02 | 01 | 1 | EXEC-03 | unit | `.venv/bin/pytest tests/test_ssh_client.py::test_execute_reconnects_on_stale_connection -x -q` | ✅ | ✅ green |
    | 03-02-01 | 02 | 1 | EXEC-01 | unit | `.venv/bin/pytest tests/test_node_exec.py -x -q` | ✅ | ✅ green |
    | 03-03-01 | 03 | 1 | EXEC-02 | unit | `.venv/bin/pytest tests/test_node_exec_all.py -x -q` | ✅ | ✅ green |

    **Wave 0 Requirements section:** Write: "Existing infrastructure covers all phase requirements."

    **Manual-Only Verifications section:** Write: "All phase behaviors have automated verification."

    **Add a "## Requirement Coverage" section:**

    | Requirement | Description | Covered By | Test Names |
    |-------------|-------------|------------|------------|
    | EXEC-01 | Run shell command on single node — structured output with exit code, stdout, stderr, duration | tests/test_ssh_client.py, tests/test_node_exec.py | `test_execute_fresh_connection_returns_exec_result`, `test_execute_nonzero_exit_code`, `test_execute_timeout_returns_timed_out`; `test_node_exec_valid_node_returns_success`, `test_node_exec_nonzero_exit_code_is_success`, `test_node_exec_timeout_returns_timed_out`, `test_node_exec_ssh_exception_returns_failure` |
    | EXEC-02 | Run same command across all/subset nodes in parallel — per-node results with all_succeeded and failed_nodes | tests/test_node_exec_all.py | `test_node_exec_all_success_all_nodes`, `test_node_exec_all_partial_failure_require_all_true`, `test_node_exec_all_partial_failure_require_all_false`, `test_node_exec_all_subset_of_nodes`, `test_node_exec_all_empty_nodes_targets_all`, `test_node_exec_all_timed_out_node_in_failed_nodes`, `test_node_exec_all_uses_asyncio_gather` |
    | EXEC-03 | SSH connections reestablished automatically after node reboot | tests/test_ssh_client.py | `test_execute_reconnects_on_stale_connection`, `test_execute_returns_error_on_double_failure`, `test_invalidate_removes_cached_connection`, `test_close_all_closes_all_connections` |

    **Validation Sign-Off:** All checkboxes checked. "Approval: approved 2026-03-24".
  </action>
  <verify>
    <automated>.venv/bin/python -c "import yaml; d=yaml.safe_load(open('.planning/phases/03-ssh-execution/03-VALIDATION.md')); assert d['nyquist_compliant'] is True; print('OK')"</automated>
  </verify>
  <acceptance_criteria>
    - File exists: `.planning/phases/03-ssh-execution/03-VALIDATION.md`
    - `grep "nyquist_compliant: true" .planning/phases/03-ssh-execution/03-VALIDATION.md` returns a match
    - `grep "EXEC-01" .planning/phases/03-ssh-execution/03-VALIDATION.md` returns at least 1 match
    - `grep "EXEC-02" .planning/phases/03-ssh-execution/03-VALIDATION.md` returns at least 1 match
    - `grep "EXEC-03" .planning/phases/03-ssh-execution/03-VALIDATION.md` returns at least 1 match
    - `grep "test_execute_reconnects_on_stale_connection" .planning/phases/03-ssh-execution/03-VALIDATION.md` returns a match
    - `grep "test_node_exec_all_uses_asyncio_gather" .planning/phases/03-ssh-execution/03-VALIDATION.md` returns a match
    - `grep "wave_0_complete: true" .planning/phases/03-ssh-execution/03-VALIDATION.md` returns a match
  </acceptance_criteria>
  <done>03-VALIDATION.md exists, maps all 3 EXEC requirements to their test functions, nyquist_compliant is true.</done>
</task>

</tasks>

<verification>
After all three tasks complete:

```bash
grep "nyquist_compliant: true" \
  .planning/phases/01-foundation/01-VALIDATION.md \
  .planning/phases/02-core-vm-lifecycle/02-VALIDATION.md \
  .planning/phases/03-ssh-execution/03-VALIDATION.md
```

All three files should return matches.

```bash
for req in FOUND-01 FOUND-02 FOUND-03 FOUND-04; do
  grep -l "$req" .planning/phases/01-foundation/01-VALIDATION.md
done

for req in CLUS-01 CLUS-02 CLUS-03 CLUS-04 CLUS-05 CLUS-06 CLUS-07; do
  grep -l "$req" .planning/phases/02-core-vm-lifecycle/02-VALIDATION.md
done

for req in EXEC-01 EXEC-02 EXEC-03; do
  grep -l "$req" .planning/phases/03-ssh-execution/03-VALIDATION.md
done
```
</verification>

<success_criteria>
- 01-VALIDATION.md, 02-VALIDATION.md, and 03-VALIDATION.md exist in their phase directories
- All three have `nyquist_compliant: true` in YAML frontmatter
- All 14 requirements (FOUND-01..04, CLUS-01..07, EXEC-01..03) are mapped to named test functions
- Each mapped test function name matches actual functions in the test files
</success_criteria>

<output>
After completion, create `.planning/phases/10-nyquist-validation-coverage/10-01-SUMMARY.md`
</output>
