Docs/Skills and code/skill ecosystem-rater

.claude/skills/ecosystem-rater/SKILL.md

Markdown124 lines9.0 KBSkill: ecosystem-rater

What the file says about itself

Ecosystem rater: Each data/<eco>-ecosystem.db is a biased sample: channels found through search, collected to different depths (one ecosystem may have 400 channels from 33 searches, another 1,500 from 130).

Mentioned on: page 02, page 10, page 12

Skill header (frontmatter)

name: ecosystem-rater description: Rate and rank YouTube ecosystems (niches) for a new creator on fixed, deterministic signals computed from the ecosystem databases in data/ — demand, room for newcomers, evergreen-ness, upload pressure and a monetization proxy — and write a cross-ecosystem scorecard. Includes a standardized search probe so demand and evergreen numbers are comparable across ecosystems. Use when the user wants to compare, score or rank ecosystems, asks which niche to enter, or asks how much room / demand / evergreen value an ecosystem has.

Ecosystem rater

What this is

Each data/<eco>-ecosystem.db is a biased sample: channels found through search, collected to different depths (one ecosystem may have 400 channels from 33 searches, another 1,500 from 130). Raw counts and all-channel medians therefore measure discovery depth, not the niche: Indian business has a median of 719 views per video across all core channels but 31,000 across its top 100. Every signal here is built to survive that bias, by one of three means:

  1. YouTube's own ranking (search results for a fixed query set), independent of discovery depth.
  2. Fixed-size slices (the top 100 core channels by subscribers; the 10th-largest channel), never "all channels found".
  3. Ratios within a channel (views per subscriber, old vs fresh videos), which cancel channel size.

Scores map raw values to 1-5 through fixed thresholds in scripts/bands.json, never ranks among the ecosystems being compared, so adding a seventh ecosystem never changes the other six. 5 is always better for a new creator. Same inputs give byte-identical output: ages are measured at each row's own observation time, never "now".

S below means .claude/skills/ecosystem-rater/scripts, run from the project root.

The signals

Population: core channels = latest successful LLM qualification is core (channel_qualification_llm_results; the rules table channel_qualifications is stale in several DBs). Long-form = over 60 s. Per-channel views = median views of long-form videos aged 30-365 days.

DimensionMetricWhat it measuresKnown bias / caveat
Demandsearch_viewsMedian views of the top-10 search results, median over queriesDepends on the query set, hence the probe
Demandceiling_subsSubscribers of the 10th-largest core channel: how big a channel the niche can supportMisses giants discovery never surfaced
Demandtop100_viewsMedian per-channel views among the top 100 core channelsNeeds 40+ channels with feeds
Roomconcentration_top10Share of top-100 views held by the top 10 (lower = less winner-take-all)
Roomsmall_reachViews per subscriber for 1k-100k channels: does the algorithm push small channels beyond their base?
Roomyoung_successOf core channels under 3 years old, share with 10k+ subscribersSurvivorship: failures are rarely discovered
Evergreensearch_old_shareShare of top search results older than 2 years: does search still serve old videos?Strongest evergreen signal
Evergreensearch_fresh_shareShare under 1 month (lower = less news-driven)Swings with the news / sports calendar; probe on 2+ days
Evergreenback_catalogue_ratiolog10(views of 1-3 y old videos / views of 1-6 mo old), within channelConfounded by channel growth; needs mined catalogues (feeds hold only the newest ~30 videos)
Evergreenview_growth_back_shareShare of monthly view growth earned by videos over 1 year oldThe gold standard; needs a second snapshot 14+ days after the first
Pressurecadence_top100Median uploads per month among the top 100 (lower = less treadmill)Relative publish dates make this coarse
Monetizationtier1_country_shareShare of top-100 channels based in high-CPM countriesCreator location, not audience location: a proxy only

Context, never scored: Shorts share, median long-form length, dormant share (newest upload over 180 days old), new-entrant share (channels under 2 years), non-Latin title share (language gap), top creator countries, breakout rate (share of a small channel's videos at 5x its own median: about 0.09-0.13 everywhere so far, so it does not discriminate).

Dimension score = median of its scored metrics, labelled low confidence when it rests on fewer than 3 metrics (unless the dimension only has 1-2). Composite = weighted mean of dimensions (weights in bands.json, override with --weights). A composite missing a weighted dimension is marked * partial and ranked below complete ones: missing is unknown, not average.

Not measured at all (say so whenever you present results): visual-asset availability and copyright risk, ad-suitability (war, disasters, religion), effort per video, the creator's own edge. These come from judgment, not this data.

Step 1: probe queries (once per ecosystem, then frozen)

The search metrics are only comparable across ecosystems if every ecosystem is probed with the same kind of query. Discovery seeds are not that: some are broad ("Australian cricket"), others are built from titles of videos already found, which biases demand upward.

Write ecosystem-ratings/<eco>/probe-queries.json:

{ "version": "v1", "language": "en", "queries": [ { "bucket": "head", "query": "crochet" }, ... ] }

Exactly 30 queries, 10 per bucket, derived from the ecosystem definition in its config-<eco>.js, not from discovered titles:

Rules for all: no years, no channel or creator names, no news events, no quotes or operators; the same language for every ecosystem being compared (normally the language the user will publish in). Show the user the list before probing. Once a probe has run, never edit the file: write a new version and move the old probe-results-*.json into an archive/ subfolder (the rater flags mixed versions).

Step 2: run the probe (network; ask first)

node S/probe-search.mjs --db <eco>          # ~30 searches, 1.5 s apart; writes probe-results-<date>.json

Do not run it without the user's go-ahead. One file per day; the rater averages all days per query. For news-sensitive ecosystems (sport, politics, markets) probe again on a second day at least a week later, since a single day can land on a big match or event.

Step 3: rate

node S/rate.mjs --all                       # every data/*.db -> ecosystem-ratings/<eco>/metrics.json + ecosystem-ratings/scorecard.md
node S/rate.mjs --db <eco>                  # one ecosystem
node S/rate.mjs --all --weights demand=2,room=1,evergreen=1,pressure=0.5,monetization=0

Without probe results the rater falls back to discovery seed searches and labels them UNSTANDARDIZED in every output.

Step 4: present

Read scorecard.md fully, then report:

  1. The ranking table (dimension scores and composite), with every low confidence, * partial and unstandardized source called out.
  2. Per ecosystem, 2-3 sentences that name the raw numbers behind its best and worst dimension ("only 5% of top science results are under a month old; 57% are over two years").
  3. Where the data contradicts intuition or an earlier subjective rating, say so plainly. Do not soften a number to fit a prior.
  4. What was not measured (see above) and what would most improve the result (next step).

Improving the data (offer, do not run unasked)

GapFixUnlocks
Unstandardized search sourceSteps 1-2Comparable search_* metrics
back_catalogue_ratio insufficientnpm run catalogue:mine for core channelsWithin-channel evergreen
view_growth_back_share insufficientnpm run metrics:snapshot again 14+ days after the last snapshotThe strongest evergreen measure
Few LLM-qualified channelsnpm run qualify:missing / stage 25Larger core population
Thin ecosystem (art: few seeds, few feeds)More discovery, then enrichMost room/demand metrics

Rules

Show the raw text
---
name: ecosystem-rater
description: Rate and rank YouTube ecosystems (niches) for a new creator on fixed, deterministic signals computed from the ecosystem databases in data/ — demand, room for newcomers, evergreen-ness, upload pressure and a monetization proxy — and write a cross-ecosystem scorecard. Includes a standardized search probe so demand and evergreen numbers are comparable across ecosystems. Use when the user wants to compare, score or rank ecosystems, asks which niche to enter, or asks how much room / demand / evergreen value an ecosystem has.
---

# Ecosystem rater

## What this is

Each `data/<eco>-ecosystem.db` is a **biased sample**: channels found through search, collected to different depths (one
ecosystem may have 400 channels from 33 searches, another 1,500 from 130). Raw counts and all-channel medians therefore measure
discovery depth, not the niche: Indian business has a median of 719 views per video across all core channels but 31,000 across its
top 100. Every signal here is built to survive that bias, by one of three means:

1. **YouTube's own ranking** (search results for a fixed query set), independent of discovery depth.
2. **Fixed-size slices** (the top 100 core channels by subscribers; the 10th-largest channel), never "all channels found".
3. **Ratios within a channel** (views per subscriber, old vs fresh videos), which cancel channel size.

Scores map raw values to 1-5 through **fixed thresholds** in `scripts/bands.json`, never ranks among the ecosystems being compared,
so adding a seventh ecosystem never changes the other six. 5 is always better for a new creator. Same inputs give byte-identical
output: ages are measured at each row's own observation time, never "now".

`S` below means `.claude/skills/ecosystem-rater/scripts`, run from the project root.

## The signals

Population: **core** channels = latest successful LLM qualification is `core` (`channel_qualification_llm_results`; the rules
table `channel_qualifications` is stale in several DBs). Long-form = over 60 s. Per-channel views = median views of long-form
videos aged 30-365 days.

| Dimension | Metric | What it measures | Known bias / caveat |
|---|---|---|---|
| Demand | `search_views` | Median views of the top-10 search results, median over queries | Depends on the query set, hence the probe |
| Demand | `ceiling_subs` | Subscribers of the 10th-largest core channel: how big a channel the niche can support | Misses giants discovery never surfaced |
| Demand | `top100_views` | Median per-channel views among the top 100 core channels | Needs 40+ channels with feeds |
| Room | `concentration_top10` | Share of top-100 views held by the top 10 (lower = less winner-take-all) | |
| Room | `small_reach` | Views per subscriber for 1k-100k channels: does the algorithm push small channels beyond their base? | |
| Room | `young_success` | Of core channels under 3 years old, share with 10k+ subscribers | Survivorship: failures are rarely discovered |
| Evergreen | `search_old_share` | Share of top search results older than 2 years: does search still serve old videos? | Strongest evergreen signal |
| Evergreen | `search_fresh_share` | Share under 1 month (lower = less news-driven) | Swings with the news / sports calendar; probe on 2+ days |
| Evergreen | `back_catalogue_ratio` | log10(views of 1-3 y old videos / views of 1-6 mo old), within channel | Confounded by channel growth; needs mined catalogues (feeds hold only the newest ~30 videos) |
| Evergreen | `view_growth_back_share` | Share of monthly view growth earned by videos over 1 year old | The gold standard; needs a second snapshot 14+ days after the first |
| Pressure | `cadence_top100` | Median uploads per month among the top 100 (lower = less treadmill) | Relative publish dates make this coarse |
| Monetization | `tier1_country_share` | Share of top-100 channels based in high-CPM countries | Creator location, not audience location: a proxy only |

**Context, never scored:** Shorts share, median long-form length, dormant share (newest upload over 180 days old), new-entrant
share (channels under 2 years), non-Latin title share (language gap), top creator countries, breakout rate (share of a small
channel's videos at 5x its own median: about 0.09-0.13 everywhere so far, so it does not discriminate).

**Dimension score** = median of its scored metrics, labelled `low` confidence when it rests on fewer than 3 metrics (unless the
dimension only has 1-2). **Composite** = weighted mean of dimensions (weights in `bands.json`, override with `--weights`). A composite
missing a weighted dimension is marked `*` partial and ranked below complete ones: missing is unknown, not average.

**Not measured at all** (say so whenever you present results): visual-asset availability and copyright risk, ad-suitability
(war, disasters, religion), effort per video, the creator's own edge. These come from judgment, not this data.

## Step 1: probe queries (once per ecosystem, then frozen)

The search metrics are only comparable across ecosystems if every ecosystem is probed with the same kind of query. Discovery seeds
are not that: some are broad ("Australian cricket"), others are built from titles of videos already found, which biases demand upward.

Write `ecosystem-ratings/<eco>/probe-queries.json`:

```json
{ "version": "v1", "language": "en", "queries": [ { "bucket": "head", "query": "crochet" }, ... ] }
```

Exactly **30 queries, 10 per bucket**, derived from the ecosystem definition in its `config-<eco>.js`, not from discovered titles:
- `head`: 1-3 words, how a viewer names the topic or a main sub-topic ("cricket", "test cricket", "crochet blanket").
- `intent`: what a viewer wants to learn or do ("how to crochet a granny square", "why is the sky blue", "how does an IPO work").
- `entity`: canonical named things with at least five years of standing (people, works, places, historic events). No currently
  trending names: they inflate fresh-share and make the ecosystem look newsier than it is.

Rules for all: no years, no channel or creator names, no news events, no quotes or operators; the same language for every ecosystem
being compared (normally the language the user will publish in). Show the user the list before probing. Once a probe has run, never
edit the file: write a new version and move the old `probe-results-*.json` into an `archive/` subfolder (the rater flags mixed versions).

## Step 2: run the probe (network; ask first)

```
node S/probe-search.mjs --db <eco>          # ~30 searches, 1.5 s apart; writes probe-results-<date>.json
```

Do not run it without the user's go-ahead. One file per day; the rater averages all days per query. For news-sensitive ecosystems
(sport, politics, markets) probe again on a second day at least a week later, since a single day can land on a big match or event.

## Step 3: rate

```
node S/rate.mjs --all                       # every data/*.db -> ecosystem-ratings/<eco>/metrics.json + ecosystem-ratings/scorecard.md
node S/rate.mjs --db <eco>                  # one ecosystem
node S/rate.mjs --all --weights demand=2,room=1,evergreen=1,pressure=0.5,monetization=0
```

Without probe results the rater falls back to discovery seed searches and labels them `UNSTANDARDIZED` in every output.

## Step 4: present

Read `scorecard.md` fully, then report:
1. The ranking table (dimension scores and composite), with every `low` confidence, `*` partial and unstandardized source called out.
2. Per ecosystem, 2-3 sentences that name the raw numbers behind its best and worst dimension
   ("only 5% of top science results are under a month old; 57% are over two years").
3. Where the data contradicts intuition or an earlier subjective rating, say so plainly. Do not soften a number to fit a prior.
4. What was not measured (see above) and what would most improve the result (next step).

## Improving the data (offer, do not run unasked)

| Gap | Fix | Unlocks |
|---|---|---|
| Unstandardized search source | Steps 1-2 | Comparable `search_*` metrics |
| `back_catalogue_ratio` insufficient | `npm run catalogue:mine` for core channels | Within-channel evergreen |
| `view_growth_back_share` insufficient | `npm run metrics:snapshot` again 14+ days after the last snapshot | The strongest evergreen measure |
| Few LLM-qualified channels | `npm run qualify:missing` / stage 25 | Larger core population |
| Thin ecosystem (art: few seeds, few feeds) | More discovery, then enrich | Most room/demand metrics |

## Rules

- Never change a threshold to move one ecosystem's score. Thresholds change only with a new `version` in `bands.json`, and then
  every ecosystem is re-rated; say which version a scorecard used.
- Never present an `insufficient` metric's raw value as a finding; it is shown as `-` for a reason.
- A composite is a summary for sorting, not a decision. The decision also rests on the unmeasured factors and the user's goals,
  so present the dimension scores, not just the composite.
- Ecosystem DBs are opened read-only. The probe writes only under `ecosystem-ratings/`.

Shown as it is in the repository at build time (2026-11-03); the home directory appears as <home>. If this page and the file differ, the file is right. Back to the catalogue.