marketing: refresh compare pages and add compare update workflow#2699
Conversation
📝 WalkthroughWalkthroughThis PR refreshes Superset's marketing comparison pages by introducing an agent workflow command, adding three new comparison/tutorial pages, updating ten existing comparison pages with repositioned product descriptions, removing legacy planning documentation, and enhancing supporting code utilities for dynamic page type labeling, FAQ extraction, and JSON-LD metadata generation. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (8)
apps/marketing/src/components/JsonLd/JsonLd.tsx (1)
140-142: Normalize keyword values before adding them to JSON-LD.
keywordsis passed through as-is. Trimming and dropping empty entries avoids low-quality schema payloads from frontmatter drift.Proposed refactor
export function ComparisonJsonLd({ title, description, publishedTime, modifiedTime, url, image, keywords, articleSection, }: ComparisonJsonLdProps) { + const normalizedKeywords = Array.from( + new Set((keywords ?? []).map((k) => k.trim()).filter((k) => k.length > 0)), + ); + const schema = { "@context": "https://schema.org", "@type": "Article", headline: title, description, ...(articleSection && { articleSection }), - ...(keywords && keywords.length > 0 && { keywords }), + ...(normalizedKeywords.length > 0 && { keywords: normalizedKeywords }),🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/marketing/src/components/JsonLd/JsonLd.tsx` around lines 140 - 142, Normalize the keywords array before injecting into the JSON-LD object: transform the existing keywords variable by mapping each entry to its trimmed string and filtering out empty/falsy results (e.g., keywords = keywords.map(k => k.trim()).filter(Boolean)), then only spread into the payload using the existing conditional spread (the place that currently does ...(keywords && keywords.length > 0 && { keywords })); update the code around the JsonLd.tsx component where keywords is used so the JSON-LD contains only non-empty, trimmed keyword values.apps/marketing/src/app/compare/page.tsx (1)
33-35: Optional: drive section rendering from a small config array.Now that there are 3 page types, a config-based render loop will make future type additions less repetitive.
Refactor sketch
const oneVsOne = pages.filter((p) => p.type === "1v1"); const roundups = pages.filter((p) => p.type === "roundup"); const tutorials = pages.filter((p) => p.type === "tutorial"); + const sections = [ + { title: "Roundups", pages: roundups }, + { title: "Workflow Tutorials", pages: tutorials }, + { title: "Head-to-Head Comparisons", pages: oneVsOne }, + ].filter((section) => section.pages.length > 0); ... - {roundups.length > 0 && ( - <CompareSection title="Roundups" pages={roundups} /> - )} - - {tutorials.length > 0 && ( - <CompareSection title="Workflow Tutorials" pages={tutorials} /> - )} - - {oneVsOne.length > 0 && ( - <CompareSection title="Head-to-Head Comparisons" pages={oneVsOne} /> - )} + {sections.map((section) => ( + <CompareSection + key={section.title} + title={section.title} + pages={section.pages} + /> + ))}Also applies to: 73-83
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/marketing/src/app/compare/page.tsx` around lines 33 - 35, The code repeats pages.filter calls and section JSX for each type (oneVsOne, roundups, tutorials); replace these with a small config array like [{ key: "1v1", title: "1v1", id: "one-v-one" }, ...] and map over it to compute filteredPages = pages.filter(p => p.type === cfg.key) and render each section (use the same rendering logic currently in the page sections around the existing oneVsOne/roundups/tutorials blocks). Update any local identifiers (oneVsOne, roundups, tutorials) to derive from the loop (e.g., filteredPages) and reuse the existing render component/markup to avoid duplication..agents/commands/refresh-compare-pages.md (1)
97-103: Consider requiring source URLs (not just “sources used”) in the command output.This makes refresh PRs auditable and easier to re-verify later.
As per coding guidelines, "Keep command definitions in `.agents/commands/` only. `.claude/commands` and `.cursor/commands` should be symlinks to `../.agents/commands`".Suggested wording tweak
Return a concise summary with: - pages added - pages updated -- sources used +- sources used (include direct URLs and access dates) - validation results - PR URL, or the proposed PR title/body if a PR was not opened🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.agents/commands/refresh-compare-pages.md around lines 97 - 103, Update the refresh-compare-pages command output to require explicit source URLs rather than just "sources used": when producing the summary fields (pages added, pages updated, sources used, validation results, PR URL/proposed PR title/body) include a "sources used" list that contains both source names and their canonical URLs; locate the command definition for "refresh-compare-pages" in .agents/commands/refresh-compare-pages.md and modify the output spec accordingly. Also ensure command placement follows the guideline: keep the authoritative command definition in .agents/commands and replace any definitions in .claude/commands or .cursor/commands with symlinks pointing to ../.agents/commands so those directories only contain symlinks to the single source of truth.apps/marketing/content/compare/superset-vs-conductor.mdx (1)
74-87: Minor formatting inconsistency in list sections.There's an empty line after "Choose Superset if you:" (line 75) but not after "Choose Conductor if you:" (line 81). This creates slight visual inconsistency in the source file, though it may render the same.
📝 Suggested fix for consistency
**Choose Superset if you:** - - Use multiple CLI agents or want the freedom to adopt new ones instantlyOr alternatively, add an empty line after line 81 as well for symmetry.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/marketing/content/compare/superset-vs-conductor.mdx` around lines 74 - 87, The two section headers "Choose Superset if you:" and "Choose Conductor if you:" are inconsistently separated from their bullet lists; make them symmetrical by adding a single blank line after the "Choose Conductor if you:" header (the block starting with that header and the bullets underneath) so both headers have one empty line before their lists, leaving the "Verdict:" block unchanged.apps/marketing/content/compare/best-ai-coding-agents-2026.mdx (2)
142-144: Minor grammar: compound adjectives need hyphens.When compound adjectives precede a noun, they should be hyphenated for clarity.
📝 Suggested fix
-**Best for:** Smaller scoped changes, pair-programming style loops, lightweight terminal usage +**Best for:** Smaller-scoped changes, pair-programming-style loops, lightweight terminal usage🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/marketing/content/compare/best-ai-coding-agents-2026.mdx` around lines 142 - 144, Update the "**Best for:**" line to hyphenate compound adjectives: change "Smaller scoped changes" to "Smaller-scoped changes" and "pair-programming style loops" to "pair-programming-style loops" (leave "lightweight terminal usage" unchanged). Edit the string containing "**Best for:** Smaller scoped changes, pair-programming style loops, lightweight terminal usage" accordingly.
88-90: Missing horizontal rule separator before section.Other major sections (e.g., lines 40, 72, 184) are preceded by
---separators, but "Detailed Breakdown" is missing one after the comparison table.📝 Suggested fix
| **Conductor** | Orchestrator | Multiple agents, Git worktrees | macOS app | Local Mac app workflow | Public pricing not emphasized on current site | + --- ## Detailed Breakdown🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/marketing/content/compare/best-ai-coding-agents-2026.mdx` around lines 88 - 90, Add the missing horizontal rule separator '---' immediately before the "## Detailed Breakdown" heading to match the pattern used for other major sections; locate the "## Detailed Breakdown" heading in the document and insert a line with three dashes directly above it (same style used at other section breaks) so the comparison table and this section are separated consistently.apps/marketing/content/compare/superset-vs-claude-code.mdx (1)
44-44: Minor grammar: "full time" should be hyphenated.When used as a compound modifier, "full-time" should be hyphenated.
📝 Suggested fix
-Claude Code is Anthropic's official coding agent. The CLI is still the core experience, but Anthropic now also documents a Desktop app for macOS and Windows if you want Claude Code without living in the terminal full time. Underneath, it is still the same product: you describe what you want, Claude reads your codebase, writes code, runs commands, and iterates through the task using Anthropic models. +Claude Code is Anthropic's official coding agent. The CLI is still the core experience, but Anthropic now also documents a Desktop app for macOS and Windows if you want Claude Code without living in the terminal full-time. Underneath, it is still the same product: you describe what you want, Claude reads your codebase, writes code, runs commands, and iterates through the task using Anthropic models.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/marketing/content/compare/superset-vs-claude-code.mdx` at line 44, The phrase "full time" in the sentence describing Claude Code should be hyphenated as "full-time" when used as a compound modifier; update the text in the paragraph that begins "Claude Code is Anthropic's official coding agent..." (the sentence mentioning "Claude Code without living in the terminal full time") to use "full-time" instead of "full time".apps/marketing/src/lib/compare-utils.ts (1)
44-51: Minor:#stripping may affect legitimate content.The regex
/[*_~>#]/gon line 48 removes all#characters, which could affect answers containing literal#symbols (e.g., "C#", "issue#123", or hex colors like "#fff"). For FAQ content this is likely rare, but consider targeting only leading#for headings:🔧 Optional fix to preserve non-heading `#` characters
function stripMarkdownFormatting(value: string): string { return value .replace(/\[([^\]]+)\]\([^)]+\)/g, "$1") .replace(/`([^`]+)`/g, "$1") - .replace(/[*_~>#]/g, "") + .replace(/^#+\s*/gm, "") + .replace(/[*_~>]/g, "") .replace(/\s+/g, " ") .trim(); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/marketing/src/lib/compare-utils.ts` around lines 44 - 51, stripMarkdownFormatting currently strips all '#' characters via .replace(/[*_~>#]/g, ...) which removes legitimate '#' like "C#" or "#fff"; update the implementation to only remove leading heading hashes instead: replace the global removal that includes '#' with two targeted replacements — one that strips markdown heading markers at line starts (e.g., remove leading 1–6 '#' plus following space using a multiline regex) and a second that still removes other markdown punctuation like '*' '_' '~' and '>' without touching '#' elsewhere; keep the existing link and code-backtick replacements and the whitespace/trim logic intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In @.agents/commands/refresh-compare-pages.md:
- Around line 97-103: Update the refresh-compare-pages command output to require
explicit source URLs rather than just "sources used": when producing the summary
fields (pages added, pages updated, sources used, validation results, PR
URL/proposed PR title/body) include a "sources used" list that contains both
source names and their canonical URLs; locate the command definition for
"refresh-compare-pages" in .agents/commands/refresh-compare-pages.md and modify
the output spec accordingly. Also ensure command placement follows the
guideline: keep the authoritative command definition in .agents/commands and
replace any definitions in .claude/commands or .cursor/commands with symlinks
pointing to ../.agents/commands so those directories only contain symlinks to
the single source of truth.
In `@apps/marketing/content/compare/best-ai-coding-agents-2026.mdx`:
- Around line 142-144: Update the "**Best for:**" line to hyphenate compound
adjectives: change "Smaller scoped changes" to "Smaller-scoped changes" and
"pair-programming style loops" to "pair-programming-style loops" (leave
"lightweight terminal usage" unchanged). Edit the string containing "**Best
for:** Smaller scoped changes, pair-programming style loops, lightweight
terminal usage" accordingly.
- Around line 88-90: Add the missing horizontal rule separator '---' immediately
before the "## Detailed Breakdown" heading to match the pattern used for other
major sections; locate the "## Detailed Breakdown" heading in the document and
insert a line with three dashes directly above it (same style used at other
section breaks) so the comparison table and this section are separated
consistently.
In `@apps/marketing/content/compare/superset-vs-claude-code.mdx`:
- Line 44: The phrase "full time" in the sentence describing Claude Code should
be hyphenated as "full-time" when used as a compound modifier; update the text
in the paragraph that begins "Claude Code is Anthropic's official coding
agent..." (the sentence mentioning "Claude Code without living in the terminal
full time") to use "full-time" instead of "full time".
In `@apps/marketing/content/compare/superset-vs-conductor.mdx`:
- Around line 74-87: The two section headers "Choose Superset if you:" and
"Choose Conductor if you:" are inconsistently separated from their bullet lists;
make them symmetrical by adding a single blank line after the "Choose Conductor
if you:" header (the block starting with that header and the bullets underneath)
so both headers have one empty line before their lists, leaving the "Verdict:"
block unchanged.
In `@apps/marketing/src/app/compare/page.tsx`:
- Around line 33-35: The code repeats pages.filter calls and section JSX for
each type (oneVsOne, roundups, tutorials); replace these with a small config
array like [{ key: "1v1", title: "1v1", id: "one-v-one" }, ...] and map over it
to compute filteredPages = pages.filter(p => p.type === cfg.key) and render each
section (use the same rendering logic currently in the page sections around the
existing oneVsOne/roundups/tutorials blocks). Update any local identifiers
(oneVsOne, roundups, tutorials) to derive from the loop (e.g., filteredPages)
and reuse the existing render component/markup to avoid duplication.
In `@apps/marketing/src/components/JsonLd/JsonLd.tsx`:
- Around line 140-142: Normalize the keywords array before injecting into the
JSON-LD object: transform the existing keywords variable by mapping each entry
to its trimmed string and filtering out empty/falsy results (e.g., keywords =
keywords.map(k => k.trim()).filter(Boolean)), then only spread into the payload
using the existing conditional spread (the place that currently does
...(keywords && keywords.length > 0 && { keywords })); update the code around
the JsonLd.tsx component where keywords is used so the JSON-LD contains only
non-empty, trimmed keyword values.
In `@apps/marketing/src/lib/compare-utils.ts`:
- Around line 44-51: stripMarkdownFormatting currently strips all '#' characters
via .replace(/[*_~>#]/g, ...) which removes legitimate '#' like "C#" or "#fff";
update the implementation to only remove leading heading hashes instead: replace
the global removal that includes '#' with two targeted replacements — one that
strips markdown heading markers at line starts (e.g., remove leading 1–6 '#'
plus following space using a multiline regex) and a second that still removes
other markdown punctuation like '*' '_' '~' and '>' without touching '#'
elsewhere; keep the existing link and code-backtick replacements and the
whitespace/trim logic intact.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 6920aeff-7df1-4dc7-92cf-96fbf45b68cd
📒 Files selected for processing (20)
.agents/commands/refresh-compare-pages.mdapps/marketing/content/compare/best-ai-coding-agents-2026.mdxapps/marketing/content/compare/best-terminal-for-ai-coding.mdxapps/marketing/content/compare/multiple-claude-code-agents-parallel.mdxapps/marketing/content/compare/superset-vs-claude-code.mdxapps/marketing/content/compare/superset-vs-codex.mdxapps/marketing/content/compare/superset-vs-conductor.mdxapps/marketing/content/compare/superset-vs-cursor.mdxapps/marketing/content/compare/superset-vs-devin.mdxapps/marketing/content/compare/superset-vs-github-copilot.mdxapps/marketing/content/compare/superset-vs-opencode.mdxapps/marketing/content/compare/superset-vs-t3-chat.mdxapps/marketing/content/compare/superset-vs-warp.mdxapps/marketing/content/compare/superset-vs-windsurf.mdxapps/marketing/docs/SEO_COMPARISON_PAGES_PLAN.mdapps/marketing/src/app/compare/[slug]/components/CompareLayout/CompareLayout.tsxapps/marketing/src/app/compare/[slug]/page.tsxapps/marketing/src/app/compare/page.tsxapps/marketing/src/components/JsonLd/JsonLd.tsxapps/marketing/src/lib/compare-utils.ts
💤 Files with no reviewable changes (1)
- apps/marketing/docs/SEO_COMPARISON_PAGES_PLAN.md
Summary
best-terminal-for-ai-coding,multiple-claude-code-agents-parallel, andsuperset-vs-t3-chat, plus the compare-page route/index metadata needed to surface roundups and tutorials correctly.agents/commands/refresh-compare-pages.mdworkflow for fresh-research compare updatesWhy / Context
The compare pages were starting to undersell the current Superset product by describing it too narrowly as only a terminal/orchestrator.
This PR shifts the compare content to the actual shipped product story and adds a repeatable update workflow for future refreshes.
How It Works
The compare route now understands page type distinctions beyond plain 1v1 comparisons, so roundups/tutorials can be listed and labeled correctly. The slug page also emits richer structured metadata (FAQ and breadcrumb JSON-LD plus page keywords from MDX content).
On the content side, the refreshed compare pages consistently position Superset as a local-first agent workspace with worktrees, chat, diff/file review, browser preview, and MCP tooling, while keeping cloud language cautious and limited to publicly supportable claims. The new refresh command codifies the expected workflow: use fresh official sources, update stale pages in the same run, and make the PR explicit about what was added vs refreshed.
Manual QA Checklist
Testing
bun run --cwd apps/marketing typecheckbun -e 'import { getComparisonPages } from "./src/lib/compare"; const pages = await getComparisonPages(); console.log(JSON.stringify({count: pages.length, slugs: pages.map((p)=>p.slug).sort()}, null, 2));'(fromapps/marketing)Known Limitations
bun run --cwd apps/marketing buildafter the final cleanup. Earlier in this workstream, the marketing build compiled app code but failed during unrelated page-data collection because the repo environment is missing the Neon connection string needed by non-compare routes like/_not-foundand/terms.