The category tree

src/catalog/taxonomy.ts (CATEGORY_TREE) defines 21 top-level entries: 19 ordinary parent categories with a watch_order (News, Sports, Movies, Drama, Comedy, Entertainment, Kids, Anime, Music, Documentary, Lifestyle, Culture, Faith, Education, Government, Events, Webcams, Shop, Sci-Fi), plus two special entries with no watch order — adult (kind: 'gated') and general (kind: 'fallback', the parent used when nothing else fits). Several parents carry children (Drama → Mystery; Comedy → Sitcoms, Stand-up, Adult animation; Documentary → True crime, Science, Nature, History, Space; and others). This tree is the taxonomy every downstream system agrees on: the classifier assigns channels into it, the recommendation vectors are built over its keys, and the Watch screen’s browse order follows watch_order.

The Jev census

Channel classification is delegated to Jev (product name TypeSafe), a single POST to https://api.typesafe.ai/v1/systemone (src/catalog/classify/jev.ts, jevSystemOne). The request carries a state string and a questions object; the response returns one answer per question, each shaped as:
The transport retries on 429 (honoring Retry-After) and on 5xx with exponential backoff, and gives up after ten attempts.

Confidence floor and path assignment

src/catalog/classify/assign.ts turns a parent answer and (optionally) a child answer into a PathAssignment:
CONFIDENCE_FLOOR = 0.5. Jev’s own choice is still used even below the floor — a low-confidence parent answer sticks as the parent, and a low-confidence child answer still sticks as the child. An unrecognized parent key (one Jev returned that isn’t in the tree) falls back to general. A child answer of 'none' means no child, deliberately.

Soft vectors: channel_paths.probabilities

The full response isn’t discarded once a single choice is picked. Jev’s probabilities map — a sparse, not-necessarily-normalized record keyed by taxonomy keys — is stored as channel_paths.probabilities and consumed directly as a soft category vector by src/recs/vector.ts (vectorFor), which prefers it whenever a channel has been classified and falls back to a tags-only one-hot vector when it hasn’t. That reuse is what makes the recommendation engine’s degradation ladder possible — see Recommendations.