feat(brief): the brief contract — top-8 synthesis, verified citations, enforce-mode gate, shared grounding spine (#4921)#4928
Conversation
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Greptile SummaryThis PR completes the "brief contract" — replacing the single-headline World Brief with a structured top-8 synthesis (cited lead + one verified line per story), porting the grounding spine to a shared edge-safe module, flipping the hallucination validator to enforce-by-default, and consolidating three inconsistent "same story?" definitions into one dual-view lexical similarity engine.
Confidence Score: 4/5The L1 to L2 to degraded cascade ensures the brief always ships; LKG preservation is untouched; all grounding and citation checks degrade gracefully. The changes are architecturally sound and heavily tested (17 brief-contract + 389-line story-identity + 281-line completeness tests). The L2 fallback block indentation is a readability hazard and the parseBriefSynthesis stray-brace case is a silent failure class, but neither affects correctness or data safety. scripts/seed-insights.mjs (L2 block indentation), scripts/_insights-brief.mjs (parseBriefSynthesis JSON extraction edge case) Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[fetchInsights] --> B{topStories.length > 0?}
B -- yes --> C[callLLM synthesis]
B -- no --> G[L2 single-headline path]
C --> D{parseBriefSynthesis valid?}
D -- null --> G
D -- parsed --> E{checkLeadGrounding?}
E -- fail --> G
E -- pass --> F[verifyCitationIndexes on lead]
F --> F2[per-line verify + validate enforce degrade to headline]
F2 --> F3[synthesized = true]
G --> H{briefCluster + topHeadline?}
H -- no --> I[degraded]
H -- yes --> J[callLLM single headline]
J --> K{validateNoHallucinatedProperNouns}
K -- pass --> L[worldBrief = LLM]
K -- enforce fail --> M[worldBrief = headline]
F3 --> N[payload + LKG preservation]
L --> N
M --> N
I --> N
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
A[fetchInsights] --> B{topStories.length > 0?}
B -- yes --> C[callLLM synthesis]
B -- no --> G[L2 single-headline path]
C --> D{parseBriefSynthesis valid?}
D -- null --> G
D -- parsed --> E{checkLeadGrounding?}
E -- fail --> G
E -- pass --> F[verifyCitationIndexes on lead]
F --> F2[per-line verify + validate enforce degrade to headline]
F2 --> F3[synthesized = true]
G --> H{briefCluster + topHeadline?}
H -- no --> I[degraded]
H -- yes --> J[callLLM single headline]
J --> K{validateNoHallucinatedProperNouns}
K -- pass --> L[worldBrief = LLM]
K -- enforce fail --> M[worldBrief = headline]
F3 --> N[payload + LKG preservation]
L --> N
M --> N
I --> N
|
| if (start === -1 || end <= start) return null; | ||
| let parsed; | ||
| try { | ||
| parsed = JSON.parse(text.slice(start, end + 1)); | ||
| } catch { | ||
| return null; | ||
| } |
There was a problem hiding this comment.
lastIndexOf('}') JSON extraction fails silently when LLM appends brace-containing prose
The fence-strip + indexOf('{')/lastIndexOf('}') extraction handles the common case correctly, and the surrounding try/catch degrades to null (L2 fallback) on a parse failure. However, if the model appends trailing prose containing a } — e.g. "I formatted this as you asked {see above}." — lastIndexOf('}') picks up the stray brace rather than the object's closing delimiter. JSON.parse then fails and the synthesis silently falls back to the single-headline path with no warning, making this failure class invisible in logs. Adding a console.warn inside the catch block would surface these cases without changing the fallback behaviour.
…roboration gate restored, lead noun-enforcement, pure composer (#4928 review round) Applies the multi-agent + cross-model review findings: - citation indexes and worldBriefSources are now STRICTLY index-locked: a story without a usable link gets a substitute source entry instead of being filtered out of the array — filtering shifted every later [n] to the wrong article (correctness + adversarial + codex) - the L1 synthesis path restores the legacy editorial bar: at least one top story must be corroborated (>=2 sources / entity corroboration) or L1 rejects to the single-headline path — synthesis no longer lowers the bar on all-single-source days (adversarial + codex) - the lead is now proper-noun-enforced against all story titles, not just anchor-grounded — a lead can name one real anchor and still fabricate (adversarial + codex) - degraded/missing story lines keep their [n] so the citation contract holds for renderers (codex) - verifyCitationIndexes covers 3-digit invented markers; bracketed years ([2026]) stay prose - digest grounding cap regression fixed: the remaining call site passes MAX_STORIES_PER_USER explicitly instead of inheriting the ported default of 8 (maintainability conf-100) - L1 assembly extracted to composeSynthesizedBrief in _insights-brief.mjs — pure and functionally tested (enforce/degrade, lockstep sources, editorial gate, fabricated-lead rejection); the L2 legacy path extracted to generateLegacySingleHeadlineBrief intact (kills the un-indented if-wrapper hazard) - renderBriefExtras strips the brief-para wrapper formatIntelBrief actually emits (the <p> regex was a no-op); dead imports removed; stale core-header consumer list updated; mirrors re-synced Battery: brief-contract (26), brief-llm (107), brief-llm-core (47), edge-functions (216), seed-insights-brief (13), retry (4), both typechecks, biome. Claude-Session: https://claude.ai/code/session_01WmhkGjZrb9RbV7pV3muFxP
Multi-agent code review — applied at f0ab384Four reviewer personas (correctness, adversarial+security, testing, maintainability+standards) plus the independent Codex cross-model pass. All actionable findings applied; held for manual merge (stacked on #4927 → #4924). Applied
Gate catch worth noting: the pre-push unit gate caught that my refactor's end-anchor had swallowed the Verified during review
Battery: brief-contract (26), brief-llm (107), brief-llm-core (47), edge-functions (216), completeness (21), seed-insights suites (17), both typechecks, biome. |
…roboration gate restored, lead noun-enforcement, pure composer (#4928 review round) Applies the multi-agent + cross-model review findings: - citation indexes and worldBriefSources are now STRICTLY index-locked: a story without a usable link gets a substitute source entry instead of being filtered out of the array — filtering shifted every later [n] to the wrong article (correctness + adversarial + codex) - the L1 synthesis path restores the legacy editorial bar: at least one top story must be corroborated (>=2 sources / entity corroboration) or L1 rejects to the single-headline path — synthesis no longer lowers the bar on all-single-source days (adversarial + codex) - the lead is now proper-noun-enforced against all story titles, not just anchor-grounded — a lead can name one real anchor and still fabricate (adversarial + codex) - degraded/missing story lines keep their [n] so the citation contract holds for renderers (codex) - verifyCitationIndexes covers 3-digit invented markers; bracketed years ([2026]) stay prose - digest grounding cap regression fixed: the remaining call site passes MAX_STORIES_PER_USER explicitly instead of inheriting the ported default of 8 (maintainability conf-100) - L1 assembly extracted to composeSynthesizedBrief in _insights-brief.mjs — pure and functionally tested (enforce/degrade, lockstep sources, editorial gate, fabricated-lead rejection); the L2 legacy path extracted to generateLegacySingleHeadlineBrief intact (kills the un-indented if-wrapper hazard) - renderBriefExtras strips the brief-para wrapper formatIntelBrief actually emits (the <p> regex was a no-op); dead imports removed; stale core-header consumer list updated; mirrors re-synced Battery: brief-contract (26), brief-llm (107), brief-llm-core (47), edge-functions (216), seed-insights-brief (13), retry (4), both typechecks, biome. Claude-Session: https://claude.ai/code/session_01WmhkGjZrb9RbV7pV3muFxP
f0ab384 to
fb42ac0
Compare
… test harness (#4928) The country-intel caching tests copy get-country-intel-brief.ts into a temp dir and rewrite its relative imports via an explicit replacement map — the new ../../../../shared/brief-llm-core.js import (citation verification, #4921) needed an entry or module resolution failed under relocation. Claude-Session: https://claude.ai/code/session_01WmhkGjZrb9RbV7pV3muFxP
…roboration gate restored, lead noun-enforcement, pure composer (#4928 review round) Applies the multi-agent + cross-model review findings: - citation indexes and worldBriefSources are now STRICTLY index-locked: a story without a usable link gets a substitute source entry instead of being filtered out of the array — filtering shifted every later [n] to the wrong article (correctness + adversarial + codex) - the L1 synthesis path restores the legacy editorial bar: at least one top story must be corroborated (>=2 sources / entity corroboration) or L1 rejects to the single-headline path — synthesis no longer lowers the bar on all-single-source days (adversarial + codex) - the lead is now proper-noun-enforced against all story titles, not just anchor-grounded — a lead can name one real anchor and still fabricate (adversarial + codex) - degraded/missing story lines keep their [n] so the citation contract holds for renderers (codex) - verifyCitationIndexes covers 3-digit invented markers; bracketed years ([2026]) stay prose - digest grounding cap regression fixed: the remaining call site passes MAX_STORIES_PER_USER explicitly instead of inheriting the ported default of 8 (maintainability conf-100) - L1 assembly extracted to composeSynthesizedBrief in _insights-brief.mjs — pure and functionally tested (enforce/degrade, lockstep sources, editorial gate, fabricated-lead rejection); the L2 legacy path extracted to generateLegacySingleHeadlineBrief intact (kills the un-indented if-wrapper hazard) - renderBriefExtras strips the brief-para wrapper formatIntelBrief actually emits (the <p> regex was a no-op); dead imports removed; stale core-header consumer list updated; mirrors re-synced Battery: brief-contract (26), brief-llm (107), brief-llm-core (47), edge-functions (216), seed-insights-brief (13), retry (4), both typechecks, biome. Claude-Session: https://claude.ai/code/session_01WmhkGjZrb9RbV7pV3muFxP
… test harness (#4928) The country-intel caching tests copy get-country-intel-brief.ts into a temp dir and rewrite its relative imports via an explicit replacement map — the new ../../../../shared/brief-llm-core.js import (citation verification, #4921) needed an entry or module resolution failed under relocation. Claude-Session: https://claude.ai/code/session_01WmhkGjZrb9RbV7pV3muFxP
…#4928 - lead validation is now citation-SCOPED per sentence: every sentence must cite, and its proper nouns ground against ONLY the stories it cites — corpus-wide validation passed shape-valid misattribution (P1); uncited sentences reject to the legacy fallback - per-story lines are rewritten to carry exactly their own [n]: all bracket markers stripped, canonical citation appended — a story-2 line citing [1] previously linked the wrong source (P1) - balanced string-aware brace scan replaces lastIndexOf('}') — stray braces in trailing prose no longer kill the parse (P3) - sourceAgeRange computed from the brief's own top stories, not the whole digest pool — the freshness footer no longer overclaims (P2) - source cap raised through both panel layers to the citation index space — [7]/[8] were orphaned by the 6-source default (P2) - pre-rollout payloads omit the freshness footer instead of rendering a literal '?h old' (P3); dead imports removed from seed-insights 6 new regressions incl. misattribution, uncited-sentence, wrong-[n] rewrite, stray/inner braces. brief-contract 32/32; typechecks clean. Claude-Session: https://claude.ai/code/session_01WmhkGjZrb9RbV7pV3muFxP
10d844c to
52e70bb
Compare
External review adjudication — 8 findings: 7 validated + fixed, 1 fixed on the stack parent (52e70bb)
brief-contract suite 32/32; both typechecks clean. Still held for manual merge. |
…ndex-space cap (#4928) The pin asserted the flat 6-source call this round replaced; the guarded invariant (sources only from explicit worldBriefSources, never fabricated from topStories) is unchanged and still asserted. Claude-Session: https://claude.ai/code/session_01WmhkGjZrb9RbV7pV3muFxP
…roboration gate restored, lead noun-enforcement, pure composer (#4928 review round) Applies the multi-agent + cross-model review findings: - citation indexes and worldBriefSources are now STRICTLY index-locked: a story without a usable link gets a substitute source entry instead of being filtered out of the array — filtering shifted every later [n] to the wrong article (correctness + adversarial + codex) - the L1 synthesis path restores the legacy editorial bar: at least one top story must be corroborated (>=2 sources / entity corroboration) or L1 rejects to the single-headline path — synthesis no longer lowers the bar on all-single-source days (adversarial + codex) - the lead is now proper-noun-enforced against all story titles, not just anchor-grounded — a lead can name one real anchor and still fabricate (adversarial + codex) - degraded/missing story lines keep their [n] so the citation contract holds for renderers (codex) - verifyCitationIndexes covers 3-digit invented markers; bracketed years ([2026]) stay prose - digest grounding cap regression fixed: the remaining call site passes MAX_STORIES_PER_USER explicitly instead of inheriting the ported default of 8 (maintainability conf-100) - L1 assembly extracted to composeSynthesizedBrief in _insights-brief.mjs — pure and functionally tested (enforce/degrade, lockstep sources, editorial gate, fabricated-lead rejection); the L2 legacy path extracted to generateLegacySingleHeadlineBrief intact (kills the un-indented if-wrapper hazard) - renderBriefExtras strips the brief-para wrapper formatIntelBrief actually emits (the <p> regex was a no-op); dead imports removed; stale core-header consumer list updated; mirrors re-synced Battery: brief-contract (26), brief-llm (107), brief-llm-core (47), edge-functions (216), seed-insights-brief (13), retry (4), both typechecks, biome. Claude-Session: https://claude.ai/code/session_01WmhkGjZrb9RbV7pV3muFxP
… test harness (#4928) The country-intel caching tests copy get-country-intel-brief.ts into a temp dir and rewrite its relative imports via an explicit replacement map — the new ../../../../shared/brief-llm-core.js import (citation verification, #4921) needed an entry or module resolution failed under relocation. Claude-Session: https://claude.ai/code/session_01WmhkGjZrb9RbV7pV3muFxP
…#4928 - lead validation is now citation-SCOPED per sentence: every sentence must cite, and its proper nouns ground against ONLY the stories it cites — corpus-wide validation passed shape-valid misattribution (P1); uncited sentences reject to the legacy fallback - per-story lines are rewritten to carry exactly their own [n]: all bracket markers stripped, canonical citation appended — a story-2 line citing [1] previously linked the wrong source (P1) - balanced string-aware brace scan replaces lastIndexOf('}') — stray braces in trailing prose no longer kill the parse (P3) - sourceAgeRange computed from the brief's own top stories, not the whole digest pool — the freshness footer no longer overclaims (P2) - source cap raised through both panel layers to the citation index space — [7]/[8] were orphaned by the 6-source default (P2) - pre-rollout payloads omit the freshness footer instead of rendering a literal '?h old' (P3); dead imports removed from seed-insights 6 new regressions incl. misattribution, uncited-sentence, wrong-[n] rewrite, stray/inner braces. brief-contract 32/32; typechecks clean. Claude-Session: https://claude.ai/code/session_01WmhkGjZrb9RbV7pV3muFxP
a2f5952 to
978267c
Compare
…ndex-space cap (#4928) The pin asserted the flat 6-source call this round replaced; the guarded invariant (sources only from explicit worldBriefSources, never fabricated from topStories) is unchanged and still asserted. Claude-Session: https://claude.ai/code/session_01WmhkGjZrb9RbV7pV3muFxP
…#4928 - lead validation is now citation-SCOPED per sentence: every sentence must cite, and its proper nouns ground against ONLY the stories it cites — corpus-wide validation passed shape-valid misattribution (P1); uncited sentences reject to the legacy fallback - per-story lines are rewritten to carry exactly their own [n]: all bracket markers stripped, canonical citation appended — a story-2 line citing [1] previously linked the wrong source (P1) - balanced string-aware brace scan replaces lastIndexOf('}') — stray braces in trailing prose no longer kill the parse (P3) - sourceAgeRange computed from the brief's own top stories, not the whole digest pool — the freshness footer no longer overclaims (P2) - source cap raised through both panel layers to the citation index space — [7]/[8] were orphaned by the 6-source default (P2) - pre-rollout payloads omit the freshness footer instead of rendering a literal '?h old' (P3); dead imports removed from seed-insights 6 new regressions incl. misattribution, uncited-sentence, wrong-[n] rewrite, stray/inner braces. brief-contract 32/32; typechecks clean. Claude-Session: https://claude.ai/code/session_01WmhkGjZrb9RbV7pV3muFxP
978267c to
3a62b07
Compare
…ndex-space cap (#4928) The pin asserted the flat 6-source call this round replaced; the guarded invariant (sources only from explicit worldBriefSources, never fabricated from topStories) is unchanged and still asserted. Claude-Session: https://claude.ai/code/session_01WmhkGjZrb9RbV7pV3muFxP
…roboration gate restored, lead noun-enforcement, pure composer (#4928 review round) Applies the multi-agent + cross-model review findings: - citation indexes and worldBriefSources are now STRICTLY index-locked: a story without a usable link gets a substitute source entry instead of being filtered out of the array — filtering shifted every later [n] to the wrong article (correctness + adversarial + codex) - the L1 synthesis path restores the legacy editorial bar: at least one top story must be corroborated (>=2 sources / entity corroboration) or L1 rejects to the single-headline path — synthesis no longer lowers the bar on all-single-source days (adversarial + codex) - the lead is now proper-noun-enforced against all story titles, not just anchor-grounded — a lead can name one real anchor and still fabricate (adversarial + codex) - degraded/missing story lines keep their [n] so the citation contract holds for renderers (codex) - verifyCitationIndexes covers 3-digit invented markers; bracketed years ([2026]) stay prose - digest grounding cap regression fixed: the remaining call site passes MAX_STORIES_PER_USER explicitly instead of inheriting the ported default of 8 (maintainability conf-100) - L1 assembly extracted to composeSynthesizedBrief in _insights-brief.mjs — pure and functionally tested (enforce/degrade, lockstep sources, editorial gate, fabricated-lead rejection); the L2 legacy path extracted to generateLegacySingleHeadlineBrief intact (kills the un-indented if-wrapper hazard) - renderBriefExtras strips the brief-para wrapper formatIntelBrief actually emits (the <p> regex was a no-op); dead imports removed; stale core-header consumer list updated; mirrors re-synced Battery: brief-contract (26), brief-llm (107), brief-llm-core (47), edge-functions (216), seed-insights-brief (13), retry (4), both typechecks, biome. Claude-Session: https://claude.ai/code/session_01WmhkGjZrb9RbV7pV3muFxP
… test harness (#4928) The country-intel caching tests copy get-country-intel-brief.ts into a temp dir and rewrite its relative imports via an explicit replacement map — the new ../../../../shared/brief-llm-core.js import (citation verification, #4921) needed an entry or module resolution failed under relocation. Claude-Session: https://claude.ai/code/session_01WmhkGjZrb9RbV7pV3muFxP
3a62b07 to
90c0916
Compare
…#4928 - lead validation is now citation-SCOPED per sentence: every sentence must cite, and its proper nouns ground against ONLY the stories it cites — corpus-wide validation passed shape-valid misattribution (P1); uncited sentences reject to the legacy fallback - per-story lines are rewritten to carry exactly their own [n]: all bracket markers stripped, canonical citation appended — a story-2 line citing [1] previously linked the wrong source (P1) - balanced string-aware brace scan replaces lastIndexOf('}') — stray braces in trailing prose no longer kill the parse (P3) - sourceAgeRange computed from the brief's own top stories, not the whole digest pool — the freshness footer no longer overclaims (P2) - source cap raised through both panel layers to the citation index space — [7]/[8] were orphaned by the 6-source default (P2) - pre-rollout payloads omit the freshness footer instead of rendering a literal '?h old' (P3); dead imports removed from seed-insights 6 new regressions incl. misattribution, uncited-sentence, wrong-[n] rewrite, stray/inner braces. brief-contract 32/32; typechecks clean. Claude-Session: https://claude.ai/code/session_01WmhkGjZrb9RbV7pV3muFxP
…ndex-space cap (#4928) The pin asserted the flat 6-source call this round replaced; the guarded invariant (sources only from explicit worldBriefSources, never fabricated from topStories) is unchanged and still asserted. Claude-Session: https://claude.ai/code/session_01WmhkGjZrb9RbV7pV3muFxP
…roboration gate restored, lead noun-enforcement, pure composer (#4928 review round) Applies the multi-agent + cross-model review findings: - citation indexes and worldBriefSources are now STRICTLY index-locked: a story without a usable link gets a substitute source entry instead of being filtered out of the array — filtering shifted every later [n] to the wrong article (correctness + adversarial + codex) - the L1 synthesis path restores the legacy editorial bar: at least one top story must be corroborated (>=2 sources / entity corroboration) or L1 rejects to the single-headline path — synthesis no longer lowers the bar on all-single-source days (adversarial + codex) - the lead is now proper-noun-enforced against all story titles, not just anchor-grounded — a lead can name one real anchor and still fabricate (adversarial + codex) - degraded/missing story lines keep their [n] so the citation contract holds for renderers (codex) - verifyCitationIndexes covers 3-digit invented markers; bracketed years ([2026]) stay prose - digest grounding cap regression fixed: the remaining call site passes MAX_STORIES_PER_USER explicitly instead of inheriting the ported default of 8 (maintainability conf-100) - L1 assembly extracted to composeSynthesizedBrief in _insights-brief.mjs — pure and functionally tested (enforce/degrade, lockstep sources, editorial gate, fabricated-lead rejection); the L2 legacy path extracted to generateLegacySingleHeadlineBrief intact (kills the un-indented if-wrapper hazard) - renderBriefExtras strips the brief-para wrapper formatIntelBrief actually emits (the <p> regex was a no-op); dead imports removed; stale core-header consumer list updated; mirrors re-synced Battery: brief-contract (26), brief-llm (107), brief-llm-core (47), edge-functions (216), seed-insights-brief (13), retry (4), both typechecks, biome. Claude-Session: https://claude.ai/code/session_01WmhkGjZrb9RbV7pV3muFxP
… test harness (#4928) The country-intel caching tests copy get-country-intel-brief.ts into a temp dir and rewrite its relative imports via an explicit replacement map — the new ../../../../shared/brief-llm-core.js import (citation verification, #4921) needed an entry or module resolution failed under relocation. Claude-Session: https://claude.ai/code/session_01WmhkGjZrb9RbV7pV3muFxP
90c0916 to
42ae7e8
Compare
…#4928 - lead validation is now citation-SCOPED per sentence: every sentence must cite, and its proper nouns ground against ONLY the stories it cites — corpus-wide validation passed shape-valid misattribution (P1); uncited sentences reject to the legacy fallback - per-story lines are rewritten to carry exactly their own [n]: all bracket markers stripped, canonical citation appended — a story-2 line citing [1] previously linked the wrong source (P1) - balanced string-aware brace scan replaces lastIndexOf('}') — stray braces in trailing prose no longer kill the parse (P3) - sourceAgeRange computed from the brief's own top stories, not the whole digest pool — the freshness footer no longer overclaims (P2) - source cap raised through both panel layers to the citation index space — [7]/[8] were orphaned by the 6-source default (P2) - pre-rollout payloads omit the freshness footer instead of rendering a literal '?h old' (P3); dead imports removed from seed-insights 6 new regressions incl. misattribution, uncited-sentence, wrong-[n] rewrite, stray/inner braces. brief-contract 32/32; typechecks clean. Claude-Session: https://claude.ai/code/session_01WmhkGjZrb9RbV7pV3muFxP
…ndex-space cap (#4928) The pin asserted the flat 6-source call this round replaced; the guarded invariant (sources only from explicit worldBriefSources, never fabricated from topStories) is unchanged and still asserted. Claude-Session: https://claude.ai/code/session_01WmhkGjZrb9RbV7pV3muFxP
…, enforce-mode hallucination gate, shared grounding spine, staleness footer (#4921) The World Brief previously narrated ONE headline of the top 8; the only mechanical hallucination guard ran in shadow mode on one product; brief citations were model-generated and never verified; and no brief signaled input staleness. This lands the contract: 1. Every top story gets prose. The World Brief is now a genuine top-8 synthesis in one structured call: a cited lead + one line per story ([n] citations mapped to the story list), tolerant fence-stripping JSON parser, and a fallback chain — L1 synthesis (parse + grounding + citation gates) → L2 legacy single-headline brief → degraded. The brief always ships. 2. Citations verified mechanically. verifyCitationIndexes (new in the shared spine) strips out-of-range [n] markers before shipping — in the World Brief lead and lines AND the country-intel brief (which previously shipped the LLM text with zero citation checks). 3. Enforce is the default. BRIEF_VALIDATOR_MODE now defaults to enforce (shadow available by env for incidents): a synthesis line that invents proper nouns degrades to its story's headline; the legacy path keeps its headline-fallback behavior. 4. One grounding implementation. extractAnchorTokens / groundingTokenSet / checkLeadGrounding / leadGroundsAgainstStory moved from scripts/lib/brief-llm.mjs into shared/brief-llm-core.js (edge-safe, parameterized story cap — no env reads), re-exported for existing consumers; SAME-function-object test pins against drift. Country-intel gains measure-only grounding telemetry. 5. Staleness footer. The insights payload carries sourceAgeRange (newest/oldest source item); the panel renders "Generated Nm ago · newest source Hh old" plus the cited per-story lines (i18n fanned out to all locales). Closes #4921 🤖 Generated with Claude Code Claude-Session: https://claude.ai/code/session_01WmhkGjZrb9RbV7pV3muFxP
…roboration gate restored, lead noun-enforcement, pure composer (#4928 review round) Applies the multi-agent + cross-model review findings: - citation indexes and worldBriefSources are now STRICTLY index-locked: a story without a usable link gets a substitute source entry instead of being filtered out of the array — filtering shifted every later [n] to the wrong article (correctness + adversarial + codex) - the L1 synthesis path restores the legacy editorial bar: at least one top story must be corroborated (>=2 sources / entity corroboration) or L1 rejects to the single-headline path — synthesis no longer lowers the bar on all-single-source days (adversarial + codex) - the lead is now proper-noun-enforced against all story titles, not just anchor-grounded — a lead can name one real anchor and still fabricate (adversarial + codex) - degraded/missing story lines keep their [n] so the citation contract holds for renderers (codex) - verifyCitationIndexes covers 3-digit invented markers; bracketed years ([2026]) stay prose - digest grounding cap regression fixed: the remaining call site passes MAX_STORIES_PER_USER explicitly instead of inheriting the ported default of 8 (maintainability conf-100) - L1 assembly extracted to composeSynthesizedBrief in _insights-brief.mjs — pure and functionally tested (enforce/degrade, lockstep sources, editorial gate, fabricated-lead rejection); the L2 legacy path extracted to generateLegacySingleHeadlineBrief intact (kills the un-indented if-wrapper hazard) - renderBriefExtras strips the brief-para wrapper formatIntelBrief actually emits (the <p> regex was a no-op); dead imports removed; stale core-header consumer list updated; mirrors re-synced Battery: brief-contract (26), brief-llm (107), brief-llm-core (47), edge-functions (216), seed-insights-brief (13), retry (4), both typechecks, biome. Claude-Session: https://claude.ai/code/session_01WmhkGjZrb9RbV7pV3muFxP
… test harness (#4928) The country-intel caching tests copy get-country-intel-brief.ts into a temp dir and rewrite its relative imports via an explicit replacement map — the new ../../../../shared/brief-llm-core.js import (citation verification, #4921) needed an entry or module resolution failed under relocation. Claude-Session: https://claude.ai/code/session_01WmhkGjZrb9RbV7pV3muFxP
…#4928 - lead validation is now citation-SCOPED per sentence: every sentence must cite, and its proper nouns ground against ONLY the stories it cites — corpus-wide validation passed shape-valid misattribution (P1); uncited sentences reject to the legacy fallback - per-story lines are rewritten to carry exactly their own [n]: all bracket markers stripped, canonical citation appended — a story-2 line citing [1] previously linked the wrong source (P1) - balanced string-aware brace scan replaces lastIndexOf('}') — stray braces in trailing prose no longer kill the parse (P3) - sourceAgeRange computed from the brief's own top stories, not the whole digest pool — the freshness footer no longer overclaims (P2) - source cap raised through both panel layers to the citation index space — [7]/[8] were orphaned by the 6-source default (P2) - pre-rollout payloads omit the freshness footer instead of rendering a literal '?h old' (P3); dead imports removed from seed-insights 6 new regressions incl. misattribution, uncited-sentence, wrong-[n] rewrite, stray/inner braces. brief-contract 32/32; typechecks clean. Claude-Session: https://claude.ai/code/session_01WmhkGjZrb9RbV7pV3muFxP
…ndex-space cap (#4928) The pin asserted the flat 6-source call this round replaced; the guarded invariant (sources only from explicit worldBriefSources, never fabricated from topStories) is unchanged and still asserted. Claude-Session: https://claude.ai/code/session_01WmhkGjZrb9RbV7pV3muFxP
42ae7e8 to
71fccd5
Compare
Closes #4921 (Bet 3 of the strategic news-pipeline review). Do not auto-merge — held for review.⚠️ Stacked on #4927 → #4924 — merge those first; this PR's diff then shrinks to its own commits.
The contract, delivered
pickBriefCluster)[n]model-generated, rendered blindverifyCitationIndexesstrips out-of-range markers — World Brief lead + lines AND the country-intel brief (which had zero citation checks)shadowescape hatch); per-line enforcement degrades an inventing line to its story's headlinecheckLeadGroundinglived only in the Pro digest's libshared/brief-llm-core.js(edge-safe, parameterized cap, no env reads), re-exported for backcompat; same-function-object test pins against drift; country-intel gains measure-only grounding telemetrysourceAgeRange+ "Generated Nm ago · newest source Hh old" + cited story lines rendered under the lead ([n] → source links viaformatIntelBrief)Safety rails
callLLMgained prompt/maxTokens overrides backward-compatibly (retry tests unchanged).{{token}}placeholders.Verification
tests/brief-contract.test.mjs(17): prompt invariants, parser (fences/prose-wrap/dup+range indexes/thin-output rejection), citation verifier, grounding-port behavior incl. fabricated-lead rejection, same-function re-export, mirror byte-parity, wiring.https://claude.ai/code/session_01WmhkGjZrb9RbV7pV3muFxP