Scope dial v2: Nearby / Region / Country / World radius on the homepage

Codex-approved evolution: the reader controls the "emotional radius" of the landing.

- Census-region "Regional" grain (geo.region_of / region_states). Scope-aware tiering
  (queries.home_tiers): closest->widest lead, confidence-gated on state + region, never
  a hard filter — blends outward so the set is always full. 'world' = the global brief.
- queries.home_brief takes a scope; /api/brief gains a scope param (nearby|region|
  country|world). Country-only / non-US homes collapse to country.
- Homepage dial replaces the 2-button toggle: adaptive stops (4 with a US state, else
  Country/World), persisted scope, "Good news closest first" framing. Concrete, soft
  section labels (Around New Jersey / Across the Northeast / Across the US / Around the
  world) so the reader sees the dial worked.

Backend 366 + frontend tests green. (Latest feed still on v1 local-first; aligning it
to the dial is the immediate follow-up.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
jay
2026-06-19 21:59:32 -04:00
parent d2a6293a13
commit 3486f3102a
5 changed files with 168 additions and 61 deletions
+16 -8
View File
@@ -82,20 +82,28 @@ def test_country_only_home_gates_near_on_confidence(app_db):
def test_home_brief_leads_with_local(app_db):
# The landing's /api/brief?home=US-NY leads with high-confidence NY good news,
# tags items by section, and titles it "Close to home". The low-conf NY story (#5)
# is not elevated as local.
# /api/brief?home=US-NY defaults to the 'nearby' scope: leads with the 'state' tier
# (high-confidence NY), titles it "Close to home", and never elevates the low-conf
# NY story (#5). CA stories (a different census region) fall to 'country'.
r = TestClient(app_db).get("/api/brief?home=US-NY&limit=10").json()
assert r["title"] == "Close to home"
near_ids = {it["id"] for it in r["items"] if it["section"] == "near"}
assert near_ids == {1, 2, 3, 4} # the high-conf NY stories
# near items all appear before any world item (local leads)
state_ids = {it["id"] for it in r["items"] if it["section"] == "state"}
assert state_ids == {1, 2, 3, 4} # high-conf NY only
a5 = next(it for it in r["items"] if it["id"] == 5)
assert a5["section"] == "country" # low-conf NY -> not 'state'
secs = [it["section"] for it in r["items"]]
if "near" in secs and "world" in secs:
assert max(i for i, s in enumerate(secs) if s == "near") < \
if "state" in secs and "world" in secs: # local leads, world trails
assert max(i for i, s in enumerate(secs) if s == "state") < \
min(i for i, s in enumerate(secs) if s == "world")
def test_home_brief_scope_country_has_no_state_tier(app_db):
# The 'country' scope drops the local lead: all US stories sit in 'country', none 'state'.
r = TestClient(app_db).get("/api/brief?home=US-NY&scope=country&limit=10").json()
secs = {it["section"] for it in r["items"]}
assert "state" not in secs and "country" in secs
def test_no_home_is_unchanged_and_unsectioned(app_db):
r = TestClient(app_db).get("/api/feed?limit=50").json()
assert all(it["section"] is None for it in r["items"])