Data Model
Core entities, MFN tables, signal tables, and append-only invariants.
Core entities
| Table | Purpose | Key relationships |
funds |
Fund with name, GP, vintage, target size, lifecycle status |
Parent of commitments, clauses, documents |
investors |
Beneficial investor entity (e.g. "Temasek") |
Parent of LPs |
lps |
Legal signer vehicle beneath an investor |
FK → investors; parent of commitments |
commitments |
LP's participation in one fund — amount, status, SL status, KYC, dates |
FK → lps, funds, fund_closings |
clauses |
Side letter clause text with version history |
FK → funds, clause_names; self-ref parent_id for genealogy |
clause_assignments |
Links a clause to a commitment (this LP has this clause) |
FK → clauses, commitments |
documents |
Uploaded .docx files with parse state, review state, gap-tracking and paragraph counts |
FK → funds, lps; R2 key for blob |
Upload + review tables
| Table / column | Purpose |
documents.review_state |
Inbox lifecycle: pending → reviewed | flagged | complete. Drives the upload Inbox tabs. |
documents.total_paragraphs + uncovered_paragraph_count |
Written by parse.ts at finish; surfaced in the review pane as the gap-summary banner ("3 unmapped paragraphs — review?"). |
documents.gap_reviewed_at |
Set when reviewer acknowledges the gap banner. Non-destructive — the original uncovered count is preserved for reporting. |
documents.last_reviewed_at |
Stamp of the most recent draft confirmation/rejection. Drives "Continue review" sort order. |
clauses.initial_clause_name_id |
Captures the AI's first classification at parse time. Preserved across reclassification so per-type accuracy stats compare against the original guess. |
clause_corrections |
Append-only log of every reviewer-driven rename/reject/text-edit. Powers the per-type accuracy chips ("MFN: 94% accuracy in this fund"). |
Investor import tables
| Table | Purpose |
import_jobs |
One row per investor spreadsheet import — filename, row count, status (completed | undone | running | cancelled | failed), completed_at, undo_expires_at. Powers the 15-minute undo window and SSE progress stream. |
import_job_records |
Join table mapping job → (entity_type, entity_id) for every investor / LP / commitment / category created. Enables FK-ordered undo without polluting hot tables. |
mapping_profiles |
Saved column mappings keyed by header tokens (not raw indices) so reordered files still resolve correctly. Auto-applied via Jaccard ≥ 0.8 header overlap. |
MFN tables
| Table | Purpose |
mfn_basic_rules | Fund-wide comparator gates (amount, date, closing) |
mfn_general_rules | Category-level include/exclude rules |
mfn_individual_rules | Per-LP include/exclude overrides |
mfn_elections | LP's election decision per clause (elected / excluded / pending) |
mfn_clause_exclusions | Carve-outs at clause level |
Signal tables
| Table | Purpose |
fund_events | Append-only SITREP signal stream — root events + derived cascades with provenance |
activity_log | Human-readable audit trail of every mutation |
lp_tasks | Per-commitment follow-up tasks with priority + due date |
Append-only invariants
Two tables are never updated or deleted. This is a compliance requirement.
fund_events — the SITREP signal stream. History is immutable for audit + replay integrity.
document_versions (Gustav convention) — document version history is append-only per legal compliance.
All deletes in these tables are logical: rows are never removed from the database or R2. "Restore" and "Checkpoint" operations create new versions.