feat: scaffold outreach engine monorepo on the novelpad-desktop stack

Workspaces: config (copied), outreach-core (schema + actions/queries +
hard gates), outreach-ai (Gemini client + embeddings copies, profiler and
mission-fit-judge agent stubs), outreach-worker (DBOS executor with
nightly ingest + hourly expiry workflows), outreach-review (RR7 review
queue v0). Initial drizzle migration incl. pgvector extension.

Stack contract: Yarn 4.5.0 + Turbo, Node 22.16, Drizzle 0.44.6 +
pgvector, DBOS 4.17.6, @google/genai on Vertex, gemini-embedding-001
@1536, React Router v7. Files copied from novelpad-desktop carry
provenance headers @ 62c56b87.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Croissant Le Doux
2026-07-16 11:08:24 -04:00
commit 14200edb60
80 changed files with 11637 additions and 0 deletions

View File

@@ -0,0 +1,134 @@
CREATE EXTENSION IF NOT EXISTS vector;--> statement-breakpoint
CREATE TYPE "public"."application_effort_estimate" AS ENUM('loi_only', 'short_form', 'full_federal', 'unknown');--> statement-breakpoint
CREATE TYPE "public"."contact_priority" AS ENUM('named', 'generic');--> statement-breakpoint
CREATE TYPE "public"."contact_source_provider" AS ENUM('apollo', 'irs_990', 'website', 'manual');--> statement-breakpoint
CREATE TYPE "public"."email_status" AS ENUM('unverified', 'valid', 'risky', 'invalid');--> statement-breakpoint
CREATE TYPE "public"."grant_source" AS ENUM('grants_gov', 'nh_state', 'irs_990pf', 'pnd_rss', 'candid', 'manual');--> statement-breakpoint
CREATE TYPE "public"."grant_status" AS ENUM('open', 'expired', 'closed');--> statement-breakpoint
CREATE TYPE "public"."icp_band" AS ENUM('below', 'primary', 'above', 'unknown');--> statement-breakpoint
CREATE TYPE "public"."match_reject_reason" AS ENUM('wrong_eligibility', 'wrong_geography', 'bad_capacity_fit', 'weak_mission_fit', 'stale_deadline', 'bad_contact', 'other');--> statement-breakpoint
CREATE TYPE "public"."match_review_status" AS ENUM('pending', 'approved', 'rejected', 'edited');--> statement-breakpoint
CREATE TYPE "public"."pipeline_event_type" AS ENUM('enrolled', 'sent', 'opened', 'replied', 'bounced', 'unsubscribed', 'brief_requested', 'brief_sent', 'demo_booked', 'demo_held', 'pilot_started', 'converted');--> statement-breakpoint
CREATE TYPE "public"."registration_status" AS ENUM('good_standing', 'lapsed', 'unknown');--> statement-breakpoint
CREATE TABLE "contacts" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"org_id" uuid NOT NULL,
"full_name" text,
"title" text,
"email" text,
"email_status" "email_status" DEFAULT 'unverified' NOT NULL,
"source_provider" "contact_source_provider",
"priority" "contact_priority" DEFAULT 'generic' NOT NULL,
"created_at" timestamp with time zone DEFAULT now()
);
--> statement-breakpoint
CREATE TABLE "grants" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"funder" text NOT NULL,
"title" text NOT NULL,
"synopsis" text,
"eligibility_entity_types" text[],
"geographic_scope" text,
"program_areas" text[],
"award_floor" integer,
"award_ceiling" integer,
"expected_awards_count" integer,
"open_date" timestamp with time zone,
"close_date" timestamp with time zone,
"match_requirement" boolean DEFAULT false NOT NULL,
"application_effort_estimate" "application_effort_estimate" DEFAULT 'unknown' NOT NULL,
"application_form_supported" boolean DEFAULT false NOT NULL,
"source_url" text NOT NULL,
"source" "grant_source" NOT NULL,
"status" "grant_status" DEFAULT 'open' NOT NULL,
"synopsis_embedding" vector(1536),
"last_verified_at" timestamp with time zone,
"created_at" timestamp with time zone DEFAULT now(),
"updated_at" timestamp with time zone DEFAULT now()
);
--> statement-breakpoint
CREATE TABLE "matches" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"org_id" uuid NOT NULL,
"grant_id" uuid NOT NULL,
"total_score" integer NOT NULL,
"subscores" jsonb,
"hard_gates_passed" boolean DEFAULT false NOT NULL,
"easy_win" boolean DEFAULT false NOT NULL,
"rationale" jsonb,
"review_status" "match_review_status" DEFAULT 'pending' NOT NULL,
"reject_reason" "match_reject_reason",
"is_hero" boolean DEFAULT false NOT NULL,
"created_at" timestamp with time zone DEFAULT now(),
"updated_at" timestamp with time zone DEFAULT now()
);
--> statement-breakpoint
CREATE TABLE "org_profiles" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"org_id" uuid NOT NULL,
"mission_statement" text,
"programs" jsonb,
"service_geography" text,
"recent_news" jsonb,
"known_funders" jsonb,
"staff" jsonb,
"budget_band" text,
"sources" jsonb,
"confidence" real,
"profile_embedding" vector(1536),
"created_at" timestamp with time zone DEFAULT now()
);
--> statement-breakpoint
CREATE TABLE "orgs" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"name" text NOT NULL,
"city" text,
"state" text DEFAULT 'NH' NOT NULL,
"ein" text,
"ntee_code" text,
"total_revenue" integer,
"fiscal_year_end" text,
"registration_status" "registration_status" DEFAULT 'unknown' NOT NULL,
"icp_band" "icp_band" DEFAULT 'unknown' NOT NULL,
"source_registry" text,
"created_at" timestamp with time zone DEFAULT now(),
"updated_at" timestamp with time zone DEFAULT now()
);
--> statement-breakpoint
CREATE TABLE "pipeline_events" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"org_id" uuid,
"contact_id" uuid,
"match_id" uuid,
"event_type" "pipeline_event_type" NOT NULL,
"payload" jsonb,
"occurred_at" timestamp with time zone DEFAULT now() NOT NULL,
"created_at" timestamp with time zone DEFAULT now()
);
--> statement-breakpoint
ALTER TABLE "contacts" ADD CONSTRAINT "contacts_org_id_orgs_id_fk" FOREIGN KEY ("org_id") REFERENCES "public"."orgs"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "matches" ADD CONSTRAINT "matches_org_id_orgs_id_fk" FOREIGN KEY ("org_id") REFERENCES "public"."orgs"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "matches" ADD CONSTRAINT "matches_grant_id_grants_id_fk" FOREIGN KEY ("grant_id") REFERENCES "public"."grants"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "org_profiles" ADD CONSTRAINT "org_profiles_org_id_orgs_id_fk" FOREIGN KEY ("org_id") REFERENCES "public"."orgs"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "pipeline_events" ADD CONSTRAINT "pipeline_events_org_id_orgs_id_fk" FOREIGN KEY ("org_id") REFERENCES "public"."orgs"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "pipeline_events" ADD CONSTRAINT "pipeline_events_contact_id_contacts_id_fk" FOREIGN KEY ("contact_id") REFERENCES "public"."contacts"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "pipeline_events" ADD CONSTRAINT "pipeline_events_match_id_matches_id_fk" FOREIGN KEY ("match_id") REFERENCES "public"."matches"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
CREATE INDEX "idx_contacts_org" ON "contacts" USING btree ("org_id");--> statement-breakpoint
CREATE INDEX "idx_contacts_email" ON "contacts" USING btree ("email");--> statement-breakpoint
CREATE UNIQUE INDEX "idx_grants_source_url" ON "grants" USING btree ("source_url");--> statement-breakpoint
CREATE INDEX "idx_grants_status" ON "grants" USING btree ("status");--> statement-breakpoint
CREATE INDEX "idx_grants_close_date" ON "grants" USING btree ("close_date");--> statement-breakpoint
CREATE INDEX "idx_grants_source" ON "grants" USING btree ("source");--> statement-breakpoint
CREATE INDEX "grants_synopsis_embedding_idx" ON "grants" USING hnsw ("synopsis_embedding" vector_cosine_ops);--> statement-breakpoint
CREATE INDEX "idx_matches_org" ON "matches" USING btree ("org_id");--> statement-breakpoint
CREATE INDEX "idx_matches_grant" ON "matches" USING btree ("grant_id");--> statement-breakpoint
CREATE INDEX "idx_matches_review_status" ON "matches" USING btree ("review_status");--> statement-breakpoint
CREATE INDEX "idx_org_profiles_org" ON "org_profiles" USING btree ("org_id");--> statement-breakpoint
CREATE INDEX "org_profiles_embedding_idx" ON "org_profiles" USING hnsw ("profile_embedding" vector_cosine_ops);--> statement-breakpoint
CREATE UNIQUE INDEX "idx_orgs_ein" ON "orgs" USING btree ("ein") WHERE "orgs"."ein" IS NOT NULL;--> statement-breakpoint
CREATE INDEX "idx_orgs_icp_band" ON "orgs" USING btree ("icp_band");--> statement-breakpoint
CREATE INDEX "idx_orgs_state" ON "orgs" USING btree ("state");--> statement-breakpoint
CREATE INDEX "idx_pipeline_events_org" ON "pipeline_events" USING btree ("org_id");--> statement-breakpoint
CREATE INDEX "idx_pipeline_events_contact" ON "pipeline_events" USING btree ("contact_id");--> statement-breakpoint
CREATE INDEX "idx_pipeline_events_match" ON "pipeline_events" USING btree ("match_id");--> statement-breakpoint
CREATE INDEX "idx_pipeline_events_type_occurred" ON "pipeline_events" USING btree ("event_type","occurred_at");