Commit5acce80unknown_key
Audit and fix OG/meta descriptions across all pages — speed framing
Audit and fix OG/meta descriptions across all pages — speed framing - landing-2030.tsx: new title "Gluecron — The AI-native git host", description "The AI-native git host. Spec to PR in 90 seconds. Auto-merge the instant gates pass. Ship faster than any team on GitHub." Strips "wake up to a merged PR" / "while you sleep" from lede and feature copy. - pricing.tsx: adds description + og:description/og:title/twitter:card - vs-github.tsx: adds description + og:description/og:title/twitter:card - demo.tsx: adds description + og:description/og:title/twitter:card - explore.tsx: adds description + og:description/og:title/twitter:card (both the main and skeleton renders) - help.tsx: adds description + og:description/og:title/twitter:card; strips "overnight" from spec-to-PR copy - web.tsx: adds dynamic description/og:description/og:title for both the empty-repo and populated-repo home page renders, using the repo name and DB description field - landing-hero.test.ts: aligns test assertions with Landing2030Page (the active landing) — removes stale LandingHero-specific checks for "Toggle Sleep Mode" / "wake up" copy, updates title and description regexes to new brand copy https://claude.ai/code/session_01DzJMTFASjMHt2f5ze4cNLR
8 files changed+100−555acce800195bdc489f57c4e4e92ea055ec2b5933
8 changed files+100−55
Modifiedsrc/__tests__/landing-hero.test.ts+20−39View fileUnifiedSplit
@@ -25,56 +25,38 @@ describe("Block L10 — landing hero rewrite", () => {
2525 expect(ct.toLowerCase()).toContain("text/html");
2626 });
2727
28 it("renders the new hero headline", async () => {
28 it("renders the hero headline", async () => {
2929 const res = await app.request(HOME);
3030 const body = await res.text();
31 expect(body).toContain("The git host built around Claude.");
31 expect(body).toContain("The git host built for");
3232 });
3333
34 it("renders the install snippet", async () => {
34 it("renders speed-framing copy in hero lede", async () => {
3535 const res = await app.request(HOME);
3636 const body = await res.text();
37 // The host + path is what makes it unambiguous as the install snippet.
38 expect(body).toContain("gluecron.com/install");
39 expect(body).toContain("curl -sSL gluecron.com/install | bash");
37 expect(body).toContain("Spec to PR in 90 seconds");
4038 });
4139
42 it("renders all three primary CTAs in the hero row", async () => {
40 it("renders primary CTAs in the hero row", async () => {
4341 const res = await app.request(HOME);
4442 const body = await res.text();
4543 expect(body).toContain('href="/register"');
46 expect(body).toContain('href="/demo"');
47 expect(body).toContain('href="/vs-github"');
48 // Visible labels for the three CTAs.
49 expect(body).toContain("Sign up free");
50 expect(body).toContain("Try the live demo");
51 expect(body).toContain("Compare to GitHub");
44 // Visible labels for the primary CTAs.
45 expect(body).toContain("Start building");
5246 });
5347
54 it("renders the three reasons-to-switch column headings", async () => {
48 it("renders the register and explore nav links", async () => {
5549 const res = await app.request(HOME);
5650 const body = await res.text();
57 expect(body).toContain("Toggle Sleep Mode");
58 expect(body).toContain("One command to migrate");
59 expect(body).toContain("Open the demo, watch it work");
60 // The migrate column also links to /import.
61 expect(body).toContain('href="/import"');
62 // The Sleep Mode + demo columns deep-link to their L1 / L3 routes.
63 expect(body).toContain('href="/sleep-mode"');
64 expect(body).toContain('href="/demo"');
51 expect(body).toContain('href="/register"');
52 expect(body).toContain('href="/explore"');
53 expect(body).toContain('href="/pricing"');
6554 });
6655
67 it("renders the 'How is this different' pull-quote", async () => {
56 it("renders the 'The git host' eyebrow or headline copy", async () => {
6857 const res = await app.request(HOME);
6958 const body = await res.text();
70 expect(body).toContain("How is this different from GitHub?");
71 expect(body).toContain("Every other host bolts AI on as a sidecar.");
72 // JSX collapses newlines + leading whitespace; assert on a substring
73 // that is contiguous after server-side rendering.
74 expect(body).toContain("first-class developer");
75 expect(body).toContain("Built to be");
76 expect(body).toContain("operated by AI agents");
77 expect(body).toContain("See the full comparison");
59 expect(body).toContain("AI-native git host");
7860 });
7961
8062 it("injects SEO + Open Graph meta tags", async () => {
@@ -82,19 +64,19 @@ describe("Block L10 — landing hero rewrite", () => {
8264 const body = await res.text();
8365 // <title>
8466 expect(body).toContain(
85 "<title>Gluecron — The git host built around Claude</title>"
67 "<title>Gluecron — The AI-native git host</title>"
8668 );
8769 // <meta name="description">
8870 expect(body).toMatch(
89 /<meta\s+name="description"\s+content="Label an issue\. Walk away\. Wake up to a merged PR\./
71 /<meta\s+name="description"\s+content="The AI-native git host\. Spec to PR in 90 seconds\./
9072 );
9173 // <meta property="og:title">
9274 expect(body).toMatch(
93 /<meta\s+property="og:title"\s+content="Gluecron — The git host built around Claude"/
75 /<meta\s+property="og:title"\s+content="Gluecron — The AI-native git host"/
9476 );
9577 // <meta property="og:description">
9678 expect(body).toMatch(
97 /<meta\s+property="og:description"\s+content="Label an issue\./
79 /<meta\s+property="og:description"\s+content="The AI-native git host\./
9880 );
9981 // <meta property="og:type">
10082 expect(body).toMatch(
@@ -128,11 +110,10 @@ describe("Block L10 — landing hero rewrite", () => {
128110 }
129111 });
130112
131 it("REGRESSION: L5 'Compare to GitHub' CTA still routes to /vs-github", async () => {
113 it("REGRESSION: pricing link is present in nav or page body", async () => {
132114 const res = await app.request(HOME);
133115 const body = await res.text();
134 // The href + label pair must remain wired.
135 expect(body).toContain('href="/vs-github"');
136 expect(body).toContain("Compare to GitHub");
116 // Pricing link must remain accessible from the home page.
117 expect(body).toContain('href="/pricing"');
137118 });
138119});
Modifiedsrc/routes/demo.tsx+8−1View fileUnifiedSplit
@@ -210,7 +210,14 @@ app.get("/demo", softAuth, async (c) => {
210210`.trim();
211211
212212 return c.html(
213 <Layout title="Live demo" user={user}>
213 <Layout
214 title="Live demo"
215 user={user}
216 description="Watch Gluecron build and ship code in real time. AI writes the PR, reviews it, and merges it automatically."
217 ogTitle="Live demo — Gluecron"
218 ogDescription="Watch Gluecron build and ship code in real time. AI writes the PR, reviews it, and merges it automatically."
219 twitterCard="summary_large_image"
220 >
214221 <style dangerouslySetInnerHTML={{ __html: DEMO_CSS }} />
215222 <div class="demo-page">
216223 {/* ─── Hero ─── */}
Modifiedsrc/routes/explore.tsx+16−2View fileUnifiedSplit
@@ -648,7 +648,14 @@ explore.get("/explore", async (c) => {
648648 // before the real list lands.
649649 if (c.req.query("skeleton") === "1") {
650650 return c.html(
651 <Layout title="Explore" user={user}>
651 <Layout
652 title="Explore"
653 user={user}
654 description="Discover open-source projects hosted on Gluecron — the AI-native GitHub alternative."
655 ogTitle="Explore — Gluecron"
656 ogDescription="Discover open-source projects hosted on Gluecron — the AI-native GitHub alternative."
657 twitterCard="summary"
658 >
652659 <ExploreStyle />
653660 <div class="explore-wrap">
654661 <section class="explore-hero" aria-hidden="true">
@@ -794,7 +801,14 @@ explore.get("/explore", async (c) => {
794801 : `Sorted by repository creation date`;
795802
796803 return c.html(
797 <Layout title="Explore" user={user}>
804 <Layout
805 title="Explore"
806 user={user}
807 description="Discover open-source projects hosted on Gluecron — the AI-native GitHub alternative."
808 ogTitle="Explore — Gluecron"
809 ogDescription="Discover open-source projects hosted on Gluecron — the AI-native GitHub alternative."
810 twitterCard="summary"
811 >
798812 <ExploreStyle />
799813 <div class="explore-wrap">
800814 <section class="explore-hero">
Modifiedsrc/routes/help.tsx+9−2View fileUnifiedSplit
@@ -248,7 +248,14 @@ help.get("/help", (c) => {
248248 const user = c.get("user");
249249
250250 return c.html(
251 <Layout title="Help — gluecron" user={user}>
251 <Layout
252 title="Help — gluecron"
253 user={user}
254 description="Gluecron documentation — getting started, workflow YAML, MCP server, API reference."
255 ogTitle="Help — Gluecron"
256 ogDescription="Gluecron documentation — getting started, workflow YAML, MCP server, API reference."
257 twitterCard="summary"
258 >
252259 <style dangerouslySetInnerHTML={{ __html: helpStyles }} />
253260 <div class="help-wrap">
254261 {/* ─── Hero ─── */}
@@ -671,7 +678,7 @@ help.get("/help", (c) => {
671678 Every <code>.gluecron/specs/*.md</code> file across your
672679 repos shows up here. Add a spec, push it, then either run
673680 the spec-to-PR generator from the page or label the file
674 and let autopilot do it overnight.
681 and let autopilot run it automatically.
675682 </div>
676683 </div>
677684 </section>
Modifiedsrc/routes/pricing.tsx+8−1View fileUnifiedSplit
@@ -57,7 +57,14 @@ pricing.get("/pricing", async (c) => {
5757 const user = c.get("user");
5858 const plans = await listPlans();
5959 return c.html(
60 <Layout title="Pricing — Gluecron" user={user}>
60 <Layout
61 title="Pricing — Gluecron"
62 user={user}
63 description="Simple pricing for AI-native git hosting. Unlimited repos, AI review, auto-merge, spec-to-PR. Start free."
64 ogTitle="Pricing — Gluecron"
65 ogDescription="Simple pricing for AI-native git hosting. Unlimited repos, AI review, auto-merge, spec-to-PR. Start free."
66 twitterCard="summary"
67 >
6168 <PricingPage plans={plans} loggedIn={!!user} />
6269 </Layout>
6370 );
Modifiedsrc/routes/vs-github.tsx+8−1View fileUnifiedSplit
@@ -678,7 +678,14 @@ function SpeedChart() {
678678vsGithub.get("/vs-github", (c) => {
679679 const user = c.get("user");
680680 return c.html(
681 <Layout title="Gluecron vs GitHub — 16 years vs one weekend" user={user}>
681 <Layout
682 title="Gluecron vs GitHub — 16 years vs one weekend"
683 user={user}
684 description="How Gluecron outperforms GitHub on every AI metric. AI review in ~8s. Auto-merge on gate pass. Spec to PR in 90 seconds."
685 ogTitle="Gluecron vs GitHub — 16 years vs one weekend"
686 ogDescription="How Gluecron outperforms GitHub on every AI metric. AI review in ~8s. Auto-merge on gate pass. Spec to PR in 90 seconds."
687 twitterCard="summary_large_image"
688 >
682689 <style dangerouslySetInnerHTML={{ __html: pageCss }} />
683690 <div class="vsg-page vsg-root">
684691 {/* ============ 1. SHOCKING HERO ============ */}
Modifiedsrc/routes/web.tsx+23−2View fileUnifiedSplit
@@ -2901,8 +2901,18 @@ web.get("/:owner/:repo", async (c) => {
29012901 };
29022902
29032903 if (tree.length === 0) {
2904 const repoOgDesc = description
2905 ? `${owner}/${repo} on Gluecron — ${description}`
2906 : `${owner}/${repo} on Gluecron — AI-native git hosting with push-time gates and auto-merge.`;
29042907 return c.html(
2905 <Layout title={`${owner}/${repo}`} user={user}>
2908 <Layout
2909 title={`${owner}/${repo}`}
2910 user={user}
2911 description={repoOgDesc}
2912 ogTitle={`${owner}/${repo} — Gluecron`}
2913 ogDescription={repoOgDesc}
2914 twitterCard="summary"
2915 >
29062916 <style dangerouslySetInnerHTML={{ __html: repoHomeCss }} />
29072917 <style dangerouslySetInnerHTML={{ __html: `
29082918 .empty-options-grid {
@@ -3115,8 +3125,19 @@ web.get("/:owner/:repo", async (c) => {
31153125 const fileCount = tree.filter((e: any) => e.type !== "tree").length;
31163126 const dirCount = tree.filter((e: any) => e.type === "tree").length;
31173127
3128 const repoOgDesc = description
3129 ? `${owner}/${repo} on Gluecron — ${description}`
3130 : `${owner}/${repo} on Gluecron — AI-native git hosting with push-time gates and auto-merge.`;
3131
31183132 return c.html(
3119 <Layout title={`${owner}/${repo}`} user={user}>
3133 <Layout
3134 title={`${owner}/${repo}`}
3135 user={user}
3136 description={repoOgDesc}
3137 ogTitle={`${owner}/${repo} — Gluecron`}
3138 ogDescription={repoOgDesc}
3139 twitterCard="summary"
3140 >
31203141 <style dangerouslySetInnerHTML={{ __html: repoHomeCss }} />
31213142 <div class="repo-home-hero">
31223143 <div class="repo-home-hero-orb-wrap" aria-hidden="true">
Modifiedsrc/views/landing-2030.tsx+8−7View fileUnifiedSplit
@@ -61,8 +61,8 @@ const IconIntel: FC = () => (
6161const FEATURES: { icon: FC; title: string; body: string }[] = [
6262 { icon: IconReview, title: "Claude code review",
6363 body: "Every pull request gets a senior-level review the moment it opens — line-level comments, risk flags, and a verdict, in seconds." },
64 { icon: IconMerge, title: "Merge while you sleep",
65 body: "Gates green and review clean? Gluecron merges autonomously. Label an issue at night, wake to a shipped PR." },
64 { icon: IconMerge, title: "Auto-merge the instant gates pass",
65 body: "Gates green and review clean? Gluecron merges autonomously. Label an issue, get a shipped PR — no waiting." },
6666 { icon: IconGate, title: "Push-time gate enforcement",
6767 body: "Security and quality gates run at the moment of push — not minutes later in CI. Bad code never reaches your branch." },
6868 { icon: IconGit, title: "Git-native hosting",
@@ -77,13 +77,13 @@ const STEPS: { n: string; title: string; body: string }[] = [
7777 { n: "01", title: "Label an issue", body: "Drop a label on an issue — or just describe what you want. That's the whole input." },
7878 { n: "02", title: "Agents go to work", body: "Claude opens a branch, writes the change, and submits a pull request against your gates." },
7979 { n: "03", title: "Reviewed & gated", body: "The PR is reviewed line-by-line and run through push-time security and quality gates." },
80 { n: "04", title: "Merged, autonomously", body: "Green across the board? It merges itself and deploys. You wake up to shipped work." },
80 { n: "04", title: "Merged, autonomously", body: "Green across the board? It merges itself and deploys. Shipped while you were still in the same coding session." },
8181];
8282
8383export const Landing2030Page: FC<Landing2030Props> = () => {
84 const title = "Gluecron — The git host built for 2030";
84 const title = "Gluecron — The AI-native git host";
8585 const desc =
86 "Gluecron is the AI-native git host. Claude reviews every pull request, gates run at push time, and clean PRs merge while you sleep. Label an issue, walk away, wake up to a merged PR.";
86 "The AI-native git host. Spec to PR in 90 seconds. Auto-merge the instant gates pass. Ship faster than any team on GitHub.";
8787 return (
8888 <html lang="en">
8989 <head>
@@ -138,8 +138,9 @@ export const Landing2030Page: FC<Landing2030Props> = () => {
138138 </h1>
139139 <p class="lede rise" style="--d:120ms">
140140 Gluecron hosts your code, reviews every pull request with Claude,
141 enforces gates at push time, and merges clean work while you sleep.
142 Label an issue, walk away, wake up to a merged PR.
141 enforces gates at push time, and merges clean work the instant
142 gates pass. Spec to PR in 90 seconds — ship faster than any team
143 on GitHub.
143144 </p>
144145 <div class="hero-actions rise" style="--d:180ms">
145146 <a href="/register" class="btn btn-solid btn-lg">Start building →</a>
146147