---
phase: "06"
plan: "01"
subsystem: observe
tags: [kernel, dmesg, observability, classifier, marker]
dependency_graph:
  requires: []
  provides: [classify_line, parse_dmesg_line, insert_marker, dmesg_mark]
  affects: [vmcluster_mcp/observe, vmcluster_mcp/server.py]
tech_stack:
  added: [re (stdlib), uuid (stdlib), datetime (stdlib)]
  patterns: [priority-ordered pattern matching, async fan-out with gather, best-effort per-node, structured_tool_handler wrap]
key_files:
  created:
    - vmcluster_mcp/observe/classifier.py
    - vmcluster_mcp/observe/dmesg.py
    - vmcluster_mcp/observe/__init__.py
    - tests/test_classifier.py
    - tests/test_dmesg_mark.py
  modified:
    - vmcluster_mcp/server.py
decisions:
  - insert_marker takes pre-generated marker string (not a UUID itself) — single UUID generated per dmesg_mark call, passed to all nodes (D-03)
  - Best-effort fan-out: partial SSH failures still return successful nodes; only ALL_NODES_FAILED returns success=False (D-17)
  - insert_marker is async to match SSHConnectionManager.execute coroutine pattern
metrics:
  duration_s: 168
  completed_date: "2026-03-23"
  tasks_completed: 2
  files_changed: 5
requirements-completed: [OBS-01]
---

# Phase 06 Plan 01: Classifier + dmesg_mark Summary

JWT-style one-liner: **dmesg marker insertion via /dev/kmsg with priority-ordered kernel log classifier and raw/ISO timestamp parser**

## What Was Built

### Task 1: Classifier + dmesg line parser (TDD)

- **`vmcluster_mcp/observe/classifier.py`**: `classify_line(text)` with compiled regex patterns in priority order: `panic > oops > kasan > ubsan > warn > normal`. Case-sensitive, first-match-wins (D-08, D-09).

- **`vmcluster_mcp/observe/dmesg.py`**: 
  - `parse_dmesg_line(line)` — handles both raw `<priority>[seconds.us] text` and ISO 8601 `<priority>YYYY-MM-DDTHH:MM:SS,us+offset text` formats. Decodes syslog priority byte for level (`priority & 7`) and facility (`priority >> 3`). Returns `DmesgLine` with classification applied, or `None` for unparseable lines.
  - `insert_marker(ssh_manager, ip, port, user, key_path, marker)` — async helper that writes pre-generated marker to `/dev/kmsg` via SSH `echo "{marker}" > /dev/kmsg`.

- **`tests/test_classifier.py`**: 20 tests covering all 6 classification categories, priority ordering, embedded pattern matching, raw format, ISO format, None returns, classification application.

### Task 2: dmesg_mark MCP tool + server wiring

- **`vmcluster_mcp/observe/__init__.py`**: `dmesg_mark(cluster_name, nodes=None)` MCP tool registered with `@mcp.tool()` and `@structured_tool_handler`. Generates a single UUID marker (`vmcluster-marker-{uuid4}`) shared across all nodes in the call. Fan-out via `asyncio.gather` with best-effort error handling (D-17). Returns `ToolResult[dict[str, str]]` mapping node name → marker string.

- **`vmcluster_mcp/server.py`**: Added `observe` to the module import block alongside `artifact`, `cluster`, `exec`, `snapshot`.

- **`tests/test_dmesg_mark.py`**: 9 tests covering: all-nodes success, same-marker verification, UUID prefix format, SSH command contains `/dev/kmsg`, subset targeting, invalid node → NODE_NOT_FOUND, one-node SSH failure (best-effort), all-nodes SSH failure → ALL_NODES_FAILED, unique marker per separate call.

## Success Criteria Verification

- [x] `classify_line()` correctly classifies all 6 categories with panic > oops > kasan > ubsan > warn > normal priority
- [x] `parse_dmesg_line()` handles both raw `[seconds.microseconds]` and ISO timestamp formats
- [x] `dmesg_mark` MCP tool inserts markers via SSH and returns per-node marker dict
- [x] Server imports observe module without error (`from vmcluster_mcp.server import mcp` succeeds)
- [x] All 29 tests pass

## Deviations from Plan

None — plan executed exactly as written.

The plan itself anticipated the `insert_marker` signature revision (from UUID-generating to marker-receiving), and Task 1 was implemented with the final revised signature directly.

## Self-Check: PASSED

Files verified:
- `vmcluster_mcp/observe/classifier.py` ✓ exists, contains `def classify_line(`
- `vmcluster_mcp/observe/dmesg.py` ✓ exists, contains `def parse_dmesg_line(` and `async def insert_marker(`
- `vmcluster_mcp/observe/__init__.py` ✓ exists, contains `@mcp.tool()` and `async def dmesg_mark(`
- `tests/test_classifier.py` ✓ exists, 20 tests
- `tests/test_dmesg_mark.py` ✓ exists, 9 tests

Commits verified:
- `c7046b5` feat(06-01): classifier, dmesg line parser, insert_marker ✓
- `06b876f` feat(06-01): dmesg_mark MCP tool + server wiring + tests ✓
