New funders/funder_grants schema + ingest990pf monthly workflow: IRS BMF state file discovers NH private foundations (747), e-file index CSVs select their latest 990-PF filings, batch ZIPs stream through fflate (4/run cap, most-hits-first, deferred logged), grants-paid rows land in funder_grants, and funders with >=2 NH grants synthesize rolling grant rows (source irs_990pf, funder_ein linked) that flow through the existing embed+match pipeline. Scoring v2: funderPrecedentSubscore tiers repeated in-state giving (1/3/5/10 -> 8/15/20/25); easy win = >=65 total AND >=12 precedent (plan's precedent floor); scale is the full 0-100. Rolling deadlines pass the runway gate. Retrieval computes per-funder in-state counts and exposes funder_ein. Lead-quality gates from the first precedent run's failures: candidate orgs exclude NTEE T* grantmakers; self-matches gated by EIN + normalized name (NHDOJ registers foundations as charities, several without resolved EINs — the first run's top 'leads' were foundations matched to themselves). Live: ~6.5GB of IRS batches processed, 2,766 grants-paid rows, 123 synthesized foundation grants, 89 easy wins across 27 orgs, credible top-10 (AIDS Response-Seacoast -> Foundation for Seacoast Health, 25/25 precedent). 153 tests green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@novelpad/outreach-core
Domain/data package for the HelmDocs grant-match outreach engine: the Drizzle schema for grants, orgs, org profiles, contacts, matches, and pipeline events, plus the actions/queries that read and write them.
Modeled on packages/core in novelpad-desktop,
following the same action/query pattern:
every read lives in a domain's queries/, every write in its actions/,
both barrel-exported, and route handlers/workflows/hooks never touch
db.select()/db.insert() directly.
Domains
grants/— open grant opportunities (Grants.gov, NH state, 990-PF, PND RSS, Candid). Ingestion upserts onsourceUrl; an hourly sweep expires anything past its close date.orgs/— NH nonprofits, plusorg_profiles(LLM/agent-researched enrichment: mission, programs, known funders, budget band, citation sources).matches/— a scored(org, grant)pair.hard-gates.tsis the deterministic, DB-free pass/fail predicate (entity eligibility, geography, ≥21-day runway, ≥$10k ceiling, application-form support) that runs before the weighted LLM subscoring pass — see its module doc for why it's kept pure.pipeline/— outreach lifecycle events (enrolled→sent→opened/replied/... →demo_booked→converted), synced from Apollo and recorded internally.
Deliberate deviations from @novelpad/core
This package is server-only — there is no client app, so several things
@novelpad/core does for its PGlite/OPFS client don't apply here:
- No PGlite, no client schema, no sync. There's exactly one database
handle type (
NpOutreachDatabase/NpOutreachTransactioninsrc/db/db.ts), not the client/server pair (NpClientDatabasevsNpDatabase)@novelpad/coreneeds to support both a local and a remote copy of the same data. - No
novelpad_core_devexport condition.@novelpad/core'spackage.jsonuses a customnovelpad_core_devcondition so dev builds resolve straight tosrc/*.tswithout a build step. This package'sexportsmap only has plaintypes/defaultconditions — runyarn build(oryarn devfor a watch build) before consuming it from a sibling workspace in dev. - No
build:migration-filesbundling step.@novelpad/core'screate:migrationalso runs a script that bundles generated SQL into a TS module the client can import for its OPFS-based local migrator. There is no local migrator here, sobin/create-migrationonly runsdrizzle-kit generateagainst the single server config. - Root
index.tsis intentionally small. It exports only the schema, DB types, and the purehard-gatespredicate — not domain client barrels, since every domain here is server-only. Everything else lives behindindex.server.ts/ the./serverexport condition.
Scripts
yarn build # tsc
yarn dev # tsc --watch
yarn typecheck # tsc --noEmit
yarn test # vitest run
yarn create:migration <name> # drizzle-kit generate against drizzle.config.ts
yarn migrate # drizzle-kit migrate
yarn studio # drizzle-kit studio