docs: complete project research

This commit is contained in:
Chandler Copeland
2026-04-03 14:47:06 -06:00
parent 6265a64a50
commit 622ca3dc21
5 changed files with 1943 additions and 1262 deletions

View File

@@ -257,3 +257,257 @@ Features that go beyond the baseline — meaningful specifically for this app's
---
*Feature research for: Teressa Copeland Homes — v1.1 Smart Document Preparation*
*Researched: 2026-03-21*
---
---
# Feature Research — v1.2 Multi-Signer Workflow
**Domain:** Multi-signer document signing — parallel dispatch, signer-scoped signing pages, completion tracking
**Researched:** 2026-04-03
**Confidence:** HIGH — grounded in existing codebase inspection (ARCHITECTURE.md already defines the implementation model), verified against DocuSign/HelloSign/PandaDoc behavioral patterns from web research
---
## Scope Note
This section covers only the v1.2 milestone additions. The existing v1.0/v1.1 features (single-signer flow, field placement, AI pre-fill, agent signature, filled preview) are already built.
**New features under research (multi-signer only):**
1. Signer list entry on the document (replace single recipient with named signer rows)
2. Field-to-signer assignment when placing fields
3. Per-signer signing tokens and parallel dispatch
4. Signer-filtered signing page
5. Per-signer completion tracking
6. Document completion: all-signed notification + final PDF to all parties
---
## Feature Landscape
### Table Stakes (Users Expect These)
These are behaviors that DocuSign, HelloSign (Dropbox Sign), and PandaDoc all implement as defaults. An agent who has used any of these tools will expect them. Missing any one of these makes the multi-signer feature feel broken.
| Feature | Why Expected | Complexity | Notes |
|---------|--------------|------------|-------|
| Name each signer (email + optional display name) | Every major tool requires identifying signers before placing fields. Without it, field assignment has no target and the email has no greeting. | LOW | Replaces the single email textarea in PreparePanel with a list of name+email rows. Names used in the email greeting ("Hi Sarah,"). Emails drive field routing. Minimum 1 signer; no enforced maximum for MVP. |
| Assign each client-visible field to a specific signer | Core of multi-signer — a field without a signer assignment is ambiguous. Which canvas does Buyer sign on vs. Seller? DocuSign, PandaDoc, and dotloop all require explicit field-to-recipient assignment before sending. | MEDIUM | Per-field signer selector in FieldPlacer. Agent-owned fields (agent signature, agent-pre-filled text) have no signer assignment — they are filled at prepare time and locked. Only client-visible fields (signature, initials, checkbox) need assignment. Date fields are client-visible but auto-stamp — they inherit the assignment of the adjacent signature/initials field they accompany. |
| Block send if any client-visible field is unassigned | DocuSign enforces this as a hard error before sending. Agents expect it because sending an unassigned signature field means no one will ever sign it. | LOW | UI validation: "Send" button is disabled and shows "2 fields have no signer assigned." Agent fixes in FieldPlacer before proceeding. |
| Each signer receives their own unique signing link | Standard behavior. Sharing one link among multiple signers is not a pattern any tool uses — it removes identity, makes completion tracking impossible, and creates legal ambiguity about who signed what. | MEDIUM | Each signer gets a JWT signed with their email stored in the `signer_email` column of `signing_tokens`. Links sent simultaneously via `Promise.all()`. |
| Each signer sees only their own fields | Expected by analogy with single-signer: when you receive a DocuSign link, you see only your fields. Seeing another signer's name on an empty signature field you cannot fill is confusing and creates "who signs where?" support calls. | MEDIUM | Signing GET endpoint filters `signatureFields` by `field.signerEmail === tokenRow.signerEmail`. Other signers' fields are simply not sent to the client. The PDF renders in full; only the interactive overlay elements are scoped. |
| Signing page shows progress ("X of Y signers have signed") | Users expect to know where the document stands. DocuSign shows "1 of 3 recipients have signed." HelloSign shows a progress indicator. In real estate, agents are frequently asked "has everyone signed yet?" | LOW | Simple count from `signing_tokens` WHERE `document_id = ? AND used_at IS NOT NULL` vs total tokens. Shown on the signing confirmation page and in the agent portal document detail. |
| Agent is notified when all signers complete | Universal behavior. DocuSign sends a "Document Completed" email to the sender by default. Without this, the agent has no way to know the document is fully executed without polling the portal. | LOW | Triggered once all tokens are claimed (`document_completed` audit event). One email to agent. Subject: "All parties have signed — [Document Name]". |
| All parties receive the final PDF when complete | DocuSign attaches the completed PDF to the completion email (subject to a 5 MB size limit). Real estate agents expect all parties to have a copy of the fully-executed document. It is also an industry norm for legal record-keeping. | MEDIUM | Each signer gets an email with a presigned download link to the final merged PDF. Agent gets the same. Not an attachment (avoids size limits and expired links from email attachment storage policies) — a time-limited download link from Vercel Blob. |
### Differentiators (Competitive Advantage)
These are not expected behaviors in a basic multi-signer implementation, but they fit the context of this specific app.
| Feature | Value Proposition | Complexity | Notes |
|---------|-------------------|------------|-------|
| Per-signer color coding on the field placement canvas | When the agent is placing fields, it is easy to lose track of which fields belong to which signer. DocuSign uses color-coded recipient fields (blue for Recipient 1, green for Recipient 2). This prevents misassignment. | LOW | CSS border/background color per `signerEmail`. Color assigned deterministically (first signer = blue, second = green, etc.). Legend shown in FieldPlacer sidebar. No user-configurable color. |
| Signing confirmation page shows what was just signed | After submitting, the signer sees "Thanks Sarah — you've signed. Bob Smith still needs to sign." This is the Dropbox Sign post-sign experience. It reduces "did it work?" anxiety without exposing any privacy details about the other signer's contact info. | LOW | Simple post-submit page. Shows: signer's own name, document name, completion timestamp, number of remaining signers (not their names/emails). Download link for the in-progress PDF (the accumulator). |
| Document status shows per-signer completion in agent portal | The agent can see at a glance which signers have completed and which have not. In single-signer this was binary (Sent/Signed). Multi-signer needs granularity: "Buyer signed March 3 — Seller has not yet signed." | MEDIUM | Agent portal document detail reads `doc.signers[]` array. Each entry shows: signer name/email, `signedAt` timestamp if completed, "Awaiting" if not. This is the `signers` JSONB column from ARCHITECTURE.md. |
### Anti-Features (Commonly Requested, Often Problematic)
| Feature | Why Requested | Why Problematic | Alternative |
|---------|---------------|-----------------|-------------|
| Sequential signing (Signer 2 cannot sign until Signer 1 completes) | "Agent should sign before the buyer" or "Seller must sign before buyer sees it" | PROJECT.md explicitly specifies parallel signing ("any order"). Sequential adds routing logic, waiting states, and re-notification emails — significant complexity for a feature the project owner explicitly rejected. For this app, the agent signs first at prepare time (before sending). The remaining signers are all clients who sign in parallel. | Agent signs before sending (already built in v1.1 as agent-signature flow). All client signers receive links simultaneously. |
| Signing reminder emails (automated resend if not signed in N days) | "What if the client doesn't sign for a week?" | Requires background job infrastructure (cron or worker queue). For a solo agent sending to known clients, a manual "Resend reminder" button is sufficient and avoids the infra cost. | Manual resend: agent can click "Resend link" in the portal for any signer who has not yet completed. This creates a new token and sends a fresh email. LOW complexity, same user outcome for 95% of cases. |
| Signer can see other signers' completed signatures on the PDF before they sign | "It feels more complete to see the buyer already signed when the seller opens the doc" | Requires a different field visibility model: fetching all signers' submitted data and rendering it on the PDF before the current signer submits. The accumulator model in ARCHITECTURE.md handles this post-hoc (each signer signs into the accumulator), not pre-emptively. Getting this right without creating privacy or sequence issues (what if buyer changes their mind?) is genuinely complex. | Signers see the base PDF with their own fields. After ALL signers complete, the final merged PDF goes to everyone — this is the standard industry pattern. |
| Signing order labels on fields ("Sign here - Signer 2") | "Helps signers know which fields are theirs" | Since each signer only sees their own fields on their signing page, the label is redundant. It adds visual noise and raises questions ("why does it say Signer 2 — am I in the right place?"). | Per-signer filtered signing page already solves this. Each signer sees a clean page with only their fields, labeled by type ("Sign here", "Initial here"). |
| Signer-level expiry (Signer A's link expires in 7 days, Signer B's in 14 days) | "Different parties have different deadlines" | Adds per-token configuration to the send flow. For real estate, all parties in a transaction are on the same deadline. Universal expiry (e.g., 14 days) covers 100% of Teressa's use cases. | All tokens for a document share the same expiry duration. Agent sets it once at send time (or uses the default). A future resend flow handles expired tokens. |
| Email address validation against a contacts database | "Warn me if I enter an email that's not in my clients list" | Signers may be parties Teressa hasn't added to her contacts (co-buyers, lenders, title officers). Restricting to the clients table introduces friction for the 30-40% of signers who aren't pre-loaded. | Free-text email input with standard email format validation only. Signer does not need a client record. This is confirmed in ARCHITECTURE.md: "signers may not be in clients table." |
| Bulk send to a document template (one form, many different transactions) | "Send this listing agreement template to 10 different clients at once" | Requires template management, variable substitution, and per-recipient field instances. Equivalent to DocuSign PowerForms. Multi-signer in v1.2 is scoped to one document, one transaction, multiple parties. | Template save is deferred to v2 per v1.1 research. Bulk send depends on templates. Correct scope is: one document, name the signers, send. |
---
## Feature Dependencies (v1.2)
```
[v1.0 Single-Signer Signing Flow]
└──extended by──> [Per-Signer Token Creation]
└──requires──> [Signer List Entry UI]
└──enables──> [Parallel Email Dispatch]
└──enables──> [Signer-Filtered Signing Page]
[v1.1 Field Placement UI]
└──extended by──> [Field-to-Signer Assignment]
└──requires──> [Signer List Entry UI] (must know signers before tagging fields)
└──enables──> [Per-Signer Color Coding]
[Signer List Entry UI]
└──gates──> [Field-to-Signer Assignment] (chicken-and-egg: signers must exist before fields can be tagged)
└──gates──> [Per-Signer Token Creation + Dispatch]
[Field-to-Signer Assignment]
└──gates──> [Send] (unassigned client-visible fields block send)
[Per-Signer Token Creation + Dispatch]
└──enables──> [Signer-Filtered Signing Page]
└──enables──> [Per-Signer Completion Tracking]
[Per-Signer Completion Tracking]
└──enables──> [Document Completion Detection]
└──triggers──> [Agent Completion Notification Email]
└──triggers──> [All-Parties Final PDF Email]
[v1.1 Accumulator PDF (signed by one signer, file updated)]
└──extended by──> [Multi-Signer Sequential Accumulation]
└──requires──> [Postgres Advisory Lock] (concurrent signer writes)
└──produces──> [Final Merged PDF]
```
### Dependency Notes
- **Signer list must be entered before fields are placed.** This is the most important UX sequencing constraint. If the agent enters signers after placing fields, existing fields have no `signerEmail` and the validation block prevents sending. Resolution: the PreparePanel prompts for signers as the first step in the prepare flow. Fields can be placed after signers are defined.
- **The chicken-and-egg problem with signer entry order.** Some agents will want to place fields first, then decide who the signers are. The system should allow out-of-order workflows: agent places fields with "Unassigned" status, then enters signers and assigns them. The send block validates that no client-visible field is unassigned at send time — it does not prevent field placement before signer entry.
- **Accumulator PDF depends on the advisory lock.** Two signers submitting simultaneously without a lock will produce a corrupted PDF (both read the same input file and write conflicting outputs). The `pg_advisory_xact_lock` pattern from ARCHITECTURE.md is mandatory, not optional.
- **Date fields inherit their signer from context.** A date field adjacent to a buyer signature field should be tagged to the buyer. Currently date fields are auto-stamped at session completion — in multi-signer, the timestamp is written at the moment that signer's token is claimed, not at "all done." A date field on buyer's row uses buyer's signing timestamp. This means date fields need a `signerEmail` assignment just like signature fields. The agent should assign date fields to the adjacent signer, or UI could auto-assign based on proximity.
- **Completion emails require the final merged PDF path to be stable.** The all-parties completion email contains a download link. That link must point to the final accumulator output, which is only stable after the last signer submits. The completion detection step in ARCHITECTURE.md (step 10b of the signing POST) correctly gates the email send until after all tokens are claimed.
---
## v1.2 Feature Definitions
### Launch With (v1.2 — this milestone)
- [ ] **Signer list entry UI** — PreparePanel adds a "Signers" section. Agent enters rows of (optional name, required email). Minimum 1 signer. Add/remove rows. Saved to `documents.signers` JSONB via PUT `/api/documents/[id]/signers`. Displayed above field placement step so signers are known before tagging begins.
- [ ] **Field-to-signer assignment** — In FieldPlacer, when placing a client-visible field (signature, initials, checkbox), a dropdown appears to select which signer the field belongs to. Options populated from `doc.signers[]`. Color-coded by signer. Unassigned fields shown in a distinct "no signer" color. Field data stores `signerEmail` in `SignatureFieldData` JSONB.
- [ ] **Per-signer color coding** — Each signer is assigned a color deterministically (first signer = blue, second = green, etc.). Placed fields render with that signer's color border/background. A color legend appears in the FieldPlacer sidebar. Maximum 4-5 signers before colors become indistinct — adequate for real estate documents which rarely have more than 3 parties.
- [ ] **Send validation: block if fields unassigned** — Before sending, server validates all client-visible fields have a `signerEmail`. UI shows: "3 fields are not assigned to a signer." Send button disabled. Agent resolves in FieldPlacer.
- [ ] **Per-signer token creation and parallel dispatch** — Send route detects `doc.signers?.length > 0`. For each signer: create a signing token with `signer_email`, send signing request email, log `signer_email_sent` audit event. All dispatched via `Promise.all()` (parallel, any order). Legacy single-signer path (no `doc.signers`) remains unchanged.
- [ ] **Signer-filtered signing page** — Signing GET endpoint reads `tokenRow.signerEmail`. Returns only `signatureFields` where `field.signerEmail === tokenRow.signerEmail`. Legacy null `signer_email` falls through to `isClientVisibleField()` (unchanged). The signing page renders the full PDF with only the current signer's fields as interactive overlays.
- [ ] **Per-signer completion tracking in portal** — Agent portal document detail shows a signers list: name/email, status ("Signed March 3 at 2:14 PM" or "Awaiting signature"). Data sourced from `doc.signers[].signedAt`. Simple read from JSONB — no new query required.
- [ ] **Document completion detection** — After each signer submits, the signing POST checks: all tokens for this document have `used_at IS NOT NULL`. If yes, fires `document_completed` audit event, sets `documents.status = 'Signed'`, sets `signedAt`, computes `pdfHash`.
- [ ] **Agent completion notification email** — On `document_completed`, send one email to the agent: "All parties have signed [Document Name]. Download the final document: [link]." Presigned download link from Vercel Blob, 30-day expiry.
- [ ] **All-parties final PDF email** — On `document_completed`, send to each signer: "The document is fully signed. Here is your copy: [link]." Same presigned download link. Personalized greeting using `signer.name` if available, fallback to email address.
- [ ] **Post-sign confirmation page** — After a signer submits, they see: document name, their name/email, signing timestamp, "X of Y signers have completed." If they are the last signer: "The document is fully executed. All parties will receive a copy by email." Download link to the current merged PDF state.
### Add After Validation (v1.x)
- [ ] **Manual resend link to a specific signer** — Agent portal: "Resend link" button per signer row in the document detail. Creates a new signing token, invalidates the old one (sets `used_at` to a sentinel value or deletes and reissues), sends fresh email. Triggered by: agent reporting a signer didn't receive their link or token expired.
- [ ] **Token expiry handling at signing time** — Currently expired tokens show a generic "link expired" error. In multi-signer context, show: "This signing link has expired. Contact [agent name] to request a new link." Include agent contact info from agent profile.
### Future Consideration (v2+)
- [ ] **Automated reminder emails** — Reminder sent to unsigned signers after N days. Requires a background job (cron). For current volume (solo agent, known clients), manual resend covers the need. Add when agent requests automation.
- [ ] **Sequential signing order** — Signer 2 does not receive their link until Signer 1 completes. PROJECT.md explicitly out of scope for v1.2. If Teressa requests this in the future, it requires: routing state machine, deferred email dispatch, per-signer "waiting" status.
- [ ] **Template-based multi-signer** — Save a field layout + signer role map as a reusable template. "Buyer/Seller roles" pre-assigned; agent enters names/emails at send time. Requires template management UI and role-based field assignment (rather than email-based). Depends on template save feature (also v2).
- [ ] **Signing deadline per document** — Agent sets an expiry date ("all signers must sign by March 15"). System rejects signing submissions after deadline. Useful for offer deadlines in real estate. Not needed for v1.2 — use token expiry as a proxy for now.
---
## Feature Prioritization Matrix (v1.2)
| Feature | User Value | Implementation Cost | Priority |
|---------|------------|---------------------|----------|
| Signer list entry UI | HIGH | LOW | P1 |
| Field-to-signer assignment | HIGH | MEDIUM | P1 |
| Send validation: block if fields unassigned | HIGH | LOW | P1 |
| Per-signer token creation + parallel dispatch | HIGH | MEDIUM | P1 |
| Signer-filtered signing page | HIGH | MEDIUM | P1 |
| Agent completion notification email | HIGH | LOW | P1 |
| All-parties final PDF email | HIGH | LOW | P1 |
| Document completion detection | HIGH | MEDIUM | P1 |
| Per-signer completion tracking in portal | MEDIUM | LOW | P1 |
| Post-sign confirmation page | MEDIUM | LOW | P1 |
| Per-signer color coding | MEDIUM | LOW | P2 |
| Manual resend link | MEDIUM | MEDIUM | P2 |
| Token expiry UX for multi-signer | LOW | LOW | P2 |
| Automated reminder emails | MEDIUM | HIGH | P3 |
| Sequential signing order | LOW | HIGH | P3 |
| Template-based multi-signer | MEDIUM | HIGH | P3 |
**Priority key:**
- P1: Must have for v1.2 milestone
- P2: Should have, add when possible within this milestone
- P3: Defer to v2+
---
## Competitor Feature Analysis (v1.2 Scope: Multi-Signer)
| Feature | DocuSign | Dropbox Sign (HelloSign) | PandaDoc | This App (v1.2) |
|---------|----------|--------------------------|----------|-----------------|
| Signer identity model | Named recipients with email + role | Named recipients with email | Named signers (Signer 1, 2, 3) | Name + email rows in PreparePanel; no role abstraction |
| Field-to-signer assignment | Explicit per-field recipient assignment, color-coded | Explicit per-field signer assignment | Explicit; template roles pre-assigned | Per-field signer dropdown with deterministic color coding |
| Parallel vs sequential dispatch | Both; configurable via signing order numbers (same number = parallel group) | Both; set via signing order | Both | Parallel only (any order); PROJECT.md explicitly rejects sequential for v1.2 |
| Signer sees only own fields | Yes — recipient-scoped signing page | Yes | Yes | Yes — field filtering by `signerEmail` in token at signing GET |
| Completion notification to sender | Yes — automatic email + PDF | Yes | Yes | Agent email on `document_completed` with download link |
| Completion PDF to all signers | Yes — configurable; PDF attached or linked | Yes | Yes | Presigned Vercel Blob link sent to each signer on completion |
| Per-signer status in sender dashboard | Yes — "Delivered", "Viewed", "Signed" per recipient | Yes | Yes | "Signed [timestamp]" or "Awaiting" per signer in portal |
| Reminder emails | Yes — configurable auto-reminders | Yes — "Send reminder" manually or auto | Yes | Manual resend (v1.2); auto-reminders deferred to v2 |
| In-progress PDF visibility | Sender can view; signers do not see others' submissions | Same | Same | Final PDF only sent on completion; no mid-process access |
---
## Behavioral Specifications (v1.2)
### UX Flow: Agent Prepares and Sends a Multi-Signer Document
1. **Agent opens document** in portal → navigates to Prepare tab
2. **Signer entry step** (new): "Who needs to sign this document?" — agent adds rows: "Sarah Chen (sarah@example.com)" and "Mike Chen (mikec@example.com)" — clicks Save Signers
3. **Field placement**: agent uses drag-drop or AI auto-place — fields appear on canvas; each client-visible field shows a signer selector; agent assigns "Signature (Sarah)" to the buyer fields and "Signature (Mike)" to the co-buyer fields — color distinction makes it clear at a glance
4. **Validation**: agent clicks Send — system checks: all client-visible fields have a signer. If not, shows "2 fields unassigned — resolve in field placement"
5. **Dispatch**: send route creates two signing tokens, sends two emails simultaneously. Agent sees: "Signing links sent to 2 signers."
6. **Signing**: Sarah opens her link and sees only her 4 fields. She signs and submits. Mike opens his link and sees only his 3 fields. He signs and submits. (Order does not matter.)
7. **Completion**: after both submit, agent receives "All parties have signed — Sales Agreement Chen." Sarah and Mike each receive an email with the final PDF download link.
### What Happens at the Signing Page (Per Signer)
The signing page is functionally identical to the existing single-signer page with one difference: only the current signer's fields appear as interactive. The PDF renders in full — the signer can read the entire document (this is legally important — they are agreeing to the whole document, not just their fields). The interactive signature/initials/checkbox overlays appear only at the positions assigned to their email.
There is no indication of where the other signers' fields are. No greyed-out fields, no "this field belongs to someone else" markers. Clean presentation with only actionable elements visible.
### Accumulator PDF Integrity
Each signer's signing POST acquires a Postgres advisory lock keyed to the document ID, reads the current accumulator path (`doc.signedFilePath ?? doc.preparedFilePath`), embeds that signer's captured signatures at their field positions, writes the result to a new path, updates `doc.signedFilePath`, then releases the lock. This is sequential within the lock — two signers who submit simultaneously are serialized, not concurrent. The final accumulator after the last signer completes is the authoritative fully-signed document. See ARCHITECTURE.md for the full implementation detail.
---
## Sources
**Multi-signer signing behavior — DocuSign:**
- [DocuSign Trainer Tips: Mastering Signing Order](https://community.docusign.com/tips-from-docusign-155/docusign-trainer-tips-mastering-signing-order-22771) — MEDIUM confidence (community doc)
- [Quick Tip: Setting a Signing Order for Recipients — DocuSign](https://www.docusign.com/en-gb/blog/quick-tip-setting-signing-order) — HIGH confidence (official)
- [Does fully executed contract automatically get emailed back to all parties? — DocuSign Community](https://community.docusign.com/esignature-111/does-fully-executed-contracts-automatically-get-emailed-back-to-all-parties-3772) — MEDIUM confidence (confirms default completion behavior)
- [Why are documents not attached to the Completed email notification — DocuSign Support](https://support.docusign.com/s/articles/Why-are-documents-not-attached-to-the-Completed-email-notification?language=en_US) — HIGH confidence (official support)
**Multi-signer signing behavior — HelloSign / PandaDoc:**
- [PandaDoc vs HelloSign feature comparison — eSignGlobal](https://www.esignglobal.com/blog/pandadoc-vs-hellosign-comparison) — MEDIUM confidence
- [How to set up a signature workflow with multiple signers — PandaDoc Blog](https://www.pandadoc.com/blog/digital-signature-workflow/) — MEDIUM confidence
- [PandaDoc vs Hellosign: Detailed Comparison 2025 — DocuPilot](https://www.docupilot.com/vs/pandadoc-vs-hellosign) — MEDIUM confidence
**Real estate multi-signer and reminder patterns:**
- [Multi-party signing workflow — eSignGlobal](https://www.esignglobal.com/blog/multi-party-signing-workflow) — MEDIUM confidence
- [Electronic Signatures for Real Estate: 2026 Guide — DocuPilot](https://www.docupilot.com/blog/electronic-signature-for-real-estate) — MEDIUM confidence
- [Lone Wolf Authentisign — Real estate's leading eSignature solution](https://www.lwolf.com/operate/esignature) — HIGH confidence (industry tool)
- [BoldSign — E-Signature for Real Estate](https://boldsign.com/solutions/electronic-signature-for-real-estate/) — MEDIUM confidence
**Implementation architecture (codebase-derived — HIGH confidence):**
- `/Users/ccopeland/temp/red/.planning/research/ARCHITECTURE.md` — direct codebase inspection, schema decisions, data flow, build order
---
*Feature research for: Teressa Copeland Homes — v1.2 Multi-Signer Workflow*
*Researched: 2026-04-03*