diff --git a/app/history/valuation/valuation-page-model.ts b/app/history/valuation/valuation-page-model.ts
index 68f66b3..5f2ab6a 100644
--- a/app/history/valuation/valuation-page-model.ts
+++ b/app/history/valuation/valuation-page-model.ts
@@ -1,7 +1,12 @@
-import { deriveValuationHeadlines, type HistoryCollection } from "@/lib/content";
+import {
+ deriveValuationHeadlines,
+ type HistoryCollection,
+ type ResolvedValuationObservation,
+} from "@/lib/content";
import type { ValuationObservation } from "@/lib/research-schema";
import type { Metadata } from "next";
+import { independenceSentence } from "../../site-copy";
import { site, socialMetadata } from "../../site";
export const mechanismLabel: Readonly<
@@ -15,19 +20,64 @@ export const mechanismLabel: Readonly<
"seed-financing": "seed financing",
};
+export const basisLabel: Readonly<
+ Record
+> = {
+ "common-stock-409a": "common-stock 409A",
+ "market-indication": "market indication",
+ "post-money": "post-money",
+ "pre-money": "pre-money",
+ "transaction-implied": "transaction implied",
+ unspecified: "basis not specified",
+};
+
+export const statusLabel: Readonly> = {
+ "agreements-signed": "agreements signed",
+ "company-confirmed": "company confirmed",
+ completed: "completed",
+ reported: "reported",
+ retrospective: "retrospective",
+};
+
export interface ValuationPageSeo {
readonly description: string;
+ readonly lead: string;
readonly title: string;
readonly yearRange: string;
}
+export interface ValuationHeadlineRow {
+ readonly basisLabel: string;
+ readonly calendarYear: number;
+ readonly display: string;
+ readonly observationId: string;
+ readonly sources: ResolvedValuationObservation["sources"];
+ readonly statusLabel: string;
+}
+
+function indefiniteArticle(phrase: string): "a" | "an" {
+ return /^[aeiou]/iu.test(phrase) ? "an" : "a";
+}
+
+function valuationYearRange(
+ headlines: ReturnType,
+): string {
+ const firstHeadline = headlines[0];
+ const latestHeadline = headlines.at(-1);
+ if (firstHeadline === undefined || latestHeadline === undefined) {
+ throw new Error("Valuation page requires at least one headline observation");
+ }
+ return firstHeadline.calendarYear === latestHeadline.calendarYear
+ ? String(firstHeadline.calendarYear)
+ : `${firstHeadline.calendarYear}–${latestHeadline.calendarYear}`;
+}
+
export function deriveValuationPageSeo(
history: Pick,
): ValuationPageSeo {
const headlines = deriveValuationHeadlines(history.valuations);
- const firstHeadline = headlines[0];
const latestHeadline = headlines.at(-1);
- if (firstHeadline === undefined || latestHeadline === undefined) {
+ if (latestHeadline === undefined) {
throw new Error("Valuation page requires at least one headline observation");
}
const latestObservation = history.valuations.find(
@@ -38,17 +88,47 @@ export function deriveValuationPageSeo(
`Valuation headline references missing observation ${latestHeadline.observationId}`,
);
}
- const yearRange = firstHeadline.calendarYear === latestHeadline.calendarYear
- ? String(firstHeadline.calendarYear)
- : `${firstHeadline.calendarYear}–${latestHeadline.calendarYear}`;
+ const yearRange = valuationYearRange(headlines);
+ const latestMechanism = mechanismLabel[latestObservation.mechanism];
+ const latestStatus = statusLabel[latestObservation.status];
+ const latestBasis = basisLabel[latestObservation.valuation.basis];
return {
description:
- `Stripe valuation history from its early venture rounds through the ${latestHeadline.display} ${latestHeadline.calendarYear} ${mechanismLabel[latestObservation.mechanism]}, with sourced financing, tender, 409A, investor-secondary, and market observations.`,
+ `Stripe valuation history from its early venture rounds through the ${latestHeadline.display} ${latestHeadline.calendarYear} ${latestMechanism}, with sourced financing, tender, 409A, investor-secondary, and market observations.`,
+ lead: [
+ `Stripe’s latest sourced private-company valuation headline is ${latestHeadline.display} in ${latestHeadline.calendarYear}, from ${indefiniteArticle(latestMechanism)} ${latestMechanism} with ${latestStatus} status, recorded as ${latestBasis}.`,
+ `This page selects one observation per year from ${yearRange}.`,
+ "Financing, tender, 409A, secondary, and market-indication figures are not interchangeable.",
+ independenceSentence,
+ ].join(" "),
title: `Stripe Valuation History by Year, ${yearRange}`,
yearRange,
};
}
+export function deriveValuationHeadlineRows(
+ history: Pick,
+): readonly ValuationHeadlineRow[] {
+ return deriveValuationHeadlines(history.valuations).map((headline) => {
+ const observation = history.valuations.find(
+ ({ id }) => id === headline.observationId,
+ );
+ if (observation === undefined) {
+ throw new Error(
+ `Valuation headline references missing observation ${headline.observationId}`,
+ );
+ }
+ return {
+ basisLabel: basisLabel[observation.valuation.basis],
+ calendarYear: headline.calendarYear,
+ display: headline.display,
+ observationId: observation.id,
+ sources: observation.sources,
+ statusLabel: statusLabel[observation.status],
+ };
+ });
+}
+
export function deriveValuationPageMetadata(
history: Pick,
): Metadata {
diff --git a/app/metadata.test.ts b/app/metadata.test.ts
index 09147aa..9c091bc 100644
--- a/app/metadata.test.ts
+++ b/app/metadata.test.ts
@@ -42,7 +42,7 @@ describe("stripedex.com public identity", () => {
`${SITE_ORIGIN}/data`,
...timelineCategoryIds.map((id) => `${SITE_ORIGIN}/history/${id}`),
]));
- expect(urls).toContain(
+ expect(urls).not.toContain(
`${SITE_ORIGIN}/history/acquisitions/openrouter-acquisition-talks-reported`,
);
expect(urls).not.toContain(`${SITE_ORIGIN}/appearances`);
diff --git a/app/page.test.tsx b/app/page.test.tsx
index e7fdc24..c36005c 100644
--- a/app/page.test.tsx
+++ b/app/page.test.tsx
@@ -63,7 +63,7 @@ describe("canonical stripedex.com history", () => {
);
expect(html).not.toContain("Loading Stripe company history");
expect(html).toContain("Stripe reportedly discusses acquiring OpenRouter");
- expect(html).toContain(
+ expect(html).not.toContain(
'href="/history/acquisitions/openrouter-acquisition-talks-reported"',
);
expect(html).toContain("Tokens Are the New Dollars");
diff --git a/app/seo.test.ts b/app/seo.test.ts
index b5b6e51..9570d17 100644
--- a/app/seo.test.ts
+++ b/app/seo.test.ts
@@ -8,7 +8,6 @@ import {
breadcrumbJsonLd,
historyCollectionJsonLd,
historyDatasetJsonLd,
- historyEventJsonLd,
siteOrganizationJsonLd,
websiteJsonLd,
} from "./seo";
@@ -100,20 +99,15 @@ describe("stripedex.com structured discovery", () => {
});
expect(collection.mainEntity.itemListElement[0]?.item).toMatchObject({
"@id": expect.stringContaining(
- "/history/appearances/appearance-2026-08-will-gaybrick-a16z#event",
+ "/history/appearances#appearance-2026-08-will-gaybrick-a16z",
),
"@type": "VideoObject",
- url: "https://stripedex.com/history/appearances/appearance-2026-08-will-gaybrick-a16z",
});
});
test("describes canonical history items and breadcrumbs", () => {
const rootHistory = historyCollectionJsonLd(
- [{
- categoryId: "company-milestones",
- id: "example-event",
- title: "Stripe reaches an example milestone",
- }],
+ [{ id: "example-event", title: "Stripe reaches an example milestone" }],
{
description: "One sourced event.",
path: "/",
@@ -125,16 +119,12 @@ describe("stripedex.com structured discovery", () => {
numberOfItems: 1,
itemListElement: [{
position: 1,
- url: "https://stripedex.com/history/company-milestones/example-event",
+ url: "https://stripedex.com/#example-event",
}],
});
const categoryHistory = historyCollectionJsonLd(
- [{
- categoryId: "company-milestones",
- id: "example-event",
- title: "Stripe reaches an example milestone",
- }],
+ [{ id: "example-event", title: "Stripe reaches an example milestone" }],
{
description: "One sourced event.",
path: "/history/company-milestones",
@@ -142,30 +132,8 @@ describe("stripedex.com structured discovery", () => {
},
);
expect(categoryHistory.mainEntity.itemListElement[0]?.url).toBe(
- "https://stripedex.com/history/company-milestones/example-event",
+ "https://stripedex.com/history/company-milestones#example-event",
);
- expect(historyEventJsonLd({
- categoryId: "acquisitions",
- categoryLabel: "Acquisitions",
- categoryOrder: 3,
- confidence: "reported",
- date: "2026-07-24",
- date_precision: "day",
- id: "openrouter-acquisition-talks-reported",
- sourceIds: ["source-990ab773c6c0913272f7"],
- sources: [{
- kind: "reporting",
- publisher: "TechCrunch",
- title: "Example source",
- url: "https://techcrunch.com/example",
- }],
- summary: "Stripe held talks to acquire AI-model marketplace OpenRouter at a reported price.",
- title: "Stripe reportedly discusses acquiring OpenRouter",
- })).toMatchObject({
- "@type": "Article",
- url: "https://stripedex.com/history/acquisitions/openrouter-acquisition-talks-reported",
- headline: "Stripe reportedly discusses acquiring OpenRouter",
- });
expect(breadcrumbJsonLd([
{ name: "History", path: "/" },
{ name: "Company milestones", path: "/history/company-milestones" },
diff --git a/app/seo.ts b/app/seo.ts
index 184853f..79b0327 100644
--- a/app/seo.ts
+++ b/app/seo.ts
@@ -1,5 +1,4 @@
-import type { CategorizedHistoryEvent, HistoryCollection } from "@/lib/content";
-import { historyEventPath } from "@/lib/history-urls";
+import type { HistoryCollection } from "@/lib/content";
import {
GITHUB_REPOSITORY_URL,
@@ -57,7 +56,6 @@ export function websiteJsonLd() {
export function historyCollectionJsonLd(
items: readonly Readonly<{
- readonly categoryId?: CategorizedHistoryEvent["categoryId"];
readonly id: string;
readonly title: string;
}>[],
@@ -90,45 +88,12 @@ export function historyCollectionJsonLd(
"@type": "ListItem",
position: index + 1,
name: item.title,
- url: item.categoryId === undefined
- ? `${url}#${item.id}`
- : absoluteUrl(historyEventPath(item.categoryId, item.id)),
+ url: `${url}#${item.id}`,
})),
},
} as const;
}
-export function historyEventJsonLd(event: CategorizedHistoryEvent) {
- const path = historyEventPath(event.categoryId, event.id);
- const url = absoluteUrl(path);
- return {
- "@context": "https://schema.org",
- "@type": "Article",
- "@id": `${url}#event`,
- url,
- headline: event.title,
- description: event.summary,
- datePublished: event.date,
- inLanguage: "en-US",
- isPartOf: { "@id": `${SITE_ORIGIN}/#website` },
- publisher: publisherJsonLd,
- about: {
- "@type": "Organization",
- name: "Stripe",
- url: "https://stripe.com/",
- },
- citation: event.sources.map((source) => ({
- "@type": "CreativeWork" as const,
- name: source.title,
- url: source.url,
- publisher: {
- "@type": "Organization" as const,
- name: source.publisher,
- },
- })),
- } as const;
-}
-
export function breadcrumbJsonLd(items: readonly BreadcrumbItem[]) {
return {
"@context": "https://schema.org",
@@ -297,8 +262,7 @@ export function appearanceCollectionJsonLd(history: HistoryCollection) {
position: index + 1,
item: {
"@type": itemType,
- "@id": `${SITE_ORIGIN}${historyEventPath("appearances", appearance.id)}#event`,
- url: `${SITE_ORIGIN}${historyEventPath("appearances", appearance.id)}`,
+ "@id": `${url}#${appearance.id}`,
name: appearance.title,
description: appearance.digest?.gist ?? appearance.significance,
datePublished: appearance.published_at ?? appearance.occurred_at,
diff --git a/app/sitemap.ts b/app/sitemap.ts
index 2d12b22..c543f2c 100644
--- a/app/sitemap.ts
+++ b/app/sitemap.ts
@@ -1,6 +1,5 @@
import type { MetadataRoute } from "next";
import { loadHistory } from "@/lib/content";
-import { historyEventPath } from "@/lib/history-urls";
import { SITE_ORIGIN } from "./site";
@@ -32,8 +31,5 @@ export default async function sitemap(): Promise {
...history.categories.map(({ id }) => ({
url: `${SITE_ORIGIN}/history/${id}`,
})),
- ...history.events.map((event) => ({
- url: `${SITE_ORIGIN}${historyEventPath(event.categoryId, event.id)}`,
- })),
];
}
diff --git a/app/x-markdown/[[...path]]/route.test.ts b/app/x-markdown/[[...path]]/route.test.ts
index 32cc26c..d067616 100644
--- a/app/x-markdown/[[...path]]/route.test.ts
+++ b/app/x-markdown/[[...path]]/route.test.ts
@@ -5,7 +5,7 @@ import { MARKDOWN_CONTENT_TYPE } from "@/lib/accept";
import { GET, HEAD, generateStaticParams } from "./route";
describe("Node markdown corpus handler", () => {
- test("serves homepage, category, event, and recovery markdown", async () => {
+ test("serves homepage, category, and recovery markdown", async () => {
const history = await loadHistory();
const openRouter = history.events.find(
({ id }) => id === "openrouter-acquisition-talks-reported",
@@ -17,7 +17,11 @@ describe("Node markdown corpus handler", () => {
new Request("https://stripedex.com/x-markdown/history/acquisitions"),
{ params: Promise.resolve({ path: ["history", "acquisitions"] }) },
);
- const event = await GET(
+ const missing = await GET(
+ new Request("https://stripedex.com/x-markdown/this-does-not-exist"),
+ { params: Promise.resolve({ path: ["this-does-not-exist"] }) },
+ );
+ const missingEvent = await GET(
new Request(
"https://stripedex.com/x-markdown/history/acquisitions/openrouter-acquisition-talks-reported",
),
@@ -31,10 +35,6 @@ describe("Node markdown corpus handler", () => {
}),
},
);
- const missing = await GET(
- new Request("https://stripedex.com/x-markdown/this-does-not-exist"),
- { params: Promise.resolve({ path: ["this-does-not-exist"] }) },
- );
const head = await HEAD(new Request("https://stripedex.com/x-markdown"), {
params: Promise.resolve({}),
});
@@ -48,28 +48,26 @@ describe("Node markdown corpus handler", () => {
expect(await acquisitions.text()).toContain(
openRouter?.title ?? "missing-openrouter",
);
- const eventBody = await event.text();
- expect(event.status).toBe(200);
- expect(eventBody).toContain("not affiliated with, endorsed by, or operated by");
- expect(eventBody).toContain("https://www.axios.com/");
const missingBody = await missing.text();
expect(missing.status).toBe(404);
expect(missingBody).toContain("https://stripedex.com/sitemap.xml");
expect(missingBody).toContain("https://stripedex.com/llms.txt");
+ expect(missingEvent.status).toBe(404);
expect(head.status).toBe(200);
expect(await head.text()).toBe("");
expect(head.headers.get("content-type")).toBe(MARKDOWN_CONTENT_TYPE);
});
- test("prebuilds every public document path, including event pages", async () => {
+ test("prebuilds public document paths without per-event routes", async () => {
const history = await loadHistory();
const params = await generateStaticParams();
expect(params).toContainEqual({ path: [] });
expect(params).toContainEqual({ path: ["about"] });
expect(params).toContainEqual({ path: ["history", "acquisitions"] });
- expect(params).toContainEqual({
+ expect(params).toContainEqual({ path: ["history", "valuation"] });
+ expect(params).not.toContainEqual({
path: ["history", "acquisitions", "openrouter-acquisition-talks-reported"],
});
- expect(params.length).toBe(8 + history.categories.length + history.events.length);
+ expect(params.length).toBe(8 + history.categories.length);
});
});
diff --git a/app/x-markdown/[[...path]]/route.ts b/app/x-markdown/[[...path]]/route.ts
index 2b10f34..d053be0 100644
--- a/app/x-markdown/[[...path]]/route.ts
+++ b/app/x-markdown/[[...path]]/route.ts
@@ -1,7 +1,6 @@
import { MARKDOWN_CONTENT_TYPE } from "@/lib/accept";
import { loadHistory } from "@/lib/content";
import {
- historyEventPath,
markdownRewritePath,
publicPathFromMarkdownRewrite,
} from "@/lib/history-urls";
@@ -26,7 +25,6 @@ export async function generateStaticParams() {
"/history/payment-volume",
"/history/valuation",
...history.categories.map(({ id }) => `/history/${id}`),
- ...history.events.map((event) => historyEventPath(event.categoryId, event.id)),
];
return publicPaths.map((pathname) => {
const rewrite = markdownRewritePath(pathname);
diff --git a/lib/AGENTS.md b/lib/AGENTS.md
index 0174970..d7c2171 100644
--- a/lib/AGENTS.md
+++ b/lib/AGENTS.md
@@ -5,7 +5,7 @@
- `automated-publication-schema.ts` – reviewed model policy and hash-only publication attestation contracts.
- `content.ts` – deterministic loading, source resolution, validation, categorization, chronology, annual-volume extraction, and valuation selection.
- `accept.ts` – Accept parsing and markdown negotiation decisions.
-- `history-urls.ts` – durable category, event, and internal Markdown rewrite paths.
+- `history-urls.ts` – durable category paths and the internal Markdown rewrite.
- `page-markdown.ts` and `llms-txt.ts` – Markdown representations of existing public pages and the agent index.
- `*.test.ts` – schema, ordering, uniqueness, and source-provenance regressions.
diff --git a/lib/history-urls.test.ts b/lib/history-urls.test.ts
index 4011fb2..6c6862d 100644
--- a/lib/history-urls.test.ts
+++ b/lib/history-urls.test.ts
@@ -2,33 +2,14 @@ import { describe, expect, test } from "bun:test";
import {
historyCategoryPath,
- historyEventPath,
isMarkdownRewritePath,
markdownRewritePath,
- parseHistoryEventPath,
publicPathFromMarkdownRewrite,
} from "./history-urls";
describe("history URL helpers", () => {
- test("builds durable category and event paths", () => {
+ test("builds durable category paths", () => {
expect(historyCategoryPath("acquisitions")).toBe("/history/acquisitions");
- expect(historyEventPath(
- "acquisitions",
- "openrouter-acquisition-talks-reported",
- )).toBe("/history/acquisitions/openrouter-acquisition-talks-reported");
- });
-
- test("parses only real category and event identity pairs", () => {
- expect(parseHistoryEventPath(
- "/history/acquisitions/openrouter-acquisition-talks-reported",
- )).toEqual({
- categoryId: "acquisitions",
- eventId: "openrouter-acquisition-talks-reported",
- });
- expect(parseHistoryEventPath("/history/payment-volume/not-an-event")).toBeNull();
- expect(parseHistoryEventPath("/history/acquisitions")).toBeNull();
- expect(parseHistoryEventPath("/history/acquisitions/Not-Valid")).toBeNull();
- expect(parseHistoryEventPath("/history/acquisitions/one/two")).toBeNull();
});
test("maps public document paths onto the internal markdown rewrite", () => {
diff --git a/lib/history-urls.ts b/lib/history-urls.ts
index a7e7f14..f72e8d5 100644
--- a/lib/history-urls.ts
+++ b/lib/history-urls.ts
@@ -1,11 +1,8 @@
-import { timelineCategoryIds, type TimelineCategoryId } from "./history-schema";
+import { type TimelineCategoryId } from "./history-schema";
export const MARKDOWN_REWRITE_PREFIX = "/x-markdown" as const;
-export const HOME_MARKDOWN_RECENT_EVENT_LIMIT = 12;
-export const HISTORY_EVENT_ID_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/u;
export type HistoryCategoryPath = `/history/${TimelineCategoryId}`;
-export type HistoryEventPath = `/history/${TimelineCategoryId}/${string}`;
export function historyCategoryPath(
categoryId: TimelineCategoryId,
@@ -13,13 +10,6 @@ export function historyCategoryPath(
return `/history/${categoryId}`;
}
-export function historyEventPath(
- categoryId: TimelineCategoryId,
- eventId: string,
-): HistoryEventPath {
- return `/history/${categoryId}/${eventId}`;
-}
-
export function markdownRewritePath(pathname: string): string {
if (pathname === "/") return MARKDOWN_REWRITE_PREFIX;
return `${MARKDOWN_REWRITE_PREFIX}${pathname}`;
@@ -35,21 +25,3 @@ export function publicPathFromMarkdownRewrite(pathname: string): string | null {
const rest = pathname.slice(MARKDOWN_REWRITE_PREFIX.length);
return rest === "" ? "/" : rest;
}
-
-export function parseHistoryEventPath(pathname: string): {
- readonly categoryId: TimelineCategoryId;
- readonly eventId: string;
-} | null {
- const match = /^\/history\/([^/]+)\/([^/]+)$/u.exec(pathname);
- if (match === null || match[1] === undefined || match[2] === undefined) {
- return null;
- }
- if (!timelineCategoryIds.includes(match[1] as TimelineCategoryId)) {
- return null;
- }
- if (!HISTORY_EVENT_ID_PATTERN.test(match[2])) return null;
- return {
- categoryId: match[1] as TimelineCategoryId,
- eventId: match[2],
- };
-}
diff --git a/lib/llms-txt.ts b/lib/llms-txt.ts
index 5132fdd..32250f6 100644
--- a/lib/llms-txt.ts
+++ b/lib/llms-txt.ts
@@ -15,7 +15,7 @@ export async function llmsTxt(): Promise {
`${site.domain} publishes an independent, open-source Stripe company history as server-rendered pages and reviewable YAML. ${independenceSentence}`,
"",
"## When to use this",
- "Use Stripedex when you need a sourced chronology of Stripe as a company: acquisitions, product launches, funding, private-company valuation, disclosed annual volume, leadership appearances, expansion, offices, publishing, or early history. Prefer a category page or its YAML download when the question is one topic. Individual events have durable pages at `/history//`. Fetch this file first, then the Markdown representation of a page by sending `Accept: text/markdown` to the same URL, or by appending `.md`.",
+ "Use Stripedex when you need a sourced chronology of Stripe as a company: acquisitions, product launches, funding, private-company valuation, disclosed annual volume, leadership appearances, expansion, offices, publishing, or early history. Prefer a category page or its YAML download when the question is one topic. Fetch this file first, then the Markdown representation of a page by sending `Accept: text/markdown` to the same URL, or by appending `.md`.",
"",
"Do not use Stripedex for Stripe product APIs, payments, billing, Connect, Atlas, OAuth, webhooks, MCP, official documentation, account data, or anything that requires Stripe to speak. This site does not process payments, create accounts, or endorse Stripe.",
"",
diff --git a/lib/page-markdown.test.ts b/lib/page-markdown.test.ts
index 6437926..0246516 100644
--- a/lib/page-markdown.test.ts
+++ b/lib/page-markdown.test.ts
@@ -27,24 +27,14 @@ describe("agent markdown representations", () => {
expect(page.body).toContain(`# Stripe Company History: ${history.events.length} Sourced Events`);
expect(page.body).toContain("not affiliated with, endorsed by, or operated by");
expect(page.body).toContain("https://stripedex.com/history/acquisitions");
- expect(page.body).toContain("## Recent events");
- expect(page.body).toContain(history.events[0]?.title ?? "missing-event");
- expect(page.body).toContain(
- `https://stripedex.com/history/${history.events[0]?.categoryId}/${history.events[0]?.id}`,
- );
- expect(page.body).not.toContain(history.events.at(-1)?.title ?? "missing-oldest");
+ expect(page.body).not.toContain(history.events[0]?.title ?? "missing-event");
+ expect(page.body).not.toContain("/history/acquisitions/openrouter-acquisition-talks-reported");
});
- test("renders a durable event page from the same sourced record", async () => {
- const page = await markdownForPath(
+ test("treats invented per-event routes as missing pages", async () => {
+ expect((await markdownForPath(
"/history/acquisitions/openrouter-acquisition-talks-reported",
- );
- expect(page.status).toBe(200);
- expect(page.body).toContain("Stripe reportedly discusses acquiring OpenRouter");
- expect(page.body).toContain("not affiliated with, endorsed by, or operated by");
- expect(page.body).toContain("https://stripedex.com/history/acquisitions");
- expect((await markdownForPath("/history/acquisitions/not-a-real-event")).status)
- .toBe(404);
+ )).status).toBe(404);
});
test("renders category, volume, about, contact, and privacy pages from the same records", async () => {
@@ -59,6 +49,13 @@ describe("agent markdown representations", () => {
expect(volume.body).toContain("2025");
expect(volume.body).toContain("total volume");
+ const valuation = await markdownForPath("/history/valuation");
+ expect(valuation.status).toBe(200);
+ expect(valuation.body).toContain("| year | valuation | basis | status | sources |");
+ expect(valuation.body).toContain("$159 billion");
+ expect(valuation.body).toContain("transaction implied");
+ expect(valuation.body).toContain("not affiliated with, endorsed by, or operated by");
+
const about = await markdownForPath("/about");
expect(about.body).toContain("founder side projects and aesthetics programs");
expect(visibleText((await markdownForPath("/privacy")).body).length).toBeGreaterThan(500);
diff --git a/lib/page-markdown.ts b/lib/page-markdown.ts
index 0a2c769..9bc9032 100644
--- a/lib/page-markdown.ts
+++ b/lib/page-markdown.ts
@@ -1,11 +1,6 @@
import {
MARKDOWN_CONTENT_TYPE,
} from "./accept";
-import {
- HOME_MARKDOWN_RECENT_EVENT_LIMIT,
- historyEventPath,
- parseHistoryEventPath,
-} from "./history-urls";
import {
aboutDescription,
aboutSections,
@@ -28,6 +23,10 @@ import {
recoveryLinks,
} from "@/app/site-copy";
import { GITHUB_REPOSITORY_URL, SITE_ORIGIN, site } from "@/app/site";
+import {
+ deriveValuationHeadlineRows,
+ deriveValuationPageSeo,
+} from "@/app/history/valuation/valuation-page-model";
import {
loadHistory,
type CategorizedHistoryEvent,
@@ -89,9 +88,8 @@ function eventMarkdown(event: CategorizedHistoryEvent): string {
event.status,
event.confidence === "confirmed" ? undefined : event.confidence,
].filter((value): value is string => value !== undefined).join(" · ");
- const path = historyEventPath(event.categoryId, event.id);
return [
- `### [${event.title}](${SITE_ORIGIN}${path})`,
+ `### ${event.title}`,
"",
status === "" ? event.date : `${event.date} · ${status}`,
"",
@@ -103,34 +101,6 @@ function eventMarkdown(event: CategorizedHistoryEvent): string {
].join("\n");
}
-function eventPageMarkdown(
- history: HistoryCollection,
- categoryId: TimelineCategoryId,
- eventId: string,
-): string | null {
- const event = history.events.find((candidate) => (
- candidate.categoryId === categoryId && candidate.id === eventId
- ));
- if (event === undefined) return null;
- const related = (event.related_events ?? []).flatMap((relatedId) => {
- const relatedEvent = history.events.find(({ id }) => id === relatedId);
- return relatedEvent === undefined
- ? []
- : [`- [${relatedEvent.title}](${SITE_ORIGIN}${historyEventPath(relatedEvent.categoryId, relatedEvent.id)})`];
- });
- return [
- heading(event.title, event.summary),
- independenceSentence,
- "",
- eventMarkdown(event).replace(/^### /u, "## "),
- ...(related.length === 0
- ? []
- : ["## Related events", "", ...related, ""]),
- `Category: [Stripe ${event.categoryLabel.toLocaleLowerCase("en-US")} history](${SITE_ORIGIN}${`/history/${event.categoryId}`})`,
- "",
- ].join("\n");
-}
-
function historyIndexMarkdown(history: HistoryCollection): string {
const categoryLinks = history.categories.map((category) => {
const count = history.events.filter(({ categoryId }) => categoryId === category.id).length;
@@ -147,13 +117,7 @@ function historyIndexMarkdown(history: HistoryCollection): string {
),
independenceSentence,
"",
- `This Markdown index covers the same ${history.events.length} sourced events as the HTML timeline. Category, annual-volume, and valuation pages repeat those records in a narrower view. Individual events also have durable pages at \`/history//\`.`,
- "",
- "## Recent events",
- "",
- ...history.events.slice(0, HOME_MARKDOWN_RECENT_EVENT_LIMIT).flatMap((event) => [
- `- [${event.title}](${SITE_ORIGIN}${historyEventPath(event.categoryId, event.id)}): ${event.date}. ${event.summary}`,
- ]),
+ `This Markdown index covers the same ${history.events.length} sourced events as the HTML timeline. Category, annual-volume, and valuation pages repeat those records in a narrower view.`,
"",
"## Browse by topic",
"",
@@ -298,16 +262,37 @@ function paymentVolumeMarkdown(history: HistoryCollection): string {
].join("\n");
}
+function markdownTableCell(value: string): string {
+ return value.replaceAll("|", "\\|").replaceAll("\n", " ");
+}
+
function valuationMarkdown(history: HistoryCollection): string {
+ const seo = deriveValuationPageSeo(history);
+ const rows = deriveValuationHeadlineRows(history);
+ const table = [
+ "| year | valuation | basis | status | sources |",
+ "| --- | --- | --- | --- | --- |",
+ ...rows.map((row) => {
+ const sources = row.sources
+ .map((source) => `[${source.publisher}](${source.url})`)
+ .join(" · ");
+ return `| ${row.calendarYear} | ${markdownTableCell(row.display)} | ${markdownTableCell(row.basisLabel)} | ${markdownTableCell(row.statusLabel)} | ${markdownTableCell(sources)} |`;
+ }),
+ ];
return [
- heading(
- "Stripe private-company valuation history",
- "Sourced private-company valuation observations for Stripe, with status, basis, and linked evidence.",
- ),
+ heading(seo.title, seo.description),
+ seo.lead,
+ "",
+ "## Yearly headlines",
+ "",
+ ...table,
+ "",
+ "## Observations and sources",
+ "",
...history.valuations.map((observation) => {
- const sources = observation.sources.map((source) => `[${source.title}](${source.url})`).join(" · ");
+ const sources = observation.sources.map((source) => `[${source.publisher}](${source.url})`).join(" · ");
return [
- `### ${observation.valuation.display}`,
+ `### ${observation.title}`,
"",
`${observation.effective_date} · ${observation.status} · ${observation.valuation.basis}`,
"",
@@ -348,12 +333,6 @@ export async function markdownForPath(pathname: string): Promise
Date: Sat, 22 Aug 2026 16:12:36 +0000
Subject: [PATCH 3/3] Assert the valuation lead from the advancing fixture
Keep the independence sentence in the derived answer lead without
using a matcher that rejected the Unicode apostrophe.
Co-authored-by: ben <0thernet@users.noreply.github.com>
---
app/history/valuation/page.test.tsx | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/app/history/valuation/page.test.tsx b/app/history/valuation/page.test.tsx
index 33d3e1c..b7ddcb3 100644
--- a/app/history/valuation/page.test.tsx
+++ b/app/history/valuation/page.test.tsx
@@ -52,11 +52,12 @@ describe("stripedex.com valuation history", () => {
expect(updatedSeo).toMatchObject({
description: expect.stringContaining("$200 billion 2027 company tender"),
- lead: expect.stringContaining("$200 billion in 2027"),
+ lead: expect.stringMatching(
+ /\$200 billion in 2027.*not affiliated with, endorsed by, or operated by/su,
+ ),
title: "Stripe Valuation History by Year, 2011–2027",
yearRange: "2011–2027",
});
- expect(updatedSeo.lead).toContain("not affiliated with, endorsed by, or operated by");
expect(updatedMetadata).toMatchObject({
description: expect.stringContaining("$200 billion 2027 company tender"),
openGraph: {