Repository navigation
⚡ Rule pages - Read the rule list for the build from a file, not from TinaCloud - #2803
Merged
Merged
Conversation
…on (#2801) The build prerenders one page for each rule folder. The list of those folders came from paged TinaCloud requests during the Docker build. scripts/validate-rule-categories.js now writes the list to rule-folders.json in the project root when its check passes. The workflow step "Validate rule categories" and `pnpm prepare:content` both run this script, so no workflow change is necessary. The file is in the Docker build context. The list has each folder in public/uploads/rules that has a rule.mdx file. Archived rules are in the list, because they have a page. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UTtNDpWnuaUUQ2winSSgMb
…folders.json (#2801) generateStaticParams got the rule list from 20 TinaCloud requests of 200 rules each. A failed request ended the paging with no error, and the build then prerendered only the rules of the earlier pages. It now reads rule-folders.json, which the content preparation writes from the content checkout. If the file is missing or is not a non-empty list of folder names, it uses the paged Tina requests as before, so local development needs no new step. One log line gives the source and the number of rules. The paging code moved from page.tsx to lib/ruleFolders.ts without a change in behavior, so that unit tests can cover the two sources. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UTtNDpWnuaUUQ2winSSgMb
…rom generateStaticParams (#2801) generateStaticParams sent the generated categoryConnection query to get the file names of all categories. That query asks for all fields of each category, of each rule in the category and of the references of those rules. In 6 of 6 build logs from 10 to 12 Nov, TinaCloud answered with status 500 ("Error fetching category"), and the build continued with no category from this request. With the local Tina server, the request fails after 7 seconds, because one rule has a related rule reference to a file that does not exist. The category pages continue to come from the children of the top categories (topCategoryWithIndexQuery) and from category-uri-title-map.json. The page finds a category only in the index of a top category, so the removed request could add only pages that show NOT FOUND. For the content at 2d41451, the result does not change: 3,933 rules and 239 categories. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UTtNDpWnuaUUQ2winSSgMb
…json (#2801) The section "Python Scripts" describes scripts/validate-rule-categories.js. The script now also writes the rule list that generateStaticParams reads. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UTtNDpWnuaUUQ2winSSgMb
13 tasks
Member
Author
|
🎬 Explainer video for this pull request (about 40 seconds, no sound): the problem, the change, and how it helps. 🤖 Generated with Claude Code pr-2803.mp4 |
joshbermanssw
approved these changes
Nov 13, 2026
This branch was previously deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
✏️
Relates to #2801. This is the fourth task of phase 2.
generateStaticParamsinapp/[filename]/page.tsxreads the list of rules fromrule-folders.json. Before, it sent 20 TinaCloud requests of 200 rules each.generateStaticParamsgets the list from Tina as before. Local development needs no new step.generateStaticParamsdoes not send the generatedcategoryConnectionquery. TinaCloud answered this query with status 500 in 7 of 7 build logs from 10 to 12 Nov. This is a separate commit. See "The removed category request".page.tsxtolib/ruleFolders.tswith no change in behavior, so that unit tests can cover the two sources.README.mdsays that the validation script writes the file.Why
Collecting page datatoGenerating static pageswas 80 to 121 seconds in 7 build logs from 10 to 12 Nov. In production run 42969510432 it was 1.5 minutes (06:10:37 to 06:12:09). In that time, the build sends the 20 rule requests, the category request that fails, and 6 small category requests.How the file gets to the build
scripts/validate-rule-categories.jswrites the names of all rule folders of the content checkout torule-folders.jsonin the repository root when its check passes. The step "Validate rule categories" inbuild-artifacts.ymlandpnpm prepare:contentboth run this script. It is the only Node content preparation that the two run. The script prints one line with the path and the number of names./deploycomment runs the workflow files ofmainand the scripts of the pull request.mainalready has this step, so/deploycan measure this change before the merge. A new workflow step would not run in that test..gitignorehas an entry for the file..dockerignorehas no entry that matches it, so the file is in the Docker build context. The file is not in the content checkout folder.COPY . .in the builder stage of theDockerfilecopies the file to/app.next buildruns in/appand reads the file from its working directory.A test image with the same
COPY . .line and the.dockerignoreof the repository has/app/rule-folders.jsonwith 3,933 entries.What
generateStaticParamsreturns_sys.relativePath). Not theurifield.public/uploads/rulesthat has arule.mdxfile.categoryConnection,topCategoryWithIndexQuery,category-uri-title-map.jsontopCategoryWithIndexQuery,category-uri-title-map.jsonSet)Set)2d41451The removed category request
generateStaticParamssent the generatedcategoryConnectionquery (50 categories for each request) to get the file names of all categories.Measured:
Error fetching category: Server responded with status code 500, 0.1 to 0.2 seconds before the line with the total. After that error, the code stops the paging of this request.2d41451: the first request fails after 7.4 seconds withUnable to find record public/uploads/rules/avoid-redundant-links.mdx.avoid-repetition/rule.mdxhas that path in itsrelatedlist, and the file isavoid-redundant-links/rule.mdx. This pull request does not show that the status 500 of TinaCloud has the same cause.The static params are the same set before and after:
2d41451categoryConnection(removed): each category file but notindexcategories, because the request failstopCategoryWithIndexQuery: the children of the top categoriescategory-uri-title-map.jsontotal=4172and 4,211 static pages. The local production build of this branch shows the same two numbers.A category file that no top category lists is the only name that the request could add. Such a file is now not prerendered.
dynamicParamsis not set, so it has its default value (true), and the first request renders the URL. The page finds a category only in the index of a top category, so it does not find this category. It then looks for a rule with that name, finds none, and renders the fallback page, which shows NOT FOUND to visitors. Before, the build prerendered that fallback page when the request did not fail. The content has no such file.Proof that the list is the same
2d41451. The query gives 3,933 names and the file gives 3,933 names. 0 names are only in the query. 0 names are only in the file.<folder>/rule.mdx) and the file sorts by folder name. Example: the query hasautomated-ui-testing-sparinglybeforeautomated-ui-testing, because-sorts before/. The order has no effect, because Next.js prerenders each entry.2d41451(12 Nov 05:15 UTC) has 3,454 rules that are not archived and 239 categories. The two sets are the same. The sitemap does not list archived rules, so this check does not cover the 479 archived rules.prerender-manifest.jsonhas 4,172/[filename]routes. They are the same set as the list that the code onmainmakes from the same content (the same requests, sent to the local Tina server): 0 differences. The build log has no[ClientFallbackPage]line.One known difference: the Tina collection matches
rule.mdxat each depth (match.include: "**/rule"). The query thus also returns the first folder of a path such asa/b/rule.mdx. The file has only folders with a directrule.mdx, because the page loads<folder>/rule.mdx. The content has no such file.When the file and TinaCloud differ
The file comes from the content checkout of the job. The pages get their data from TinaCloud, which has the head of the content branch. The head can be some commits newer than the checkout. Before
next buildstarts,tinacms buildwaits until TinaCloud reports that its index of the branch is complete.getRuleDatagets "Unable to find record", so the page is the fallback page, which shows NOT FOUND to visitors. The sitemap lists the URL. The page stays until the Tina webhook revalidates the path, or until the next deploy.dynamicParamsis not set, so it has its default value (true): the first request renders the page from TinaCloud and Next.js stores it. The sitemap of this build does not list the URL.Collecting page data.For production, the job "Check Content Freshness & Retrigger if Stale" starts a new deploy when the head of the content branch is not the commit of the checkout. That deploy uses the newer checkout. For staging and pull request slots, the difference stays until the next deploy.
Phase 3 of #2801 removes this difference, because the build then reads the pages from the same checkout.
Times
CI (TinaCloud):
Collecting page datatoGenerating static pages(7 logs: 84.5, 91.6, 91.8, 80.3, 120.8, 92.8 and 104.5 s). That is approximately 1.5 minutes.topCategoryWithIndexQueryrequests stay. They load approximately 500 documents. The 20 rule requests loaded 3,933 documents. Measure the time with/deployon this pull request.Local Tina server, content
2d41451(measured):allRulesPathsrequestscategoryConnectionrequesttopCategoryWithIndexQueryrequestsgenerateStaticParamsinnext devCollecting page datainnext buildHow to check a CI run
📋 validate-rule-categories: wrote <N> rule folders to .../rule-folders.json.📋 generateStaticParams: rules=<N> source=rule-folders.json. The two numbers must be the same.source=Tina (rule-folders.json: <cause>), the file did not get to the build, and the build used the paged requests.🚀 generateStaticParams: rules + categories total=<M>and the number of static pages must agree with a build of the same content before this change.Error fetching category: Server responded with status code 500.Checks
pnpm exec tinacms build --local --skip-indexing --skip-cloud-checks(with--port 4102 --datalayer-port 9102), thengit diff --quiet -- tina/tina-lock.json: no difference.pnpm lint: 344 files, no problem.pnpm test: 30 suites passed and 1 skipped. 260 tests passed and 8 skipped. 28 tests are new.tsc --noEmit: no error.pnpm run validate:rule-categories <content>(the CI command) andpnpm prepare:content: each wrote 3,933 names. With content that fails the check, the command stops with exit code 1 and writes no file.tinacms build --local --skip-cloud-checks -c "NODE_ENV=production next build"(with the same ports): success, 4,211 static pages.Notes
rule-folders.json(170 KB) to.next/standalone, so the file is also in the final image. The production server does not read it: Next.js 16.2.4 callsgenerateStaticParamsonly in the build and innext dev.Not verified
categoryConnectionrequest, and for the 6topCategoryWithIndexQueryrequests that stay.Dockerfileof the repository. It needs the build secrets.Merge order
#2802 changes the imports, removes lines 19 to 159 and changes
generateMetadatainapp/[filename]/page.tsx. A three-way merge of the two changes to that file (git merge-file) has no conflict.Screenshot (optional)
✏️
Log lines from local runs with content
2d41451:🤖 Generated with Claude Code
https://claude.ai/code/session_01UTtNDpWnuaUUQ2winSSgMb