# Phase 6: Integration and Hardening - Context

**Gathered:** 2026-04-13
**Status:** Ready for planning

<domain>
## Phase Boundary

Wire DWM capture (DwmGetDxSharedSurface from Phase 5) into WindowTarget and WindowRegionTarget so they transparently use it when available, fall back to monitor-crop when not. Add crash resilience, graceful error handling for edge-case window states, and verify no resource leaks. Covers: DWM-06, DWM-07, DWM-08, DWM-09, DWM-10, DWM-11, DWM-12.

</domain>

<decisions>
## Implementation Decisions

### Integration Strategy
- **D-01:** Modify `WindowTarget.capture()` to try DWM capture first via `captureWindowDwm(hwnd)`, fall back to existing `captureWindowViaMonitor(win)` when DWM returns null
- **D-02:** Modify `WindowRegionTarget.capture()` similarly — get full window PNG via DWM, then crop sub-region using sharp (since DWM returns PNG, not an Image object)
- **D-03:** Add `captureWindowBest(win)` helper to `window-utils.ts` that encapsulates the DWM-first-then-fallback logic, used by both targets
- **D-04:** No new CaptureTarget subclass — DWM is a capture *mechanism*, not a different *target* (per Phase 5 ARCHITECTURE.md decision)
- **D-05:** No changes to server.ts, CaptureConfig, or MCP tool schemas — integration is fully internal to the target classes (DWM-09: transparent to agents)

### Fallback Behavior
- **D-06:** Probe DWM availability once at first capture, cache the result — don't re-probe every frame (per PITFALLS.md integration pitfall #3)
- **D-07:** If DWM capture fails for a specific window (returns null from `captureWindowDwm`), fall back to monitor-crop for that capture only — don't disable DWM globally
- **D-08:** Log which capture backend was used (DWM or monitor-crop) at debug level so agents/developers can diagnose quality differences
- **D-09:** If native addon fails to load entirely (MODULE_NOT_FOUND), `isDwmCaptureAvailable()` returns false and all captures silently use monitor-crop — no error surfaced to agent

### Window State Handling (DWM-11)
- **D-10:** Check `IsWindow(hwnd)` in the native addon before DwmGetDxSharedSurface (already done in Phase 5)
- **D-11:** For minimized windows: throw structured error from WindowTarget (existing behavior) — DWM shared surface may return stale content for minimized windows
- **D-12:** For cloaked UWP windows: if DwmGetDxSharedSurface returns null/fails, fall back to monitor-crop which will also likely fail — let the existing "window not visible" error propagate

### Crash Resilience (DWM-10)
- **D-13:** SEH wrapper already in place from Phase 5 (`__try/__except` in capture.cpp) — converts access violations to NAPI errors
- **D-14:** The TypeScript wrapper (`dwm-capture.ts`) already catches all errors and returns null — the MCP server never sees a native crash
- **D-15:** No child-process isolation — the in-process + SEH + try/catch layering provides sufficient protection

### Resource Leak Prevention (DWM-12)
- **D-16:** All D3D11 resources use `ComPtr<T>` RAII wrappers (already in place from Phase 5)
- **D-17:** Add a GDI handle leak test: run 100+ captures in a loop, check `GetGuiResources()` before and after — handle count must be stable within +/- 5
- **D-18:** The staging texture is created and destroyed per capture — no persistent GPU resources between calls

### WindowRegionTarget Crop Strategy
- **D-19:** DWM capture returns PNG buffer, not a raw Image object. Use sharp to decode PNG → crop sub-region → re-encode to PNG. This adds ~5-10ms but is architecturally clean
- **D-20:** The existing `win.captureImageSync()` in WindowRegionTarget (which triggers WM_PRINT flicker) MUST be replaced — this is actually the worse of the two targets since it doesn't even use monitor-crop

### Claude's Discretion
- Whether to cache the sharp import or import at module level
- Exact log message wording for backend selection
- Whether to add a `captureBackend` field to session metadata (nice-to-have, not required)
- Test file organization for the leak test

</decisions>

<canonical_refs>
## Canonical References

**Downstream agents MUST read these before planning or implementing.**

### Phase 5 Implementation (what to integrate)
- `src/capture/targets/dwm-capture.ts` — TypeScript wrapper: `isDwmCaptureAvailable()`, `captureWindowDwm(hwnd)`
- `native/src/capture.cpp` — DwmGetDxSharedSurface implementation with SEH wrapper
- `native/src/addon.cpp` — NAPI exports: isAvailable(), captureWindow()

### Existing Targets (what to modify)
- `src/capture/targets/window-target.ts` — WindowTarget using monitor-crop, needs DWM-first integration
- `src/capture/targets/window-region-target.ts` — WindowRegionTarget using WM_PRINT (captureImageSync), needs DWM-first + sharp crop
- `src/capture/targets/window-utils.ts` — Shared utilities, add `captureWindowBest()` helper

### Architecture Decisions
- `.planning/phases/05-native-addon-and-dwm-capture/05-CONTEXT.md` — Phase 5 decisions (API choice, crash isolation, async architecture)
- `.planning/research/ARCHITECTURE.md` — Integration strategy: modify WindowTarget internals, no new target class
- `.planning/research/PITFALLS.md` — Pitfall #5 (DWM unavailable under RDP), integration pitfalls section

</canonical_refs>

<code_context>
## Existing Code Insights

### Reusable Assets
- `dwm-capture.ts`: Ready-to-use `isDwmCaptureAvailable()` and `captureWindowDwm(hwnd)` — just wire into targets
- `window-utils.ts`: Already has `findWindow()` and `captureWindowViaMonitor()` — add `captureWindowBest()` alongside
- `sharp` dependency: Already in package.json — use for PNG decode → crop → re-encode in WindowRegionTarget

### Established Patterns
- `WindowTarget.capture()`: async, returns `Promise<Buffer>`, uses `captureWindowViaMonitor(win)` then `image.toPngSync()`
- `WindowRegionTarget.capture()`: async, returns `Promise<Buffer>`, uses `win.captureImageSync()` (WM_PRINT!) then `image.cropSync()`
- Error handling: throws with descriptive messages, scheduler catches and records skipped frames

### Integration Points
- `window-utils.ts`: Add `captureWindowBest(win)` that tries DWM then falls back to monitor-crop
- `window-target.ts`: Replace `captureWindowViaMonitor(win) + toPngSync()` with `captureWindowBest(win)`
- `window-region-target.ts`: Replace `win.captureImageSync()` with DWM capture + sharp crop

</code_context>

<specifics>
## Specific Ideas

- WindowRegionTarget currently uses `win.captureImageSync()` which triggers WM_PRINT — this is actually worse than WindowTarget's monitor-crop approach. Phase 6 fixes both targets but this one benefits more from the DWM upgrade.
- The `captureWindowBest()` function should return a PNG Buffer directly (not an Image object) since DWM returns PNG. This means WindowTarget simplifies (no more `image.toPngSync()`) but WindowRegionTarget needs sharp for the crop step.
- GDI handle leak test should use `node -e` inline script calling the native addon in a loop — same pattern as Phase 5 validation.

</specifics>

<deferred>
## Deferred Ideas

None — discussion stayed within phase scope

</deferred>

---

*Phase: 06-integration-and-hardening*
*Context gathered: 2026-04-13*
