CodeIssuesPull RequestsActionsSecurityInsights
✨ AI
More
Settings

feat(marketing): /building-itself proof page + STRATEGY-30 doc #5453

Merged⚡ AI-generatedXSccantynz wants to mergefeat/building-itself-proof-pagemainopened 24d ago
4 changed files+321−0
Addeddocs/STRATEGY-30.md+73−0View fileUnifiedSplit
1# STRATEGY-30 — the thirty moves (2026-08-08)
2
3Owner-approved direction. Ground rule that overrides every move below:
4**private repos are never on display.** Public surfaces show aggregate
5numbers only — counts, rates, timings. Never repo names, PR titles, file
6paths, diffs, or issue text from private repos. Public-repo detail is
7opt-in per repo, never default.
8
9Opening three: **#1 proof page → #6 WordPress → #16 billing.**
10(Proof brings attention, WordPress brings an unserved market, billing makes
11both pay.)
12
13## Act I — Weaponize the proof we already have
141. `/building-itself` — public live page: aggregate stats of this platform
15 developing itself through its own PRs/gates/AI. Counts only (privacy
16 rule). *(shipped 2026-08-08)*
172. The health loop as THE demo: score → priced fix → one click → PR →
18 merge → score rises. 60-second recording; landing centerpiece.
193. Agent onboarding as the wedge: "hire an AI teammate in 60 seconds" —
20 the `?repo=`-pinned Agent Brief (/connect/claude).
214. Productize the repair queue as **Autopilot**: what the owner's
22 subscription agent does internally, sold to customers on their metered
23 quota.
245. Live-edit streaming on the homepage: a public demo repo anyone can
25 prompt, files appearing as the AI writes.
26
27## Act II — Take the markets GitHub can't see
286. WordPress on-ramp (issue #209): zip import → SFTP re-sync → wp-admin
29 plugin that deploys merged PRs back to the live site.
307. Same tiers for Shopify/Wix/Squarespace theme developers.
318. Agencies: one dashboard, N client sites, health scores as the monthly
32 client report.
339. Switching is a 5-minute story: bulk import brings issues, PRs, and
34 comments — "bring everything, lose nothing."
3510. Publish the 40-repo/1,700-PR migration case study (numbers only; no
36 private repo names without explicit approval).
37
38## Act III — Be what GitHub structurally can't
3911. "Nothing broken merges" as the brand guarantee (gates as identity).
4012. Honest-by-design as positioning: gates that say "couldn't run" instead
41 of fake-green; empty states that tell the truth.
4213. Public health badges (shields) — opt-in per repo, never default.
4314. Sell the intelligence as an API (health, PR risk, security grouping)
44 for repos we don't even host.
4515. Spec-to-PR for non-programmers: describe in English, review a PR.
46
47## Act IV — Money (unblocks on issue #200 pricing decisions)
4816. Metered AI billing with markup — every agent action is revenue.
4917. BYOK enterprise tier: their Anthropic key, our platform fee.
5018. Free tier with a visible AI allowance; the health page's "+N points"
51 buttons are the natural upgrade moment.
5219. Marketplace: third-party gates/skills/agents, revenue share.
5320. White-label "your own Gluecron" for platform businesses (davenroe,
54 Vapron, bookaride are customer #1 proof).
55
56## Act V — Distribution
5721. Anthropic connector directory listing (unblock the seeding task).
5822. One-click .dxt install everywhere.
5923. `/demo` always alive with real (honestly-gated) agent activity.
6024. Content flywheel: merged fixes auto-draft changelog/blog posts
61 (ai-release-notes exists).
6225. Repos as viral objects: collaborator invites → gates/health exposure →
63 account imports. Instrument the loop.
64
65## Act VI — Moats that compound
6626. Import fidelity (issues+PRs+comments) — keep it flawless; it reverses
67 switching costs.
6827. Cross-repo intelligence: org-wide refactors and health rollups.
6928. Data moat: cost-per-task + gate outcomes → smarter model routing →
70 compounding margin advantage.
7129. Reliability as theater: publish the restore drills.
7230. Speed as identity: "issues become PRs in minutes, not sprints."
73 Everything above serves that sentence.
Modifiedsrc/app.tsx+5−0View fileUnifiedSplit
104104import orgRoutes from "./routes/orgs";
105105import notificationRoutes from "./routes/notifications";
106106import onboardingRoutes from "./routes/onboarding";
107import buildingItselfRoutes from "./routes/building-itself";
107108import adminRoutes from "./routes/admin";
108109import adminDeletionsRoutes from "./routes/admin-deletions";
109110import adminStripeRoutes from "./routes/admin-stripe";
299300 // already caught by the `hasSession` branch above, so only the
300301 // identical logged-out render reaches here.
301302 p === "/connect" ||
303 p === "/building-itself" ||
302304 p === "/help" ||
303305 p === "/changelog" ||
304306 p === "/enterprise" ||
10311033// MCP client). Must be mounted before webRoutes, whose /:owner route would
10321034// otherwise swallow "/connect" as a username.
10331035app.route("/", connectHubRoutes);
1036// Building-itself proof page — STRATEGY-30 #1. Aggregate-only, public.
1037// Mounted here (before webRoutes) so /:owner can't swallow the path.
1038app.route("/", buildingItselfRoutes);
10341039// Push Watch — per-commit live status (gates + deploy + latency) at /:owner/:repo/push/:sha
10351040app.route("/", pushWatchRoutes);
10361041// Org Secrets Manager — BLOCK M2 — /orgs/:slug/settings/secrets
Addedsrc/routes/building-itself.tsx+242−0View fileUnifiedSplit
1/**
2 * /building-itself — the public proof page (STRATEGY-30 move #1).
3 *
4 * Gluecron develops itself through its own pull requests, gates, and AI.
5 * This page shows that happening, live, as AGGREGATE NUMBERS ONLY.
6 *
7 * PRIVACY RULE (owner directive 2026-08-08, overrides everything): the
8 * owner's repositories are private and must never be on display. Nothing
9 * on this page may name a repository, quote a PR title, show a file path,
10 * a diff, an issue body, or a username other than the platform's. Counts,
11 * rates, and timings only — platform-wide sums that identify nothing.
12 *
13 * Honest-degradation rule applies: a stat that is genuinely zero renders
14 * as zero. No fabricated liveness.
15 */
16
17import { Hono } from "hono";
18import { gte, sql } from "drizzle-orm";
19import { db } from "../db";
20import {
21 deployments,
22 issues,
23 prComments,
24 pullRequests,
25 repositories,
26} from "../db/schema";
27import { Layout } from "../views/layout";
28import { softAuth } from "../middleware/auth";
29import type { AuthEnv } from "../middleware/auth";
30
31const buildingItself = new Hono<AuthEnv>();
32buildingItself.use("*", softAuth);
33
34const styles = `
35 .bi-wrap { max-width: 1100px; margin: 0 auto; padding: var(--space-8) var(--space-4); }
36 .bi-hero { text-align: center; margin-bottom: var(--space-8); }
37 .bi-eyebrow {
38 display: inline-flex; align-items: center; gap: 8px;
39 font-family: var(--font-mono); font-size: 11px; letter-spacing: .18em;
40 text-transform: uppercase; color: var(--text-muted); font-weight: 600;
41 margin-bottom: 14px;
42 }
43 .bi-eyebrow-dot {
44 width: 8px; height: 8px; border-radius: 9999px;
45 background: linear-gradient(135deg, var(--accent), var(--accent-2));
46 box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 18%, transparent);
47 }
48 .bi-title {
49 font-family: var(--font-display); font-weight: 800;
50 font-size: clamp(34px, 5vw, 54px); letter-spacing: -0.03em;
51 line-height: 1.05; margin: 0 0 12px; color: var(--text-strong);
52 }
53 .bi-title-grad {
54 background-image: linear-gradient(135deg, var(--accent) 0%, var(--accent) 55%, var(--accent-2) 100%);
55 -webkit-background-clip: text; background-clip: text;
56 -webkit-text-fill-color: transparent; color: transparent;
57 }
58 .bi-sub { font-size: 16px; color: var(--text-muted); max-width: 640px; margin: 0 auto; line-height: 1.6; }
59 .bi-grid {
60 display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
61 gap: var(--space-4); margin-bottom: var(--space-8);
62 }
63 .bi-stat {
64 padding: var(--space-5); text-align: center;
65 background: var(--bg-elevated); border: 1px solid var(--border);
66 border-radius: var(--r-lg); position: relative; overflow: hidden;
67 }
68 .bi-stat::before {
69 content: ''; position: absolute; top: 0; left: 0; right: 0; height: 2px;
70 background: linear-gradient(90deg, transparent, var(--accent) 50%, transparent);
71 opacity: .6;
72 }
73 .bi-stat-n {
74 font-family: var(--font-mono); font-size: 40px; font-weight: 700;
75 color: var(--text-strong); line-height: 1;
76 }
77 .bi-stat-label {
78 margin-top: 8px; font-size: var(--t-sm); color: var(--text-muted);
79 }
80 .bi-stat-window { font-size: 11px; color: var(--text-faint); text-transform: uppercase; letter-spacing: .08em; margin-top: 4px; }
81 .bi-how {
82 background: var(--bg-elevated); border: 1px solid var(--border);
83 border-radius: var(--r-lg); padding: var(--space-6);
84 }
85 .bi-how h2 { font-size: var(--t-lg); margin: 0 0 var(--space-4); }
86 .bi-step { display: flex; gap: 14px; padding: 10px 0; border-bottom: 1px solid var(--border-subtle); }
87 .bi-step:last-child { border-bottom: none; }
88 .bi-step-n {
89 font-family: var(--font-mono); font-weight: 700; color: var(--accent);
90 flex-shrink: 0; width: 24px;
91 }
92 .bi-step-text { font-size: var(--t-base); line-height: 1.55; }
93 .bi-privacy {
94 margin-top: var(--space-6); text-align: center;
95 font-size: var(--t-sm); color: var(--text-faint);
96 }
97`;
98
99buildingItself.get("/building-itself", async (c) => {
100 const user = c.get("user");
101 const weekAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);
102
103 // Every stat is a platform-wide COUNT — nothing identifiable. Each query
104 // is independently best-effort; a DB blip renders a 0, never a 500.
105 const count = async (q: () => Promise<Array<{ n: number }>>) => {
106 try {
107 const [row] = await q();
108 return row?.n ?? 0;
109 } catch {
110 return 0;
111 }
112 };
113
114 const [
115 prsMergedWeek,
116 prsMergedTotal,
117 aiReviewsWeek,
118 issuesClosedWeek,
119 deploysWeek,
120 repoCount,
121 ] = await Promise.all([
122 count(() =>
123 db
124 .select({ n: sql<number>`count(*)::int` })
125 .from(pullRequests)
126 .where(gte(pullRequests.mergedAt, weekAgo))
127 ),
128 count(() =>
129 db
130 .select({ n: sql<number>`count(*)::int` })
131 .from(pullRequests)
132 .where(sql`${pullRequests.state} = 'merged'`)
133 ),
134 count(() =>
135 db
136 .select({ n: sql<number>`count(*)::int` })
137 .from(prComments)
138 .where(
139 sql`${prComments.isAiReview} = true AND ${prComments.createdAt} >= ${weekAgo}`
140 )
141 ),
142 count(() =>
143 db
144 .select({ n: sql<number>`count(*)::int` })
145 .from(issues)
146 .where(
147 sql`${issues.state} = 'closed' AND ${issues.updatedAt} >= ${weekAgo}`
148 )
149 ),
150 count(() =>
151 db
152 .select({ n: sql<number>`count(*)::int` })
153 .from(deployments)
154 .where(gte(deployments.createdAt, weekAgo))
155 ),
156 count(() =>
157 db.select({ n: sql<number>`count(*)::int` }).from(repositories)
158 ),
159 ]);
160
161 const stats: Array<{ n: number; label: string; window: string }> = [
162 { n: prsMergedWeek, label: "pull requests merged", window: "last 7 days" },
163 { n: aiReviewsWeek, label: "AI reviews posted", window: "last 7 days" },
164 { n: issuesClosedWeek, label: "issues closed", window: "last 7 days" },
165 { n: deploysWeek, label: "deployments", window: "last 7 days" },
166 { n: prsMergedTotal, label: "pull requests merged", window: "all time" },
167 { n: repoCount, label: "repositories hosted", window: "all time" },
168 ];
169
170 return c.html(
171 <Layout title="The platform that builds itself — gluecron" user={user}>
172 <style dangerouslySetInnerHTML={{ __html: styles }} />
173 <div class="bi-wrap">
174 <div class="bi-hero">
175 <div class="bi-eyebrow">
176 <span class="bi-eyebrow-dot" aria-hidden="true" />
177 Live from this platform's own database
178 </div>
179 <h1 class="bi-title">
180 Gluecron <span class="bi-title-grad">builds itself</span>.
181 </h1>
182 <p class="bi-sub">
183 This platform is developed on this platform. Every feature you can
184 see shipped as a pull request here — branch, gates, AI review,
185 merge, deploy. These are the live numbers.
186 </p>
187 </div>
188
189 <div class="bi-grid">
190 {stats.map((s) => (
191 <div class="bi-stat">
192 <div class="bi-stat-n">{s.n.toLocaleString()}</div>
193 <div class="bi-stat-label">{s.label}</div>
194 <div class="bi-stat-window">{s.window}</div>
195 </div>
196 ))}
197 </div>
198
199 <div class="bi-how">
200 <h2>How a change ships here</h2>
201 <div class="bi-step">
202 <span class="bi-step-n">1</span>
203 <span class="bi-step-text">
204 A problem is found — by a person, by the health engine's priced
205 improvement list, or by the AI itself.
206 </span>
207 </div>
208 <div class="bi-step">
209 <span class="bi-step-n">2</span>
210 <span class="bi-step-text">
211 The fix is implemented on a branch and opened as a pull request —
212 often by an AI agent, always reviewable.
213 </span>
214 </div>
215 <div class="bi-step">
216 <span class="bi-step-n">3</span>
217 <span class="bi-step-text">
218 Push-time gates run: tests, secret scan, security scan,
219 mergeability, AI review. A gate that cannot run says so — it
220 never fakes a pass.
221 </span>
222 </div>
223 <div class="bi-step">
224 <span class="bi-step-n">4</span>
225 <span class="bi-step-text">
226 A human merges. The deploy timer picks it up and the change is
227 live in about a minute — including every part of the page you
228 are reading now.
229 </span>
230 </div>
231 </div>
232
233 <p class="bi-privacy">
234 Numbers are platform-wide aggregates. Repositories on gluecron are
235 private by default and are never shown here.
236 </p>
237 </div>
238 </Layout>
239 );
240});
241
242export default buildingItself;
Modifiedsrc/views/layout.tsx+1−0View fileUnifiedSplit
366366 <div class="footer-col-title">Platform</div>
367367 <a href="/docs">Docs</a>
368368 <a href="/help">Quickstart</a>
369 <a href="/building-itself">Builds itself</a>
369370 <a href="/status">Status</a>
370371 <a href="/api/graphql">GraphQL</a>
371372 <a href="/mcp">MCP server</a>
372373
c comment · e edit title · m merge · a approve · r request changes · ? shortcuts