- Step 3.5: fetch signerEmail from tokenRow after atomic claim
- Step 7: accumulate pattern — read from signedFilePath or preparedFilePath as working PDF
- Step 7: write to JTI-keyed _partial_ path to prevent concurrent signer collisions
- Step 8a: date stamps scoped to this signer's date fields (D-09)
- Step 8b: signable fields scoped to this signer's fields (Pitfall 4)
- Step 9.5: JTI-keyed datestamped temp file to prevent collision
- Step 10.5: update signedFilePath to this signer's partial after each signing
- Step 11: remaining-token count check before completion attempt
- Step 11: completionTriggeredAt atomic guard (UPDATE WHERE IS NULL RETURNING)
- Step 12: status='Signed' only in completion winner block (fixes first-signer-wins bug)
- Step 13: agent notification + signer completion emails only at completion
- Legacy null-signerEmail tokens fall through all signer filters unchanged
- Loop over doc.signers when populated: one createSigningToken per signer with signerEmail
- Dispatch all signing emails in parallel via Promise.all
- Preserve legacy single-signer path unchanged when signers is null/empty
- Replace NEXT_PUBLIC_BASE_URL with APP_BASE_URL for signing URLs
- Add audit event with metadata.signerEmail for each signer in multi-signer path
- Import DocumentSigner from schema for type casting
- Sends plain-text email with signed document download link to signer
- Follows established createTransporter() + sendMail() pattern
- Subject: 'Signed copy ready: {documentName}', 72h expiry in body text
- createSigningToken now accepts optional signerEmail param and persists to DB
- Added createSignerDownloadToken (72h TTL, purpose: signer-download)
- Added verifySignerDownloadToken with purpose claim validation
- Migration 0010_sharp_archangel.sql adds signer_email to signing_tokens
- Migration adds signers JSONB column to documents
- Migration adds completion_triggered_at TIMESTAMP to documents
- Additive-only: no DROP columns, no ALTER TYPE, no backfills
- Applied successfully to local Neon/Postgres instance
- Remove 3 console.log statements that printed blank count, all blank descriptions, and AI classifications
- These were development debug statements; not appropriate for production code
- Tests pass (prepare-document.test.ts: 10/10), TypeScript clean
- Red PAGE N label stamped on each image so GPT-4o correctly attributes fields to pages
- Prompt: add 'blank line above label' signature block pattern (common in real estate docs)
- Prompt: explicit rule — place field on blank underline, not on the (Label) text below it
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Max heights reduced: text/date 16pt, initials 18pt, signature 26pt
- 0.5% y nudge pushes fields onto the underline instead of floating above it
- Prompt specifies 1.2% height for text/date, 1.8% for signatures
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace fixed 144x36 with AI widthPct/heightPct clamped to per-type min/max
(signatures 100-250x20-40pt, initials 36-80x16-28pt, date 60-130x14-24pt, text 60-280x14-24pt)
- Prompt: explicit 'no inline body text' rule — if text is part of a sentence, skip it
- Prompt: widthPct should match visual underline width, heightPct kept thin (~2-2.5%)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- extractPdfText now renders each page to JPEG via @napi-rs/canvas + pdfjs-dist (108dpi)
- field-placement.ts sends rendered page images to GPT-4o with vision (detail: high)
- AI can now visually identify underlines, signature blocks, date fields, initials boxes
- System prompt focuses on visual cues (blank lines, boxes) not text pattern matching
- Handles multi-field lines: separate fields for signature blank and date blank on same line
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- extractPdfText now returns TextLine[] with yPct/xPct per line instead of flat text blob
- AI can now see spatial layout (where blank lines/underscores actually are vs body text)
- Rewrote system prompt: explicit rules about blank lines/underscores/signature blocks,
place ALL blanks even without prefill value, match field type to label pattern
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- gpt-4o-mini replaced with gpt-4o for better placement accuracy
- checkbox removed from schema enum and filtered in loop (positions are input-dependent)
- y coordinate clamped to [0, pageHeight - fieldHeight] to prevent fields rendering
outside the PDF canvas when AI returns yPct near 100%
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
react-pdf calls new DOMMatrix() at module level — static import causes
500 on document page during SSR. Same pattern as PdfViewer/PdfViewerWrapper.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Empty string workerSrc is falsy — PDFWorker.workerSrc getter throws before
_setupFakeWorkerGlobal can dynamically import the worker file.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add onAiAutoPlace prop and handleAiAutoPlaceClick with aiLoading state to PreparePanel
- Add violet AI Auto-place button above Preview button (Draft documents only)
- Add aiPlacementKey state and handleAiAutoPlace callback in DocumentPageClient
- handleAiAutoPlace: POST /api/documents/[id]/ai-prepare, merges textFillData, increments aiPlacementKey, resets previewToken
- Pass aiPlacementKey to PdfViewerWrapper and onAiAutoPlace to PreparePanel
- Add aiPlacementKey?: number to FieldPlacerProps interface
- Add aiPlacementKey to loadFields useEffect dependency array for re-fetch on AI placement
- Thread aiPlacementKey through PdfViewer and PdfViewerWrapper prop chains
- 13-02-SUMMARY.md created with accomplishments, decisions, file list
- STATE.md: plan position advanced to 2/4, decisions logged, session updated
- ROADMAP.md: phase 13 progress updated to 2 summaries
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Create 13-01-SUMMARY.md with TDD execution results and decisions
- Update STATE.md: phase 13 in progress, plan 1/4 complete
- Update ROADMAP.md: phase 13 status set to In Progress (1/4 summaries)
- Mark AI-01, AI-02 requirements complete in REQUIREMENTS.md
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add ai-coords.test.ts with 3 test cases covering US Letter (612x792 pts)
- Tests import from lib/ai/field-placement which does not exist yet (RED phase)
- Cases: text field near top, checkbox near bottom-right, client-sig at center
4 plans in 4 sequential waves covering:
- Plan 01 (TDD): openai install, extract-text.ts, field-placement.ts, aiCoordsToPagePdfSpace unit test
- Plan 02: POST /api/documents/[id]/ai-prepare route with all guards
- Plan 03: UI wiring — aiPlacementKey in FieldPlacer, AI Auto-place button in PreparePanel
- Plan 04: Unit test gate + human E2E verification checkpoint
Satisfies AI-01, AI-02. Completes v1.1 Smart Document Preparation milestone.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>