16 KiB
Project Research Summary
Project: teressa-copeland-homes Domain: Real estate agent portal — PDF e-signing with document template management Researched: 2026-04-06 Confidence: HIGH — all research based on direct codebase inspection + verified competitor analysis
Executive Summary
This milestone (v1.3) adds a document template system to an existing, working PDF e-signing portal. The core product is already built: forms library, drag-drop field placement, AI auto-place, agent signature, multi-signer document sending, and the full signing flow are all live. The template feature is a focused extension — a new document_templates database table, a template editor that reuses the existing FieldPlacer component with a minor callback abstraction, and an "apply template" path in the new-document flow. No new npm packages are required. The entire feature is a schema extension plus UI reuse.
The recommended approach follows the industry-standard template model used by DocuSign, PandaDoc, and OpenSign: templates store a field layout with signer role labels (not actual emails), and roles are resolved to real signer emails at apply time. Template fields must be snapshotted — deep-copied with fresh UUIDs — into each new document at creation. Templates are never live references. This is the correct pattern for document immutability and is already the implicit design of the existing documents.signatureFields JSONB column. The implementation must not break this invariant.
The primary risks are all data-integrity issues that are easy to introduce and hard to recover from: field ID collisions if template fields carry stable IDs into documents, signer role labels leaking into the signing flow as fake email addresses, and hint text being burned into prepared PDFs as printed content. These risks are fully preventable if the document-creation route enforces ID re-stamping, role-to-email validation, and clear separation of hint vs textFillData. Build order matters: schema and CRUD API before editor UI, editor UI before apply flow, and the FieldPlacer persistence abstraction before the template editor.
Key Findings
Recommended Stack
The existing stack handles this feature entirely. See .planning/research/STACK.md for full detail.
Core technologies:
- Drizzle ORM + PostgreSQL: New
document_templatestable via additive migration — zero changes to existing tables - JSONB columns:
signatureFields+signerSlotsondocument_templates, matching the existing pattern ondocuments - @cantoo/pdf-lib + react-pdf: No changes needed — template editor renders the same PDF, no new PDF manipulation
- FieldPlacer (existing component): Reused via a single
onPersistcallback prop addition — not duplicated - openai / GPT-4.1: Existing AI auto-place pipeline reused via a parallel
/api/templates/[id]/ai-prepareroute
One schema distinction is critical: form_templates is a read-only seeded catalog of 120+ Utah/CCIM PDF forms. document_templates is agent-authored field layouts. These are different concerns and must remain separate tables with a FK from new to old.
Expected Features
See .planning/research/FEATURES.md for full detail and competitor analysis.
Must have (table stakes — P1):
- Save a prepared document as a template (name, source form FK, field layout with role labels, text hints)
- Template list in portal (
/portal/templates) with rename and delete actions - Apply template when adding a document to a client (one-click pre-loaded document creation)
- Field layout is copied (snapshot), never referenced live
- Signer roles ("Seller", "Buyer", "Agent") stored in template; real emails assigned at apply time
- Text fill hints stored in template; actual values resolved from client record at apply time
- Agent can re-edit template (overwrite save, no versioning at v1)
- AI auto-place works in template editor (reuses existing pipeline)
Should have (competitive — P2, after core validates):
- "Create template from existing document" — promote a prepared document to a template
- Template preview — read-only PDF view with field overlays before applying
Defer (v2+):
- Template versioning — not needed until agent has >10 templates and reports pain
- Template sharing / team access — requires multi-agent architecture; out of scope
- Template categories / folders — flat list with good names is sufficient at <20 templates
- Public/shareable template links — distinct product surface requiring anonymous sessions
Architecture Approach
The architecture is a clean extension of the existing document flow. The document_templates table holds field layouts and signer role slots. A POST /api/templates/[id]/apply route performs the document creation: copy PDF, deep-clone fields with fresh UUIDs, resolve role labels to emails from the agent-supplied map, insert document row. The template editor reuses FieldPlacer via an onPersist callback prop (a 5-line addition to the 822-line component). See .planning/research/ARCHITECTURE.md for full data flow and component boundaries.
Major components:
document_templatestable — new schema; stores name, formTemplateId FK, signatureFields JSONB (role labels, not emails), signerSlots JSONB, timestamps- Template CRUD API (
/api/templates,/api/templates/[id]) — list, create, fetch, save fields, soft-delete - Template editor UI (
TemplateEditorClient+TemplatePanel) — thin wrapper over existingFieldPlacerwith template-mode save; shares layout withDocumentPageClient - Apply operation (
POST /api/templates/[id]/apply) — the core integration: PDF copy + field snapshot + role-to-email resolution + document INSERT AddDocumentModalupdate — adds "Start from template" tab; showsTemplatePickerand role-to-email assignment UI/api/templates/[id]/ai-prepare— mirrors existingai-prepareroute; writes todocument_templates.signatureFields
Critical Pitfalls
See .planning/research/PITFALLS.md for full detail, recovery strategies, and verification checklist.
-
Field ID collision — Template fields must NOT carry IDs into documents. Stamp fresh
crypto.randomUUID()IDs on every field at document creation time. Never copy field IDs from the template row. Recovery is expensive (DB migration + textFillData rekeying). -
Template edit retroactively changing existing documents — Never resolve a document's fields from the template at runtime.
documents.signatureFieldsis the authoritative field list for the life of that document. TheformTemplateIdFK is lineage metadata only. Violating this breaks active signing sessions. -
Signer role labels leaking into
signerEmail— Templates use role strings ("Seller") where emails go. The document-creation route must enforce a complete role-to-email map before writing to DB. Validate allsignerEmailvalues pass email format before INSERT. Role labels insignerEmailsilently break signing token routing downstream. -
hintvstextFillDataconfusion —hintis a placeholder shown in the signing UI; it must never be written to the PDF.textFillDataholds agent-typed values that are burned into the prepared PDF. Storing hints intextFillDataprints instruction text as PDF content. Distinction must be enforced in the template authoring phase. -
Template deletion breaking document history — No hard deletes. Use soft-delete (
archivedAtcolumn). If hard delete is required, block it whendocumentsrows reference the template. Never useON DELETE CASCADEondocuments.formTemplateId— that would destroy client documents.
Implications for Roadmap
Based on the dependency graph in ARCHITECTURE.md and the pitfall-to-phase mapping in PITFALLS.md, four phases are recommended. Each phase is independently deployable and leaves the existing system working.
Phase 1: Schema + Template CRUD API
Rationale: Everything else depends on the table existing and the API working. Schema is additive — no existing tables change. Safe to deploy to production immediately.
Delivers: document_templates table with migration; GET/POST /api/templates; GET/PATCH/DELETE /api/templates/[id]; TemplateSignerSlot interface in schema.ts
Addresses: Table stakes "save a prepared template", "template list with rename/delete"
Avoids: Template deletion cascade (establish soft-delete pattern here); file/DB desync (validate filename at create time)
Research flag: Standard patterns (follows existing forms-library/route.ts and documents/[id]/route.ts exactly — skip phase research)
Phase 2: FieldPlacer Abstraction + Template Editor UI
Rationale: FieldPlacer must have the onPersist callback before the template editor can reuse it. This is a non-breaking change — all existing document consumers pass no prop, behavior unchanged. Template editor then becomes a thin configuration layer.
Delivers: onPersist prop on FieldPlacer; TemplateEditorClient + TemplatePanel; /portal/(protected)/templates/[templateId]/page.tsx; portal nav link
Addresses: "Agent can re-edit template", signer role slot management in editor
Avoids: Building a separate FieldPlacer implementation (diverging drag-drop logic); branching inside the 822-line component
Research flag: Standard patterns (direct component extension, well-understood — skip phase research)
Phase 3: Apply Template Flow
Rationale: The apply operation is the value-delivery moment — it depends on Phases 1+2. This phase requires the most careful implementation due to three critical pitfalls (field ID collision, role-to-email validation, snapshot independence).
Delivers: POST /api/templates/[id]/apply; updated AddDocumentModal with "Start from template" tab; TemplatePicker component; role-to-email assignment UI; known hint keys auto-resolved from client record
Addresses: Core table stakes "apply template when adding a document"; signer role resolution; text fill hint auto-population
Avoids: Field ID collision (stamp UUIDs at apply time, never copy from template); role labels in signerEmail (validate before INSERT); live field resolution (snapshot is the rule)
Research flag: Needs careful implementation review — the three never-acceptable shortcuts in PITFALLS.md all live here. No external research needed, but this phase warrants the most internal code review.
Phase 4: Template List UI + Portal Integration
Rationale: The list page can be built after the API exists (Phase 1), but is sequenced last because it depends on the full round-trip working (create template in editor, list it, apply it). Completing this phase closes the v1.3 loop.
Delivers: TemplatesPageClient; /portal/(protected)/templates/page.tsx; template cards with field count, source form name, rename/delete actions; archived template filtering
Addresses: "Template list in portal" table stakes; "template name includes form name" differentiator
Avoids: Archived templates surfacing in library (filter archivedAt IS NULL in list API); no preview confusion (show field count summary on card)
Research flag: Standard patterns (matches existing ClientCard + ConfirmDialog + ClientsPageClient pattern — skip phase research)
Phase Ordering Rationale
- Schema must precede all API work (no table = no routes)
- FieldPlacer abstraction must precede template editor (can't reuse a component that isn't abstracted)
- Apply flow depends on both the API (template data) and the editor (template creation) being in place
- List UI is independently testable but benefits from the full create-apply round-trip being available for manual verification
- All phases avoid touching the signing flow, PDF preparation, email sending, and audit events — this isolation is deliberate and must be maintained
Research Flags
Phases with standard patterns (skip research-phase):
- Phase 1: Schema migration follows established drizzle-kit pattern (migrations 0000-0011 already in place); CRUD API follows
forms-library/route.tsanddocuments/[id]/route.tsexactly - Phase 2: FieldPlacer abstraction is a well-understood prop injection; template editor composes existing components
- Phase 4: List UI follows existing
ClientsPageClient+ClientCard+ConfirmDialogpattern verbatim
Phases needing implementation-time attention (not external research, but internal discipline):
- Phase 3: Three critical pitfalls converge here. Recommend a peer review checklist against PITFALLS.md "Looks Done But Isn't" section before marking phase complete.
Confidence Assessment
| Area | Confidence | Notes |
|---|---|---|
| Stack | HIGH | Direct codebase inspection; zero new packages; existing JSONB pattern already in production |
| Features | HIGH | DocuSign, PandaDoc, OpenSign all confirmed; competitor analysis corroborates feature set; solo-agent scope confirmed from PROJECT.md |
| Architecture | HIGH | Based on direct inspection of 10 source files and 12 drizzle migrations; no external assumptions |
| Pitfalls | HIGH | Derived from schema.ts type analysis and existing route code; all pitfalls trace to specific lines in the codebase |
Overall confidence: HIGH
Gaps to Address
-
FieldPlacer
signerEmailformat validation: STACK.md flags that FieldPlacer and PreparePanel components may have email-format validation that would reject role strings. Confirm noz.string().email()or similar check runs onsignerEmailduring template editing before implementing Phase 2. If validation exists, introduce amode: "template" | "document"prop alongsideonPersist. -
Role-to-email mapping UX for single-signer documents: FEATURES.md notes that for solo-agent, single-signer use case the role-to-email mapping step can default to the client's email automatically. The exact trigger condition (when to auto-default vs when to prompt) needs to be defined in Phase 3 planning. Suggested rule: if the template has exactly one non-agent signer role, pre-populate it with the assigned client's email.
-
updatedAtauto-update convention: Existing tables setupdatedAtexplicitly in update queries (no DB trigger).document_templatesmust follow the same convention. Flag this in Phase 1 implementation notes.
Sources
Primary (HIGH confidence)
- Direct inspection:
src/lib/db/schema.ts— full table schema,SignatureFieldDatainterface,textFillDatatype - Direct inspection:
src/app/api/documents/route.ts— document creation flow (library path + upload path) - Direct inspection:
src/app/api/documents/[id]/fields/route.ts— GET/PUT pattern for signatureFields - Direct inspection:
src/app/portal/_components/AddDocumentModal.tsx— modal flow for new document creation - Direct inspection:
drizzle/migrations 0000-0011 — confirmed additive migration pattern - Direct inspection:
package.json— confirmed zero new packages needed - DocuSign: Working with Templates
- OpenSign: Create Templates
- OpenSign: Use Template to Create Documents
Secondary (MEDIUM confidence)
- PandaDoc: Creating Dynamic Document Templates — template variable and role model
- BoldSign: Create eSignature Templates for Unlimited Reuse — competitor feature comparison
Research completed: 2026-04-06 Ready for roadmap: yes