Repository navigation
docs: legal pages mark their English fallback and get zh-Hant from zh-Hans; one main landmark - #318
Merged
Merged
Conversation
…ds or fewer Adds a `seoTitle:` frontmatter line to every English page whose built `<title>` read as one or two words plus the ` | ObjectOS` suffix, so the tab/result title carries the terms a reader searches for while the H1, sidebar and breadcrumb keep the short noun (#166's mechanism). - 67 pages: each gains exactly one line; no `title`, `description` or body line changes. - Every title leads with the page's own specific term, lifted from its description and headings, and renders at 52–60 characters including the suffix (measured on the built HTML). - The three duplicated titles (Approvals, Dashboards, Notifications — each used by a build/configure page and a use page) are now distinct. - English only. Locale siblings are translation artifacts that a non-translator commit may not modify (AGENTS.md, Translation workflow; check-translation-ownership.mjs); the translation pass carries the new key over, and the output report lists the 163 siblings now missing it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FeA1nwBz1ohH65dvffUGKr
…AQ headings, title-weighted per-locale search, consistency pass - Light-mode muted foreground to hsl(0 0% 40%); code comments recoloured in both shiki themes. - Tables get an always-drawn scrollbar and a scroll-driven trailing fade. - FAQ and License FAQ questions become headings. - /api/search builds one locale's index on that locale's first search and weights title > heading > text; check-search-locales gains own-title-buried; smoke-docs asks /api/search in every locale with a nonce control. - Consistency: one data-residency table, one license-validation sentence, ObjectSchema.create, "license" spelling, Configure title, glossary order plus AI seat and Position, logo to the docs home, a translated llms.txt example, three unsourced configure/ai claims removed (with their locale siblings), release-following lines pointed at a populated feed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FeA1nwBz1ohH65dvffUGKr
…ests Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FeA1nwBz1ohH65dvffUGKr
…comments Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FeA1nwBz1ohH65dvffUGKr
…ix apiMethods primitives
The Views page declared views inside a defineObject call under an
object-level `view` key. ObjectStack provides no defineObject, and
ObjectSchema.create rejects `view` as an unknown key. Its views are a
defineView container ({ list, listViews, form, formViews }, each view
bound through `data`) registered on the stack with `views: [...]`, which
both samples now show; both parse with @objectstack/spec 17.6.0.
The REST API page's allowed apiMethods values drop the eight retired ones
the spec strips at parse.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeA1nwBz1ohH65dvffUGKr
…ype-check `next typegen` loads next.config.mjs, and fumadocs-mdx 15.0.7's createMDX() starts init() without awaiting it (dist/next/index.js:14-20). init rewrites every .source/*.ts with fs.writeFile (dist/core-DlDe_Eze.js:232-236), which truncates first. typegen ends in process.exit(0), so it can exit inside that window and leave .source/server.ts empty for tsc: TS2306, CI run 37478051451. Running the fumadocs-mdx CLI after typegen makes the CLI, which awaits its writes, the last writer before tsc. typegen does not read .source. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FeA1nwBz1ohH65dvffUGKr
…ries, zh-Hant 404, Open-in links follow the page shown - patches/fumadocs-ui@16.8.12.patch: the nine accessible names fumadocs-ui 16.8.12 hard-codes in English (Open Search, Toggle Theme, Open/Collapse Sidebar, Copy Anchor Link, Copy/Copied Text, Toggle Menu, and Radix's "Main") read from its i18n context with the old literal as default. Eight keys are a backport of upstream 16.9.0's own names; lib/ui-text supplies all nine through RootProvider in every locale. - app/[lang]/docs/layout.tsx: page-tree entries (sidebar, breadcrumb, prev/next footer) for pages a locale has no translation of carry lang="en", using the docs page's own translatedLocales detection. - app/not-found.tsx: the 404 copy moves into lib/ui-text (notFound), so the zh-Hant string is generated from zh-Hans by gen-zh-hant and checked by --check like every other Traditional string. - Open in ChatGPT / Claude on a translated page sends the assistant to the translated page itself; English pages and fallbacks keep the English .mdx. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FeA1nwBz1ohH65dvffUGKr
…pec 17.7.0 views.mdx: every list-view fragment carries its required top-level columns, fontWeight is a string, the list view types table states the required keys the spec declares, and each fragment says what it omits. flows.mdx: every sample is rewritten from the old trigger/steps shape to FlowSchema's nodes and edges. Email goes through a notify node with channels: ['email']; requires lists automation and triggers. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FeA1nwBz1ohH65dvffUGKr
…ative GitHub icon, code blocks off the landmark list, a localized Open-in prompt axe 4.14.0 reported four failures on every docs page in every locale, English included (#308): - landmark-one-main: the docs layout has no main landmark. DocsPage passes its other props to the article it renders, so the docs page sets role="main" there; no patch needed. - label-content-name-mismatch: the language switcher shows the current language but was named "Choose a language" only. It is now named "English — Choose a language", "Deutsch — Sprache wählen", visible text first, both halves from the i18n context. - svg-img-alt: the GitHub icon was an svg role="img" with no name inside a link already named "GitHub"; it is aria-hidden now. - landmark-unique: every code block's scroll viewport was an unnamed role="region". The role goes; tabIndex 0 stays for keyboard scrolling. The last three are hunks added to patches/fumadocs-ui@16.8.12.patch; its header says why each is a hunk and not a slot, and what an upgrade must re-check. None of the three is fixed upstream as of 16.16.2. The "Open in ChatGPT / Claude" prompt sentence was English in every locale. It is now the ui-text key pageActionsOpenInLLMPrompt, upstream 16.9.0's key name, placeholder and English, translated in six locales and generated for zh-Hant. ui-text's table() now also holds every locale's placeholders to English, so a translation that drops {url} fails the build. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FeA1nwBz1ohH65dvffUGKr
…ree entries #298 and #305 made two promises about every localized docs page that no gate checked afterwards: no control is named in English, and every page-tree entry for an untranslated page carries lang="en". The locale surface gate now reads them off the prerendered HTML under apps/docs/.next/server/app/<locale>/, script bodies (the RSC payload) skipped: - english-chrome-name: an accessible name (aria-label, title, alt, placeholder, svg title, .sr-only text) on a localized page that is one of the English pages' names, outside lang="en". The English set is read off the built English pages, minus GitHub and www.objectos.ai. - untranslated-entry-unmarked: a sidebar item or previous/next card for a page the locale has no source file for, whose text does not resolve to lang="en". The oracle is the content tree, not the app's detection. - translated-entry-marked-english: the over-marking direction. Guards keep it from passing over nothing (a page missing from a locale's build, no English name to compare, no entry of a kind recognised in a locale), and a live control feeds both #305 shapes, built from the run's real oracle values, through the same reader on every run. Ten self-test cases, and the control shown red with a reader blinded to names or to lang. On the HTML main built at cec227a it reports 6239 English names and 5974 unmarked entries; on #305's tree, 0 and 0. No ci.yml change: the existing Locale surface step runs the gate after the build, and pnpm turbo run test runs its self-test. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FeA1nwBz1ohH65dvffUGKr
Below 1280px fumadocs-ui 16.8.12 shows the page's table of contents as a bar rendered in a <header> outside every sectioning element, so the page had two banner landmarks: the site header (#nd-subnav) and this bar. axe 4.14.0 at 390px reported landmark-unique and landmark-no-duplicate-banner on all 48 runs of the #308 sample (8 sections, en/zh-Hans/de, light/dark), on the tree that already carried the other #308 fixes. The bar is a disclosure for the page's headings, so the patch renders it as a <div>; no CSS selects header by element, and the 390px screenshots do not change. Upstream 16.16.2 still renders it as a <header>. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FeA1nwBz1ohH65dvffUGKr
…e inside an English body #305 localized the names of the heading-anchor and code-copy buttons. On a fallback page both buttons sit in the page body, which #298 marks lang="en", so "Ankerlink kopieren" was read with English rules: WCAG 3.1.2, the inverse of what #305 fixed. On main at cf449fa that was 520 + 115 names per Latin-script locale over 55 fallback pages, and 336 + 58 in zh-Hans and zh-Hant over 31. Both buttons now take locale from the useI18n() they already read and render it as their own lang (patches/fumadocs-ui@16.8.12.patch, the two #305 hunks). On a page in the route locale this repeats what html lang says; inside an English body it keeps the name in its own language. check-locale-surface.mjs gains the inverse rule, localized-name-marked-english: one of a locale's own names (read off its built pages, the names that resolve to the locale, minus the English set) inside a lang="en" region with no lang of its own fails. Two self-test cases (red as #305 shipped it, green through an inherited lang), the fixture body now carries a localized anchor button with its own lang, and the live control feeds the inverse shape too, shown red with a reader blind to lang on names. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FeA1nwBz1ohH65dvffUGKr
The "nothing built" guard in check-locale-surface.mjs fired artifact-missing only when no locale directory held any HTML at all. The legal pages and the locale roots live in the same directories, so a build (or a fixture tree) holding them but no docs page fell through to one docs-page-html-missing per page plus the blind-reader guards. #312 adds legal-page fixtures to every self-test case, and with them this file's "docs pages not built" case read exactly that way. Measured on this tip with #312's own two commits applied in memory (git merge-tree --merge-base 4da46c5): before this change 1 self-test case failed, after it 48 cases over 25 rules pass. The guard now asks whether any <locale>/docs page was built. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FeA1nwBz1ohH65dvffUGKr
…ce; zh-Hant generated from zh-Hans - privacy and terms on de, ja, es, fr and ko: the English entry is marked lang="en" (title, date, body, back link) and introduced by the #298 notice (ui-text notTranslated) in the route locale. en and zh-Hans render as before. - The zh-Hans entry of each page moves, unchanged, into a zh-Hans.json beside it; gen-zh-hant converts it to zh-Hant.json like lib/ui-text, so /zh-Hant/privacy and /zh-Hant/terms show Traditional text and gen-zh-hant --check covers it. contentLocales, hreflang and the sitemap gain zh-Hant. - check-locale-surface: STATIC_PAGES lists zh-Hant, and three rules read the built legal pages (legal-english-unmarked, legal-translation-shows-english, legal-page-unread), with self-test cases. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FeA1nwBz1ohH65dvffUGKr
…econd main HomeLayout already renders main#nd-home-layout around its header and the page, so the page's own main was a nested, duplicate landmark on /privacy and /terms in every locale (axe: landmark-no-duplicate-main, landmark-main-is-top-level, landmark-unique). Same classes, so nothing moves on screen. The check-locale-surface legal fixture now mirrors that structure. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FeA1nwBz1ohH65dvffUGKr
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.
Fixes #312
The legal pages
/LOCALE/privacyand/LOCALE/termsnow handle locales they are not written in correctly:lang="en", and the 290 English-fallback locale URLs render English under lang="xx" with no notice, and the docs chrome (search, TOC, Copy Markdown, language picker) is English in every locale #298 notice (ui-textnotTranslated) appears above it in the page's own locale.gen-zh-hant, the same way as every other zh-Hant string.articleinstead of a secondmainnested inHomeLayout'smain#nd-home-layout. This clears axelandmark-no-duplicate-main,landmark-main-is-top-levelandlandmark-unique, and nothing moves on screen.What changed
apps/docs/app/[lang]/{privacy,terms}/page.tsxcontentgets the English entry, withlang="en"on the title, date, body and back link.Callout,role="note",data-untranslated-notice) in the route locale, placed before the English text.enentry is unchanged byte for byte, socheck-positioningrule (c) still reads it.hrefLang="zh-Hant"alternate link.article, with the same classes, becauseHomeLayoutalready rendersmain#nd-home-layoutaround the header and the page.apps/docs/app/[lang]/{privacy,terms}/zh-Hans.json(new): the zh-Hans entry, moved out ofpage.tsxwith no change (deep-equal to the HEAD entry). It is JSON because the generator converts a data file, the same reasonlib/ui-text/is JSON.apps/docs/app/[lang]/{privacy,terms}/zh-Hant.json(new, generated):gen-zh-hant(OpenCCs2twp).contentLocales, hreflang, canonical and the sitemap pick up zh-Hant through thecontentkeys, the mechanism Docs metadata: the index share card prints a 511-character description over its title, /privacy and /terms ship no metadata, and an internal MDX note leaks into llms-full.txt and Copy Markdown #299 set up.apps/docs/scripts/gen-zh-hant.mjs: the single ui-text pair becomesFIXED_PAIRS(ui-text, privacy, terms). As a result,--checkcovers the legal copy..github/scripts/check-locale-surface.mjsSTATIC_PAGESlists zh-Hant.legal-english-unmarked: an unlisted locale shows English text that does not resolve toen.legal-translation-shows-english: a listed locale shows English text, or text marked English.legal-page-unread: a page is missing, the oracle is empty, or an unlisted locale shows no English text.artifact-missing.HomeLayoutputs inside its main element, and it skips script content. The fixture mirrors the real structure:HomeLayout's main holds the header and the page's article.Branch and rebase
git rebase --onto origin/main 4da46c5 staged/issue-312. The pre-rebase commits were 297882f and 85b6b4b.check-locale-surface.mjson top of Docs accessibility baseline: no main landmark, the language switcher's name contradicts its text, an unnamed svg role=img, unnamed code regions — on every page in every locale #308's rule.git range-diffshows one context-only difference in commit 1: aroundcollect's return line andevaluate's signature, which Docs accessibility baseline: no main landmark, the language switcher's name contradicts its text, an unnamed svg role=img, unnamed code regions — on every page in every locale #308 changed. Commit 2 is patch-identical (=).english-chrome-name, the page-tree rules, its reader and its live control.legal-*rules, thelegal…reader,legalFixturePagesand 5 cases.legal: nullfixup is not needed and not applied. Docs accessibility baseline: no main landmark, the language switcher's name contradicts its text, an unnamed svg role=img, unnamed code regions — on every page in every locale #308's42da2b1makes its built-pages guard count docs pages only, and the merged self-test passes as-is, including✓ docs pages not built fired [artifact-missing].Verification on the rebased tip (e6bf746, real
--forcebuild)pnpm install --frozen-lockfile:Lockfile is up to date, exit 0.pnpm turbo run type-check --continue --force: Tasks 1 successful, VERDICT command-exit 0.NEXT_PRIVATE_STANDALONE=true pnpm turbo run build --force: Generating static pages (1038/1038), Tasks 1 successful, VERDICT command-exit 0. There are 332 OG font-fetch proxy warnings, the same count as every earlier build.pnpm turbo run test --force:✓ 10 self-test(s) passed, VERDICT command-exit 0.check-locale-surface: exit 0 with✓ every advertised URL has a source file …, on a real build. Both cards' sections are present.wrong0 on all 14 non-English pages, 12 or 14 text nodes on each fallback, sitemap 301/301.check-locale-surface --self-test:✓ self-test: 48 case(s) over 25 rule(s) and 3 artifact(s).check-positioning✓,check-search-locales✓ (all 8 locales answer 200), andgen-zh-hant --check:✓ zh-Hant: 62 generated file(s) match ….origin/main...HEADis now only this card's 8 files.hotlong: unset exit 0; armed✓ 8 file(s) changed, no translation artifacts touched.✗ translation PRs may only touch translation artifacts.(exit 1, as expected).--self-test✓, and Output--files:✓ … (256 pre-existing finding(s) reported).check-node-floorand--self-test,check-half-states --self-test: all exit 0./privacyand/termsin en, de and zh-Hant, at 1440 light, 1440 dark and 390 light (18 runs): 0 violations of any rule.landmark-no-duplicate-main,landmark-main-is-top-levelandlandmark-uniqueare 0.svg-img-altis now 0 as well. Docs accessibility baseline: no main landmark, the language switcher's name contradicts its text, an unnamed svg role=img, unnamed code regions — on every page in every locale #308's patch makes the header GitHub iconaria-hidden, and the built/de/privacyhas nosvg role="img"./de/privacyand/zh-Hant/termsat 1440 and 390, and looked at. The/de/privacyPNGs are byte-identical to the pre-rebase ones (sha256ed1644f3e921…,9f32ffbe995e…).Verification before the rebase (on 4da46c5; HEAD then 85b6b4b)
The rebased tip is verified again in the section above. The measurements below were taken before the rebase. 85b6b4b differs from the built c3ad871 only in a comment reflow in
check-locale-surface.mjs, so its app tree is byte-identical to the build measured here.CI case, red before, green after. The committed gate was run against a fresh
--forcebuild of the base (4da46c5):✗ locale surface: 14 finding(s).legal-english-unmarked×10: ja, de, es, fr and ko, for both pages. "11 of the 11 English text node(s) de shows for privacy resolve to lang=de".legal-translation-shows-english×2: zh-Hant, for both pages.missing-url×2: the zh-Hant legal URLs are missing from the sitemap.On this branch's build (c3ad871, the same app tree as HEAD) the gate exits 0 with
✓ every advertised URL has a source file …. The legal table showswrong0 on all 14 non-English pages, and the sitemap has 301 entries (301 expected).Built-HTML check. Chromium parsed every built page with JavaScript off, and the DOM was asked which
langeach text node resolves to (closest('[lang]')). All 16 pages (8 locales × 2) pass, on both the 297882f build and the c3ad871 build:lang="en"elements in the content, and every English text node resolves toen. The notice is visible, matches ui-textnotTranslated, and resolves to the route locale.zh-Hant.json, and per-strings2twp(zh-Hans)equalszh-Hant.json. No English text, nolang="en". Canonical is the page's own zh-Hant URL.The same checks also passed against
next start, with JavaScript on, at 1440 and 390: 32 of 32 on each build, and 0 console errors.axe 4.14.0 on
/privacyand/termsin en, de and zh-Hant, hydrated, at 1440 light, 1440 dark and 390 light (18 runs each side):landmark-no-duplicate-mainlandmark-main-is-top-levellandmark-uniquesvg-img-alt(the header GitHub icon; 0 after the rebase onto #308)document.querySelectorAll('main')returns 2 before and 1 after.Screenshots, before and after the article change:
/de/privacyat 1440 and 390. The before and after PNGs are byte-identical (sha256ed1644f3e921…at 1440,9f32ffbe995e…at 390), so nothing moved, and both were looked at.Screenshots (both widths looked at):
/de/privacyand/zh-Hant/termsat 1440 and 390./de/privacy: the German notice sits above the English policy./zh-Hant/terms: 服務條款 / 許可 / 託管服務 / 返回首頁, and the header search reads 搜尋.Reverse verification, run once against committed HEAD. Renaming
backinprivacy/zh-Hans.jsonmakestscexit 2 with TS2339 (Property 'back' does not exist). The file was then restored byte-identical (blob 983f34dec0cc == HEAD,git diff HEADempty).Gate ablation. With the header and nav skip removed, the self-test turns red: the clean baseline fires
legal-english-unmarkedandlegal-translation-shows-englishon the brand text in the header. Restored byte-identical (blob 9183f0ddcd6e).Gates (type-check, build and the post-build gates at c3ad871; turbo test,
check-locale-surfaceand its self-test again at 85b6b4b)pnpm turbo run type-check --continue --force: Tasks 1 successful, VERDICT command-exit 0.NEXT_PRIVATE_STANDALONE=true pnpm turbo run build --force: Generating static pages (1038/1038), Tasks 1 successful, VERDICT command-exit 0. There are 332 OG font-fetch proxy warnings, the same count as the base build.pnpm turbo run test --force:✓ 10 self-test(s) passed, VERDICT command-exit 0.check-locale-surface: exit 0 (line above).--self-test:✓ self-test: 36 case(s) over 18 rule(s) and 3 artifact(s).check-positioning:✓ positioning: 4 copies equal their constants; … the en entry of 2 legal pages.check-search-locales:✓ search locales: all 8 locales answer 200 ….gen-zh-hant --check:✓ zh-Hant: 62 generated file(s) match the zh-Hans sources byte for byte.(60 before this change, plus the 2 legal files.)git diff --name-status --no-renames origin/main...HEAD; origin/main is cf449fa, so the merge base is still 5d2f837 and the list has 23 files, Docs chrome still speaks English inside localized pages: fumadocs' hard-coded aria-labels, English page-tree names without lang="en", no zh-Hant 404 copy #305's included; after the rebase it is this card's 8), with actorhotlong:✓ 23 file(s) changed, no translation artifacts touched.✗ translation PRs may only touch translation artifacts.(exit 1, as expected).4da46c5...HEAD), armed:✓ 8 file(s) changed, no translation artifacts touched.✓ translations gate passed.--self-test✓.--files:✓ translation output gate passed (256 pre-existing finding(s) reported).check-node-floorand--self-test,check-half-states --self-test: all exit 0.Acceptance notes
check-locale-surface's final✓summary sentence (now Docs accessibility baseline: no main landmark, the language switcher's name contradicts its text, an unnamed svg role=img, unnamed code regions — on every page in every locale #308's wording) does not mention the legal rules. The legal result is printed in its own table. A follow-up could add a clause.readHtmland this card'slegalMainText. A follow-up could fold the legal rules ontoreadHtml.Pushed head
7c17974onclaude/pm-dispatch-objectos-ju9td1, a fast-forward with no force. It starts at the remote branch headfa2390f, mergesorigin/main@9d3ff08(#317; after the merge the diff againstmainis empty), and cherry-picks39b76e1ande6bf746.git diff origin/main 7c17974is byte-identical togit diff origin/main e6bf746.Generated by Claude Code