Docs/Skills and code/src/

src/analysis/channel-queues-report.js

Code62 lines5.5 KBsrc/

import fs from "node:fs";
import path from "node:path";

export function writeChannelQueueReports({ result, channels, generatedAt, runId, apply, sourcePriorityRunId, root }) {
  const reportDir = path.join(root, "reports");
  fs.mkdirSync(reportDir, { recursive: true });
  const stamp = generatedAt.replaceAll(":", "-").replaceAll(".", "-");
  const jsonPath = path.join(reportDir, `channel-queues-${stamp}.json`);
  const mdPath = path.join(reportDir, `channel-queues-${stamp}.md`);
  const nameByUrl = new Map(channels.map(channel => [channel.channel_url, channel.name]));
  const scoreRows = result.ratings.map(row => ({ channel_url: row.channel_url, channel_name: nameByUrl.get(row.channel_url) ?? row.channel_url, ...row.queue }));
  const queueReports = result.queues.map(queue => {
    const rows = scoreRows.filter(row => row.queue_key === queue.key);
    const scored = rows.filter(row => row.score != null);
    const ranked = rows.filter(row => row.score != null && row.rank != null).sort((a, b) => a.rank - b.rank);
    return { ...queue, rated_count: ranked.length, scored_unranked_count: scored.length - ranked.length, unknown_count: rows.length - scored.length, rankings: ranked.slice(0, 30) };
  });
  const report = {
    generated_at: generatedAt, run_id: runId, scoring_version: result.scoring_version ?? "channel-queues-v3-evidence-led",
    mode: apply ? "APPLY" : "DRY_RUN", source_priority_run_id: sourcePriorityRunId,
    channel_count: channels.length, queue_count: result.queues.length, rating_rows: scoreRows.length,
    queue_reports: queueReports,
    qualification_diagnostics: result.diagnostics ?? null,
    scoring_notes: [
      "Scores are queue-specific prioritization heuristics, not probabilities or universal quality verdicts.",
      "Eligibility is evaluated before rank: confidently qualified core channels rank before adjacent channels; unknown or low-confidence channels remain qualification-pending.",
      "Missing component values remain NULL, appear explicitly in component evidence, and shrink the available-component mean toward neutral 50 in proportion to missing configured weight; no missing component is zero-filled.",
      "Evidence coverage includes missing components in its denominator. A supported status requires adequate coverage, qualification confidence, and queue-specific sample requirements; otherwise a scored row is provisional.",
      "Static growth potential uses captured channel/video fields only; it does not require repeated snapshots and does not claim to measure live acceleration.",
      "Queue scoring v5 uses raw channel metadata, qualification based on raw metadata and captured video fields only; channel profiles, vocabulary, concepts, patterns, embeddings, clusters and legacy channel-priority scores are excluded.",
      "Tiny subscriber counts are denominator-smoothed; absolute reach and repeatability are required alongside relative reach.",
      "Reference seed membership is separate from usefulness: qualified seeds are scored on evidence, while irrelevant seeds remain visible as conflicts and unqualified seeds remain visible as pending, neither receives an ordinary rank.",
      "Inferred reference candidates must pass core/adjacent ecosystem qualification; irrelevant, unknown, and low-confidence candidates cannot rank in the reference queue.",
      "Reference usefulness components use diminishing returns rather than count-based caps that made many channels tie at the same maximum score.",
      "Niche rarity is not proof of audience demand; missing embeddings and sparse metadata are not treated as novelty."
    ]
  };
  fs.writeFileSync(jsonPath, JSON.stringify(report, null, 2));

  const lines = [
    "# Channel Queue Ratings", "", `- Generated: ${generatedAt}`,
    `- Mode: **${report.mode}**${apply ? " (persisted to SQLite)" : " (preview only; queue ratings not persisted)"}`,
    `- Channels: ${channels.length}`, `- Queue ratings computed: ${scoreRows.length}`,
    `- Source priority run: ${sourcePriorityRunId ?? "none"}`, "",
    "> Scores are transparent prioritization heuristics, not probabilities or quality verdicts. Unknown means insufficient evidence, not a zero score.", ""
  ];
  for (const queue of queueReports) {
    lines.push(`## ${queue.label}`, "", queue.purpose, "", `Ranked: ${queue.rated_count}/${channels.length}; scored but unranked: ${queue.scored_unranked_count}; unscored / pending: ${queue.unknown_count}.`, "");
    if (!queue.rankings.length) { lines.push("No channels currently have enough evidence for a score.", ""); continue; }
    lines.push("| Rank | Channel | Score | Confidence | Eligibility | Coverage | Sample | Status | Reason codes |", "|---:|---|---:|---:|---|---:|---:|---|---|");
    for (const row of queue.rankings.slice(0, 20)) {
      lines.push(`| ${row.rank} | [${String(row.channel_name).replaceAll("|", "\\|")}](${row.channel_url}) | ${row.score ?? "—"} | ${row.confidence ?? 0} | ${row.eligibility ?? "unknown"} | ${row.coverage ?? 0} | ${row.sample_size ?? 0} | ${row.status} | ${(row.reason_codes ?? []).join(", ")} |`);
    }
    lines.push("");
    for (const row of queue.rankings.slice(0, 5)) lines.push(`- **${row.channel_name}** (${row.score}/100; confidence ${row.confidence}/100): ${row.rationale}`);
    lines.push("");
  }
  lines.push("## Qualification diagnostics", "", "```json", JSON.stringify(report.qualification_diagnostics, null, 2), "```", "", "## Interpretation guardrails", "", ...report.scoring_notes.map(note => `- ${note}`), "");
  fs.writeFileSync(mdPath, lines.join("\n"));
  return { report, queueReports, scoreRows, mdPath, jsonPath };
}

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.