docs(blog): add Langfuse v4 launch post - #3502
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
@claude review |
There was a problem hiding this comment.
This PR is mostly a new blog post (low risk) plus a small helper in lib/markdown-component-renderers.js for converting <Cards> blocks to Markdown. The inline finding is a real, reproducible bug: the new card-conversion regex silently drops non-matching cards (e.g. single-line self-closing cards) whenever at least one card in the group matches, which deletes real content from content/marketing/careers.mdx in the generated Markdown export today. Given that's silent data loss on existing content (not just a missed case for the new post), I'd like a human to confirm the fix before merging.
Extended reasoning...
Overview
The PR adds the Langfuse v4 launch blog post (content/blog/2026-08-13-langfuse-v4.mdx) and a new replaceCardGroupsWithMarkdown/renderSelfClosingCards helper in lib/markdown-component-renderers.js intended to preserve self-closing <Card> content when converting MDX <Cards> blocks to plain Markdown for the public/md-src export (used by llms.txt/PDF generation).
Security risks
None. This is a docs-content addition plus a build-time text transform used only for a secondary Markdown export; it does not touch auth, data handling, or any runtime product code.
Level of scrutiny
The blog post itself warrants light scrutiny (content-only, no logic). The renderer change, though small, warrants closer scrutiny because it's a regex-based transform with a silent-fallback design (?? fullMatch) — exactly the kind of code where a partial match can quietly corrupt output instead of failing loudly. That's what happened here: I verified by inspecting content/marketing/careers.mdx that it contains a <Cards> group mixing multi-line self-closing cards (which the new regex matches) with single-line self-closing cards (which it doesn't), and confirmed the existing bug report's claim that the guard (cardMatches.length === 0) only fires on zero matches, not partial matches, so non-matching cards are dropped rather than falling back to raw JSX.
Other factors
This is exactly the kind of bug that should block approval per the review guidelines — it causes silent content loss on a page that already exists in production, not just an edge case in the new post. No prior reviews from me exist on this PR, so this is a fresh assessment rather than a repeat of prior feedback.
| function renderSelfClosingCards(cardsSource) { | ||
| const cardMatches = [ | ||
| ...cardsSource.matchAll(/^\s*<Card\b([\s\S]*?)^\s*\/>\s*$/gm), | ||
| ]; | ||
| if (cardMatches.length === 0) return null; | ||
|
|
||
| const lines = []; | ||
| for (const [, attributes] of cardMatches) { | ||
| const title = extractAttributeString(attributes, "title"); | ||
| const href = extractAttributeString(attributes, "href"); | ||
| const description = extractAttributeString(attributes, "description"); | ||
|
|
||
| if (!title || !href) return null; | ||
|
|
||
| lines.push(`- [${title}](${href})${description ? `: ${description}` : ""}`); | ||
| } | ||
|
|
||
| return `\n${lines.join("\n")}\n`; |
There was a problem hiding this comment.
🟡 The new renderSelfClosingCards helper (lib/markdown-component-renderers.js:54-71) uses a regex that only matches self-closing <Card ... /> tags when the closing /> sits alone on its own line, so single-line cards never match it. Worse, when a <Cards> group mixes matching and non-matching cards, the block is still rewritten because cardMatches.length > 0, so the non-matching cards are silently dropped instead of falling back to the raw JSX. This is reproducible today: running the exporter on content/marketing/careers.mdx drops the "Documentation", "Roadmap", and "Example project" links, keeping only the two multi-line cards.
Extended reasoning...
The bug: renderSelfClosingCards finds Card tags with /^\s*<Card\b([\s\S]*?)^\s*\/>\s*$/gm. The ^\s* immediately before \/> (combined with the m flag) requires the closing /> to be the first non-whitespace token on its own line. That's only true for the multi-line attribute style used by the new v4 blog post itself (each attribute on its own line, /> alone on the last line). The far more common single-line style, e.g. <Card title=\"Documentation\" href=\"/docs\" icon={<BookOpen />} />, never matches because /> is preceded by other content on the same line.\n\nWhy this causes silent data loss, not just a missed conversion: replaceCardGroupsWithMarkdown calls renderSelfClosingCards(cardsSource) ?? fullMatch per <Cards>...</Cards> block. The ?? fullMatch fallback is only reached when cardMatches.length === 0 (i.e. zero cards in the group matched). If a group mixes matching and non-matching cards — some multi-line self-closing cards plus one or more single-line/children-based cards — cardMatches.length is still greater than zero, so the function returns a real markdown list instead of null. The whole <Cards> block is then replaced by that list, and every non-matching card inside it silently vanishes from the output with no warning, error, or fallback.\n\nConcrete proof from real repo content: content/marketing/careers.mdx has exactly this mixed pattern:\njsx\n<Cards num={3}>\n <Card title=\"GitHub Repositories\" href=\"...\" icon={<Github />} /> {/* multi-line, matches */}\n <Card title=\"GitHub Discussions\" href=\"...\" icon={<Github />} /> {/* multi-line, matches */}\n <Card title=\"Documentation\" href=\"/docs\" icon={<BookOpen />} /> {/* single-line, does NOT match */}\n <Card title=\"Roadmap\" href=\"/docs/roadmap\" icon={<ListOrdered />} /> {/* single-line, does NOT match */}\n <Card title=\"Example project\" href=\"/docs/demo\" icon={<Joystick />} /> {/* single-line, does NOT match */}\n</Cards>\n\nI ran replaceComponentsWithMarkdown directly on this file's contents. The output collapses the block to:\n\n- [GitHub Repositories](https://github.com/orgs/langfuse/repositories)\n- [GitHub Discussions](https://github.com/orgs/langfuse/discussions)\n\nThe Documentation, Roadmap, and Example project links are gone entirely — not left as raw JSX, just deleted. This directly contradicts the PR's own stated goal of "preserve self-closing Card content and links in generated Markdown."\n\nA second, related manifestation (also verified by a node repro) is a <Cards> group that mixes a self-closing <Card ... /> with a children-based <Card ...>...</Card>. The non-greedy [\s\S]*? in the regex can span across an entire children-based card to reach a later self-closing card's />, or simply skip the children card altogether — either way the children card's title/href/content disappears from the output with cardMatches.length > 0 still true, so the safety fallback never fires.\n\nWhy the existing code doesn't catch this: the only guard is cardMatches.length === 0, which assumes "no matches" is the only failure mode. It doesn't check that every <Card\b> in the source was actually captured, so partial matches are treated as success.\n\nSuggested fix: only accept the conversion when every <Card\b occurrence in cardsSource was captured by the match — e.g. compare (cardsSource.match(/<Card\b/g) ?? []).length against cardMatches.length, and return null (falling back to the safe raw-JSX path) whenever they differ. Widening the regex to also accept single-line self-closing cards would fix the common case, but the count-based guard is what actually closes the silent-data-loss hole for any pattern the regex doesn't anticipate (including future children-based cards).\n\nImpact: this only affects the secondary Markdown/PDF export path (public/md-src, used by llms.txt and PDF generation), not the live rendered site — the actual blog post and site pages render correctly via JSX regardless. But it silently deletes real, existing content (careers page links) from that export today, and will keep doing so for any other page with a similar mixed <Cards> group.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
There are 2 total unresolved issues (including 1 from previous review).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit c8d3f89. Configure here.
Import the lucide icons used by the changelog CTA cards, format the upgrade FAQ, harden Cards-to-Markdown conversion against partial matches, and keep /docs/v4 on the docs overview while redirecting the blog .md URL. Co-authored-by: Max Deichmann <max@langfuse.com>
Co-authored-by: Max Deichmann <max@langfuse.com>
Co-authored-by: Max Deichmann <max@langfuse.com>
Co-authored-by: Max Deichmann <max@langfuse.com>
Co-authored-by: Max Deichmann <max@langfuse.com>
|
|
||
| Langfuse v4 is live on Langfuse Cloud and generally available for [self-hosted deployments](/self-hosting/upgrade/upgrade-guides/upgrade-v3-to-v4). It makes it faster to debug, evaluate, and monitor complex LLM applications by letting you work with every LLM call, tool execution, and agent step directly. Initial table loads over large datasets drop from seconds to milliseconds, and dashboards over longer time ranges load at least 10x faster in large projects. | ||
|
|
||
| Langfuse Cloud becomes v4-only on **November 15, 2026**. Most projects need no migration. If **Action required** lists checks for your project, complete them before this date. |
There was a problem hiding this comment.
grammar issue here, "If Action required lists checks for your project, complete them before this date."
There was a problem hiding this comment.
I do not think that Action Required is a good way to refer to this, I would call it v4 Upgrade Check or similar
|
|
||
| Langfuse Cloud becomes v4-only on **November 15, 2026**. Most projects need no migration. If **Action required** lists checks for your project, complete them before this date. | ||
|
|
||
| <Cards num={3} className="gap-3"> |
There was a problem hiding this comment.
did not realize first that these were clickable, I would suggest they have an arrow, seems like generally useful for all cards
|
|
||
| Langfuse v4 is much faster and also ships new ways to search, monitor, evaluate, and query your application data. | ||
|
|
||
| <Cards num={2} className="gap-3"> |
There was a problem hiding this comment.
can these open in a new tab?
|
|
||
| ## What we shipped in Langfuse v4 | ||
|
|
||
| Langfuse v4 is much faster and also ships new ways to search, monitor, evaluate, and query your application data. |
There was a problem hiding this comment.
| Langfuse v4 is much faster and also ships new ways to search, monitor, evaluate, and query your application data. | |
| Langfuse v4 is much faster and also ships new ways to search, monitor, evaluate, and query your application data. Check them out: |
|
|
||
| <VersionTimeline deployments={["cloud"]} /> | ||
|
|
||
| New projects already use v4 and need no migration. For existing projects, organization owners can open the **Migration sidebar** and the [**Migration status page**](https://cloud.langfuse.com/v4-migration) to see if any actions are required: |
There was a problem hiding this comment.
here it is not called "Action Required", this naming is better
There was a problem hiding this comment.
add a line break, this should be two paragraphs or an UL
|
|
||
| You can also join a live Q&A with the Langfuse engineering team. Bring migration questions or your actual setup: | ||
|
|
||
| - [Wed, Aug 19 · 9:00 AM CEST](https://lu.ma/vtp4ofae) |
There was a problem hiding this comment.
Why CEST time here? I'd show a list of timezones, PST, EST, CEST, bangalore time, singapore time
Rename Action Required to v4 Upgrade Check, add card arrows, open feature cards in a new tab, split the Cloud migration copy, and list Q&A times across PT/ET/CEST/IST/SGT. Co-authored-by: Max Deichmann <max@langfuse.com>

Summary
Why
Langfuse v4 needs a calm, performance-led launch post that helps every reader quickly understand what changed and whether they need to act. Independent reviews covered newcomers, non-technical leads, Cloud owners, self-hosted operators, skeptical migration users, SDK/API integrators, and data-platform engineers.
Validation
Note
Low Risk
Documentation, redirects, and static-site component changes only; no application runtime or auth/data paths are modified.
Overview
Langfuse v4 launch and Cloud cutover date. Adds a changelog entry for the v4 GA announcement (audience cards, feature links, migration assistant screenshot, Q&A links) and points the top site banner at it. Permanent redirects move the former blog URL to
/changelog/2026-08-13-langfuse-v4.November 15, 2026 replaces placeholder “date will follow” language in compatibility snippets,
VersionTimeline, v4 overview, blob export, public API ingestion callouts, and the upgrade FAQ. Cloud rollout copy now centers the v4 Upgrade Check / migration status page instead of generic “Action required” UI.Docs UI and md-src pipeline. Linked
Cardcomponents can show a trailing chevron whenarrowis set (used heavily on the new post). Plain-Markdown generation converts self-closingCardsgroups to bullet links (only when the whole group is supported) and rendersVersionTimelineas text lines.Removes several internal
research/*.mdnotes that are no longer needed in-repo.Reviewed by Cursor Bugbot for commit 66e77da. Bugbot is set up for automated code reviews on this repo. Configure here.
Greptile Summary
The PR adds the Langfuse v4 launch post and extends plain-Markdown generation to preserve links and descriptions from self-closing Card groups.
Confidence Score: 4/5
The PR appears safe to merge, with a non-blocking robustness issue in partial Card-group conversion.
The new post’s repository-backed dependencies resolve, while the Markdown converter should verify that it consumed the complete Cards group before replacing it.
Files Needing Attention: lib/markdown-component-renderers.js
Flowchart
%%{init: {'theme': 'neutral'}}%% flowchart LR A["Content MDX"] --> B["replaceComponentsWithMarkdown"] B --> C["Convert supported Cards to Markdown links"] C --> D["stripMdxForPlainMarkdown"] D --> E["public/md-src Markdown"] E --> F[".md routes / LLM consumers"]Prompt To Fix All With AI
Reviews (1): Last reviewed commit: "docs(blog): refine Langfuse v4 launch po..." | Re-trigger Greptile
Context used: