4.9 KiB
4.9 KiB
Phase 14: Multi-Signer Schema - Context
Gathered: 2026-04-03 Status: Ready for planning
## Phase BoundaryAdditive DB migration only — no backend logic changes, no UI changes. Add three new nullable columns to existing tables and extend the SignatureFieldData TypeScript interface with an optional signerEmail field. Every new column is nullable so all existing single-signer documents continue to work without any backfill or code changes. Phase 14 is schema-only; the sign handler rewrite and completion detection logic belong in Phase 15.
Schema Shape
- D-01:
documents.signersJSONB shape is{ email: string; color: string }[]— not a plainstring[]. Color is stored alongside email so Phase 16 can retrieve consistent per-signer colors from the DB without recalculating by index. Example:[{ email: "buyer@x.com", color: "#6366f1" }]. - D-02:
signingTokens.signerEmailis a nullableTEXTcolumn. NULL means legacy single-signer token (existing behavior unchanged). - D-03:
documents.completionTriggeredAtis a nullableTIMESTAMPcolumn. Used as an atomic completion guard in Phase 15:UPDATE documents SET completionTriggeredAt = NOW() WHERE id = $1 AND completionTriggeredAt IS NULL RETURNING id. Only Phase 15 writes to it. - D-04:
SignatureFieldDatainterface gets an optionalsignerEmail?: stringfield — the same additive-nullable pattern already used fortype?: SignatureFieldType.
Status Tracking
- D-05: No
Partially Signedvalue added todocumentStatusEnum. Partial-signed state is computed dynamically in Phase 16 by countingsigningTokens WHERE usedAt IS NOT NULLvs total tokens for the document. AvoidsALTER TYPEmigration complexity.
Bug Fix Scope
- D-06: The first-signer-wins bug (sign handler unconditionally sets
status='Signed'on any submission) is NOT fixed in Phase 14. Phase 14 is schema-only. Phase 15 owns the full sign handler rewrite including this fix.
Migration Approach
- D-07: Use existing pattern: edit
schema.ts, rundrizzle-kit generateto producedrizzle/0010_*.sql, commit both, rundrizzle-kit migrateagainst Neon in deployment. Migration must be additive-only (no column removals, no type changes, no backfills).
Claude's Discretion
- Migration file naming and exact SQL details — Drizzle generates these automatically from schema.ts changes.
- Order of changes within the migration (columns vs interface) — planner decides.
<canonical_refs>
Canonical References
Downstream agents MUST read these before planning or implementing.
Schema
teressa-copeland-homes/src/lib/db/schema.ts— Current schema; all new columns and interface changes go hereteressa-copeland-homes/drizzle/— Existing migration files (0000–0009); new migration is 0010
Drizzle Config
teressa-copeland-homes/drizzle.config.ts— Migration output dir and DB credentials config
Research
.planning/research/ARCHITECTURE.md— Multi-signer schema design section (additive-only pattern, advisory lock approach, migration strategy).planning/research/PITFALLS.md— First-signer-wins bug description (Phase 14 adds the column Phase 15 needs to fix it)
</canonical_refs>
<code_context>
Existing Code Insights
Reusable Assets
getFieldType(field)inschema.ts— Pattern for safe nullable field reads with fallback;signerEmailshould follow the same additive-nullable patternisClientVisibleField(field)inschema.ts— Already filters by field type; Phase 15 will add signer filtering here or alongside it
Established Patterns
- Additive nullable columns: Prior migrations (0006–0009) show the pattern: add nullable columns to schema.ts, generate migration, migrate. No backfills needed.
- JSONB typed columns:
signatureFields,textFillData,emailAddressesall usejsonb().$type<T>()—documents.signersfollows the same pattern with$type<{ email: string; color: string }[]>() drizzle-kit generate+drizzle-kit migrate: The project has 10 migrations already. Always generate, never hand-write SQL.
Integration Points
signingTokenstable: newsignerEmailcolumn is nullable; existing token creation code inPOST /api/documents/[id]/senddoes not need to change in Phase 14SignatureFieldDatainterface: addingsignerEmail?: stringis backwards-compatible — all existing field JSONB rows silently getundefinedfor this field
</code_context>
## Specific Ideas- The
{ email, color }shape fordocuments.signerswas chosen overstring[]so Phase 16's PreparePanel can display the signer list with consistent colors without needing to recalculate or store colors elsewhere.
None — discussion stayed within phase scope.
Phase: 14-multi-signer-schema Context gathered: 2026-04-03