Fixes the federal mismatch class (boys' camp × NIH research center): - Peer precedent: federalPrecedent paginates USASpending (≤500 awards/ program) and name-matches every recipient against primary-ICP NH registry orgs (shared normalizeOrgNameForMatching, also used by the self-match gate). The 25-pt precedent tiers now key off program_state_peer_award_count — Dartmouth renewals and SBIR LLCs no longer grant precedent to community nonprofits. Raw count + peer- annotated award list stay as review evidence (peer badges, peers-first). - Mission-fit floor (12/30, grants_gov only): below it a match is stored with fit_viable=false and hidden from the pending queue, hero selection, and easy-win. Foundation-synthesized grants exempt (generic synopses). - Mission-fit judge live (judgeMatches, 06:15, 200/night best-first): JUDGE_MODEL reads the synopsis against the org profile with an explicit ignore-eligibility-breadth instruction; graded verdict with required citations; deterministic verdict→points map (27/18/8/0) sets missionFit, total, easy-win, and viability. Verdicts survive nightly re-scores via an upsert splice and re-enter the judge queue when the org profile is re-researched (org_profiles.updated_at). First sweep: 81/149 programs have NH history, only 6 have peer history; queue-head judging zeroes the research-mechanism garbage (mismatch) while surfacing genuine strong fits. 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