[object Object]

← back to Rentv

pr/jobs: make generate-coverage-report gap detection TIER-AWARE, consistent with the /coverage denominator. It flagged count===0 for every scoped type in every metro, so up to 60 FALSE gaps (15 major-tier types × 4 secondary metros — e.g. pr_agency in Central Valley) inflated gap_count and misdirected the research pipeline. The tier 'denominator fix' was applied to the /coverage API (index.js) but never to this gap report. Now: universal types are gaps in any metro; major types only in the big metros. Verified

d76172e5bbeb775a698b2126cd9d23413e2aeb23 · 2026-08-05 22:54:54 -0700 · Steve

Files touched

Diff

commit d76172e5bbeb775a698b2126cd9d23413e2aeb23
Author: Steve <steve@designerwallcoverings.com>
Date:   Wed Aug 5 22:54:54 2026 -0700

    pr/jobs: make generate-coverage-report gap detection TIER-AWARE, consistent with the /coverage denominator. It flagged count===0 for every scoped type in every metro, so up to 60 FALSE gaps (15 major-tier types × 4 secondary metros — e.g. pr_agency in Central Valley) inflated gap_count and misdirected the research pipeline. The tier 'denominator fix' was applied to the /coverage API (index.js) but never to this gap report. Now: universal types are gaps in any metro; major types only in the big metros. Verified
---
 src/pr/jobs/index.js | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/src/pr/jobs/index.js b/src/pr/jobs/index.js
index af5132d6..47bd49cc 100644
--- a/src/pr/jobs/index.js
+++ b/src/pr/jobs/index.js
@@ -647,7 +647,7 @@ const handlers = {
   async 'generate-coverage-report'(job) {
     const state = job.payload.state || 'CA';
     const metros = geo.metrosFor(state);
-    const { ORG_TYPE_KEYS } = require('../lib/taxonomy');
+    const { ORG_TYPE_KEYS, ORG_TYPE_TIER } = require('../lib/taxonomy');
     const excludeTypes = await settings.get('coverage_exclude_types', ['bank', 'credit_union']);
     const scopedTypes = ORG_TYPE_KEYS.filter((t) => !excludeTypes.includes(t));
     const grid = [];
@@ -660,8 +660,14 @@ const handlers = {
       const map = Object.fromEntries(byType.map((r) => [r.organization_type, r]));
       grid.push({ metro: m.key, label: m.label, coverage: scopedTypes.map((t) => ({ type: t, count: (map[t] || {}).n || 0, verified: (map[t] || {}).verified || 0 })) });
     }
+    // Gap = an EXPECTED cell that's empty, per the tier model (same rule as the /coverage denominator
+    // in index.js): universal types are expected in every metro; 'major' types only in the big metros.
+    // Without this, a specialist type missing in a secondary metro (e.g. pr_agency in Central Valley)
+    // was reported as a FALSE gap, inflating gap_count and misdirecting the research pipeline.
+    const majorMetros = new Set(geo.MAJOR_METRO_KEYS);
+    const expected = (type, metro) => ORG_TYPE_TIER[type] === 'universal' || (ORG_TYPE_TIER[type] === 'major' && majorMetros.has(metro));
     const gaps = [];
-    for (const g of grid) for (const c of g.coverage) if (c.count === 0) gaps.push({ metro: g.metro, type: c.type });
+    for (const g of grid) for (const c of g.coverage) if (c.count === 0 && expected(c.type, g.metro)) gaps.push({ metro: g.metro, type: c.type });
     const reportDoc = { state, generated_at: new Date().toISOString(), grid, gap_count: gaps.length, gaps: gaps.slice(0, 500) };
     await settings.set('coverage_report_' + state, reportDoc, 'system:coverage');
     return { gaps: gaps.length };

← e21420df auto-save: 2026-08-05T22:46:27 (7 files) — data/deals-regist  ·  back to Rentv  ·  pr/geo+jobs: Cody gate — make the coverage major-metro model 0d18fabb →