Docs/Skills and code/src/
src/series-analysis/report.js
const n = (x, d = 0) => (x == null || !Number.isFinite(Number(x)) ? "n/a" : Number(x).toLocaleString("en-US", { maximumFractionDigits: d }));
const compact = x => (x == null ? "n/a" : new Intl.NumberFormat("en", { notation: "compact", maximumFractionDigits: 1 }).format(x));
const mins = s => (s == null ? "n/a" : `${Math.round(s / 60)}m`);
const years = d => (d == null ? "n/a" : d < 60 ? `${Math.round(d)}d` : d < 700 ? `${Math.round(d / 30)}mo` : `${(d / 365).toFixed(1)}y`);
const esc = s => String(s ?? "").replace(/\|/g, "\\|").replace(/\s+/g, " ");
const url = r => `https://www.youtube.com/watch?v=${r.video_id}`;
/** One-line reason a video earned its place, from the numbers that actually drove the score. */
export function whyLine(r) {
const bits = [];
if (r.outlier_ratio != null) bits.push(`${r.outlier_ratio.toFixed(1)}x ${r.outlier_basis === "channel-median" ? "its channel's typical video" : "its channel's subscriber count"}`);
if (r.structures?.length) bits.push(`packaging: ${r.structures.join(", ")}`);
if (r.subject) bits.push(`subject: ${r.subject}`);
if (r.duration_seconds) bits.push(`${mins(r.duration_seconds)} long`);
if (r.format_flags?.length) bits.push(`caution: ${r.format_flags.join(", ")}`);
return bits.join("; ");
}
function table(rows, withRank = true) {
const head = `| ${withRank ? "# | " : ""}Video | Channel (subs) | Views | Age | Views/day | vs channel | Fit | Perf | Learn | Score |\n|${withRank ? "---|" : ""}---|---|---|---|---|---|---|---|---|---|`;
const body = rows.map((r, i) => `| ${withRank ? `${i + 1} | ` : ""}[${esc(r.title)}](${url(r)}) | ${esc(r.channel_name)} (${compact(r.subscribers)}) | ${compact(r.views)} | ${years(r.age_days)} | ${compact(r.views_per_day)} | ${r.outlier_ratio == null ? "n/a" : `${r.outlier_ratio.toFixed(1)}x`}${r.outlier_basis === "subscribers" ? "*" : ""} | ${n(r.fit, 2)} | ${n(r.performance, 2)} | ${n(r.learnability, 2)} | ${n(r.learn_score, 3)} |`);
return [head, ...body].join("\n");
}
export function buildReport({ spec, runId, scored, selected, lists, queries, coverage, transcripts }) {
const eligible = scored.filter(r => r.learn_score != null);
const lines = [];
lines.push(`# Series analysis: ${spec.name}`, "", `_Run ${runId}. ${spec.premise}_`, "");
lines.push("## Learn from these", "",
`Top ${selected.length} of ${eligible.length} eligible videos (${scored.length} enriched). Score = ${Object.entries(spec.scoring.weights).map(([k, v]) => `${v} x ${k}`).join(" + ")}; at most ${spec.scoring.maxPerChannelInTop} per channel and 2 per company so the set is varied.`, "",
table(selected), "", "`*` = ratio is against subscriber count because the channel's recent uploads could not be sampled (weaker evidence).", "");
lines.push("### Why each was picked", "");
selected.forEach((r, i) => {
const t = transcripts.get(r.video_id);
lines.push(`${i + 1}. **${esc(r.title)}** (${esc(r.channel_name)}): ${whyLine(r) || "n/a"}${t ? `. Transcript: \`${t}\`` : ""}`);
});
lines.push("");
if (lists.breakouts.length) lines.push("## Breakouts: small channels that beat their own norm", "",
"These are the best evidence that the packaging, not the audience, did the work.", "", table(lists.breakouts), "");
if (lists.giants.length) lines.push("## Reference giants: huge reach, hard to replicate", "",
"Large channels (3M+ subscribers). Study the framing, but expect their numbers to come from the audience they already have.", "", table(lists.giants, false), "");
if (lists.contrast.length) lines.push("## Contrast set: same packaging, weak result", "",
"On-series titles that underperformed. Compare against the winners to see which part of the formula is not carrying the video.", "", table(lists.contrast, false), "");
const excluded = scored.filter(r => r.excluded);
const reasons = excluded.reduce((m, r) => ((m[r.excluded.replace(/ \(.*/, "")] = (m[r.excluded.replace(/ \(.*/, "")] ?? 0) + 1), m), {});
lines.push("## Coverage and caveats", "",
`- Candidates found: ${coverage.candidates}; metadata fetched for ${coverage.enriched}; channels fetched ${coverage.channels} (${coverage.channelsWithBaseline} with a usable recent-uploads baseline).`,
`- Excluded from ranking: ${Object.entries(reasons).map(([k, v]) => `${v} ${k}`).join(", ") || "none"}.`,
`- LLM fit judgements: ${coverage.llmJudged}${coverage.llmJudged ? "" : " (heuristic title fit only; run with --llm or the judge command to add them)"}.`,
"- Fit is read from titles (failure vocabulary, structure, named company). It cannot see thumbnails or the actual script yet.",
"- Views are lifetime totals, not first-30-day numbers. Views/day floors age at 30 days so recent uploads are not over-credited.",
"- Search results are a snapshot of what YouTube returned today for each query.", "");
lines.push("## Search plan", "", "| Tier | Queries | Done | Raw results |", "|---|---|---|---|",
...queries.map(q => `| ${q.tier} | ${q.n} | ${q.done ?? 0} | ${q.results ?? 0} |`), "");
return lines.join("\n");
}
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.