---
phase: 11-agent-saved-signature-and-signing-workflow
verified: 2026-03-21T00:00:00Z
status: human_needed
score: 9/9 automated must-haves verified
re_verification: false
human_verification:
  - test: "Agent draws signature at /portal/profile and thumbnail appears after save"
    expected: "Canvas replaced by img element showing the drawn signature PNG after clicking Save Signature"
    why_human: "DOM state transitions (canvas → img swap) and canvas drawing behavior cannot be verified programmatically"
  - test: "Agent clicks Update Signature to redraw and replace their saved signature"
    expected: "Canvas reappears, new signature drawn and saved, thumbnail updates to show new drawing"
    why_human: "State machine toggling (isDrawing true/false) and successful PUT round-trip require live browser session"
  - test: "Agent drags red Agent Signature token onto a document page and a red field box appears"
    expected: "Drop registers as type 'agent-signature'; red field box appears at drop coordinates on PDF canvas"
    why_human: "Drag-and-drop interaction and PDF overlay rendering require live browser verification"
  - test: "Agent prepares a document with agent-signature fields and saved signature — prepared PDF contains the embedded PNG"
    expected: "Downloaded PDF opened in viewer shows agent signature image at placed field coordinates"
    why_human: "PDF content validation (embedded PNG at correct position) requires downloading and visually inspecting the prepared file"
  - test: "Client signing link for prepared document does NOT show an agent-signature overlay"
    expected: "Client signing session shows client-signature and initials overlays only; no agent-signature prompt appears"
    why_human: "Client-facing UI rendering requires loading the signing session in a browser; isClientVisibleField filter verified in code but visual absence must be confirmed"
---

# Phase 11: Agent Saved Signature and Signing Workflow — Verification Report

**Phase Goal:** Agent draws a signature once, saves it to their profile, places agent signature fields on documents, and applies the saved signature during preparation — before the document is sent to the client.
**Verified:** 2026-03-21
**Status:** human_needed — all automated checks pass; 5 items require live browser verification
**Re-verification:** No — initial verification

---

## Goal Achievement

### Observable Truths

| # | Truth | Status | Evidence |
|---|-------|--------|----------|
| 1 | Agent can navigate to /portal/profile via the nav | VERIFIED | `PortalNav.tsx` line 10: `{ href: "/portal/profile", label: "Profile" }` entry in `navLinks` array; link renders in nav |
| 2 | Agent can draw a signature on the canvas and save it | VERIFIED | `AgentSignaturePanel.tsx`: SignaturePad canvas mounted in `useEffect`, `handleSave()` calls `fetch('/api/agent/signature', { method: 'PUT', ... })` and sets `savedData` on success |
| 3 | A thumbnail of the saved signature appears after saving | VERIFIED | `AgentSignaturePanel.tsx` lines 59-77: `if (!isDrawing && savedData)` branch renders `<img src={savedData} .../>` — state-driven conditional render |
| 4 | Agent can click Update Signature to redraw and replace their saved signature | VERIFIED | `AgentSignaturePanel.tsx` line 70: `onClick={() => { setIsDrawing(true); setError(null); }}` on Update Signature button; canvas remounts via `useEffect([isDrawing])` |
| 5 | Agent signature palette token appears in the FieldPlacer (red 'Agent Signature' token) | VERIFIED | `FieldPlacer.tsx` line 76: `{ id: 'agent-signature', label: 'Agent Signature', color: '#dc2626' }` in `PALETTE_TOKENS`; rendered by map at line 713 |
| 6 | Placing an agent-signature field saves it in signatureFields with type 'agent-signature' | VERIFIED | `FieldPlacer.tsx` line 259: `validTypes` set includes `'agent-signature'`; `PALETTE_TOKENS` provides the type id used when drop is processed |
| 7 | Prepared PDF contains agent signature PNG at each agent-sig field coordinate | VERIFIED | `prepare-document.ts` lines 33-37: `embedPng(agentSignatureData)` before field loop; lines 145-154: `page.drawImage(agentSigImage, { x, y, width, height })` for `agent-signature` fields |
| 8 | Prepare route returns 422 with `agent-signature-missing` when fields exist but no sig saved | VERIFIED | `prepare/route.ts` lines 51-57: `hasAgentSigFields && !agentSignatureData` guard returns `{ error: 'agent-signature-missing', ... }` at status 422 |
| 9 | Agent signature is never visible to the client via the signing session | VERIFIED | `sign/[token]/route.ts` line 90: `signatureFields: (doc.signatureFields ?? []).filter(isClientVisibleField)`; `isClientVisibleField()` in schema.ts line 36-38 returns false for `agent-signature` type |

**Score:** 9/9 automated truths verified

---

## Required Artifacts

| Artifact | Provides | Status | Details |
|----------|----------|--------|---------|
| `src/lib/db/schema.ts` | `agentSignatureData TEXT` column on users table | VERIFIED | Line 45: `agentSignatureData: text("agent_signature_data")` present on `users` pgTable |
| `drizzle/0008_windy_cloak.sql` | DB migration applying the column | VERIFIED | File exists; content is `ALTER TABLE "users" ADD COLUMN "agent_signature_data" text;` |
| `src/app/api/agent/signature/route.ts` | GET/PUT endpoints for reading and writing agent signature | VERIFIED | Exports both `GET` and `PUT`; auth guard on both; PUT validates `data:image/png;base64,` prefix and 50KB size limit; reads/writes `agentSignatureData` via drizzle |
| `src/app/portal/_components/AgentSignaturePanel.tsx` | Client component with signature_pad canvas, save/update/thumbnail flow | VERIFIED | Full implementation: SignaturePad DPR init, `handleSave()` with fetch, thumbnail/draw state machine, Update Signature and Cancel buttons |
| `src/app/portal/(protected)/profile/page.tsx` | Server component fetching agentSignatureData and rendering AgentSignaturePanel | VERIFIED | Fetches `agentSignatureData` from DB via `db.query.users.findFirst`; passes as `initialData` prop to `AgentSignaturePanel` |
| `src/lib/pdf/prepare-document.ts` | `preparePdf()` with `agentSignatureData` param; embed-once-draw-many at agent-sig field coordinates | VERIFIED | 5th param `agentSignatureData: string | null = null`; `PDFImage` imported from `@cantoo/pdf-lib`; `embedPng` called once before field loop; `drawImage` called per agent-sig field |
| `src/app/api/documents/[id]/prepare/route.ts` | Fetches agentSignatureData from DB; 422 guard; passes to preparePdf() | VERIFIED | `users` and `getFieldType` imported from schema; `agentUser` lookup before `preparePdf()`; 422 guard at lines 51-57; `preparePdf()` called with 5 args at line 59 |

---

## Key Link Verification

| From | To | Via | Status | Details |
|------|----|-----|--------|---------|
| `profile/page.tsx` | `AgentSignaturePanel.tsx` | `initialData={user?.agentSignatureData ?? null}` prop | VERIFIED | Line 27: `<AgentSignaturePanel initialData={user?.agentSignatureData ?? null} />` — prop carries DB value |
| `AgentSignaturePanel.tsx` | `/api/agent/signature` | `fetch('/api/agent/signature', { method: 'PUT', body: { dataURL } })` | VERIFIED | Lines 40-44: fetch with PUT method, JSON body containing `{ dataURL }` |
| `PortalNav.tsx` | `/portal/profile` | `navLinks` entry | VERIFIED | Line 10: `{ href: "/portal/profile", label: "Profile" }` — rendered as Link |
| `prepare/route.ts` | `prepare-document.ts` | `preparePdf(srcPath, destPath, textFields, sigFields, agentSignatureData)` | VERIFIED | Line 59: 5-arg call with `agentSignatureData` as 5th argument |
| `prepare/route.ts` | `schema.ts` | `db.query.users.findFirst({ columns: { agentSignatureData: true } })` | VERIFIED | Lines 44-48: finds user by session ID, columns only `agentSignatureData` |
| `sign/[token]/route.ts` | `schema.ts` | `.filter(isClientVisibleField)` strips agent-sig fields from client payload | VERIFIED | Line 90: `isClientVisibleField` filter applied before returning `signatureFields` to client |

---

## Requirements Coverage

| Requirement | Source Plan | Description | Status | Evidence |
|-------------|-------------|-------------|--------|---------|
| AGENT-01 | 11-01, 11-03 | Agent can draw and save a signature to their account profile (drawn once, reused) | SATISFIED | `AgentSignaturePanel.tsx` full draw/save/thumbnail implementation; `GET/PUT /api/agent/signature` reads/writes `agentSignatureData` on `users` table |
| AGENT-02 | 11-01, 11-03 | Agent can update their saved signature at any time | SATISFIED | "Update Signature" button sets `isDrawing(true)`; same PUT endpoint overwrites previous value; thumbnail updates to new dataURL on save |
| AGENT-03 | 11-01, 11-03 | Agent can place agent signature field markers on a PDF | SATISFIED | `FieldPlacer.tsx` `PALETTE_TOKENS` line 76 includes `agent-signature` red token; `validTypes` set at line 259 accepts it |
| AGENT-04 | 11-02, 11-03 | Agent applies their saved signature to agent signature fields during document preparation (before sending to client) | SATISFIED | `prepare-document.ts` embeds PNG via `embedPng + drawImage`; `prepare/route.ts` fetches from DB and passes to `preparePdf()`; 422 guard prevents silent no-op when sig missing |

All 4 requirements declared across plans 11-01, 11-02, and 11-03 are fully satisfied. No orphaned requirements found for Phase 11 in REQUIREMENTS.md.

---

## Anti-Patterns Found

| File | Line | Pattern | Severity | Impact |
|------|------|---------|----------|--------|
| `prepare-document.ts` | 98, 105, 116, 140 | "placeholder" in comments | Info | These are code comments describing visual field marker style ("Blue 'Sign Here' placeholder"), not stub implementations. No impact. |

No blocker or warning anti-patterns found. All handler functions have real implementations. No `return null`, empty objects, or `console.log`-only bodies.

---

## Human Verification Required

### 1. Agent draws and saves signature — canvas-to-thumbnail transition

**Test:** Log in to the portal, navigate to /portal/profile, draw a distinctive signature on the canvas, click "Save Signature".
**Expected:** The canvas disappears and is replaced by an `<img>` element showing the drawn signature as a thumbnail.
**Why human:** The `isDrawing` / `savedData` state machine drives a conditional render. The `fetch PUT` result must succeed and the component must re-render correctly. Canvas drawing and the resulting PNG cannot be inspected programmatically.

### 2. Update Signature replaces the thumbnail with a new drawing

**Test:** From the thumbnail view on /portal/profile, click "Update Signature", draw a different signature, click "Save Updated Signature".
**Expected:** The new thumbnail replaces the previous one; visiting /portal/profile in a fresh session shows the updated signature.
**Why human:** Requires verifying that the PUT route returns 200, the state updates correctly, and the DB stores the new value. The full round-trip requires a live browser session.

### 3. Red Agent Signature token appears and can be placed in FieldPlacer

**Test:** Open any document in the portal's prepare view. Confirm the FieldPlacer palette shows a red "Agent Signature" token. Drag it onto any page of the document PDF canvas.
**Expected:** A red field box appears at the drop coordinates on the PDF.
**Why human:** Drag-and-drop interaction and PDF canvas overlay rendering require a live browser. Color rendering (#dc2626) and field positioning need visual confirmation.

### 4. Prepared PDF contains the embedded agent signature PNG at the correct position

**Test:** With a saved signature and an agent-signature field placed on a document, click Prepare. Download the prepared PDF.
**Expected:** Opening the downloaded PDF shows the agent's signature image at exactly the position where the field token was dropped.
**Why human:** PDF content inspection (embedded image at exact coordinates) requires downloading and viewing the file. Coordinate accuracy (PDF user-space vs. screen coordinates) can only be confirmed visually.

### 5. Client signing session does not expose the agent-signature field

**Test:** Use the signing link for the prepared document. Open it in a browser (or a private window to simulate the client).
**Expected:** No overlay, prompt, or signature box appears at the agent-signature field position. Client-signature and initials overlays (if placed) still appear normally.
**Why human:** The `isClientVisibleField` filter is verified in code, but the absence of a UI element in the rendered signing session must be confirmed visually.

---

## Gaps Summary

No gaps. All automated truths are verified, all required artifacts exist and contain substantive implementations, and all key links are wired. The 5 human verification items are behavioral/visual confirmations that automated grep-level analysis cannot substitute for. According to 11-03-SUMMARY.md, all 5 steps were approved by a human during Plan 03 execution.

---

_Verified: 2026-03-21_
_Verifier: Claude (gsd-verifier)_
