Context
learn/tree.json has two downstream consumers: the portal learn-viewer nav, AND the checked-in public SEO/LLM outputs apps/portal/llms.txt + apps/portal/sitemap.xml (generated from tree.json by buildScripts/release/prepare.mjs → getSitemapXml/getLlmsTxt). A tree.json change that doesn't regenerate those leaves the public SEO surface stale (missing/extra URLs).
The Problem (recurrence)
This footgun has now hit two PRs in one session: #12512 (my learn/ prune — de-published audit URLs lingered in the SEO outputs; @neo-gpt's RA2) and #12521 (gpt's OwnAgentTeam guide — the new URL absent from the SEO outputs; my RA). The regeneration is a manual step that's easy to miss on any tree.json edit. Both reviewers caught it by hand — exactly the kind of friction CI should mechanize.
The Fix
A CI assertion (extend lint-tree-json or a sibling lint) that regenerates the SEO outputs in-memory from the current tree.json and fails if the checked-in apps/portal/llms.txt / apps/portal/sitemap.xml differ (modulo volatile fields like sitemap lastmod, which already has stable-fallback handling). Fail-message: "tree.json changed but SEO outputs are stale — regenerate via the prepare.mjs SEO step."
Acceptance Criteria
Related
Empirical: #12512 RA2 + #12521 RA (same class). Source: buildScripts/docs/seo/generate.mjs, buildScripts/release/prepare.mjs. Sibling tree.json guards: #12511/#12513.
Origin Session ID: c2800781-b204-4883-a52f-2979a560718b
Context
learn/tree.jsonhas two downstream consumers: the portal learn-viewer nav, AND the checked-in public SEO/LLM outputsapps/portal/llms.txt+apps/portal/sitemap.xml(generated from tree.json bybuildScripts/release/prepare.mjs→getSitemapXml/getLlmsTxt). A tree.json change that doesn't regenerate those leaves the public SEO surface stale (missing/extra URLs).The Problem (recurrence)
This footgun has now hit two PRs in one session: #12512 (my learn/ prune — de-published audit URLs lingered in the SEO outputs; @neo-gpt's RA2) and #12521 (gpt's OwnAgentTeam guide — the new URL absent from the SEO outputs; my RA). The regeneration is a manual step that's easy to miss on any tree.json edit. Both reviewers caught it by hand — exactly the kind of friction CI should mechanize.
The Fix
A CI assertion (extend
lint-tree-jsonor a sibling lint) that regenerates the SEO outputs in-memory from the currenttree.jsonand fails if the checked-inapps/portal/llms.txt/apps/portal/sitemap.xmldiffer (modulo volatile fields like sitemaplastmod, which already has stable-fallback handling). Fail-message: "tree.json changed but SEO outputs are stale — regenerate via the prepare.mjs SEO step."Acceptance Criteria
apps/portal/llms.txt/sitemap.xmlare out of sync withlearn/tree.json(URL set), ignoring volatilelastmod.Related
Empirical: #12512 RA2 + #12521 RA (same class). Source:
buildScripts/docs/seo/generate.mjs,buildScripts/release/prepare.mjs. Sibling tree.json guards: #12511/#12513.Origin Session ID: c2800781-b204-4883-a52f-2979a560718b