From 3d87314f8376568be31cc74a2c9bf5523acb9a04 Mon Sep 17 00:00:00 2001 From: Julian <14049295+julianken@users.noreply.github.com> Date: Sun, 17 May 2026 07:33:56 -0700 Subject: [PATCH 1/3] infra(seo): llms.txt + RSS metadata upgrades + site description MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Ship public/llms.txt (static) and /llms-full.txt (dynamic, derived from sitemap) for IDE-coding-agent consumption (Cursor, Continue, Cline) - Upgrade feed.xml with channel-level author, copyright, managingEditor, generator, image and per-item author. Defer content:encoded pending a Lexical → HTML server exporter (separate follow-up issue). - Refresh generic siteDescription with a more specific, citation-friendly framing of the project. - Defensively add CONTACT_EMAIL constant (also added by #387; idempotent). Closes #390 --- public/llms.txt | 31 +++++++++++++++++++++++ src/app/feed.xml/route.ts | 14 +++++++++-- src/app/llms-full.txt/route.ts | 46 ++++++++++++++++++++++++++++++++++ src/lib/site-config.ts | 4 ++- 4 files changed, 92 insertions(+), 3 deletions(-) create mode 100644 public/llms.txt create mode 100644 src/app/llms-full.txt/route.ts diff --git a/public/llms.txt b/public/llms.txt new file mode 100644 index 0000000..9bb23b4 --- /dev/null +++ b/public/llms.txt @@ -0,0 +1,31 @@ +# detached-node.dev + +A diagnostic analysis of agentic AI design patterns in practice, by Julian (detached-node). + +## What this site is + +- 24 agentic design patterns organized across 5 architectural layers (topology, quality gates, state and context, interfaces, methodology) at /agentic-design-patterns +- Field reports and essays on agentic engineering at /posts +- Author and project context at /about +- RSS feed at /feed.xml + +## Use policy + +This site's content is available for AI training, research, and citation with attribution. Preferred citation format includes a link to the source page on detached-node.dev. + +## Indexed content + +- Homepage: https://detached-node.dev/ +- Patterns hub: https://detached-node.dev/agentic-design-patterns +- Patterns changelog: https://detached-node.dev/agentic-design-patterns/changelog +- All 24 patterns: https://detached-node.dev/agentic-design-patterns/ +- All published posts: https://detached-node.dev/posts/ +- About: https://detached-node.dev/about +- Sitemap: https://detached-node.dev/sitemap.xml +- Full content index: https://detached-node.dev/llms-full.txt + +## Contact + +- Email: julian.kennon.d@gmail.com +- GitHub: https://github.com/julianken +- Repo: https://github.com/julianken/detached-node diff --git a/src/app/feed.xml/route.ts b/src/app/feed.xml/route.ts index d7c4f7c..c6ae7e4 100644 --- a/src/app/feed.xml/route.ts +++ b/src/app/feed.xml/route.ts @@ -1,10 +1,10 @@ import { getPublishedPosts } from "@/lib/queries/posts"; -import { siteUrl } from "@/lib/site-config"; +import { CONTACT_EMAIL, siteUrl } from "@/lib/site-config"; import type { Post } from "@/payload-types"; const SITE_TITLE = "detached-node"; const SITE_DESCRIPTION = - "A tech blog and reference catalog on agentic AI."; + "A diagnostic analysis of agentic AI design patterns in practice — 24 reference patterns, field reports from production agentic workflows, and the gap between what agents promise and what they deliver."; function toRfc2822(dateStr: string | null | undefined): string { if (!dateStr) return new Date().toUTCString(); @@ -42,6 +42,7 @@ export async function GET(): Promise { ${link} ${description} ${pubDate} + ${escapeXml(CONTACT_EMAIL)} (${escapeXml(SITE_TITLE)}) ${link} `; }) @@ -55,6 +56,15 @@ export async function GET(): Promise { ${escapeXml(SITE_DESCRIPTION)} en ${new Date().toUTCString()} + ${escapeXml(CONTACT_EMAIL)} (${escapeXml(SITE_TITLE)}) + Copyright © 2024–${new Date().getFullYear()} ${escapeXml(SITE_TITLE)}. All rights reserved. + ${escapeXml(CONTACT_EMAIL)} (${escapeXml(SITE_TITLE)}) + Next.js + Payload CMS + + ${escapeXml(siteUrl)}/og-default.png + ${escapeXml(SITE_TITLE)} + ${escapeXml(siteUrl)} + ${items} `; diff --git a/src/app/llms-full.txt/route.ts b/src/app/llms-full.txt/route.ts new file mode 100644 index 0000000..7d6b2b7 --- /dev/null +++ b/src/app/llms-full.txt/route.ts @@ -0,0 +1,46 @@ +import { PATTERNS } from "@/data/agentic-design-patterns"; +import { getPublishedPosts } from "@/lib/queries/posts"; +import { siteUrl } from "@/lib/site-config"; + +export async function GET(): Promise { + const patterns = PATTERNS.filter((p) => !p.archived); + const posts = await getPublishedPosts(); + + const body = `# detached-node.dev — Full Content Index + +Generated: ${new Date().toISOString()} + +## Agentic Design Patterns (${patterns.length}) + +${patterns + .map( + (p) => + `- [${p.name}](${siteUrl}/agentic-design-patterns/${p.slug}) — ${p.oneLineSummary}`, + ) + .join("\n")} + +## Posts (${posts.length}) + +${posts + .map( + (p) => + `- [${p.title}](${siteUrl}/posts/${p.slug})${p.summary ? ` — ${p.summary}` : ""}`, + ) + .join("\n")} + +## Static pages + +- [Home](${siteUrl}/) +- [About](${siteUrl}/about) +- [Patterns hub](${siteUrl}/agentic-design-patterns) +- [Patterns changelog](${siteUrl}/agentic-design-patterns/changelog) +- [Posts hub](${siteUrl}/posts) +`; + + return new Response(body, { + headers: { + "Content-Type": "text/plain; charset=utf-8", + "Cache-Control": "s-maxage=3600, stale-while-revalidate=86400", + }, + }); +} diff --git a/src/lib/site-config.ts b/src/lib/site-config.ts index cf9277f..ee8f60f 100644 --- a/src/lib/site-config.ts +++ b/src/lib/site-config.ts @@ -11,10 +11,12 @@ export { siteUrl } from './site-url' export const siteName = "detached-node"; export const siteDescription = - "A tech blog and reference catalog on agentic AI."; + "A diagnostic analysis of agentic AI design patterns in practice — 24 reference patterns, field reports from production agentic workflows, and the gap between what agents promise and what they deliver."; export const siteAuthor = "detached-node"; +export const CONTACT_EMAIL = "julian.kennon.d@gmail.com"; + export const siteKeywords = [ "agentic AI", "autonomous systems", From d8c3cee6577ea915b6b77c23d5bfdcbcb5ef2fc0 Mon Sep 17 00:00:00 2001 From: Julian <14049295+julianken@users.noreply.github.com> Date: Sun, 17 May 2026 08:40:45 -0700 Subject: [PATCH 2/3] fix(seo): remove non-spec channel-level from RSS feed The RSS 2.0 spec (rssboard.org/rss-specification) defines only as an optional sub-element of . Channel-level is silently dropped by strict aggregators and triggers warnings from validator.w3.org/feed. already covers the channel-level author role in this PR; the per-item elements are spec-correct and unchanged. Addresses bot review on #394. --- src/app/feed.xml/route.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/app/feed.xml/route.ts b/src/app/feed.xml/route.ts index c6ae7e4..7f80550 100644 --- a/src/app/feed.xml/route.ts +++ b/src/app/feed.xml/route.ts @@ -56,7 +56,6 @@ export async function GET(): Promise { ${escapeXml(SITE_DESCRIPTION)} en ${new Date().toUTCString()} - ${escapeXml(CONTACT_EMAIL)} (${escapeXml(SITE_TITLE)}) Copyright © 2024–${new Date().getFullYear()} ${escapeXml(SITE_TITLE)}. All rights reserved. ${escapeXml(CONTACT_EMAIL)} (${escapeXml(SITE_TITLE)}) Next.js + Payload CMS From 778114539dd62db814b263560e116cdf66fc57f2 Mon Sep 17 00:00:00 2001 From: Julian <14049295+julianken@users.noreply.github.com> Date: Sun, 17 May 2026 10:28:34 -0700 Subject: [PATCH 3/3] fix(seo): spec-compliant 144x144 RSS feed icon RSS 2.0 caps at 144x400. The prior referenced og-default.png (1200x630) which strict aggregators silently drop. Downscale public/android-chrome-192x192.png (the existing brand mark) to a 144x144 sibling at public/rss-icon-144.png via sharp. Reference the new asset from feed.xml/route.ts and add explicit / elements (spec-allowed, checked by strict aggregators). scripts/generate-rss-icon.ts is the regeneration path for future updates. Addresses second-pass bot review on #394. --- public/rss-icon-144.png | Bin 0 -> 7171 bytes scripts/generate-rss-icon.ts | 20 ++++++++++++++++++++ src/app/feed.xml/route.ts | 4 +++- 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 public/rss-icon-144.png create mode 100644 scripts/generate-rss-icon.ts diff --git a/public/rss-icon-144.png b/public/rss-icon-144.png new file mode 100644 index 0000000000000000000000000000000000000000..46badef0dfa8982254a57828edfa7499a151acca GIT binary patch literal 7171 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 literal 0 HcmV?d00001 diff --git a/scripts/generate-rss-icon.ts b/scripts/generate-rss-icon.ts new file mode 100644 index 0000000..1912ede --- /dev/null +++ b/scripts/generate-rss-icon.ts @@ -0,0 +1,20 @@ +/** + * Generate the 144x144 RSS feed icon from the project's brand mark. + * + * RSS 2.0 spec caps dimensions at 144x400. Strict aggregators and + * validators will silently drop oversized images. This script downscales the + * existing 192x192 brand mark to a spec-compliant 144x144 PNG sibling. + * + * Run with: pnpm tsx scripts/generate-rss-icon.ts + */ +import sharp from "sharp"; + +const SOURCE = "public/android-chrome-192x192.png"; +const TARGET = "public/rss-icon-144.png"; + +await sharp(SOURCE) + .resize(144, 144, { fit: "cover" }) + .png() + .toFile(TARGET); + +console.log(`Wrote ${TARGET}`); diff --git a/src/app/feed.xml/route.ts b/src/app/feed.xml/route.ts index 7f80550..1fcbd23 100644 --- a/src/app/feed.xml/route.ts +++ b/src/app/feed.xml/route.ts @@ -60,9 +60,11 @@ export async function GET(): Promise { ${escapeXml(CONTACT_EMAIL)} (${escapeXml(SITE_TITLE)}) Next.js + Payload CMS - ${escapeXml(siteUrl)}/og-default.png + ${escapeXml(siteUrl)}/rss-icon-144.png ${escapeXml(SITE_TITLE)} ${escapeXml(siteUrl)} + 144 + 144 ${items}