---
phase: 05-artifact-pipeline
plan: "01"
subsystem: artifact
tags: [artifact, registry, content-addressed, mcp-tools, tdd]
dependency_graph:
  requires: []
  provides: [artifact_register, artifact_list, artifact_diff, ArtifactRegistry, ArtifactDiff]
  affects: [vmcluster_mcp.server, vmcluster_mcp.models]
tech_stack:
  added: [hashlib.sha256, os.replace atomic write, ELF magic detection]
  patterns: [content-addressed storage, atomic JSON persistence, TDD]
key_files:
  created:
    - vmcluster_mcp/artifact/registry.py
    - tests/test_artifact_registry.py
  modified:
    - vmcluster_mcp/models.py
    - vmcluster_mcp/artifact/__init__.py
    - vmcluster_mcp/server.py
decisions:
  - ArtifactRegistry.diff() returns ToolResult(ARTIFACT_NOT_FOUND) when artifact id unknown — provides safe error boundary at registry layer
  - _scan_bins() uses ELF magic bytes (0x7fELF) rather than file mode bits — portable across extracted archives
  - kernel_version defaults to "unknown" string (not None) when utsrelease.h missing and no caller value — satisfies BuildArtifact non-optional field
metrics:
  duration: "207s"
  completed: "2026-03-23"
  tasks_completed: 2
  files_modified: 5
requirements-completed: [ART-01, ART-04, ART-05]
---

# Phase 05 Plan 01: Artifact Registry Core Summary

Content-addressed artifact registry with sha256 hash_tree, atomic JSON persistence (os.replace), .ko module and ELF binary scanning, and three wired MCP tools (artifact_register, artifact_list, artifact_diff).

## Tasks Completed

| Task | Description | Commit |
|------|-------------|--------|
| 1 | ArtifactDiff model + ArtifactRegistry with hash_tree and atomic persistence | 559a568 |
| 2 | Wire artifact_register, artifact_list, artifact_diff tools to MCP server | 953de85 |

## What Was Built

### ArtifactDiff model (`vmcluster_mcp/models.py`)
Added after `InstallStatus`:
- `artifact_id_a`, `artifact_id_b`: IDs of compared artifacts
- `changed_modules: list[str]`: .ko filenames that differ by hash
- `changed_bins: list[str]`: userspace binary filenames that differ
- `git_log: str | None`: optional git log between sha refs

### ArtifactRegistry (`vmcluster_mcp/artifact/registry.py`)
- `hash_tree(root)`: deterministic sha256 of directory tree; excludes `__pycache__` and `.git`; processes files in sorted order
- `_detect_kernel_version(root)`: reads `include/generated/utsrelease.h` for `#define UTS_RELEASE`
- `_scan_modules(root)`: finds `.ko` files via `Path.rglob("*.ko")`
- `_scan_bins(root)`: finds ELF binaries (non-.ko) using `\x7fELF` magic header
- `ArtifactRegistry.load()`: reads JSON; starts empty if file missing
- `ArtifactRegistry.save()`: writes to `{path}.tmp` then `os.replace()` (atomic)
- `ArtifactRegistry.register()`: computes hash, deduplicates by artifact_id, auto-detects kernel version, saves
- `ArtifactRegistry.list_artifacts()`: returns all `BuildArtifact` entries
- `ArtifactRegistry.get(id)`: returns `BuildArtifact | None`
- `ArtifactRegistry.diff(id_a, id_b)`: re-hashes source trees, returns `ArtifactDiff` or `ToolResult(ARTIFACT_NOT_FOUND)`

### MCP Tools (`vmcluster_mcp/artifact/__init__.py`)
- `artifact_register`: validates source_path, registers tree, returns `ToolResult[BuildArtifact]`
- `artifact_list`: lists all artifacts, returns `ToolResult[list[BuildArtifact]]`
- `artifact_diff`: validates both IDs, diffs source trees, returns `ToolResult[ArtifactDiff]`

### Server wiring (`vmcluster_mcp/server.py`)
Added `artifact` to the import block that triggers tool registration on startup.

## Test Coverage

19 unit tests in `tests/test_artifact_registry.py` covering:
- hash_tree determinism, content sensitivity, exclusions
- load/save (including atomic write spy via monkeypatch)
- register: BuildArtifact fields, ISO 8601 timestamp, .ko scanning, ELF scanning, dedup, kernel auto-detection
- list_artifacts: empty and populated cases
- diff: changed modules/bins, unchanged trees (dedup), unknown artifact_id

All 207 tests pass (existing 188 + 19 new).

## Deviations from Plan

None — plan executed exactly as written.

## Known Stubs

None — all three tools are fully wired with real implementation.

## Self-Check

- [x] `vmcluster_mcp/artifact/registry.py` — created ✓
- [x] `tests/test_artifact_registry.py` — created ✓
- [x] `vmcluster_mcp/models.py` — ArtifactDiff added ✓
- [x] `vmcluster_mcp/artifact/__init__.py` — 3 MCP tools ✓
- [x] `vmcluster_mcp/server.py` — artifact import added ✓
- [x] Commit `559a568` — Task 1 ✓
- [x] Commit `953de85` — Task 2 ✓

## Self-Check: PASSED
