---
phase: 10-nyquist-validation-coverage
plan: 03
type: execute
wave: 1
depends_on: []
files_modified:
  - .planning/phases/06-kernel-observability/06-VALIDATION.md
  - .planning/phases/07-network-node-fault-injection/07-VALIDATION.md
autonomous: true
requirements:
  - OBS-01
  - OBS-02
  - OBS-03
  - FAULT-01
  - FAULT-02
  - FAULT-03
  - FAULT-04
  - FAULT-05
  - FAULT-06
  - FAULT-07

must_haves:
  truths:
    - "06-VALIDATION.md exists and maps OBS-01..03 to passing test names and assertion evidence"
    - "07-VALIDATION.md exists and maps FAULT-01..07 to passing test names and assertion evidence"
    - "Both VALIDATION.md files have nyquist_compliant: true in frontmatter"
  artifacts:
    - path: ".planning/phases/06-kernel-observability/06-VALIDATION.md"
      provides: "Test-evidence mapping for OBS-01..03"
      contains: "nyquist_compliant: true"
    - path: ".planning/phases/07-network-node-fault-injection/07-VALIDATION.md"
      provides: "Test-evidence mapping for FAULT-01..07"
      contains: "nyquist_compliant: true"
  key_links:
    - from: "06-VALIDATION.md Per-Task Verification Map"
      to: "tests/test_dmesg_mark.py, tests/test_dmesg_collect.py, tests/test_classifier.py"
      via: "requirement-to-test-name mapping"
      pattern: "OBS-0[1-3]"
    - from: "07-VALIDATION.md Per-Task Verification Map"
      to: "tests/test_fault_registry.py, tests/test_net_partition.py, tests/test_net_impair.py, tests/test_net_heal.py, tests/test_net_fault_list.py, tests/test_node_crash.py"
      via: "requirement-to-test-name mapping"
      pattern: "FAULT-0[1-7]"
---

<objective>
Create VALIDATION.md artifacts for Phases 06 and 07 to achieve Nyquist compliance for Kernel Observability and Network & Node Fault Injection phases.

Purpose: Provide structured test-evidence mapping for all observability and fault injection requirements so the Nyquist auditor confirms these phases as compliant, completing the final step toward full v1.0 Nyquist compliance.

Output: Two VALIDATION.md files — 06-VALIDATION.md and 07-VALIDATION.md — each with `nyquist_compliant: true` in frontmatter and a filled Per-Task Verification Map.
</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 06-VALIDATION.md for Phase 6 (Kernel Observability)</name>
  <read_first>
    - $HOME/.claude/get-shit-done/templates/VALIDATION.md (template structure to follow exactly)
    - .planning/phases/06-kernel-observability/06-01-PLAN.md
    - .planning/phases/06-kernel-observability/06-02-PLAN.md
    - .planning/phases/06-kernel-observability/06-01-SUMMARY.md
    - .planning/phases/06-kernel-observability/06-02-SUMMARY.md
    - .planning/phases/06-kernel-observability/06-VERIFICATION.md (existing evidence)
    - tests/test_dmesg_mark.py (OBS-01 evidence: test_dmesg_mark_all_nodes_returns_success, test_dmesg_mark_marker_starts_with_prefix, test_dmesg_mark_ssh_command_writes_to_kmsg, test_dmesg_mark_unique_marker_per_call)
    - tests/test_dmesg_collect.py (OBS-02/03 evidence: test_collect_marker_filters_before_marker, test_collect_filter_level_warn_plus, test_collect_classification_applied)
    - tests/test_classifier.py (OBS-03 evidence: test_classify_panic, test_classify_oops, test_classify_kasan, test_classify_ubsan, test_classify_warn)
  </read_first>
  <files>.planning/phases/06-kernel-observability/06-VALIDATION.md</files>
  <action>
    Create `.planning/phases/06-kernel-observability/06-VALIDATION.md` using the template structure exactly.

    **Frontmatter:**
    ```yaml
    ---
    phase: 6
    slug: kernel-observability
    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_dmesg_mark.py tests/test_dmesg_collect.py tests/test_classifier.py -x -q`
    - Full suite command: `.venv/bin/pytest tests/ -q`
    - Estimated runtime: ~6 seconds

    **Sampling Rate section:** Quick run command above after every task commit. Full suite after every wave. Max feedback latency: 10 seconds.

    **Per-Task Verification Map:**

    | Task ID | Plan | Wave | Requirement | Test Type | Automated Command | File Exists | Status |
    |---------|------|------|-------------|-----------|-------------------|-------------|--------|
    | 06-01-01 | 01 | 1 | OBS-01 | unit | `.venv/bin/pytest tests/test_dmesg_mark.py -x -q` | ✅ | ✅ green |
    | 06-01-02 | 01 | 1 | OBS-03 | unit | `.venv/bin/pytest tests/test_classifier.py -x -q` | ✅ | ✅ green |
    | 06-02-01 | 02 | 1 | OBS-02 | unit | `.venv/bin/pytest tests/test_dmesg_collect.py -x -q` | ✅ | ✅ green |
    | 06-02-02 | 02 | 1 | OBS-03 | unit | `.venv/bin/pytest tests/test_dmesg_collect.py::test_collect_classification_applied tests/test_classifier.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 |
    |-------------|-------------|------------|------------|
    | OBS-01 | Insert timestamped dmesg marker on nodes before an operation; marker appears in subsequent dmesg_collect output | tests/test_dmesg_mark.py | `test_dmesg_mark_all_nodes_returns_success`, `test_dmesg_mark_all_nodes_same_marker`, `test_dmesg_mark_marker_starts_with_prefix`, `test_dmesg_mark_ssh_command_writes_to_kmsg`, `test_dmesg_mark_single_node_subset`, `test_dmesg_mark_invalid_node_name`, `test_dmesg_mark_ssh_failure_one_node_best_effort`, `test_dmesg_mark_all_nodes_fail`, `test_dmesg_mark_unique_marker_per_call` |
    | OBS-02 | Collect structured dmesg output since a named marker, filtered by log level | tests/test_dmesg_collect.py | `test_collect_basic_parses_lines`, `test_collect_marker_filters_before_marker`, `test_collect_marker_not_found_returns_empty`, `test_collect_filter_level_warn_plus`, `test_collect_filter_level_err_plus`, `test_collect_filter_level_all_includes_everything`, `test_collect_result_summary_fields`, `test_tool_dmesg_collect_returns_per_node_dict`, `test_tool_dmesg_collect_single_node_subset`, `test_tool_dmesg_collect_passes_since_marker`, `test_tool_dmesg_collect_invalid_filter_level` |
    | OBS-03 | Classified dmesg result includes typed events: oops, kernel panics, KASAN, UBSAN, WARNING | tests/test_classifier.py, tests/test_dmesg_collect.py | `test_classify_panic`, `test_classify_oops`, `test_classify_kasan`, `test_classify_ubsan`, `test_classify_warn`, `test_classify_normal`, `test_classify_panic_wins_over_warn`, `test_classify_oops_wins_over_kasan`, `test_parse_raw_kern_info`, `test_parse_raw_kern_warning`, `test_parse_classification_applied`; `test_collect_classification_applied` |

    **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/06-kernel-observability/06-VALIDATION.md')); assert d['nyquist_compliant'] is True; print('OK')"</automated>
  </verify>
  <acceptance_criteria>
    - File exists: `.planning/phases/06-kernel-observability/06-VALIDATION.md`
    - `grep "nyquist_compliant: true" .planning/phases/06-kernel-observability/06-VALIDATION.md` returns a match
    - `grep "OBS-01" .planning/phases/06-kernel-observability/06-VALIDATION.md` returns at least 1 match
    - `grep "OBS-02" .planning/phases/06-kernel-observability/06-VALIDATION.md` returns at least 1 match
    - `grep "OBS-03" .planning/phases/06-kernel-observability/06-VALIDATION.md` returns at least 1 match
    - `grep "test_dmesg_mark_unique_marker_per_call" .planning/phases/06-kernel-observability/06-VALIDATION.md` returns a match
    - `grep "test_collect_marker_filters_before_marker" .planning/phases/06-kernel-observability/06-VALIDATION.md` returns a match
    - `grep "test_classify_kasan" .planning/phases/06-kernel-observability/06-VALIDATION.md` returns a match
    - `grep "wave_0_complete: true" .planning/phases/06-kernel-observability/06-VALIDATION.md` returns a match
  </acceptance_criteria>
  <done>06-VALIDATION.md exists, maps all 3 OBS requirements to their test functions, nyquist_compliant is true.</done>
</task>

<task type="auto">
  <name>Task 2: Create 07-VALIDATION.md for Phase 7 (Network and Node Fault Injection)</name>
  <read_first>
    - $HOME/.claude/get-shit-done/templates/VALIDATION.md (template structure to follow exactly)
    - .planning/phases/07-network-node-fault-injection/07-01-PLAN.md
    - .planning/phases/07-network-node-fault-injection/07-02-PLAN.md
    - .planning/phases/07-network-node-fault-injection/07-03-PLAN.md
    - .planning/phases/07-network-node-fault-injection/07-04-PLAN.md
    - .planning/phases/07-network-node-fault-injection/07-05-PLAN.md
    - .planning/phases/07-network-node-fault-injection/07-01-SUMMARY.md
    - .planning/phases/07-network-node-fault-injection/07-04-SUMMARY.md
    - .planning/phases/07-network-node-fault-injection/07-05-SUMMARY.md
    - .planning/phases/07-network-node-fault-injection/07-VERIFICATION.md (existing evidence)
    - tests/test_fault_registry.py (FAULT-07 evidence: test_save_uses_atomic_write, test_round_trip_persistence, test_reconcile_retains_partition_with_matching_tag, test_reconcile_prunes_partition_with_missing_tag)
    - tests/test_net_partition.py (FAULT-01 evidence: test_build_partition_rules_comment_tag_format, test_build_partition_rules_action_is_drop, test_apply_partition_rules_calls_run_host_cmd_for_each_rule)
    - tests/test_net_impair.py (FAULT-02 evidence: test_build_impairment_cmd_latency_and_jitter, test_build_impairment_cmd_loss, test_net_impair_registers_fault_and_returns_handle)
    - tests/test_net_heal.py (FAULT-03/04 evidence: test_net_heal_partition_calls_remove_rules_by_comment, test_net_heal_all_removes_all_cluster_faults, test_net_heal_all_net_fault_list_empty_after_success)
    - tests/test_net_fault_list.py (FAULT-05/07 evidence: test_net_fault_list_returns_both_fault_types, test_net_fault_list_calls_reconcile_on_first_call, test_net_fault_list_reflects_reconciled_state)
    - tests/test_node_crash.py (FAULT-06 evidence: test_node_crash_calls_destroy_domain, test_node_crash_restart_true_calls_start_domain_by_name, test_node_crash_returns_running_nodestatus_after_restart_and_wait)
  </read_first>
  <files>.planning/phases/07-network-node-fault-injection/07-VALIDATION.md</files>
  <action>
    Create `.planning/phases/07-network-node-fault-injection/07-VALIDATION.md` using the template structure exactly.

    **Frontmatter:**
    ```yaml
    ---
    phase: 7
    slug: network-node-fault-injection
    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_fault_registry.py tests/test_net_partition.py tests/test_net_impair.py tests/test_net_heal.py tests/test_net_fault_list.py tests/test_node_crash.py -x -q`
    - Full suite command: `.venv/bin/pytest tests/ -q`
    - Estimated runtime: ~12 seconds

    **Sampling Rate section:** Quick run command above after every task commit. Full suite after every wave. Max feedback latency: 20 seconds.

    **Per-Task Verification Map:**

    | Task ID | Plan | Wave | Requirement | Test Type | Automated Command | File Exists | Status |
    |---------|------|------|-------------|-----------|-------------------|-------------|--------|
    | 07-01-01 | 01 | 1 | FAULT-07 | unit | `.venv/bin/pytest tests/test_fault_registry.py -x -q` | ✅ | ✅ green |
    | 07-02-01 | 02 | 1 | FAULT-01 | unit | `.venv/bin/pytest tests/test_net_partition.py -x -q` | ✅ | ✅ green |
    | 07-03-01 | 03 | 1 | FAULT-02 | unit | `.venv/bin/pytest tests/test_net_impair.py -x -q` | ✅ | ✅ green |
    | 07-03-02 | 03 | 1 | FAULT-03 | unit | `.venv/bin/pytest tests/test_net_heal.py -k "heal" -x -q` | ✅ | ✅ green |
    | 07-03-03 | 03 | 1 | FAULT-04 | unit | `.venv/bin/pytest tests/test_net_heal.py -k "heal_all" -x -q` | ✅ | ✅ green |
    | 07-04-01 | 04 | 1 | FAULT-05 | unit | `.venv/bin/pytest tests/test_net_fault_list.py -x -q` | ✅ | ✅ green |
    | 07-04-02 | 04 | 1 | FAULT-06 | unit | `.venv/bin/pytest tests/test_node_crash.py -x -q` | ✅ | ✅ green |
    | 07-05-01 | 05 | 1 | FAULT-07 | unit | `.venv/bin/pytest tests/test_net_fault_list.py::test_net_fault_list_calls_reconcile_on_first_call tests/test_fault_registry.py -k "reconcile" -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 |
    |-------------|-------------|------------|------------|
    | FAULT-01 | Partition cluster into two groups via iptables DROP rules on bridge; rules carry vmcluster:<cluster>:<handle_id> comment tag | tests/test_net_partition.py | `test_build_partition_rules_1_vs_1_produces_two_rules`, `test_build_partition_rules_2_vs_2_produces_eight_rules`, `test_build_partition_rules_symmetric`, `test_build_partition_rules_comment_tag_format`, `test_build_partition_rules_targets_bridge_interface`, `test_build_partition_rules_action_is_drop`, `test_apply_partition_rules_calls_run_host_cmd_for_each_rule`, `test_apply_partition_rules_returns_comment_tags`, `test_apply_partition_rules_raises_on_failure` |
    | FAULT-02 | Apply tc netem impairment to egress traffic (latency, jitter, loss, corruption) | tests/test_net_impair.py | `test_build_impairment_cmd_starts_with_tc_netem`, `test_build_impairment_cmd_latency_and_jitter`, `test_build_impairment_cmd_loss`, `test_build_impairment_cmd_corrupt`, `test_build_impairment_cmd_reorder`, `test_build_impairment_cmd_combined`, `test_apply_impairment_calls_run_host_cmd`, `test_net_impair_registers_fault_and_returns_handle`, `test_net_impair_stores_tap_as_rule_identifier` |
    | FAULT-03 | Heal a specific network fault by handle ID; healed handle no longer appears in net_fault_list | tests/test_net_heal.py | `test_net_heal_returns_error_for_unknown_handle`, `test_net_heal_partition_calls_remove_rules_by_comment`, `test_net_heal_partition_removes_registry_entry_after_cleanup`, `test_net_heal_partition_succeeds_even_if_rules_already_absent`, `test_net_heal_impairment_calls_remove_impairment`, `test_net_heal_impairment_succeeds_when_tap_already_absent`, `test_net_heal_impairment_removes_registry_entry_after_cleanup` |
    | FAULT-04 | Heal all active faults for a cluster in one call; net_fault_list returns empty list after | tests/test_net_heal.py | `test_net_heal_all_returns_zero_for_empty_cluster`, `test_net_heal_all_removes_all_cluster_faults`, `test_net_heal_all_continues_after_per_handle_failure`, `test_net_heal_all_net_fault_list_empty_after_success` |
    | FAULT-05 | List all currently active network faults for a cluster | tests/test_net_fault_list.py | `test_net_fault_list_returns_both_fault_types`, `test_net_fault_list_returns_empty_list_when_no_active_faults`, `test_net_fault_list_filters_by_cluster_name`, `test_net_fault_list_includes_parameters_dict`, `test_net_fault_list_returns_netfaultinfo_instances` |
    | FAULT-06 | Hard-reset a node (virsh destroy + start) to simulate unclean failure; returns after SSH reachability confirmed | tests/test_node_crash.py | `test_node_crash_unknown_node_returns_node_not_found`, `test_node_crash_calls_destroy_domain`, `test_node_crash_best_effort_if_domain_already_off`, `test_node_crash_invalidates_ssh_cache_before_restart`, `test_node_crash_restart_false_does_not_start_or_wait`, `test_node_crash_restart_true_calls_start_domain_by_name`, `test_node_crash_restart_true_wait_ssh_calls_wait_for_ssh`, `test_node_crash_returns_running_nodestatus_after_restart_and_wait`, `test_node_crash_ssh_timeout_returns_failure` |
    | FAULT-07 | Active fault state survives MCP server restart — persisted to disk atomically, reconciled against live kernel state on startup | tests/test_fault_registry.py, tests/test_net_fault_list.py | `test_save_uses_atomic_write`, `test_save_creates_parent_dirs`, `test_register_saves_immediately`, `test_round_trip_persistence`, `test_reconcile_retains_partition_with_matching_tag`, `test_reconcile_prunes_partition_with_missing_tag`, `test_reconcile_retains_impairment_with_existing_qdisc`, `test_reconcile_prunes_impairment_with_missing_qdisc`, `test_reconcile_saves_after_pruning`, `test_net_fault_list_calls_reconcile_on_first_call`, `test_net_fault_list_reflects_reconciled_state`, `test_ensure_reconciled_sets_flag` |

    **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/07-network-node-fault-injection/07-VALIDATION.md')); assert d['nyquist_compliant'] is True; print('OK')"</automated>
  </verify>
  <acceptance_criteria>
    - File exists: `.planning/phases/07-network-node-fault-injection/07-VALIDATION.md`
    - `grep "nyquist_compliant: true" .planning/phases/07-network-node-fault-injection/07-VALIDATION.md` returns a match
    - `grep "FAULT-01" .planning/phases/07-network-node-fault-injection/07-VALIDATION.md` returns at least 1 match
    - `grep "FAULT-04" .planning/phases/07-network-node-fault-injection/07-VALIDATION.md` returns at least 1 match
    - `grep "FAULT-07" .planning/phases/07-network-node-fault-injection/07-VALIDATION.md` returns at least 1 match
    - `grep "test_build_partition_rules_comment_tag_format" .planning/phases/07-network-node-fault-injection/07-VALIDATION.md` returns a match
    - `grep "test_reconcile_prunes_partition_with_missing_tag" .planning/phases/07-network-node-fault-injection/07-VALIDATION.md` returns a match
    - `grep "test_net_heal_all_net_fault_list_empty_after_success" .planning/phases/07-network-node-fault-injection/07-VALIDATION.md` returns a match
    - `grep "test_node_crash_returns_running_nodestatus_after_restart_and_wait" .planning/phases/07-network-node-fault-injection/07-VALIDATION.md` returns a match
    - `grep "test_net_fault_list_calls_reconcile_on_first_call" .planning/phases/07-network-node-fault-injection/07-VALIDATION.md` returns a match
    - `grep "wave_0_complete: true" .planning/phases/07-network-node-fault-injection/07-VALIDATION.md` returns a match
  </acceptance_criteria>
  <done>07-VALIDATION.md exists, maps all 7 FAULT requirements to their test functions, nyquist_compliant is true.</done>
</task>

</tasks>

<verification>
After both tasks complete:

```bash
grep "nyquist_compliant: true" \
  .planning/phases/06-kernel-observability/06-VALIDATION.md \
  .planning/phases/07-network-node-fault-injection/07-VALIDATION.md
```

Both files should return matches.

```bash
for req in OBS-01 OBS-02 OBS-03; do
  grep -c "$req" .planning/phases/06-kernel-observability/06-VALIDATION.md
done

for req in FAULT-01 FAULT-02 FAULT-03 FAULT-04 FAULT-05 FAULT-06 FAULT-07; do
  grep -c "$req" .planning/phases/07-network-node-fault-injection/07-VALIDATION.md
done
```

All counts should be >= 1.
</verification>

<success_criteria>
- 06-VALIDATION.md and 07-VALIDATION.md exist in their phase directories
- Both have `nyquist_compliant: true` in YAML frontmatter
- All 10 requirements (OBS-01..03, FAULT-01..07) are mapped to named test functions
- Each mapped test function name matches actual functions in the test files
- Combined with Plans 01 and 02: all 7 phase directories (01–07) have VALIDATION.md
- Nyquist audit discovers no missing_phases — overall status is compliant or partial
</success_criteria>

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