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.