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>
Precedent was always a valid criterion for federal grants — the index
just didn't cover them. Now: ingest captures ALN/CFDA numbers from
fetchOpportunity (grants.alns); nightly federalPrecedent workflow
(04:45) queries USASpending award search per distinct program (free
official API, 3-year NH lookback) and stamps program_state_award_count
+ sample recipients onto open grants (multi-ALN keeps highest). Match
retrieval feeds the same funderStateGrantCount input and 25-point tiers
foundations use; detail page shows the recipients-evidence table with
an incumbent-renewal caution.
Also: detail refresh now rotates oldest-verified-first
(serverMapGrantVerification) — the Set-based partition re-fetched the
same 200 every pass, leaving 365/565 grants ALN-less.
Live: 564/564 grants ALN-tagged, 156 programs swept, 85 with NH
history, 468 grants carrying precedent, first federal easy-wins (66pts,
25/25 precedent, Aug-24 deadline). 158 tests green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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>
matchGrants (nightly 05:15): per candidate org (NH + good standing +
primary ICP) ensure a v0 NTEE-derived profile embedding (RETRIEVAL_QUERY,
confidence 0.2 stub the Stage 4 profiler upgrades in place), retrieve
top-50 open grants by pgvector cosine with SQL-enforced cheap gates
(deadline >=21d, ceiling >=10K), run entity/geography gates in TS,
score deterministically, upsert pair-keyed matches, reassign hero.
Scoring: pure scoreMatch (mission fit 30 / capacity 15 / competition 15
/ effort 10 / runway 5; precedent's 25 reserved until the 990-PF index;
easy win >= 50/75). Eligibility gate now pattern-matches Grants.gov
applicantTypes prose, conservatively (ambiguous entries do not admit).
application_form_supported ignored for pass/fail per the manual-first
decision, still recorded in rationale.
Schema: unique (org_id, grant_id) on matches; unique org_id on
org_profiles (latest-profile semantics). Review queue query now ordered
hero > easy-win > score and capped at 100.
Live run: 64 orgs -> 3,200 matches in 28s, 0 easy wins / max 39 — the
honest result of an NIH-heavy 200-grant corpus vs NH service nonprofits;
engine mechanics verified, corpus breadth is the next lever.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
NHDOJ: parser rebuilt for the real 8-column registry layout (Reg. No. |
Charity Name | Address | City | State | Zip | Status | Report Due) with
single-letter G/X/S statuses; Reg. No. is the stable upsert key (new
orgs.registration_number column + partial unique index, enum gains
'suspended' via idempotent ADD VALUE); out-of-state registrants keep
their real state. Akamai-safe fetch headers + NHDOJ_REGISTRY_PDF_PATH
local-file override.
ProPublica: zero-hit state-scoped searches return 404, not an empty
list — map to no-candidates instead of failure (tripped the systemic-
failure breaker at 60/200 on first contact). Enrichment queue now
prioritizes NH good-standing orgs over the out-of-state tail.
PND: feed retired upstream (HTML shell on every historical path) —
documented as rework candidate, low priority.
run-once.ts: supervised one-off runner through the durable DBOS
handles (workflow modules now export run*Now accessors); drop the
double pool.end() after DBOS.shutdown().
First supervised run: 200 Grants.gov opportunities (1 auto-expired),
13,632 orgs from the 427-page registry, enrichment at failed=0 with
121/200 EIN resolution in the NH-priority batch.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>