# TrapMap Managed PNG Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Remove TrapMap's platform-specific drawing dependency and produce the same bounded 300 DPI PNG with a managed encoder that runs inside Clumsy.

**Architecture:** Keep `TrapMapImageExporter.ExportIfEnabled` and its file-reservation/publication behavior. Replace only the renderer with an internal RGBA raster surface, primitive drawing functions, a compact embedded bitmap font, and `StbImageWriteSharp` for PNG encoding; insert the 300 DPI `pHYs` chunk after encoding.

**Tech Stack:** C# 10, .NET Standard 2.0, `StbImageWriteSharp` 1.16.7, PowerShell contract/PNG parsing tests.

## Global Constraints

- Do not inspect or modify `TrajPlanner`.
- Do not commit or stage any file.
- Do not change grid construction, Painter behavior, movement behavior, switches, output location, naming, 300 DPI, 4 pixels per cell, 4000-pixel edge limit, or 50 MiB limit.
- The only new image package is `StbImageWriteSharp` version 1.16.7; do not add native assets or another graphics package.
- `TrapMapImageExporter` must have no runtime reference to `System.Drawing.Common` or `System.Drawing`.
- Preserve collision-safe temporary-file reservation, encoded-size validation, atomic publication, and contained export failures.

---

### Task 1: Replace System.Drawing rendering with a managed Stb PNG encoder

**Files:**
- Modify: `ClumsyPilot/ClumsyPilot.csproj`
- Modify: `ClumsyPilot/TrapMapImageExporter.cs`
- Modify: `ClumsyPilot/tests/verify_trapmap_image.ps1`
- Modify: `docs/superpowers/specs/2026-07-22-trap-map-image-and-console-design.md`
- Modify: `docs/superpowers/plans/2026-07-22-trap-map-image-and-console.md`

**Interfaces:**
- Preserve: `TrapMapImageExporter.ExportIfEnabled(bool, TrapMapImageExportRequest)` and all public request/result properties and constants.
- Add only private implementation units: `RgbaSurface`, integer drawing helpers, bitmap-font helpers, and `PngWriter`.
- `RenderToTemporaryPng` continues to consume the existing request/dimensions and write to the already exclusively reserved stream.

- [ ] **Step 1: Add dependency-removal and PNG-structure assertions**

Update `verify_trapmap_image.ps1` before production code. Require that:

```powershell
if ($project.PackageReference.Include -contains 'System.Drawing.Common') {
    throw 'TrapMap must not depend on System.Drawing.Common.'
}
if ($project.Target.Name -contains 'DeployFrameworkDrawingRuntime') {
    throw 'Legacy drawing-runtime deployment target remains.'
}
if (-not ($project.PackageReference | Where-Object {
    $_.Include -eq 'StbImageWriteSharp' -and $_.Version -eq '1.16.7'
})) { throw 'Exact managed PNG package is missing.' }
if ($exporterSource -match 'System\.Drawing|\bBitmap\b|\bGraphics\b|ImageFormat') {
    throw 'Exporter still uses the external drawing API.'
}
```

Parse the generated PNG without loading a drawing assembly. Verify signature, one `IHDR`, one `pHYs`, one or more `IDAT`, and `IEND`; verify every chunk CRC. Assert `IHDR` width/height and RGBA8 fields and `pHYs` values `11811,11811,1`. Decode representative pixels with a test-only PNG decoder or the Stb package and reuse the existing color/Y-inversion assertions.

Add a clean-output assertion after build:

```powershell
$drawingDll = Join-Path (Split-Path -Parent $AssemblyPath) 'System.Drawing.Common.dll'
if (Test-Path -LiteralPath $drawingDll) {
    throw 'System.Drawing.Common.dll must not be deployed for TrapMap.'
}
```

- [ ] **Step 2: Run RED verification**

```powershell
dotnet build ClumsyPilot\ClumsyPilot.csproj --no-restore -v:minimal
powershell -ExecutionPolicy Bypass -File ClumsyPilot\tests\verify_trapmap_image.ps1
```

Expected: the image test fails because the package, deployment target, `using System.Drawing`, and renderer still exist.

- [ ] **Step 3: Remove the drawing package and deployment target**

Delete the `System.Drawing.Common` `PackageReference` and the entire `DeployFrameworkDrawingRuntime` target. Add `<PackageReference Include="StbImageWriteSharp" Version="1.16.7" />`. Do not change the target framework or other references. Ensure a clean build cannot retain the old DLL: the verification command must remove only `ClumsyPilot/bin/Debug/netstandard2.0/System.Drawing.Common.dll` before rebuilding, after resolving and validating that exact path is under the project output directory.

- [ ] **Step 4: Implement the RGBA raster surface**

Replace drawing types with a private surface backed by `byte[]` in RGBA order. Required primitives and semantics:

```csharp
SetPixel(int x, int y, byte r, byte g, byte b, byte a = 255);
FillRectangle(int x, int y, int width, int height, Color32 color);
DrawLine(int x0, int y0, int x1, int y1, Color32 color, int thickness);
DrawRectangle(int x, int y, int width, int height, Color32 color, int thickness);
DrawCircle(int centerX, int centerY, int radius, Color32 color, int thickness);
FillCircle(int centerX, int centerY, int radius, Color32 color);
FillPolygon(PointD[] points, Color32 color);
```

Clip every primitive to the surface. Use pre-clipped Bresenham lines, scale-normalized scanline polygon filling, and a canvas-clipped bounded circle scan whose work is proportional to visible rows/columns rather than radius. Preserve exact white, red, LightGray `(211,211,211)`, black, blue, and LimeGreen `(0,255,0)` colors. Convert vehicle/workstation world positions using the existing discrete-grid-aligned transform.

- [ ] **Step 5: Implement deterministic bitmap text**

Embed a private 5×7 ASCII glyph table for code points 32–126. Draw scaled glyphs using integer pixels; unsupported characters render as `?`. Use a 2× scale for header text and 1× scale for the workstation label. Keep all five header baselines inside `HeaderHeightPixels=140` with fixed non-overlapping line boxes. Continue building the same five metadata lines, including occupancy rate; sanitize only the exported header text, not logs.

- [ ] **Step 6: Encode PNG and add 300 DPI metadata**

Use `StbImageWriteSharp.ImageWriter.WritePng` to encode the RGBA buffer. Then insert:

```text
pHYs: X=11811, Y=11811, unit=1
```

Encode into a temporary `MemoryStream`, validate the PNG signature and first `IHDR` chunk, then copy the signature+IHDR, append the 13-byte `pHYs` chunk, and copy the remaining encoded chunks. Use big-endian integers and standard CRC-32 over `pHYs`+data. Do not close the caller-owned reserved stream before the existing file-size/atomic-move flow finishes.

- [ ] **Step 7: Update documentation and run GREEN verification**

Remove all claims that TrapMap deploys or requires `System.Drawing.Common`; document the pure C# encoder and BCL-only runtime.

Run from a clean output state:

```powershell
powershell -ExecutionPolicy Bypass -File ClumsyPilot\tests\verify_trapmap_inputs.ps1
dotnet build ClumsyPilot\ClumsyPilot.csproj --no-restore -v:minimal
powershell -ExecutionPolicy Bypass -File ClumsyPilot\tests\verify_trapmap_grid.ps1
powershell -ExecutionPolicy Bypass -File ClumsyPilot\tests\verify_trapmap_lifecycle.ps1
powershell -ExecutionPolicy Bypass -File ClumsyPilot\tests\verify_trapmap_image.ps1
git diff --check
$staged = git diff --cached --name-only; if ($staged) { throw "Unexpected staged files: $staged" }
```

Expected: build succeeds without a drawing DLL in output; all tests pass; PNG parser reports correct dimensions, CRCs, 300 DPI metadata, RGBA pixels, unique filenames, file size at or below 50 MiB, and no temporary files.
