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 @@
export * from './list-pending-review-matches.server.js';

View File

@@ -0,0 +1,41 @@
import { eq } from 'drizzle-orm';
import type { NpOutreachDatabase, NpOutreachTransaction } from '#~/db/db.js';
import { schema } from '#~/db/db.js';
/**
* Pending matches joined to their org and grant — the shape the review
* queue renders. One row per pending (org, grant) match, most useful
* columns only; the full match row (subscores, rationale) can be fetched
* by id when a detail view lands.
*/
export interface PendingReviewMatch {
id: string;
orgName: string;
grantTitle: string;
funder: string;
totalScore: number;
easyWin: boolean;
isHero: boolean;
closeDate: Date | null;
}
export async function serverListPendingReviewMatches(
db: NpOutreachDatabase | NpOutreachTransaction,
): Promise<PendingReviewMatch[]> {
return db
.select({
id: schema.matches.id,
orgName: schema.orgs.name,
grantTitle: schema.grants.title,
funder: schema.grants.funder,
totalScore: schema.matches.totalScore,
easyWin: schema.matches.easyWin,
isHero: schema.matches.isHero,
closeDate: schema.grants.closeDate,
})
.from(schema.matches)
.innerJoin(schema.orgs, eq(schema.matches.orgId, schema.orgs.id))
.innerJoin(schema.grants, eq(schema.matches.grantId, schema.grants.id))
.where(eq(schema.matches.reviewStatus, 'pending'));
}