diff --git a/backend/src/pipeline/publish.ts b/backend/src/pipeline/publish.ts index d09a7b4..27ac515 100644 --- a/backend/src/pipeline/publish.ts +++ b/backend/src/pipeline/publish.ts @@ -349,8 +349,9 @@ export async function publishDirect( /** * Publishing is always automatic — there's no draft/review state (see schema doc). - * A cluster of size 1 publishes as-is via the same path; synthesizeArticle lightly - * rewrites rather than merges when there's only one source. + * Callers should route a size-1 cluster to publishDirect instead — there's nothing to + * merge, so an LLM rewrite would only add risk (hallucinated attribution, altered + * facts) for no synthesis benefit. See priorityQueue.ts's runSynthesisCycle. */ export async function publishCluster( provider: InferenceProvider, diff --git a/backend/src/pipeline/synthesis.ts b/backend/src/pipeline/synthesis.ts index fd1898f..21bacbf 100644 --- a/backend/src/pipeline/synthesis.ts +++ b/backend/src/pipeline/synthesis.ts @@ -37,8 +37,6 @@ const SYSTEM_PROMPT = `You are a neutral news synthesis assistant. Given summari - Stays neutral and factual, without editorializing - Is 2-4 short paragraphs -If only one source is provided, lightly rewrite it in your own words rather than merging, and do not attribute it to any outlet other than that single given source. - After the article, on a new line, write exactly "${TAG_DELIMITER}" followed by 2-4 short comma-separated topic/entity tags (e.g. proper nouns, named events) that this article is about. If nothing salient qualifies, leave the tag line empty.`; export interface SynthesisResult { diff --git a/backend/src/queue/priorityQueue.ts b/backend/src/queue/priorityQueue.ts index 3894bc8..15323e3 100644 --- a/backend/src/queue/priorityQueue.ts +++ b/backend/src/queue/priorityQueue.ts @@ -186,7 +186,14 @@ export async function runSynthesisCycle(provider: InferenceProvider, settings: G // in practice a cluster's items are all near-duplicate coverage of the same // story, so they'd all match the same event's filter anyway when they match at all. const eventId = cluster.items.map((i) => claimedEventId(i, activeEvents)).find((id) => id !== null) ?? undefined; - const article = await publishCluster(provider, settings, cluster, { eventId }); + // A single-item cluster has nothing to merge — publish the source's own text + // verbatim instead of asking the LLM to "lightly rewrite" it, which only risked + // introducing errors (or fabricated attribution — see synthesis.ts) with no + // actual synthesis to justify the risk. + const article = + cluster.items.length === 1 + ? await publishDirect(cluster.items[0], settings, { eventId }) + : await publishCluster(provider, settings, cluster, { eventId }); contentItemsDb.assignCluster( cluster.items.map((i) => i.id), cluster.id