---
phase: 06-integration-and-hardening
verified: 2026-04-12T21:45:00Z
status: human_needed
score: 4/4
overrides_applied: 0
human_verification:
  - test: "Run start_capture with a window target and verify DWM-quality capture occurs transparently"
    expected: "Debug logs show 'via DWM' backend selection; output image matches window content without flicker"
    why_human: "Requires a running MCP server, a visible target window, and visual quality comparison"
  - test: "Rename or delete native/build/Release/dwm-capture.node, then run start_capture with a window target"
    expected: "Capture succeeds using node-screenshots fallback; no error surfaced to agent; debug logs show 'via node-screenshots fallback'"
    why_human: "Requires running server and verifying agent-visible behavior with addon missing"
  - test: "Capture a minimized window and verify structured error returned"
    expected: "Error message 'Window is minimized: ...' returned; MCP server continues accepting commands"
    why_human: "Requires running MCP server and testing error propagation through the protocol"
  - test: "Run npx tsx tests/gdi-leak-test.ts on Windows with built native addon"
    expected: "Script reports PASS with GDI handle drift within +/- 5 after 120 captures"
    why_human: "Requires Windows machine with built native addon and a visible target window"
---

# Phase 6: Integration and Hardening Verification Report

**Phase Goal:** WindowTarget and WindowRegionTarget transparently use DWM capture when available, with graceful fallback and crash resilience
**Verified:** 2026-04-12T21:45:00Z
**Status:** human_needed
**Re-verification:** No -- initial verification

## Goal Achievement

### Observable Truths

| # | Truth | Status | Evidence |
|---|-------|--------|----------|
| 1 | An agent using start_capture with a window target gets DWM-quality captures without changing any tool parameters (transparent upgrade) | VERIFIED | `window-target.ts` delegates to `captureWindowBest(win)` which tries DWM first; no MCP schema changes; no server.ts changes |
| 2 | If the native addon is missing or fails to load, window capture falls back to monitor-crop and the agent sees no error | VERIFIED | `dwm-capture.ts` loadAddon() catches load errors, returns null; `isDwmCaptureAvailable()` returns false; `captureWindowBest()` caches `dwmAvailable=false` and uses `win.captureImageSync()` fallback path |
| 3 | Capturing a minimized, cloaked, or destroyed window returns a structured error (not a crash) and the MCP server continues running | VERIFIED | Both targets check `win.isMinimized()` and throw structured Error; destroyed windows: `findWindow()` re-enumerates `Window.all()` each call, returns null, targets throw "Window not found"; native addon has SEH `__try/__except` (capture.cpp:182), TypeScript wrapper catches all errors and returns null (dwm-capture.ts:62) |
| 4 | Running 100+ consecutive captures shows no GDI handle leak (handle count stable within +/- 5) | VERIFIED | `tests/gdi-leak-test.ts` runs 120 captures, checks `getGdiHandleCount()` drift against +/- 5 threshold; native addon exports `GetGdiHandleCount` via `GetGuiResources` API (addon.cpp:51-54) |

**Score:** 4/4 truths verified

### Required Artifacts

| Artifact | Expected | Status | Details |
|----------|----------|--------|---------|
| `src/capture/targets/window-utils.ts` | captureWindowBest() helper with DWM-first-then-fallback logic | VERIFIED | Exports `captureWindowBest` (line 41) and `findWindow` (line 17); imports from dwm-capture.js (line 7); DWM availability cache (line 11); debug logging for both backends |
| `src/capture/targets/window-target.ts` | WindowTarget using captureWindowBest | VERIFIED | Imports `captureWindowBest` from window-utils (line 6); calls it at line 29; no `captureImageSync` calls remain |
| `src/capture/targets/window-region-target.ts` | WindowRegionTarget using captureWindowBest + sharp extract | VERIFIED | Imports `captureWindowBest` (line 7) and `sharp` (line 9); uses `sharp.extract()` for cropping (line 56); no `captureImageSync` or `cropSync` calls remain |
| `native/src/addon.cpp` | getGdiHandleCount export calling GetGuiResources | VERIFIED | `GetGdiHandleCount` function at line 51; exported in Init at line 65; calls `GetGuiResources(GetCurrentProcess(), GR_GDIOBJECTS)` |
| `src/capture/targets/dwm-capture.ts` | TypeScript wrapper for getGdiHandleCount | VERIFIED | `getGdiHandleCount()` exported at line 44; NativeAddon interface includes it at line 15; returns -1 when addon unavailable |
| `tests/gdi-leak-test.ts` | Leak validation script running 100+ captures | VERIFIED | 96-line script; CAPTURE_COUNT=120; imports getGdiHandleCount and captureWindowDwm; drift threshold check at line 81 |

### Key Link Verification

| From | To | Via | Status | Details |
|------|----|-----|--------|---------|
| window-utils.ts | dwm-capture.ts | import isDwmCaptureAvailable, captureWindowDwm | WIRED | Line 7: `import { isDwmCaptureAvailable, captureWindowDwm } from "./dwm-capture.js"` |
| window-target.ts | window-utils.ts | import captureWindowBest | WIRED | Line 6: `import { findWindow, captureWindowBest } from "./window-utils.js"`; used at line 29 |
| window-region-target.ts | window-utils.ts | import captureWindowBest | WIRED | Line 7: `import { findWindow, captureWindowBest } from "./window-utils.js"`; used at line 40 |
| tests/gdi-leak-test.ts | dwm-capture.ts | import getGdiHandleCount, captureWindowDwm | WIRED | Lines 13-17: imports all three functions; used throughout test body |
| dwm-capture.ts | native addon | require .node binary | WIRED | Line 25: `req("../../../native/build/Release/dwm-capture.node")`; NativeAddon interface includes getGdiHandleCount |

### Data-Flow Trace (Level 4)

Not applicable -- these are capture utilities, not UI components rendering dynamic data.

### Behavioral Spot-Checks

| Behavior | Command | Result | Status |
|----------|---------|--------|--------|
| TypeScript compiles | `npx tsc --noEmit` | Clean -- no errors | PASS |
| No captureImageSync in window-target.ts | grep captureImageSync | No matches | PASS |
| No captureImageSync in window-region-target.ts | grep captureImageSync | No matches | PASS |
| No cropSync in window-region-target.ts | grep cropSync | No matches | PASS |
| captureWindowBest exported and used | grep captureWindowBest | Exported in window-utils.ts, imported+used in both targets | PASS |
| Commits exist | git log for 4 commit hashes | All 4 commits verified (69dd655, c59b9ee, 5adc259, b4b5ab7) | PASS |
| No anti-patterns in modified files | grep TODO/FIXME/placeholder | No matches | PASS |

### Requirements Coverage

| Requirement | Source Plan | Description | Status | Evidence |
|-------------|------------|-------------|--------|----------|
| DWM-06 | 06-01 | WindowTarget automatically uses DWM capture when native addon is available | SATISFIED | WindowTarget.capture() delegates to captureWindowBest() which tries DWM first |
| DWM-07 | 06-01 | WindowTarget falls back to monitor-crop when addon unavailable or DWM fails | SATISFIED | captureWindowBest() falls back to captureImageSync; isDwmCaptureAvailable()=false when addon missing |
| DWM-08 | 06-01 | WindowRegionTarget also benefits from DWM capture | SATISFIED | WindowRegionTarget uses captureWindowBest() + sharp extract |
| DWM-09 | 06-01 | Fallback is transparent -- agents see no difference in MCP tool interface | SATISFIED | No changes to server.ts or MCP schemas; integration is internal to target classes |
| DWM-10 | 06-01 | Native addon crash does not terminate MCP server | SATISFIED | SEH __try/__except in capture.cpp; dwm-capture.ts catches all errors returns null; captureWindowBest falls back |
| DWM-11 | 06-01 | Addon handles minimized, cloaked, and destroyed windows gracefully | SATISFIED | isMinimized() check with structured Error in both targets; findWindow() re-enumerates so destroyed windows return "not found"; native IsWindow() check |
| DWM-12 | 06-02 | COM/DirectX resources properly released, no GDI handle leaks | SATISFIED | getGdiHandleCount() in native addon + TypeScript wrapper; leak test validates 120 captures with drift +/- 5 |

### Anti-Patterns Found

| File | Line | Pattern | Severity | Impact |
|------|------|---------|----------|--------|
| (none) | - | - | - | - |

No anti-patterns detected in any modified files.

### Human Verification Required

### 1. DWM Transparent Upgrade

**Test:** Run `start_capture` with a window target against a visible application (e.g., Notepad) and check debug logs
**Expected:** Debug logs show "Captured window ... via DWM"; output image is correct and flicker-free
**Why human:** Requires running MCP server, visible target window, and visual quality inspection

### 2. Graceful Fallback Without Addon

**Test:** Rename `native/build/Release/dwm-capture.node` to `.bak`, then run `start_capture` with a window target
**Expected:** Capture succeeds via node-screenshots fallback; no error visible to agent; debug logs show "via node-screenshots fallback"
**Why human:** Requires running server and verifying agent-facing behavior with addon removed

### 3. Minimized Window Structured Error

**Test:** Minimize a target window, then attempt `start_capture` targeting it
**Expected:** Error message "Window is minimized: ..." returned to agent; MCP server continues accepting subsequent commands
**Why human:** Requires running MCP server and testing error propagation through MCP protocol

### 4. GDI Leak Test on Windows

**Test:** Run `npx tsx tests/gdi-leak-test.ts` on Windows machine with built native addon
**Expected:** Script outputs PASS with GDI handle drift within +/- 5 after 120 captures
**Why human:** Requires Windows machine with compiled native addon and a visible target window for DWM capture

### Gaps Summary

No gaps found. All 4 roadmap success criteria are verified at the code level. All 7 requirements (DWM-06 through DWM-12) are satisfied by the implementation.

Human verification is needed to confirm runtime behavior: transparent DWM upgrade, graceful fallback, structured error handling, and GDI leak stability -- all require a running MCP server or native addon on Windows.

---

_Verified: 2026-04-12T21:45:00Z_
_Verifier: Claude (gsd-verifier)_
