Added INIT-01 through INIT-04 requirements to REQUIREMENTS.md. Updated ROADMAP.md with Phase 11.1 goal, success criteria, and plan list. Created three plan files mirroring Phase 11 structure: 11.1-01 (DB migration, API routes, AgentInitialsPanel, FieldPlacer token), 11.1-02 (preparePdf agentInitialsData param + prepare route guard), 11.1-03 (human E2E verification checkpoint). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
14 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
| phase | plan | type | wave | depends_on | files_modified | autonomous | requirements | must_haves | |||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 11.1-agent-and-client-initials | 02 | execute | 2 |
|
|
true |
|
|
Purpose: Fulfills INIT-03 — agent's saved initials are baked into the prepared PDF before it reaches the client. Confirms INIT-04 — the existing 'initials' (client-initials) branch is deliberately left untouched; client-initials already work end-to-end.
Output: Prepared PDFs with embedded agent initials; 422 error when initials are missing; client signing session unaffected; zero regressions.
<execution_context> @/Users/ccopeland/.claude/get-shit-done/workflows/execute-plan.md @/Users/ccopeland/.claude/get-shit-done/templates/summary.md </execution_context>
@.planning/STATE.md @.planning/phases/11.1-agent-and-client-initials/11.1-01-SUMMARY.mdFrom src/lib/pdf/prepare-document.ts (current function signature — Phase 11 complete, agentInitialsData NOT YET present):
export async function preparePdf(
srcPath: string,
destPath: string,
textFields: Record<string, string>,
sigFields: SignatureFieldData[],
agentSignatureData: string | null = null, // Added Phase 11
// Phase 11.1 adds: agentInitialsData: string | null = null,
): Promise<void>
Current 'initials' branch in preparePdf() — DO NOT TOUCH (this is client-initials):
} else if (fieldType === 'initials') {
// Purple "Initials" placeholder — transparent background, border + label only
page.drawRectangle({
x: field.x, y: field.y, width: field.width, height: field.height,
borderColor: rgb(0.49, 0.23, 0.93), borderWidth: 1.5,
});
page.drawText('Initials', {
x: field.x + 4, y: field.y + 4, size: 8, font: helvetica,
color: rgb(0.49, 0.23, 0.93),
});
}
Current agent-signature branch in preparePdf() (Phase 11 — working — do not change):
} else if (fieldType === 'agent-signature') {
if (agentSigImage) {
page.drawImage(agentSigImage, {
x: field.x,
y: field.y,
width: field.width,
height: field.height,
});
}
}
Pattern for embed-once-draw-many (Phase 11 confirmed working):
// BEFORE the field loop — embed once:
let agentSigImage: PDFImage | null = null;
if (agentSignatureData) {
agentSigImage = await pdfDoc.embedPng(agentSignatureData);
}
// Phase 11.1 parallel pattern — add after agentSigImage block:
let agentInitialsImage: PDFImage | null = null;
if (agentInitialsData) {
agentInitialsImage = await pdfDoc.embedPng(agentInitialsData);
}
CRITICAL: Do NOT call embedPng() inside the field loop. The Phase 11 pattern calls it once before the loop and reuses the PDFImage reference. Phase 11.1 follows the same pattern.
From src/app/api/documents/[id]/prepare/route.ts (Phase 11 state — current):
// Current: fetches only agentSignatureData
const agentUser = await db.query.users.findFirst({
where: eq(users.id, session.user.id),
columns: { agentSignatureData: true }, // Phase 11.1: ADD agentInitialsData: true
});
const agentSignatureData = agentUser?.agentSignatureData ?? null;
// Phase 11 guard (existing — leave unchanged):
const hasAgentSigFields = sigFields.some(f => getFieldType(f) === 'agent-signature');
if (hasAgentSigFields && !agentSignatureData) {
return Response.json(
{ error: 'agent-signature-missing', message: '...' },
{ status: 422 }
);
}
// Current call (5 args):
await preparePdf(srcPath, destPath, textFields, sigFields, agentSignatureData);
PDFImage type note: PDFImage is already imported (or available as a type import) from @cantoo/pdf-lib in prepare-document.ts since Phase 11. Use the same import pattern. Do not add a new import if PDFImage is already destructured from the existing import.
Task 1: preparePdf() — add agentInitialsData param and embed at agent-initials field coordinates teressa-copeland-homes/src/lib/pdf/prepare-document.ts Three targeted changes to `preparePdf()` — no other logic changes:1. Add agentInitialsData parameter as the 6th parameter with a default of null (so the existing 5-arg call site in the prepare route still compiles until Task 2 updates it):
export async function preparePdf(
srcPath: string,
destPath: string,
textFields: Record<string, string>,
sigFields: SignatureFieldData[],
agentSignatureData: string | null = null,
agentInitialsData: string | null = null, // ADD THIS
): Promise<void>
2. Embed agent initials image once — add this block immediately AFTER the existing agentSigImage embed block (before the field loop):
// Embed agent initials image once — reused across all agent-initials fields
let agentInitialsImage: PDFImage | null = null;
if (agentInitialsData) {
agentInitialsImage = await pdfDoc.embedPng(agentInitialsData);
}
If PDFImage is already in the existing @cantoo/pdf-lib import destructure, use it directly. If not, add PDFImage to the existing import — do not create a new import statement.
3. Add 'agent-initials' branch in the field loop — add it as a new else if block after the existing 'agent-signature' branch:
} else if (fieldType === 'agent-initials') {
if (agentInitialsImage) {
page.drawImage(agentInitialsImage, {
x: field.x,
y: field.y,
width: field.width,
height: field.height,
});
}
// If no initials saved: the prepare route guards against this with 422 before calling preparePdf
}
Do NOT modify:
- The
'initials'branch (client-initials purple placeholder) — leave completely untouched - The
'agent-signature'branch — leave completely untouched - Any other field type branches or function logic cd /Users/ccopeland/temp/red/teressa-copeland-homes && npx tsc --noEmit 2>&1 | head -20 TypeScript compiles clean; preparePdf() function signature now has 6 parameters (last two optional); agent-initials branch draws the image; existing 'initials' (client-initials) and 'agent-signature' branches are untouched; no regressions on other field type branches.
1. Update the existing DB query to fetch agentInitialsData alongside agentSignatureData in the same findFirst() call (one DB round-trip, not two):
// BEFORE:
const agentUser = await db.query.users.findFirst({
where: eq(users.id, session.user.id),
columns: { agentSignatureData: true },
});
const agentSignatureData = agentUser?.agentSignatureData ?? null;
// AFTER:
const agentUser = await db.query.users.findFirst({
where: eq(users.id, session.user.id),
columns: { agentSignatureData: true, agentInitialsData: true }, // ADD agentInitialsData
});
const agentSignatureData = agentUser?.agentSignatureData ?? null;
const agentInitialsData = agentUser?.agentInitialsData ?? null; // ADD
2. Add the agent-initials 422 guard — add this block immediately AFTER the existing hasAgentSigFields guard (NOT before it):
// Guard: agent-initials fields present but no initials saved
const hasAgentInitialsFields = sigFields.some(f => getFieldType(f) === 'agent-initials');
if (hasAgentInitialsFields && !agentInitialsData) {
return Response.json(
{ error: 'agent-initials-missing', message: 'No agent initials saved. Go to Profile to save your initials first.' },
{ status: 422 }
);
}
3. Update the preparePdf() call to pass agentInitialsData as the 6th argument:
// BEFORE (5 args):
await preparePdf(srcPath, destPath, textFields, sigFields, agentSignatureData);
// AFTER (6 args):
await preparePdf(srcPath, destPath, textFields, sigFields, agentSignatureData, agentInitialsData);
Do not change any other logic in the route handler — DB update, audit logging, and response all remain unchanged. cd /Users/ccopeland/temp/red/teressa-copeland-homes && npx tsc --noEmit 2>&1 | head -20 TypeScript compiles clean; prepare route fetches both agentSignatureData and agentInitialsData in a single query; guard returns 422 with { error: 'agent-initials-missing' } when agent-initials fields exist but no initials saved; preparePdf called with 6 args including agentInitialsData.
After both tasks complete, verify the full Plan 02 state:npx tsc --noEmitpasses with zero errorsnpm run devstarts without errors (ornpm run buildsucceeds)- Agent-initials round-trip test: a. Visit /portal/profile, draw and save initials b. Open a document in the portal, place an "Agent Initials" field (orange token) on any page c. Fill text fields and click Prepare d. Prepare succeeds (200 response) e. Download the prepared PDF and verify the agent initials PNG is embedded at the correct position
- Agent-initials guard test — prepare with no saved initials:
a. Place an agent-initials field on a document for an account with no initials saved
b. Prepare route returns 422 with
{ error: 'agent-initials-missing' } - Agent-signature regression test — confirm Phase 11 behavior unchanged: a. Place an agent-signature field, ensure a signature is saved, prepare the document b. Signature still embeds correctly; no regression
- Client-initials regression test — confirm existing 'initials' behavior unchanged: a. Place a purple "Initials" field, prepare the document b. Prepared PDF shows purple "Initials" placeholder at the field location c. Open the signing link — client still sees the initials overlay and can initial the field
- Client signing page isolation test: a. Open the signing link for a document with agent-initials fields b. Signing page does NOT show an overlay at agent-initials coordinates (isClientVisibleField already returns false from Plan 01)
<success_criteria>
- INIT-03: Agent's saved initials PNG is embedded at each agent-initials field coordinate in the prepared PDF; field is never exposed to the client
- INIT-04: Existing 'initials' type (client-initials) behavior confirmed unchanged — client is still prompted to initial during signing session
- Prepare fails with actionable 422 when agent-initials fields exist but no initials saved
- Agent-signature embedding (Phase 11) still works — no regression
- TypeScript build clean; zero new npm packages; no regressions on any other field type </success_criteria>