Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions public/llms.txt
Original file line number Diff line number Diff line change
@@ -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/<slug>
- All published posts: https://detached-node.dev/posts/<slug>
- 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
14 changes: 12 additions & 2 deletions src/app/feed.xml/route.ts
Original file line number Diff line number Diff line change
@@ -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();
Expand Down Expand Up @@ -42,6 +42,7 @@ export async function GET(): Promise<Response> {
<link>${link}</link>
<description>${description}</description>
<pubDate>${pubDate}</pubDate>
<author>${escapeXml(CONTACT_EMAIL)} (${escapeXml(SITE_TITLE)})</author>
<guid isPermaLink="true">${link}</guid>
</item>`;
})
Expand All @@ -55,6 +56,15 @@ export async function GET(): Promise<Response> {
<description>${escapeXml(SITE_DESCRIPTION)}</description>
<language>en</language>
<lastBuildDate>${new Date().toUTCString()}</lastBuildDate>
<author>${escapeXml(CONTACT_EMAIL)} (${escapeXml(SITE_TITLE)})</author>
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMPORTANT — Channel-level <author> is not in the RSS 2.0 spec.

Per rssboard.org/rss-specification, <author> is documented only under "Elements of <item>":

<author> is an optional sub-element of <item>. It's the email address of the author of the item.

There is no <author> listed in either the required or optional channel elements section. Channel-level author info uses <managingEditor> (which this PR already adds correctly on the next line) and <webMaster>.

Practical impact: the per-item <author> additions are spec-correct. The channel-level one will trigger warnings from validator.w3.org/feed and is silently dropped by strict aggregators. xmllint --noout only verifies well-formed XML — it does not validate against the RSS 2.0 schema, so the green test plan does not catch this.

Fix: delete this line; <managingEditor> already covers the role.

<copyright>Copyright © 2024–${new Date().getFullYear()} ${escapeXml(SITE_TITLE)}. All rights reserved.</copyright>
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION — Mixed signal on AI/content use policy.

public/llms.txt:13 explicitly states the content is "available for AI training, research, and citation with attribution." But this <copyright> element emits "All rights reserved" into the RSS feed.

Strict aggregators and AI ingestion pipelines that read both surfaces will see a contradiction. Three options:

  1. Drop "All rights reserved" — e.g., Copyright © 2024–${year} ${SITE_TITLE}. (no rights clause).
  2. Switch to a Creative Commons hint: CC BY 4.0 — citation requested.
  3. Match the llms.txt language: Copyright © 2024–${year} ${SITE_TITLE}. Available for AI training, research, and citation with attribution.

Verified via curl http://localhost:3000/feed.xml | xmllint --noout (parses) and side-by-side with public/llms.txt.

Non-blocking — neither a spec violation nor a runtime issue.

<managingEditor>${escapeXml(CONTACT_EMAIL)} (${escapeXml(SITE_TITLE)})</managingEditor>
<generator>Next.js + Payload CMS</generator>
<image>
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMPORTANT — RSS 2.0 <image> sub-element exceeds maximum dimensions.

og-default.png is 1200 × 630, but the RSS 2.0 spec (Harvard original, RSSBoard) is unambiguous:

Maximum value for width is 144, default value is 88.
Maximum value for height is 400, default value is 31.

Strict aggregators (and validator.w3.org/feed) will reject this; lenient ones silently drop the <image> block. Same spec-compliance class as the channel-level <author> you just removed in d8c3cee.

Two ways out:

  1. Generate a 144×144 (or 88×88) variant — e.g. public/rss-icon-144.png — and reference it here instead. This is what most production RSS feeds do; the OG image is the wrong asset for this slot.
  2. Drop the <image> block entirely. <image> is optional in RSS 2.0; absence is preferable to a spec-violating one that some readers will reject.

Option 1 is the right end-state, but option 2 is a clean one-line revert if you want to unblock merge today and file the icon work as a follow-up.

<url>${escapeXml(siteUrl)}/og-default.png</url>
<title>${escapeXml(SITE_TITLE)}</title>
<link>${escapeXml(siteUrl)}</link>
</image>
<atom:link href="${escapeXml(siteUrl)}/feed.xml" rel="self" type="application/rss+xml" />${items}
</channel>
</rss>`;
Expand Down
46 changes: 46 additions & 0 deletions src/app/llms-full.txt/route.ts
Original file line number Diff line number Diff line change
@@ -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<Response> {
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",
},
});
}
4 changes: 3 additions & 1 deletion src/lib/site-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.";
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION (plan-controlled, not implementer-controlled per R9)siteDescription is not refreshed in the schema-emitted copy or the PWA manifest.

Two pre-existing duplicate copies of the old "A tech blog and reference catalog on agentic AI." string remain on this branch:

  • src/lib/schema/config.ts:20 — used by JSON-LD schema generators; this is the copy Google + AI citation crawlers pick up
  • src/app/manifest.webmanifest:4 — PWA install prompt + browser bookmark metadata

This is plan-controlled — issue #390 names only src/lib/site-config.ts, so the implementer correctly followed scope. But the PR's stated goal is "citation-friendly framing", and the schema config copy is exactly what citation crawlers consume. Updating only site-config.ts leaves the goal partially unmet.

Two options for follow-up:

  1. Amend this PR to also update both copies (small, mechanical, low-risk).
  2. File a follow-up issue to extend the description-refresh to all three call sites.

Either is reasonable; flagging here so the gap doesn't silently survive.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION (plan-controlled, R9)siteDescription refresh isn't propagated to the two other copies that still ship the old "tech blog and reference catalog" string:

  • src/lib/schema/config.ts:19-20 — JSON-LD SITE_CONFIG.description (rendered into the WebSite schema on every page)
  • src/app/manifest.webmanifest:4 — PWA install prompt description

This is the same finding as the prior bot review's #2, restated because it's unaddressed in d8c3cee. Framing as plan-controlled (R9) rather than implementer-controlled: the PR scope as written touched siteDescription in one file; nothing said "and propagate everywhere". The downstream consumer here is the JSON-LD WebSite entity, which is exactly the surface infra(seo) PRs should keep in lockstep — leaving it stale means search engines and Schema.org consumers see the old description even after this ships.

Either fold both edits into this PR (3-line change total) or file a follow-up issue tagged seo and reference it in the merge commit.


export const siteAuthor = "detached-node";

export const CONTACT_EMAIL = "julian.kennon.d@gmail.com";

export const siteKeywords = [
"agentic AI",
"autonomous systems",
Expand Down
Loading