Skip to content

fix(docs): order llms-full.txt by the navigation tree - #201

Merged
hotlong merged 1 commit into
mainfrom
claude/issue-196-llms-full-navigation-order
Aug 26, 2026
Merged

fix(docs): order llms-full.txt by the navigation tree#201
hotlong merged 1 commit into
mainfrom
claude/issue-196-llms-full-navigation-order

Conversation

@hotlong

@hotlong hotlong commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Fixes #196

llms-full.txt enumerated source.getPages(LANG) and joined the result unsorted, so the order pages appear in was whatever order the loader happened to finish reading files in. Stable inside one build, not across builds.

What changed

One file, apps/docs/app/llms-full.txt/route.ts. The route now walks source.getPageTree(LANG)meta.json order, which is the site navigation — with flattenTree, and resolves each tree node back to its page with source.getNodePage. Everything else about the route is untouched: each page's text is still resolved against that page's own URL through absoluteLinks(text, pageUrl), exactly as landed in #178 (9a1a7ad).

Two smaller things in the same file, both consequences of the enumeration moving:

  • The LANG pin's doc comment named source.getPages() as the call whose language argument is mandatory. It now names source.getPageTree() too, because that is the call this route makes, and dropping the argument from it has the identical 335-pages-over-7-locales failure. Wording pinned to the sibling comment in llms.txt.
  • getNodePage returns undefined for a node it cannot resolve. This throws rather than filtering. A silently dropped page is a worse defect than the one being fixed here, and this is the only place a drop could enter.

Why the navigation tree, not a sort

Per the grading comment on the card. Unlike sitemap.xml, where entry order carries nothing to a crawler, the order pages appear in this file is read — it is one long document. So a deterministic order is necessary and not sufficient: alphabetical would be reproducible and worse than the accident it replaces, scattering build/, deploy/ and operate/ through a flat A-to-Z list. llms.txt already walks the tree, so the index and the full text now agree on sequence rather than contradicting each other. Reproducibility falls out of fixing the meaning.

Verification

All builds are pnpm run build in apps/docs, measured on .next/server/app/llms-full.txt.body. The two "after" builds are on the final commit, 8d1cfed.

Determinism, the card's reproduction inverted

build tree sha256 of the body
base 1 origin/main 9a1a7ad, untouched a6b849f121bf34b9cc4496fc3d1925bdee95ebfdeaf07dd42ac70440b3df67c6
base 2 same tree, next build 80f7471b18f552854fb5798824e69a06641088fada2fd7e68653995c0ed32885
after 1 8d1cfed, untouched e84953ee1066becf3a6fae11099f7cd3c3179009ffe5435a2cd34f14c6cea310
after 2 same tree, next build e84953ee1066becf3a6fae11099f7cd3c3179009ffe5435a2cd34f14c6cea310

The defect reproduces on this tree, with the card's exact signature: the two baseline bodies first diverge at the 17th # heading, where the whole configure/ group and the whole deploy/ group trade places. After the change, two consecutive builds are byte-identical.

Membership unchanged

measurement before after
page blocks 79 79
#-prefixed lines 93 93
heading lines 798 798
link targets 646 646
body bytes 685243 685243
body lines 12835 12835
sha256 of the sorted line multiset 35c58edea210d75f... 35c58edea210d75f...

The last row is the strong form of the claim: sorting the body's lines before hashing makes the hash blind to sequence and sensitive to everything else, so a permutation of whole page blocks is the only edit that leaves it unchanged. It is identical across all four builds above.

The #-prefixed line count breaks down as 79 page titles plus 14 shell comments inside fenced code blocks. Reading the 79 per-page prerenders in the same build (.next/server/app/llms.mdx/**) gives 79 page titles and the same 14 in-fence lines, with zero pages leaving a fence unclosed — so that split is a property of each page's own text, not of the order they are concatenated in.

Three independent counts agree that the page set is 79 and unchanged: the 79 prerendered llms.mdx bodies, the locale-surface gate's oracle (79 logical docs page(s), derived from content/docs/), and the 12 meta.json files checked directory by directory against the 79 English .mdx files. No orphan page — nothing that getPages(LANG) returns is unreachable from a meta.json — so the stop-and-report condition on the card did not arise.

The resulting order is the navigation

llms.txt lists its sections in this order: Overview, Use, Build, Configure, Deploy, Operate, Resources, Reference.

llms-full.txt now opens with the five root-level pages in root meta.json order — ObjectOS, Why ObjectOS, Extend Existing Systems, Quickstart, Architecture — and the first section after them is Use, the same one llms.txt lists first: Using ObjectOS, Working with records, Using views, Dashboards, Approvals, Notifications, Profile and settings. That is use/meta.json verbatim. Build, Configure, Deploy, Operate, Resources and Reference follow in the same sequence as the index.

Gates

gate verdict
pnpm run type-check (fumadocs-mdx, next typegen, tsc --noEmit) clean, no diagnostics
pnpm run build Compiled successfully in 34.9s, 996 static pages
node .github/scripts/check-locale-surface.mjs every advertised URL has a source file, and every source file is advertisedsitemap.xml 346 advertised / 346 expected / 0 unexpected / 0 missing / 0 duplicated
pnpm turbo run test 3 self-test(s) passed
check-translation-ownership.mjs 0 translation artifacts touched, 1 other file
control-byte scan of the changed file none

Every gate above was run on 8d1cfed, except turbo run test and the ownership check, whose inputs this diff does not touch.

Cards in flight


Generated by Claude Code

`llms-full.txt` enumerated `source.getPages(LANG)` and joined the result
unsorted, so page order was whatever order the loader finished reading
files in. That is stable inside one build and not across builds: two
consecutive builds of an untouched tree emitted the same 79 pages with
the whole `configure/` group and the whole `deploy/` group swapped.

Order by `source.getPageTree(LANG)` — `meta.json` order, the site
navigation — rather than by a sort. Sorting would be deterministic and
worse: the order pages appear in this file is read, and alphabetical
would scatter `build/`, `deploy/` and `operate/` through a flat A-to-Z
list. `llms.txt` already walks the tree, so the index and the full text
now agree on sequence. Reproducibility falls out of that.

Membership is unchanged: same 79 page blocks, same 93 `#`-prefixed
lines, same 798 heading lines, same 646 link targets, and the sorted
line multiset of the served body is byte-identical to the old one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G5zjYc2BoFV2NjKBBapC7C
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[finding] llms-full.txt page order is not reproducible either — same root cause as #194, second surface

2 participants