diff --git a/docs/public-knowledge.md b/docs/public-knowledge.md index e08c06f..10bed30 100644 --- a/docs/public-knowledge.md +++ b/docs/public-knowledge.md @@ -39,8 +39,15 @@ general Markdown out of the image and explicitly includes that directory. Staged and private notes therefore cannot enter production through a broad build context. -`/knowledge/:slug` resolves only against the local published graph. Unknown -slugs return 404; the site does not probe Cameron's PDS for a fallback document. +Knowledge entries keep a stable `slug` as their repository, graph, manifest, +and ATProto record identity. An optional nested `route`, such as +`language-models/gemini`, places the public page under a subject namespace +without changing that identity. The Standard.site mirror uses the same nested +path. When `route` differs from `slug`, `/knowledge/` remains a permanent +compatibility redirect to the canonical route. + +Knowledge routes resolve only against the local published graph. Unknown routes +return 404; the site does not probe Cameron's PDS for a fallback document. ## Landing map @@ -62,7 +69,8 @@ editorial judgment. Frontmatter carries the editorial contract: -- public identity: `title`, `slug`, `summary`, `kind`, and `status`; +- public identity: `title`, stable `slug`, optional nested `route`, `summary`, + `kind`, and `status`; - claim handling: `claimMode`, `perspectiveOwner`, `confidence`, and `sources`; - graph structure: `topics` and `related`; - provenance: `aiAssisted`, `generatedBy`, and optional `sourceDigest`; diff --git a/knowledge/published/gemini-model-series.md b/knowledge/published/gemini-model-series.md index a65d36e..b3d5c6b 100644 --- a/knowledge/published/gemini-model-series.md +++ b/knowledge/published/gemini-model-series.md @@ -47,11 +47,12 @@ aiAssisted: true generatedBy: Co updated: '2026-07-21T19:40:00.000Z' reviewStatus: approved -reviewedBy: Cameron -reviewedAt: '2026-07-21T20:11:05.846Z' publishedAt: '2026-07-21T20:11:05.846Z' -reviewedContentDigest: 'sha256:68ec53edfe69d9b23c4ebcc4a7c68adf99dd7a49bb7e648e453db3ba796016a5' -reviewReceiptDigest: 'sha256:0fa4b7e701786356d57d82348c79c322ff4ec34090741d986ad0293f7839e06d' +route: language-models/gemini +reviewedBy: Cameron +reviewedAt: '2026-07-21T21:07:09.400Z' +reviewedContentDigest: 'sha256:75f2a631c52120507501a5cdddf610448dd6b06240fd0fc6f18b0aecd7ce360f' +reviewReceiptDigest: 'sha256:e98a047546ff1da02a17c22a9e3c2bd630615a1cc4f418a0d6ef8ee945b2fdc3' --- The [Gemini model series](https://deepmind.google/models/gemini/) is Google DeepMind's family of proprietary, natively multimodal foundation models. Gemini models accept combinations of text, code, images, audio, and video, and Google deploys them through consumer products, developer APIs, enterprise services, and agent systems. The series has evolved from multimodal understanding and long context toward explicit reasoning, tool use, computer control, and long-running agent workflows. diff --git a/package.json b/package.json index 76d2253..1ca40ad 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "knowledge:promote": "tsx scripts/promote-knowledge.ts", "knowledge:sync": "tsx scripts/sync-knowledge-atproto.ts", "test:markdown": "tsx --test src/markdown.test.ts", - "test:content": "tsx --test src/about-content.test.ts src/blog-data.test.ts", + "test:content": "tsx --test src/about-content.test.ts src/blog-data.test.ts src/knowledge.test.ts", "test": "pnpm test:markdown && pnpm test:content && pnpm knowledge:check", "typecheck": "tsc --noEmit" }, diff --git a/scripts/check-knowledge.ts b/scripts/check-knowledge.ts index 60c1dff..4021b9a 100644 --- a/scripts/check-knowledge.ts +++ b/scripts/check-knowledge.ts @@ -49,7 +49,7 @@ function scanKnowledgeStyle(entry: Awaited if ( entry.kind !== "journal" && entry.related.length > 0 - && !/\[[^\]]+\]\(\/knowledge\/[a-z0-9-]+\)/.test(body) + && !/\[[^\]]+\]\(\/knowledge\/[a-z0-9-]+(?:\/[a-z0-9-]+)*\)/.test(body) ) { findings.push({ severity: "review", diff --git a/scripts/sync-knowledge-atproto.ts b/scripts/sync-knowledge-atproto.ts index f5dc747..e8f9b85 100644 --- a/scripts/sync-knowledge-atproto.ts +++ b/scripts/sync-knowledge-atproto.ts @@ -6,6 +6,7 @@ import { TID } from "@atproto/common-web"; import "../src/env.ts"; import { knowledgeContentDigest, + knowledgeRoute, loadKnowledgeGraph, type KnowledgeEntry, } from "../src/knowledge.ts"; @@ -148,7 +149,7 @@ function desiredPublication(): PublicationRecord { function publicMarkdown(entry: KnowledgeEntry): string { const absoluteLinks = entry.body.replace( - /\]\(\/knowledge\/([a-z0-9-]+)([^)]*)\)/g, + /\]\(\/knowledge\/([a-z0-9-]+(?:\/[a-z0-9-]+)*)([^)]*)\)/g, "](https://cameron.stream/knowledge/$1$2)", ); const sources = entry.sources.length === 0 @@ -187,7 +188,7 @@ function desiredDocument(entry: KnowledgeEntry, publicationUri: string): Standar description: entry.summary, publishedAt: (entry.publishedAt ?? entry.updated).toISOString(), updatedAt: entry.updated.toISOString(), - path: `/${entry.slug}`, + path: `/${knowledgeRoute(entry)}`, tags: [...new Set(["knowledge", entry.kind, ...entry.topics])], textContent: stripMarkdown(markdown).slice(0, 100_000), content: { @@ -494,7 +495,9 @@ async function main(): Promise { const manifestEntry = manifest.entries[entry.slug]; const createRkey = `knowledge-${entry.slug}`; const matchingDocuments = allDocuments.filter( - (record) => record.value.site === publicationPlan.uri && record.value.path === `/${entry.slug}`, + (record) => + record.value.site === publicationPlan.uri + && record.value.path === `/${knowledgeRoute(entry)}`, ); const rkeyCollision = allDocuments.find( (record) => rkeyFromUri(record.uri) === createRkey && !matchingDocuments.includes(record), @@ -588,7 +591,7 @@ async function main(): Promise { } if ( verifiedDocument.value.site !== verifiedPublication.uri || - verifiedDocument.value.path !== `/${entry.slug}` + verifiedDocument.value.path !== `/${knowledgeRoute(entry)}` ) { throw new Error(`${entry.slug}: post-write document publication/path verification failed`); } @@ -617,7 +620,7 @@ async function main(): Promise { slug: entry.slug, uri: verifiedDocument.uri, cid: verifiedDocument.cid, - canonicalUrl: `${PUBLICATION_URL}/${entry.slug}`, + canonicalUrl: `${PUBLICATION_URL}/${knowledgeRoute(entry)}`, }); } if (manifestChanged) writeManifest(manifest); diff --git a/src/components/knowledge-entry.tsx b/src/components/knowledge-entry.tsx index 022a3ce..e5839c6 100644 --- a/src/components/knowledge-entry.tsx +++ b/src/components/knowledge-entry.tsx @@ -1,6 +1,7 @@ import { raw } from "hono/html"; import { getProfile } from "../data.ts"; import { + knowledgeHref, loadKnowledgeGraph, type KnowledgeEntry as KnowledgeEntryData, } from "../knowledge.ts"; @@ -9,11 +10,12 @@ import { PageNav } from "./page-nav.tsx"; import { ProtocolLinks } from "./protocol-links.tsx"; function EntryLink({ entry }: { entry: KnowledgeEntryData }) { - return {entry.title}; + return {entry.title}; } -export async function getKnowledgeEntry(slug: string): Promise { - return (await loadKnowledgeGraph()).bySlug.get(slug); +export async function getKnowledgeEntry(routeOrLegacySlug: string): Promise { + const graph = await loadKnowledgeGraph(); + return graph.byRoute.get(routeOrLegacySlug) ?? graph.bySlug.get(routeOrLegacySlug); } export async function KnowledgeEntry({ diff --git a/src/components/knowledge-index.tsx b/src/components/knowledge-index.tsx index 444f44c..892d80b 100644 --- a/src/components/knowledge-index.tsx +++ b/src/components/knowledge-index.tsx @@ -3,6 +3,7 @@ import { getProfile } from "../data.ts"; import { formatKnowledgeDate, knowledgeDocumentClassLabel, + knowledgeHref, loadKnowledgeGraph, type KnowledgeEntry, } from "../knowledge.ts"; @@ -19,7 +20,7 @@ function PageList({ entries, searchable = false }: { entries: KnowledgeEntry[]; ? `${entry.title} ${entry.summary} ${entry.kind} ${entry.topics.join(" ")}`.toLowerCase() : undefined} > - + {entry.title} {knowledgeDocumentClassLabel(entry.kind) && ( @@ -82,7 +83,7 @@ export async function KnowledgeIndex() {

NOW

{raw(nowHtml)}
diff --git a/src/index.tsx b/src/index.tsx index eb0f221..110b4db 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -27,6 +27,7 @@ import { getKnowledgeDocumentUri, getKnowledgePublicationUri, } from "./knowledge-atproto.ts"; +import { knowledgeHref, knowledgeRoute } from "./knowledge.ts"; type JSXElement = | import("hono/utils/html").HtmlEscapedString @@ -216,12 +217,15 @@ app.get("/knowledge", async (c) => { app.get("/knowledge/cameron-pfiffer", (c) => c.redirect("/knowledge/cameron", 301)); -app.get("/knowledge/:slug", async (c) => { - const slug = c.req.param("slug"); - const entry = await getKnowledgeEntry(slug); +app.get("/knowledge/:route{.+}", async (c) => { + const requestedRoute = c.req.param("route").replace(/^\/+|\/+$/g, ""); + const entry = await getKnowledgeEntry(requestedRoute); if (!entry) return c.notFound(); + if (requestedRoute !== knowledgeRoute(entry)) { + return c.redirect(knowledgeHref(entry), 308); + } const publicationUri = getKnowledgePublicationUri(); - const documentUri = getKnowledgeDocumentUri(slug); + const documentUri = getKnowledgeDocumentUri(entry.slug); const stream = renderToReadableStream( { + const entry = parseKnowledgeDocument( + draftSource("gemini-model-series", "language-models/gemini"), + "gemini-model-series.md", + true, + ); + + assert.equal(knowledgeRoute(entry), "language-models/gemini"); + assert.equal(knowledgeHref(entry), "/knowledge/language-models/gemini"); + assert.equal(entry.slug, "gemini-model-series"); +}); + +test("binds a route change into the reviewed content digest", () => { + const flat = parseKnowledgeDocument(draftSource("gemini-model-series"), "flat.md", true); + const nested = parseKnowledgeDocument( + draftSource("gemini-model-series", "language-models/gemini"), + "nested.md", + true, + ); + + assert.notEqual(knowledgeContentDigest(flat), knowledgeContentDigest(nested)); +}); + +test("rejects routes that are not normalized path segments", () => { + assert.throws(() => parseKnowledgeDocument( + draftSource("gemini-model-series", "/language-models/Gemini"), + "invalid-route.md", + true, + )); +}); + +test("captures multiple knowledge path segments in Hono", async () => { + const app = new Hono(); + app.get("/knowledge/:route{.+}", (context) => context.text(context.req.param("route"))); + + const response = await app.request("/knowledge/language-models/gemini"); + assert.equal(response.status, 200); + assert.equal(await response.text(), "language-models/gemini"); +}); + +test("indexes canonical nested routes and rejects route collisions", async () => { + const root = await mkdtemp(join(tmpdir(), "knowledge-routes-")); + try { + await mkdir(join(root, "staged")); + await writeFile( + join(root, "staged", "gemini-model-series.md"), + draftSource("gemini-model-series", "language-models/gemini"), + ); + await writeFile( + join(root, "staged", "gemini-alias.md"), + draftSource("gemini-alias", "language-models/gemini"), + ); + + const graph = await loadKnowledgeGraph({ root, includeDrafts: true }); + assert.equal(graph.byRoute.get("language-models/gemini")?.slug, "gemini-alias"); + assert.ok(graph.errors.includes( + "duplicate route language-models/gemini (gemini-alias, gemini-model-series)", + )); + } finally { + await rm(root, { recursive: true, force: true }); + } +}); diff --git a/src/knowledge.ts b/src/knowledge.ts index d551f18..aea64cc 100644 --- a/src/knowledge.ts +++ b/src/knowledge.ts @@ -9,9 +9,14 @@ const KnowledgeSourceSchema = z.object({ url: z.string().url(), }); +const KnowledgeRouteSchema = z.string().regex( + /^[a-z0-9]+(?:-[a-z0-9]+)*(?:\/[a-z0-9]+(?:-[a-z0-9]+)*)*$/, +); + const KnowledgeMetadataSchema = z.object({ title: z.string().trim().min(1), slug: z.string().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/), + route: KnowledgeRouteSchema.optional(), summary: z.string().trim().min(1).max(500), kind: z.enum([ "agent", @@ -56,6 +61,7 @@ export interface KnowledgeEntry extends KnowledgeMetadata { export interface KnowledgeGraph { entries: KnowledgeEntry[]; bySlug: Map; + byRoute: Map; errors: string[]; } @@ -68,6 +74,14 @@ export function knowledgeDocumentClassLabel( return undefined; } +export function knowledgeRoute(entry: Pick): string { + return entry.route ?? entry.slug; +} + +export function knowledgeHref(entry: Pick): string { + return `/knowledge/${knowledgeRoute(entry)}`; +} + function assertPublishable(metadata: KnowledgeMetadata, sourceName: string): void { if (metadata.claimMode !== "factual" && !metadata.perspectiveOwner) { throw new Error(`${sourceName}: perspective entries require perspectiveOwner`); @@ -166,6 +180,7 @@ export function knowledgeContentDigest(entry: KnowledgeEntry): string { const projection = { title: entry.title, slug: entry.slug, + route: entry.route, summary: entry.summary, kind: entry.kind, status: entry.status, @@ -212,6 +227,7 @@ export async function loadKnowledgeGraph(options?: { ...drafts, ]; const bySlug = new Map(); + const byRoute = new Map(); for (const entry of entries) { const existing = bySlug.get(entry.slug); @@ -220,6 +236,14 @@ export async function loadKnowledgeGraph(options?: { continue; } bySlug.set(entry.slug, entry); + + const route = knowledgeRoute(entry); + const existingRoute = byRoute.get(route); + if (existingRoute) { + errors.push(`duplicate route ${route} (${existingRoute.slug}, ${entry.slug})`); + continue; + } + byRoute.set(route, entry); } for (const entry of entries) { @@ -236,7 +260,7 @@ export async function loadKnowledgeGraph(options?: { for (const entry of entries) entry.backlinks.sort(); entries.sort((a, b) => a.title.localeCompare(b.title)); - return { entries, bySlug, errors }; + return { entries, bySlug, byRoute, errors }; } export function formatKnowledgeDate(date: Date): string {