Add reminders & follow-ups (W1) (v0.1.0:92)
First-class reminders tied to the fundraising grid — foundation of the agreed reminders -> NL-search -> bot-mutations plan (keep LP data off third-party LLMs). - reminders table (migration 0006; logical FK to fundraising_investors.id + denormalized name), CRUD at /api/reminders (soft-delete; open/done/snoozed/ cancelled; assignee; source; source_row_id resolution) - read-only derived reminder_status grid column (overdue/due_soon/open), filterable; orphan reconciler cancels reminders when an investor leaves the grid - Reminders page, Dashboard "Reminders Due" card, daily-digest reminders section - per-investor last_activity_at recency rollup (shared block for the W2 NL query) - tests: test_reminders.py + digest reminders test (31/31 green, render-smoke green)
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
-- Manual rollback for 0006_reminders.sql (never auto-applied).
|
||||
-- Drops the whole reminders feature table. Per the never-hard-delete guardrail this
|
||||
-- discards reminder history, so only run it to reverse a bad migration on a dev/copy DB.
|
||||
DROP INDEX IF EXISTS idx_reminders_assignee;
|
||||
DROP INDEX IF EXISTS idx_reminders_due;
|
||||
DROP INDEX IF EXISTS idx_reminders_status;
|
||||
DROP INDEX IF EXISTS idx_reminders_investor;
|
||||
DROP TABLE IF EXISTS reminders;
|
||||
@@ -0,0 +1,45 @@
|
||||
-- Reminders & follow-ups — a real tickler/task model tied to the fundraising grid.
|
||||
--
|
||||
-- ADDITIVE + REVERSIBLE (CLAUDE.md guardrail #3): one new table + indexes; nothing
|
||||
-- existing is touched. Until now the only follow-up surfaces were the grid's binary
|
||||
-- `follow_up` checkbox (no date, owner, or status) and communications.next_action_date
|
||||
-- (tied to a single logged comm). This gives investors first-class reminders with a due
|
||||
-- date, status lifecycle, assignee, and provenance — the foundation for "who needs a
|
||||
-- follow-up?" queries, the daily digest's due/overdue section, and (later) bot-proposed
|
||||
-- reminders behind the Matrix approval gate.
|
||||
--
|
||||
-- investor_id is a LOGICAL foreign key to fundraising_investors(id) — deliberately NOT a
|
||||
-- declared SQLite FOREIGN KEY, matching opportunities.fundraising_investor_id (migration
|
||||
-- 0005). fundraising_investors rows are upserted by source_row_id on every grid save with
|
||||
-- a STABLE id (so the link survives saves), but a row dropped from the grid is DELETEd —
|
||||
-- there is nothing to cascade, and reconcile_grid_reminders() cancels the orphans on the
|
||||
-- next save (the pipeline reconciler's twin). investor_name is denormalized so a reminder
|
||||
-- stays readable in history even after its grid row is gone. investor_id is nullable: a
|
||||
-- reminder can be a standalone team task not tied to any investor.
|
||||
--
|
||||
-- contact_id is an optional logical FK to contacts(id) (the specific person). assignee_id
|
||||
-- is a logical ref to users(id) (NULL = team-wide). created_by holds a users.id OR a
|
||||
-- non-user sentinel ('bot'/'automation'), so it is plain TEXT with no FK.
|
||||
CREATE TABLE IF NOT EXISTS reminders (
|
||||
id TEXT PRIMARY KEY,
|
||||
investor_id TEXT, -- logical FK -> fundraising_investors.id (NULL = standalone task)
|
||||
investor_name TEXT, -- denormalized; survives grid-row deletion
|
||||
contact_id TEXT, -- optional logical FK -> contacts.id
|
||||
title TEXT NOT NULL,
|
||||
details TEXT,
|
||||
due_date TEXT, -- ISO date 'YYYY-MM-DD' (or datetime)
|
||||
status TEXT NOT NULL DEFAULT 'open', -- open | done | snoozed | cancelled
|
||||
snoozed_until TEXT,
|
||||
assignee_id TEXT, -- logical ref -> users.id; NULL = team-wide
|
||||
created_by TEXT, -- users.id, or 'bot' / 'automation'
|
||||
source TEXT NOT NULL DEFAULT 'human', -- human | bot | automation
|
||||
completed_at TEXT,
|
||||
created_at TEXT DEFAULT (datetime('now')),
|
||||
updated_at TEXT DEFAULT (datetime('now')),
|
||||
deleted_at TEXT
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_reminders_investor ON reminders(investor_id) WHERE deleted_at IS NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_reminders_status ON reminders(status) WHERE deleted_at IS NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_reminders_due ON reminders(due_date) WHERE deleted_at IS NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_reminders_assignee ON reminders(assignee_id) WHERE deleted_at IS NULL;
|
||||
Reference in New Issue
Block a user