Docs/Skills and code/src/

src/topic-research/report.js

Code77 lines4.8 KBsrc/

import { stamp } from "./util.js";
import { sourceUrl } from "./sources/youtube.js";

const dedupe = s => String(s ?? "").replace(/\b(\S+)\s+\1\b/gi, "$1");
const esc = s => String(s ?? "").replace(/\|/g, "\\|").replace(/\s*\n\s*/g, " ");

/** Source links for an item, one per distinct doc, pinned to the first verified timestamp. */
function cites(item, docByKey) {
  const byDoc = new Map();
  for (const ev of item.evidence) {
    const cur = byDoc.get(ev.doc);
    if (!cur || (ev.verified && !cur.verified)) byDoc.set(ev.doc, ev);
  }
  return [...byDoc.values()].map(ev => {
    const doc = docByKey.get(ev.doc);
    return `[${ev.doc} ${stamp(ev.t)}${ev.verified ? "" : "?"}](${sourceUrl(doc, ev.t)})`;
  }).join(" ");
}

const strength = i => (i.source_count > 1 ? `${i.source_count} sources` : "1 source") + (i.verified_quotes ? "" : ", quote unverified");
const flags = i => [i.conflict && `⚠ ${i.conflict}`, i.note].filter(Boolean).join(" ");

export function renderReport({ topic, search, docs, extractions, synthesis: s }) {
  const docByKey = new Map(docs.map(d => [d.key, d]));
  const L = [];
  L.push(`# Research brief: ${topic}`, "");
  L.push(`_Built from ${s.sourcesUsed.length} YouTube transcripts. Every item links to the video at the timestamp it was said; \`?\` means the quote could not be matched verbatim to the transcript. Items backed by one source only are marked._`, "");
  L.push("## The story in brief", "", `**${s.one_line}**`, "", s.overview, "");

  L.push("## Timeline", "", "| When | What happened | Support | Sources |", "|---|---|---|---|");
  for (const e of s.timeline) L.push(`| ${esc(e.date)} | ${esc(e.event)}${e.significance ? ` _${esc(e.significance)}_` : ""} ${esc(flags(e))} | ${strength(e)} | ${cites(e, docByKey)} |`);
  L.push("");

  L.push("## Hard numbers", "", "| Metric | Value | As of | Support | Sources |", "|---|---|---|---|---|");
  for (const f of s.figures) L.push(`| ${esc(f.metric)}${f.context ? ` _(${esc(f.context)})_` : ""} | **${esc(dedupe(f.value))}** ${esc(flags(f))} | ${esc(f.as_of)} | ${strength(f)} | ${cites(f, docByKey)} |`);
  L.push("");

  L.push("## Shocking / surprising points", "");
  s.surprises.forEach((p, i) => L.push(`${i + 1}. **${p.point}** (${p.strength ?? "?"}; ${strength(p)}) ${p.why_surprising ? `— ${p.why_surprising}` : ""} ${flags(p)} ${cites(p, docByKey)}`));
  L.push("");

  L.push("## Why it happened", "");
  for (const c of s.causes) L.push(`- **${c.factor}** _(${c.kind}, ${strength(c)})_ — ${c.explanation} ${flags(c)} ${cites(c, docByKey)}`);
  L.push("");

  L.push("## Key people and entities", "");
  for (const e of s.entities ?? []) L.push(`- **${e.name}** (${e.type}) — ${e.role}`);
  L.push("");

  if (s.contested?.length) {
    L.push("## Contested or shaky claims", "");
    for (const c of s.contested) L.push(`- ${c.claim} — _${c.why_contested}_`);
    L.push("");
  }
  L.push("## Still open (what the sources do not settle)", "");
  for (const q of s.open_questions ?? []) L.push(`- **${q.question}** ${q.why ? `— ${q.why}` : ""}`);
  L.push("");
  L.push("## Next: verify with Google AI Mode", "", "_Queued queries (AI Mode source not wired in yet):_", "");
  for (const q of s.verification_queries ?? []) L.push(`- ${q}`);
  L.push("");

  L.push("## Sources", "", "| Key | Video | Channel | Views | Length | Lang | Facts (verified/total) |", "|---|---|---|---|---|---|---|");
  for (const d of docs) {
    const e = extractions.find(x => x.key === d.key);
    const total = e ? Object.values(e.stats).reduce((a, x) => a + x.total, 0) : 0;
    const ok = e ? Object.values(e.stats).reduce((a, x) => a + x.verified, 0) : 0;
    const used = s.sourcesUsed.includes(d.key) ? "" : d.error ? " _(no captions available)_" : " _(excluded: no usable speech or off-topic)_";
    L.push(`| ${d.key} | [${esc(d.meta.title)}](${sourceUrl(d)})${used} | ${esc(d.meta.channel)} | ${d.meta.views?.toLocaleString("en-IN") ?? "?"} | ${stamp(d.meta.duration_seconds)} | ${d.lang ?? (d.error ? "no transcript" : "?")} | ${e ? `${ok}/${total}` : "-"} |`);
  }
  L.push("", "## Method and caveats", "",
    `- Search: ${search.queries.length} fixed query templates, ${search.candidatesSeen} videos seen, ${search.candidatesKept} passed the topic/noise filter, top ${search.selected.length} by a fixed score (log views + query hits + length + framing words).`,
    "- Extraction: one structured LLM pass per transcript (temperature 0.1, all answers cached by prompt hash). Quotes are verified in code against the transcript text.",
    "- Synthesis merges facts across videos; items with no valid supporting fact id are dropped. Support counts are computed in code, not by the model.",
    "- Limits: transcripts are auto-captions; YouTube explainers repeat each other, so \"several sources\" can still be one original claim. Treat all numbers as leads to confirm, not as facts, until checked against filings or news.");
  return L.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.