Skip to content

llms.txt orders undeclared API resources differently from the site #25

Description

@oreofeolurin

Rewritten. The original version of this issue said llms.txt lists operations in spec order and so ignores x-nav-order. That was wrong. llms.txt lists resources (tags), not operations, and the Pagefind records don't use insertion order for ranking. I checked by putting x-nav-order into a real spec and diffing the generated files, which come out identical either way. #26 was written against the original description, so apologies to @anisayakmitra-in for the wasted trip. The actual inconsistency is below, and it's in the same function.

The problem

apiResources() in scripts/build-search.mjs builds the resource list for llms.txt by seeding declared tags from spec.tags, then picking up whatever else turns up as it walks the paths:

const tags = new Map();
for (const t of spec.tags ?? []) tags.set(t.name, );
for (const item of Object.values(spec.paths ?? {})) {
  for (const op of Object.values(item)) {
    if (op && Array.isArray(op.tags)) for (const t of op.tags) if (!tags.has(t)) tags.set(t, "");
  }
}

So undeclared tags land in traversal order. normalize() in lib/openapi.ts sorts them alphabetically after the declared ones:

.sort((a, b) => {
  const ia = tagOrder.indexOf(a);
  const ib = tagOrder.indexOf(b);
  if (ia === -1 && ib === -1) return a.localeCompare(b);
  if (ia === -1) return 1;
  if (ib === -1) return -1;
  return ia - ib;
})

The two disagree whenever a spec has tags that aren't declared at the document root, which is most specs coming out of codegen. On a NestJS-generated spec I get Requests, Customers, Webhooks, Events, Payments, … in llms.txt against Configuration, Customers, Events, … in the sidebar. Same resources, different order, no reason for it.

Low stakes, but it's the kind of drift that makes an LLM reading llms.txt describe the docs in an order nobody sees on the site.

Proposed solution

Mirror normalize()'s ordering in apiResources(): declared tags in document order, everything else alphabetically after them.

Alternatives

The real fix is that build-search.mjs re-parses the spec instead of going through normalize(), which is also why the tag display-name splitter is duplicated there (see #20). One shared implementation would close this whole class of drift. That's a bigger change than this issue needs, since the script is plain .mjs running outside the Next build, but worth noting as the direction.

Context

Came out of reviewing #21. Not a regression from anything, the two orderings have just never been compared.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions