import type { ToolRecord } from "./content/schema"; import { WORD } from "./content/schema"; /** Deterministic editorial quality score 0–100 (completeness × evidence × freshness). Documented in docs/editorial-guide.md. */ export function qualityScore(r: Pick, linkState: string = "unchecked"): number { let s = 0; const w = WORD(r.description); s += Math.min(25, Math.round((w / 200) * 25)); // description depth (≈200 words = full marks) s += Math.min(10, r.capabilities.length * 2); // capabilities s += Math.min(5, r.limitations.length * 2); // honesty: limitations s += Math.min(5, r.workflows.length + r.advantages.length); // workflows/advantages s += Math.min(6, r.alternatives.length * 2); // graph connectivity s += r.comparison_notes ? 4 : 0; s += Math.min(5, r.tags.length + r.use_cases.length); // discoverability s += Math.min(15, r.sources.length * 5); // evidence s += r.docs_url ? 3 : 0; s += r.repo_url || r.source_model === "proprietary" ? 2 : 0; s += r.license_spdx || r.source_model === "proprietary" ? 3 : 0; s += r.pricing_url || r.pricing_model.every((p) => ["free", "open-source"].includes(p)) ? 3 : 0; s += r.confidence === "high" ? 8 : r.confidence === "medium" ? 4 : 0; s += linkState === "ok" ? 6 : linkState === "redirected" ? 3 : linkState === "unchecked" ? 2 : 0; return Math.max(0, Math.min(100, s)); }