From ed6b8bce23a45f6da53dbd225b050c07064883e6 Mon Sep 17 00:00:00 2001 From: Chandler Copeland Date: Fri, 3 Apr 2026 15:04:08 -0600 Subject: [PATCH] docs(phase-14): gather context for multi-signer schema phase --- .../phases/14-multi-signer-schema/.gitkeep | 0 .../14-multi-signer-schema/14-CONTEXT.md | 94 +++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 .planning/phases/14-multi-signer-schema/.gitkeep create mode 100644 .planning/phases/14-multi-signer-schema/14-CONTEXT.md diff --git a/.planning/phases/14-multi-signer-schema/.gitkeep b/.planning/phases/14-multi-signer-schema/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/.planning/phases/14-multi-signer-schema/14-CONTEXT.md b/.planning/phases/14-multi-signer-schema/14-CONTEXT.md new file mode 100644 index 0000000..8a0b912 --- /dev/null +++ b/.planning/phases/14-multi-signer-schema/14-CONTEXT.md @@ -0,0 +1,94 @@ +# 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 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 (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) + + + + +## 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 (0006–0009) 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()` — `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 + + + + +## 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*