← back to Rentv
docs/CRE_PR_RESEARCH_OPERATIONS.md
70 lines
# CRE PR Intelligence — Research Operations Runbook
## Start the system
```sh
createdb rentv_pr # once
npm run pr:migrate # applies src/pr/migrations/*.sql idempotently
npm start # the rentv site (PR workspace mounts at /admin/pr-intelligence)
npm run pr:worker # the research worker (separate process; safe to Ctrl-C)
npm run pr:seed:ca # optional: CA Phase-1 seed (curated candidates + FDIC discovery)
```
Local preview without auth: `OPEN=1 PORT=9705 node server.js` (never set OPEN in prod).
## Start a California research run
Admin → **Research** → pick the metro (the mandated order is listed) → *Queue discovery
run*. Or POST `/api/pr/research/start {state:"CA", metro:"la", category:"bank"}`.
The run executes on the worker: registry pass (FDIC) first, then the query matrix
through the search API (needs `PR_EXA_API_KEY` or `PR_BRAVE_API_KEY`; without a key the
run records "search API unconfigured" and completes the registry portion only).
## Pause / resume
**Runs & Jobs** page → ⏸ Pause on a run pauses it AND its queued jobs; ▶ Resume re-queues
them. Every run keeps a `checkpoint` (registry cursor + matrix index) and resumes from
it — kill the worker mid-run and restart to verify (a stale `running` claim re-queues
after 15 min automatically).
## Review an organization
**Review Queue** → left list (↑/↓ to navigate) → center shows the profile, evidence
excerpts with clickable source links, and duplicate candidates → **A**ccept / **R**eject
/ **D**efer / **S**tale, or Edit (JSON patch) / Merge. Accepting stamps
`manually_verified` + `last_verified_at`. The right column previews the grounded letter
and saves internal research notes.
## How LinkedIn URLs are found and verified
`verify-person` jobs call the authorized search API with
`site:linkedin.com/in "<name>" "<company>"`, store the URL + indexed title/snippet as
`found_uncorroborated`, and only flip to `found_corroborated` when the person's org+title
already have company-site / press / registry / manual evidence. Manual entry and CSV
import of user-supplied LinkedIn exports are always available (People page / Research →
Import). Indexed titles are displayed with an explicit "needs corroboration" tag.
## Refresh cadence
`refresh-stale-records` (queue it from Runs & Jobs, or cron the worker's queue) marks
verified contacts stale after 90 days (priority ≥60) / 180 days (others), preserves the
old title in `role_history`, and queues re-verification.
## Saturation & the coverage report
Queue `generate-coverage-report {state:"CA"}` — the dashboard then shows every empty
metro×category cell. Treat a metro/category group as provisionally saturated when three
consecutive complete runs create no meaningful new unique organizations and the matrix
is exhausted; the run counters (`records_created` per run) make this visible on the
Runs page.
## Unlock Arizona
Settings → **Run gate check now** (the seven CA-gate criteria are computed live from the
data, never hand-set) → when it passes, **Unlock Arizona**. An explicit admin override
exists and is audit-logged as `gate.arizona_override_unlock`. Until unlocked, any AZ
discovery enqueue fails with `AZ_LOCKED` (HTTP 423).
## Export researched data
Organizations / People pages → ⬇ Export CSV honors the current filters; Sources page
exports all field evidence; Settings exports suppression; Campaigns export outreach
history. Every contact export includes verification status, source URLs, and
last-verified so unverified data cannot masquerade as verified.
## Process referrals / suppress a contact
Inbox → open thread → **Add referred contact** (creates the person with the reply as its
source + a follow-up task) · **Opt out / suppress** adds to `pr_suppression`
immediately and un-eligibles the person everywhere (enrollment, drafts, send all
re-check it).