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,2 @@
export * from './insert-match.server.js';
export * from './set-match-review.server.js';

View File

@@ -0,0 +1,28 @@
import type { NpOutreachDatabase, NpOutreachTransaction } from '#~/db/db.js';
import { schema } from '#~/db/db.js';
export type NewMatchInput = Omit<
typeof schema.matches.$inferInsert,
'id' | 'createdAt' | 'updatedAt' | 'reviewStatus'
>;
/**
* Records a scored (org, grant) match. Always lands in `reviewStatus:
* 'pending'` — the review decision is a separate step via
* `serverSetMatchReview`.
*/
export async function serverInsertMatch(
db: NpOutreachDatabase | NpOutreachTransaction,
match: NewMatchInput,
): Promise<string> {
const [row] = await db
.insert(schema.matches)
.values({ ...match, reviewStatus: 'pending' })
.returning({ id: schema.matches.id });
if (row == null) {
throw new Error('serverInsertMatch: insert returned no row');
}
return row.id;
}

View File

@@ -0,0 +1,35 @@
import { eq, sql } from 'drizzle-orm';
import type { NpOutreachDatabase, NpOutreachTransaction } from '#~/db/db.js';
import { schema } from '#~/db/db.js';
type ReviewStatus = (typeof schema.matches.$inferSelect)['reviewStatus'];
type RejectReason = (typeof schema.matches.$inferSelect)['rejectReason'];
/**
* Records a human reviewer's decision on a match. `rejectReason` is only
* meaningful (and only ever set) when `reviewStatus` is `'rejected'` — any
* other status clears it, so a previously-rejected match doesn't carry a
* stale reason if it's later re-approved after editing.
*/
export async function serverSetMatchReview(
db: NpOutreachDatabase | NpOutreachTransaction,
{
matchId,
reviewStatus,
rejectReason,
}: {
matchId: string;
reviewStatus: ReviewStatus;
rejectReason?: RejectReason;
},
): Promise<void> {
await db
.update(schema.matches)
.set({
reviewStatus,
rejectReason: reviewStatus === 'rejected' ? (rejectReason ?? null) : null,
updatedAt: sql`now()`,
})
.where(eq(schema.matches.id, matchId));
}