Files
2026-04-03 15:04:08 -06:00

4.9 KiB
Raw Permalink Blame History

Phase 14: Multi-Signer Schema - Context

Gathered: 2026-04-03 Status: Ready for planning

## Phase Boundary

Additive 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.

## Implementation Decisions

Schema Shape

  • D-01: documents.signers JSONB shape is { email: string; color: string }[] — not a plain string[]. 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.signerEmail is a nullable TEXT column. NULL means legacy single-signer token (existing behavior unchanged).
  • D-03: documents.completionTriggeredAt is a nullable TIMESTAMP column. 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: SignatureFieldData interface gets an optional signerEmail?: string field — the same additive-nullable pattern already used for type?: SignatureFieldType.

Status Tracking

  • D-05: No Partially Signed value added to documentStatusEnum. Partial-signed state is computed dynamically in Phase 16 by counting signingTokens WHERE usedAt IS NOT NULL vs total tokens for the document. Avoids ALTER TYPE migration 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, run drizzle-kit generate to produce drizzle/0010_*.sql, commit both, run drizzle-kit migrate against 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 here
  • teressa-copeland-homes/drizzle/ — Existing migration files (00000009); 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) in schema.ts — Pattern for safe nullable field reads with fallback; signerEmail should follow the same additive-nullable pattern
  • isClientVisibleField(field) in schema.ts — Already filters by field type; Phase 15 will add signer filtering here or alongside it

Established Patterns

  • Additive nullable columns: Prior migrations (00060009) show the pattern: add nullable columns to schema.ts, generate migration, migrate. No backfills needed.
  • JSONB typed columns: signatureFields, textFillData, emailAddresses all use jsonb().$type<T>()documents.signers follows 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

  • signingTokens table: new signerEmail column is nullable; existing token creation code in POST /api/documents/[id]/send does not need to change in Phase 14
  • SignatureFieldData interface: adding signerEmail?: string is backwards-compatible — all existing field JSONB rows silently get undefined for this field

</code_context>

## Specific Ideas
  • The { email, color } shape for documents.signers was chosen over string[] so Phase 16's PreparePanel can display the signer list with consistent colors without needing to recalculate or store colors elsewhere.
## Deferred Ideas

None — discussion stayed within phase scope.


Phase: 14-multi-signer-schema Context gathered: 2026-04-03