The scope — what Ink reads for built 2026-09-20

Short answer: an admin writes what they want to read, in their own words; the desk turns it into ticked sub-fields and a few custom searches; the admin confirms; from the next morning the brew reads only that. One scope, shared by every admin. Clear it and Ink reads the whole world again.

I no longer want the general news material distribution. I want the topics I scoped … a menu on the top right corner of Ink tab. add a "scope" command to it. And it will open a subpage, ask me to give a text description of what I like to read. And then it will generate proper tags or sub fields for me. After my confirmation, the next day's brew will only focus on these topics. This "scope" command is only visible for admin for now, and all admin share the same scope.the owner, 2026-09-20

1 · What the pool is made of

Every brew keeps the whole harvest of its morning as pegs-all.json: about 2,400 stories a day, from Wikipedia's Current events, 37 Google News country editions, six topic feeds in five editions, 59 outlet feeds, and 189 free Google News searches (172 sub-field rows + 17 kinds of event). Ten days of pools were read for this page: six on the box (09-09 → 09-13, 09-20) and four on localhost.

The taxonomy a story is born with. One tree and three flat axes:

STORY ├─ beat ─────────── one of 8 ┐ the ONLY hierarchy: │ └─ sub-field ── one of 164 (+custom)┘ every sub-field belongs to exactly one beat ├─ region ───────── none or one of 13 flat, orthogonal: "Markets · Southeast Asia" ├─ kind ─────────────── none or one of 17 flat, orthogonal: the shape of the event └─ layer · front · outlets provenance and attention, not topic

The checklist's eight「World › Africa / Latin America / …」rows are regions dressed as sub-fields; the scope treats them as regions and never as a topic, so the topical list is 164. The free-text tags an editor writes on a commissioned piece (["buffett", "succession"]) are a different thing — subject words for the repeat check, not a taxonomy.

The two facts that shaped the design

FactMeasuredWhat it forced
half the pool carries no sub-field47% — every story that came from an edition, an outlet feed or a topic feed has a beat only, and an edition's beat is「World」by defaulta filter on today's tags would see half the pool, and the personas write mostly from the other half: 63% of the 182 pieces commissioned on the box came from a peg with no sub-field
the sub-field counts are a quotaevery checklist search returns 8 lines, so all 172 rows sit at 7–8 a day; only history (26) and obituaries (12) differ, because Wikipedia feeds them tooa distribution read off the raw tags is flat by construction — the pool has to be tagged before it can be read

2 · Recovering the sub-field, three passes

PassHowCostRecovered (7 days, 16,704 stories)
① the harvest's own tagsa story a checklist search found carries that row's sub-field from birth; the fold merges it onto the same story seen in a feedfree51%
② keyword ruleseach checklist row is a query a OR b OR c; split on OR, match every phrase as a whole word against title + snippet; a hit gives beat and sub-field at oncefree13%
③ one Flash passthe rest, 40 headlines a call, the closed list in the system prompt; one sub-field and one region each, thinking off, JSON mode$0.25 for the seven days · ~3.5¢ a morning36%
still empty1%

Region: 43% from the harvest (a named place in the title, an edition's country, a regional checklist row), 32% from the Flash pass, 25% genuinely placeless — science, tech and life pieces mostly name no country.

3 · The distribution, once the pool is tagged

Beat

World
533/day
Culture
360
Markets
348
Science
314
Life & relationships
256
Sports
246
Tech & AI
203
China
108

The raw tags said World 29%: edition inflation. Tagged, World is 22%.

Region

North America
30%
no place named
25%
Western Europe
7%
UK & Ireland
6%
China
6%
Middle East & N. Africa
5%
Southeast Asia · South Asia
4% each
Oceania · Japan & Korea · Latin America
3% each
Sub-Saharan Africa · C&E Europe
2% each
Russia
1%

Sub-fields: the 8/day floor breaks

With the untagged half tagged, the range runs from 6 to 76 a day. A sub-field near 8/day is one only the checklist search reaches (90–100% of its stories came from that search); one well above it is what the world's editions and feeds push on their own.

The world pushes these/dayfrom the searchThe search-only tail (~8/day, 90–100% from the search)
crime and trials769%sleep · sex · grief · disability · faith and meaning · poetry · language · art crime · architecture · esports · quantum · drugs and addiction · private equity · shipping and logistics · philanthropy · winter sports · women's sport · cycling · the diaspora · Chinese sport
elections6511%
diplomacy and summits5713%
public health5613%
war and ceasefires4218%
celebrity · military and defence · disasters3820%
history · medicine · courts and rulings32–3722–71%
football (world) · pop music · cricket · schools · film25–2926–31%

Per beat, the heads: World → crime, elections, diplomacy · Science → public health, medicine, astronomy · Culture → celebrity, history, pop music · Markets → airlines, commodities, economics research · Tech → AI models, AI in use, gadgets · Sports → world football, cricket, American football · Life → schools, housing, travel · China → policy, trade, Taiwan. Beat × region: World is spread across every region (Middle East 65/day, Western Europe 56, Southeast Asia 47); every other beat is North America plus placeless.

4 · Why a scope needs custom rows

The owner's own example: start-ups, tech, science, Bay Area real estate, Shanghai real estate, super-luxury real estate worldwide.

WantedOn the treeExpected pool
start-upsMarkets › startups (+ IPOs and deals, private equity)~30/day
techthe whole Tech & AI beat, 16 sub-fields~200/day
sciencethe whole Science beat, 20 sub-fields~310/day
Bay Area real estateno fit — property × North America is a continent
Shanghai real estateno fit — property × China is a country
super-luxury real estate, worldwideno fit — property has no price tier; luxury and retail is shops

The taxonomy's finest grain is a sub-field × a region, and a region is a continent. Cities and price tiers are below it. But a sub-field is nothing more than a name and a Google News query, and the search layer runs every row every day for free — so the scope can add rows:

Markets › Bay Area real estate "Bay Area housing OR San Francisco home prices OR Silicon Valley real estate" Markets › Shanghai real estate "Shanghai property OR Shanghai housing market OR 上海楼市" lang: zh Markets › trophy real estate "penthouse sold OR record home sale OR mansion OR trophy property"

A custom row behaves exactly like the built-in 164: the harvest searches it (a zh row in the Chinese edition as well), the fold tags stories with it, the Flash tagger sees it in the closed list so feed and edition stories can land in it too, and it is ticked. Each brings in roughly 8 to 20 stories a day, mostly from the search itself.

5 · The design, as built

the app · Ink tab ⊕ (top right) › Scope… admin only; the reader's ⋯ carries it too │ ▼ the Scope page a 600-column sub-page (the Feedback page's shape) │ state line: Scope on · 39 sub-fields · 3 custom searches · set by CW, Sep 20 23:10 │ textarea: "start-ups, tech, science, Bay Area real estate, …" │ [Suggest topics] ──► POST /api/admin/ink/scope/draft one Flash call, ~3 s (leaf app.ink_scope) │ ◄── {summary, subs: [ "Markets › startups", … ], custom: [ {beat, sub, query, lang} ], regions: []} │ the pick: one block per beat — the header ticks the whole beat, a chip per sub-field; │ custom rows as dashed chips under their beat, carrying the query, × removes; │ regions as a last block (none ticked = anywhere) │ [Confirm scope] ──► POST /api/admin/ink/scope {scope} validated (clean_scope), saved as settings ink_scope │ [Clear scope] ──► POST /api/admin/ink/scope {scope: null} ▼ the next morning ink_brew.stage_pegs reads scope() → ink_harvest.harvest(date, scope, tagger) │ read + search as before, plus the custom rows → the whole pool, ~2,400 stories, still kept (pegs-all.json) │ fold → one story one peg │ tag_pool: ① harvest tags · ② keyword rules · ③ tag_batch (Flash, 40 a call) ~60 calls, ~3¢ │ in_scope: any ticked sub-field ∧ (no regions ticked ∨ the story's region is ticked) │ select(pool in scope) → ~160 for the meeting; the tail seats fill from the ticked sub-fields ▼ the meeting · the pitches · the desk · the writers — unchanged, on a pool that is only the scope
PieceWhereWhat
the storesettings.ink_scope{text, subs: ["Beat › sub"…], custom: [{beat, sub, query, lang}], regions, updated, by} — empty or missing = no scope
the routesrun_room.py · /api/admin/ink/scope (GET · POST) · /scope/draftunder /api/admin/, so the central gate makes them admin-only; the console's /api/admin/ink carries the scope read-only
the draft + the taggerink_brew.py · scope() · clean_scope() · scope_draft() · tag_batch()the eighteenth task leaf, app.ink_scope, on the Flash seat; a card on Models & APIs under Research
the harvestink_harvest.py · taxonomy() · scope_subs() · in_scope() · keyword_rules() · tag_pool()model-free: the brew hands in the Flash call; without one the pass stops after the keyword rules. harvest(…, scope, tagger); the checklist rows carry a language
the pageroom-ui.html · #inkScope · openScopein DRAWERS, the back gesture, the wide-mode list; the ⊕ menu's first item on the Ink tab for admins; the reader's ⋯ after Feedback…
the consoleInk tab › The scoperead-only: what is set, by whom, when — it is set from the app
decisionA scoped morning never widens itself. The old rule sent a harvest under 40 pegs to the Serper fallback. Under a scope the fallback would be unscoped, so a thin morning is kept thin and logged; the admin widens the scope or clears it.
decisionThe whole pool is still read and kept. The scope filters the meeting, not the harvest: every morning's pegs-all.json is the full, now fully tagged, pool. Widening the scope tomorrow costs nothing today; the tags are the record.
decisionTagging runs only under a scope. No scope = the brew exactly as before, no new call. With one, ~3¢ a morning on the Flash seat.
decision · v1102The Ink tab's corner is a ⋯, not the ⊕. v1101 put Scope… into the ⊕ (one menu on every tab, v861); the owner overruled it the same night:「the top right corner of the ink tab should not be the + menu, it should be the ... menu」. Ink starts nothing — it is read — so its corner holds what the tab can do: today one command, Scope…, admin only (the ⋯ shows only to an admin; the ⊕ is hidden on Ink). The reader's ⋯ no longer carries it.
decision · v1102The harvests are rows on the Scope page. One drawer row a morning — date · stories · to the meeting · in scope — and a row opens the morning's whole pool as a page: filled tile = read at the meeting, ring = front, a filter row on top, a row opens the story. Read from the brew folders (pegs-all.json · pegs.json · coverage.json); /api/admin/ink/harvests + /harvest/{date}.
rejected · 09-20 nightThe pick list. The owner on v1101's page:「trying to understand my text and give me a laundry list of the tags. You can never exhaust the tags. And the three real estate bubbles look ugly and don't match the others.」Right on both counts: the chips are the harvest's vocabulary, not the owner's, and the custom rows are the seam showing. See §7.

7 · The redesign — the brief IS the scope built v1103

The owner:「our harvest should be different. It's no longer pull all the sources and sort out what I need. It should be proactive search on my interested topic. Don't translate my text into tags from the existing database; do fuzzy and semantic search using my text on the web for news.」

the page your brief · Parse · the readback line · every search, compact by theme · Confirm / Clear. (v1104: the harvests are their own page.) the harvest under a scope the wide harvest does NOT run — no editions, feeds, Wikipedia, checklist, tags. the brief → the desk expands it into ~40 search queries (synonyms · angles · places · players; English, and Chinese where the theme is Chinese) → Google News search, every query, every morning → fold (one story one peg) → a Flash relevance read of EVERY story against the brief itself: in or out · which theme of yours · how close → ranked by closeness, then attention → ~160 to the meeting → pitches · desk · writers, unchanged. Yesterday's best stories → follow-up queries, so a running story is chased. the meaning lives at the expansion (many phrasings of what you mean) and at the read (each story judged against your text); the web search between them is keyword search, run wide. No closed list to exhaust. clear = the old wide harvest, the whole world.

As built (v1103). The page: your brief · Read it back (one Flash call: the readback line, the themes in your words, the number of searches) · Confirm / Clear · the harvest rows. The store: ink_scope = {text, summary, themes, queries:[{q, lang, theme}], updated, by}. The morning: ink_brew.stage_pegsreading_scope()ink_harvest.harvest_brief: every query searched (Google News RSS, 20 lines each, the Chinese edition for a zh query) → fold → judge_pool (the Flash seat reads every story against the brief: in · theme · closeness 0–3 · beat, 40 a call) → the stories in, ranked by (1 + closeness) × attention with the usual discounts → ~160 to the meeting; yesterday's pegs with closeness ≥ 2 → up to 10 follow-up queries (followup_queries). The harvest page shows ● ●● ●●● per story. The pick list, the tag recovery and the custom rows of v1101 are gone from the code; §2–3 stay as the study they were.

The dry run, 09-21, the owner's brief
queries drafted (6 themes: start-ups · tech · science · Bay Area · Shanghai · super-luxury; 12 in Chinese)45
lines → stories after the fold772 → 641
in scope after the read (●●● 110 · ●● 291 · ● 30) · out431 · 210
by theme: tech 137 · start-ups 109 · science 62 · super-luxury 53 · Bay Area 38 · Shanghai 32
to the meeting · very close160 · 104
time · cost (18 Flash calls)15 s · $0.016

8 · Broad and narrow — the sections map built v1105

The owner, reading the parsed searches:「biotech is not mentioned under tech. How can we solve this?」— and then:「I'm no expert in news. I just like the search to be reasonably well covered, not skewed towards a subset of the news space. I want the user to get what he wants with best effort.」

A finite list of phrases can never cover a broad theme; that was the tags' problem too. So Parse now decides, per theme, broad or narrow:

What it isWhat the morning doesOn the Scope page
broada whole section of the news — tech, science, health, sports, business, politics, world, culture, markets, environment, real estate, education, law and crime, China, lifestylereads the section WHOLE: every outlet feed it has, its Google News topic feed in two editions, its standing checklist searches — exhaustive by construction; the relevance read still decides what is close. 2–4 queries for the editor's particular angle, if the brief names one「tech · the whole technology section · 8 feeds · 17 standing searches」
narrowa place, a price tier, a company, a nicheenumerated: 10–15 searches covering the theme's sub-topics, players, places and phrasingsthe searches, compact

The sections map (lib/ink/harvest.json › sections, 15 sections, 92 feeds of which 33 new, 173 standing searches) is a second key over the same sources, by ordinary section names. It does not replace the eight beats: those stay the writing side's labels (hard news, the art director's accents). The eight were a lopsided map for reading — World and Markets were catch-alls, Health was split between Science and Life, Environment and Real estate had no home — so reading broad themes is keyed to sections instead. New feeds: Politico, The Hill, Guardian politics/law/environment/health/education/property, STAT, Medical Xpress, WHO, KFF, Nature, Science, Carbon Brief, Inside Climate News, Mongabay, The Real Deal, Realtor.com, Mansion Global, Inside Higher Ed, Chalkbeat, Times Higher Education, Caixin, China Daily, Courthouse News, ABA Journal, NYT style/travel/politics. A feed that fails is logged and skipped.

The dry run, 09-21, the owner's brief, with sections
Parse: tech → the technology section, science → the science section; start-ups, Bay Area, Shanghai, super-luxury enumerated at 15 searches each68 searches + 2 sections
lines: 68 searches 1,116 · technology whole 351 (26/26 sources) · science whole 368 (26/27)1,835
stories after the fold · in scope after the read (●●● 220 · ●● 553 · ● 57)1,210 · 830
by theme: tech 295 · science 185 · start-ups 155 · super-luxury 90 · Shanghai 65 · Bay Area 40
to the meeting · time · cost (32 Flash calls)160 · 33 s · $0.03

What the first sample morning taught (09-22 on dev, the owner's brief)

SeenCauseFix
the Shanghai theme was thirty developer sales-office notices from one portal, rated ●●● by the reada listing is squarely「Shanghai real estate」to a reader that was not told ads are not newsad-like lines are dropped before the fold (售楼处 · 官方专线 · 24小时响应 · sponsored · press release …, logged: 74 of 1,912) and the read is told a listing, a promotion or a press release is 0 however close its subject
after that, the Shanghai theme had twelve stories in scope and none at the meetingthree doors in a row: the tail seats handed each theme ONE story and stopped; the language limit (12 non-English) was the wide harvest's rule; and the brew's section-page guard counted a Chinese headline as one「word」and refused all twelvethe tail cycles the themes until its seats are used; under a brief a third of the seats may be non-English; the guard counts CJK characters as words
「实得率超216%!杨浦滨江「外滩元境二期」推约110-174㎡风貌洋房」reached the meeting at ●●● — a soft ad: a launch report about one named developmentit looks like news; the read's ad rule named listings and hotlines, not advertorialtwo signals before the fold — a bracketed project name AND a sales term (实得率 · 推约 · 户型 · 均价 · 认筹 · 加推 · ㎡ …) — so a market report quoting prices survives; and the read is told the soft ad's tells: one named development quoting its own sales terms with no market fact and no event is 0, and a portal or self-media account is news only with a market fact from a source other than the seller
a theme's LPR / PBOC rate stories rated ●● for Shanghai real estatethe read is lenient on adjacencyleft as is: rates move property; the meeting can still pass them over

6 · Status

DateWhat
2026-09-22v1106 (owner): the harvest page opens with a count strip — one tile a theme (in scope · seats), the Stats page's tiles; a tap filters the list to the theme. The soft-ad rule (§8). The owner's questions answered on the page: the coral tile = read at the meeting, grey = in the pool only, the ring = front; ●●● squarely · ●● within · ● loosely · out; attention = outlets + countries + 2×front pages + Wikipedia, no model.
2026-09-21v1105 (owner:「reasonably well covered, not skewed」): §8 — the sections map, broad themes read whole, narrow themes enumerated; dry run 1,210 stories → 830 in → 160, 33 s, 3¢.
2026-09-21v1104 (owner): the Ink ⋯ holds TWO commands, each its own page — Scope… (the brief · Parse · the readback line · every search, compact by theme, Chinese ones muted · Confirm / Clear; no status block) and Harvests… (the mornings as drawer rows, a row opens the day). The owner's question「what does 51 searches a morning mean」→ the number of queries the brief was parsed into, each run against Google News every morning, 4–8 per theme.
2026-09-20 · nightv1103: the brief-driven search harvest BUILT (§7) — the page is the brief + the readback; under a scope the wide harvest does not run. Dry run on the owner's brief: 45 searches → 641 stories → 431 in → 160 to the meeting, 15 s, 1.6¢. v1102: the Ink tab's corner is the ⋯ (one command, admin only); the harvest rows + the harvest page. The pick list rejected by the owner.
2026-09-20the ten pools read; the recovery run over 16,704 stories ($0.25); the distribution above. Built the same night: the store, the routes, the draft, the tagger, the harvest under a scope, the Scope page, the console line, the eighteenth leaf. Owed: the owner sets the first scope on the box tonight; the 09-21 03:00 brew is the first scoped morning — read its log line「scope: N of M stories in scope」and the by-sub counts in coverage.json.
built 2026-09-20 · siblings: coverage and the pitch · the brew · the budget meeting