# AGENT-001 fix — inline customer-confirmed wipe: anti-retarget re-resolution

**Branch:** `fix/agent-001-wipe-durable-reresolve` (off `main` @ d17b5ab) — **PENDING REVIEW, NOT deployed.**
**Status:** branch-only. No agent build, no deploy, no golden rebake, no re-provision. That is reserved for a supervised session (this fix touches the destructive storage-wipe path).

## The bug

`internal/localapi/disks.go` `handleDiskFormat`, the data-bearing **customer-confirmed** branch
(`dec.Allowed`), inspected and gate-bound the device by its durable id but then called
`s.disks.Format(ctx, req.Device, …)` on the **caller-supplied mutable `/dev` path**. The durable id
only bound the *confirmation*; it was never re-resolved to the device just before `mkfs`. If a USB
re-enumeration reassigned `/dev/sdbN` to a **different physical disk** between inspection and `mkfs`
(the classify→mkfs TOCTOU), the wipe hit the wrong drive — data loss on an unintended disk.

The signed-jobs path (`internal/signedjobs/wipe.go` `WipeExecutor.Execute`) already does this
correctly: resolve durable→device, re-derive the device's durable id and require an exact match,
re-inspect, then format the *re-resolved* device. The inline path did not.

## What changed

- **New `internal/localapi/wipe_reresolve.go`:**
  - `antiRetargetResolve(durableID, resolve, derive, inspect)` — pure, injected-deps helper mirroring
    `WipeExecutor`: refuse empty durable id; resolve→current device; re-derive and require exact
    match (a `/dev` node now pointing at a different disk derives a different id → refuse); re-inspect
    and require still-`DataBearing()`. Returns the **re-resolved device** to format.
  - `(*Server).reresolveDurableForWipe` wires it with the real `storage.ResolveDurableDevice` /
    `storage.DeviceDurableID` / `s.disks.InspectDevice`.
- **`internal/localapi/disks.go`:** the `dec.Allowed` branch now calls `s.reresolveWipe(ctx, deviceDurable)`
  and formats the **returned** device (never `req.Device`). On any refusal it returns `409 Conflict`
  with the reason and does **not** call `mkfs`. The response/log now reference the re-resolved device.
- **`internal/localapi/server.go`:** new injectable field `reresolveWipe`, defaulted in `NewServer` to
  `s.reresolveDurableForWipe`. Production behaviour unchanged (same real functions); the seam exists so
  the inline path is unit-testable without real `/dev/disk/by-*`.
- **Tests:** `wipe_reresolve_test.go` covers `antiRetargetResolve` directly — happy path, empty durable,
  no-longer-resolves, **durable-id mismatch (the core retarget case)**, no-longer-data-bearing,
  re-inspect error. The existing integration test `TestFormat_DataBearing_UserDataConfirmed_Formats`
  stubs `reresolveWipe` (the test helper sets it). Full `go build/vet/test ./...` green at the branch HEAD.

## Why it can't break legitimate wipes

`storage.DeviceDurableID` produces `byid:`/`byuuid:` ids and `storage.ResolveDurableDevice` accepts
exactly those — they round-trip. A normal user-data drive resolves back to itself, re-derives the same
id, and inspects data-bearing → format proceeds. Only three new refusals occur, all correct:
durable id gone, durable-id mismatch (retarget), or no-longer-data-bearing.

## Review checklist for the supervised merge+deploy+golden-rebake session

1. Confirm `antiRetargetResolve` refuses on mismatch/gone/blank/empty (run `go test ./internal/localapi/ -run AntiRetarget -v`).
2. Sanity-check `DeviceDurableID`↔`ResolveDurableDevice` scheme round-trip on the real demo USB (`felhom-usb`) so a genuine customer wipe still resolves+matches (read-only `DeviceDurableID` on the live device; do NOT wipe).
3. Confirm the `409 Conflict` refusal surfaces sensibly in the controller UI (controller `agentapi` maps the format response — verify it doesn't treat 409 as a hard error that hides the reason).
4. Bump agent version + CHANGELOG, build, golden-rebake, re-provision per the agent deploy runbook.
5. Live-validate one happy customer-confirmed wipe on a demo scratch drive (re-resolve matches → mkfs) and confirm the audit log line shows the re-resolved device == the confirmed durable id.
