The Second Layer of AI SEO: Identity, Values, and a Map That Updates Itself

A while back I wrote a three part series on AI SEO: why your site now has a non human audience, how I implemented the first layer on this Next.js site, and how to test it and where it is heading. That series built the plumbing: an AI aware robots.txt, an llms.txt, a structured ai-context endpoint, and JSON-LD on every page.

Then I went back through all of it with fresh eyes, and found two gaps that only show up once the catalog has grown to a few hundred posts. This is the write up of the second pass. Everything here is running in production at destbreso.com.


Two questions the first pass never answered

The first layer is good at one thing: telling a machine what pages exist here. A crawler reads llms.txt, follows the links, indexes the articles. Useful, but incomplete.

Ask a modern AI answer engine two different questions and the gap appears:

  • "Who is David Estévez and what does he stand for?" The first layer had no answer. My values lived in a manifesto page as prose a human reads, invisible to a machine that wants to quote a source.
  • "What does he write about?" The first layer answered with a hand typed list that was already wrong, because I had shipped six new series since I wrote it.

The first is branded and personal brand SEO: an engine should be able to state who you are, from a source, without guessing. The second is scalability: the answer has to reflect today's catalog, not a snapshot from the day I wrote the code.


Identity and values as data, not prose

The fix for the first gap was to stop treating identity as page copy and start treating it as a typed record. One source of truth, fanned out to every machine surface:

identity: {
  tagline: "Mathematician and software developer sharing a personal lab ...",
  mission: "An unconventional portfolio: a personal canvas and a lab for ...",
  languages: ["English", "Spanish"],
  values: [
    { name: "Curiosity over gatekeeping", description: "No prerequisites, just curiosity." },
    { name: "Learning by building", description: "A lab that grows with every addition." },
    // ...four more, lifted verbatim from the manifesto
  ],
  writesAbout: ["Algorithm design and analysis", "Data structures", /* ... */],
}

That single object now feeds the Person JSON-LD (as description, disambiguatingDescription, and knowsLanguage), the llms.txt file (a "Who I Am" and "What I Value" block), the ai-context JSON, and the FAQ. Edit it once, and every surface an AI reads updates together. The values are quoted straight from my manifesto, so the human home and the machine record can never disagree.

The rule I held myself to: a machine readable claim that overstates is worse than no claim at all. Every value, every domain, every sentence traces back to something a person already wrote and meant.


A knowledge map that updates itself

The second gap was self inflicted. My knowsAbout list, the schema property that tells an engine what I know, was thirteen topics I typed by hand. Every new series made it more incomplete.

So I deleted the list and derived it from the content instead:

// Curated durable anchors, guaranteed present even for a fresh catalog...
const CORE = ["Mathematics", "Algorithm Design", "Machine Learning", /* ... */];

// ...plus every live series (a collection with two or more published posts).
export function deriveKnowsAbout(): string[] {
  return dedupe([...CORE, ...publishedCollections()]).slice(0, 32);
}

Now the same derived list drives Person.knowsAbout, the "What I Write About" block in llms.txt, and the topic_areas array in the JSON endpoint. Publish a new series, from parameterized complexity to a field guide on algorithms under a handicap, and it shows up in all three with no code change.

The design choice worth naming is the split. Pure derivation is empty on day one, when there is nothing to derive from. Pure curation is stale by month three. A small curated core plus derivation from live content gives you both: a sensible answer immediately, and an accurate one forever.


One FAQ, two audiences

The FAQ was a small disaster hiding in plain sight. There were two copies: the visible page with about twenty questions, and a separate hand kept array of ten questions that fed the FAQPage schema. They had drifted, and one schema answer even named the wrong framework version. Search engines were reading the stale copy.

I collapsed both into one typed source that the page renders and the schema reads:

export const faqCategories: FaqCategory[] = [ /* ... */ ];
export const allFaqs = faqCategories.flatMap((c) => c.questions);

Then I added a category aimed squarely at conversational search: "Who is David Estévez?", "What does he write about?", "What values guide this site?", "What is destbreso.com?". These are the exact questions a person types into an AI assistant, and now there is one canonical, quotable answer to each, visible to humans and machines from the same file.


Open Graph images without the busywork

The last piece was almost embarrassing. Every article's metadata pointed at a social preview image named og-blog-<slug>.png. None of those files had ever existed. Every share, on every platform, fell back to nothing.

The lazy fix is to draw one image per post forever. The scalable fix is one route that renders a card on demand:

// /og?title=...&eyebrow=... -> a terminal styled 1200x630 card, generated at the edge
export function ogImageUrl({ title, eyebrow }) {
  const qs = new URLSearchParams({ title, eyebrow });
  return `${siteConfig.url}/og?${qs}`;
}

Every surface, blog posts, packages, demos, the site default, now points at that one generator. A new post gets a correct preview card the moment it ships, and I never touch an image file.


The real test: publish something and watch

The honest test of a machine readable layer is not a validator passing once. It is this: add new content and check what updates on its own.

When I publish a new series now, the topic appears in llms.txt, in the JSON endpoint, and in Person.knowsAbout. The series index gets a correct sitemap entry (I also fixed a slug bug that had been emitting 404 sitemap URLs for any collection with an accented name). A new post gets an Open Graph card and a BlogPosting node with a real image. Nothing on that list is something I do by hand.

That is the difference between the two layers. The first tells a machine your site exists. The second tells it who you are, what you value, and what you actually cover, and keeps telling the truth as you change. The self updating part is what turns AI SEO from a one time chore into something that just runs.


Series Navigation

  1. Your Website Has a New Audience (And It's Not Human) -> Why AI SEO matters
  2. Implementing AI SEO on a Next.js Site -> The practical guide
  3. Testing AI SEO and What Comes Next -> Verification and the road ahead
  4. The Second Layer: Identity and Self Maintenance -> You are here

References