Repository navigation
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
Description
Activity
- addedenhancementNew feature or requestNew feature or request
on Oct 6, 2026 objectstack-fleet commented
on Oct 6, 2026 ContributorAuthorMore actionsClaim: PM loop,
repo:objectosseat
Session:session_01FeA1nwBz1ohH65dvffUGKr
Account:hotlong(the seat's linked user, asGET /useranswers it; always the card's assignee)
Branch:claude/pm-dispatch-objectos-ju9td1, the session's one designated push branch. This card builds on #305's fumadocs-ui patch and ui-text keys, so it is staged on #305's local branchstaged/issue-305. At push time it is rebased after #301 (PR #310) and #305, and only its own commits are replayed.
Worktree:objectos-issue-308
Domain: n/a (single-lane repo)
Seat:repo:objectos(objectstack-ai/objectstack#9831)
File surface:- the docs layout's
mainlandmark (apps/docs/app/[lang]/docs/layout.tsxor a layout slot); - 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 pinned fumadocs-ui patch, for the switcher name, the svg name and the code-region names;
apps/docs/lib/ui-text*, plus zh-Hant throughgen-zh-hant;apps/docs/components/ai/page-actions.tsx, the prompt sentence;- a rule and self-test in
.github/scripts/check-locale-surface.mjs; - its
ci.ymlstep, if one is needed.
Stay out of #301's files (
global.css,mdx-components.tsx,source.config.ts, the search route,smoke-docs.mjs, the logo link) and everycontent/docs/**page.
Stop on breach and explain in the report.
Container & model: M,mode:subagent,model: default.
Clause-②: no
Thread-read: none
Serial constraints cleared: no. Pushes are serial on the one branch: #301 → #305 → this card.
Generated by Claude Code
- the docs layout's
objectstack-fleet commented
on Oct 6, 2026 ContributorAuthorMore actionsos-dev-report
{ "issue": 308, "status": "staged", "branch": "staged/issue-308", "worktree": "/home/user/objectos-issue-308", "base": "1f5ea81ef289eae88c116a560cef93578a94ebc2 (staged/issue-305 as dispatched; based on cec227a)", "final_commit": "40fe51c6d8c18d2bf551b40fc28adc5d905a9515", "commits": [ "0d96c43 docs: a main landmark, a language switcher named by its text, a decorative GitHub icon, code blocks off the landmark list, a localized Open-in prompt", "295d79c ci: read the built pages for English chrome names and unmarked page-tree entries", "40fe51c docs: the docs page's table-of-contents bar is not a second banner" ], "pr": null, "pr_body": "/tmp/claude-0/-home-user/2224823d-3484-535e-94a5-c071ba6af395/scratchpad/issue-308/pr-body.md (first line: Fixes #308)", "session": "session_01FeA1nwBz1ohH65dvffUGKr (subagent; the parent seat session, per the Claim)", "premise_still_valid": true, "summary": "All six card items are done and committed on staged/issue-308 (3 commits on 1f5ea81). Nothing is pushed. (1) Main landmark: DocsPage passes its other props to the article it renders, so the docs page sets role=main there. This is a fumadocs option; no hunk. (2) Language switcher: a patch hunk names the trigger CURRENT — chooseLanguage ('English — Choose a language', 'Deutsch — Sprache wählen'), visible text first, both halves from the i18n context. (3) GitHub icon: aria-hidden instead of an unnamed svg role=img; every link that renders it is already named GitHub. (4) Code blocks: the scroll viewport loses its unnamed role=region and keeps tabIndex 0. Same rule (landmark-unique), fixed in place: at 390px the table-of-contents bar was a second banner header, now a div. (5) The Open-in prompt is the ui-text key pageActionsOpenInLLMPrompt (upstream 16.9.0's name, {url} placeholder and English), translated in six locales, zh-Hant generated. ui-text's table() now also requires every locale to carry English's placeholders. (6) check-locale-surface.mjs gains three rules over the prerendered HTML (english-chrome-name, untranslated-entry-unmarked, translated-entry-marked-english), guards against passing over nothing, a live control, and ten self-test cases. No ci.yml change. axe on 8 sections x en/zh-Hans/de x light/dark: the four rules go from 48/48/48/24 runs to 0 at 1440px, and 0 at 390px. All ten before/after screenshot pairs are byte-identical.", "files": [ "patches/fumadocs-ui@16.8.12.patch: 4 new hunks (language-select.js name, shared/index.js GitHub svg aria-hidden, codeblock.js role removed, docs/page/slots/toc.js header to div); header gains a #308 section and a rewritten WHEN THIS FILE CAN BE DELETED. #305's hunks unchanged.", "pnpm-lock.yaml: fumadocs-ui patch hash only; frozen install exit 0", "apps/docs/app/[lang]/docs/[[...slug]]/page.tsx: role=main on DocsPage (with an upgrade note), one label (openInLLMPrompt). Outside the claimed surface, see deviations.", "apps/docs/components/ai/page-actions.tsx: the prompt from labels.openInLLMPrompt with a replacer function", "apps/docs/lib/ui-text.ts: pageActionsOpenInLLMPrompt (app key, not passed to fumadocs); table(locale, text) also checks placeholders against English", "apps/docs/lib/ui-text/{zh-Hans,ja,de,es,fr,ko}.json: +1 key each; zh-Hant.json generated by gen-zh-hant", ".github/scripts/check-locale-surface.mjs: built-pages section (reader, 3 rules, 3 guards, live control), 10 self-test cases, header section, summary line" ], "measurements": { "axe_1440_before_1f5ea81": "48 runs: landmark-one-main 48 runs/48 nodes, label-content-name-mismatch 48/48, svg-img-alt 48/48, landmark-unique 24/24 (pages with 2+ code blocks); other: region 2254 nodes, color-contrast 1147 nodes (axe-before.json)", "axe_1440_after_40fe51c": "landmark-one-main 0, label-content-name-mismatch 0, svg-img-alt 0, landmark-unique 0; region 48 nodes (desktop TOC, 1 per run), color-contrast 1147 unchanged (axe-after-1440.json)", "axe_390_before_TOC_fix_295d79c": "four rules 0/0/0/48 runs (landmark-unique = two banner headers), landmark-no-duplicate-banner 48 (axe-390-pretoc.json). The four rules at 390px on 1f5ea81: NOT MEASURED, reason: the 1f5ea81 build was replaced before the 390px run was added", "axe_390_after_40fe51c": "four rules 0/0/0/0, landmark-no-duplicate-banner 0 (axe-after-390.json); drawer open (switcher visible), en/zh-Hans/de quickstart: no violation of the four, label-content-name-mismatch passes", "axe_trial_rebase": "my first two commits cherry-picked onto staged/issue-305 at 4da46c5 (contains #301 5d2f837): built, 1440px four rules 0, color-contrast 48 nodes (axe-rebased.json)", "ci_rule_red_green": [ "red, main's HTML (cec227a, #305's scratch baseline-html, with this script): english-chrome-name 6239 (zh-Hans/zh-Hant 1067, others 821), untranslated-entry-unmarked 5974 (687 x2 + 920 x5), exit 1 (gate-on-main-cec227a-html.log)", "red, ablation on this branch: searchOpen mapped to the literal 'Open Search' and the page-tree transform bypassed. ablation-replace.mjs proof: anchors 1 -> 0, blobs 843ba86 -> 94831ac and ac65f07 -> 9518d5a. Built de/docs/operate/backup.html: aria-label 'Open Search' x0 -> x2, span lang=en x23 -> x7. Gate: english-chrome-name 1120 (160 per locale), untranslated-entry-unmarked 5974, exit 1 (ablation/gate-mutated.log). Restored: blob == HEAD, git diff HEAD empty, both files", "green, restored and rebuilt at 40fe51c: 0 English names, untranslated entries 687/687/920 x5 all marked, translated 635/635/402 x5 none marked English, exit 0 (gates/locale-surface.log)", "green on the trial-rebased tree (with #301's www.objectos.ai link and logo-to-docs link): exit 0, untranslated 717/950 all marked (gate-rebased.log)", "self-test: 41 cases over 21 rules; the live control is green with the real reader and red with a reader blind to names or to lang" ], "screenshots_identity": "10 before (1f5ea81) / after (40fe51c) PNG pairs, byte-identical sha256", "prompt": "all 8 locales x {operate/backup fallback, build/data translated}, read from the live popover: localized, {url} filled, 0 console errors; English byte-identical to the old literal (prompt-check.json vs prompt-check-before.json)" }, "gates": [ "HEAD 40fe51c for every line below", "pnpm install --frozen-lockfile: exit 0", "pnpm turbo run type-check --continue --force: Tasks 1 successful; os-verify-lock VERDICT command-exit 0", "NEXT_PRIVATE_STANDALONE=true pnpm turbo run build --force: Generating static pages (1052/1052); Tasks 1 successful; VERDICT command-exit 0 (351 OG font-fetch proxy warnings, same count as the 1f5ea81 build)", "pnpm turbo run test --force: ✓ 10 self-test(s) passed; VERDICT command-exit 0", "check-locale-surface: exit 0, ✓ ... and no built page outside `en` names a control in English, shows an untranslated page-tree entry without lang=\"en\", or marks a translated one English", "check-locale-surface --self-test: ✓ self-test: 41 case(s) over 21 rule(s) and 3 artifact(s) ... both live controls demonstrated able to fire negative-control-passed", "check-positioning: ✓ positioning: 4 copies equal their constants; the brand is right in 659 pages and 2 llms bodies; no stale sentence in 79 English sources and the en entry of 2 legal pages", "check-search-locales: ✓ search locales: all 8 locales answer 200, find \"permissions\", find every own page by its title, and find nothing for a nonce", "gen-zh-hant --check: ✓ zh-Hant: 62 generated file(s) match the zh-Hans sources byte for byte. Under a one-character drift in the new zh-Hant string: exit 1, restored", "translations Ownership (workflow argv git diff --name-status --no-renames origin/main...HEAD; --actor hotlong): exit 0, inert with TRANSLATION_BOT_LOGIN unset; armed: ✓ 16 file(s) changed, no translation artifacts touched", "translations Freshness: ✓ translations gate passed; Output --files: ✓ translation output gate passed (98 pre-existing finding(s) reported); Output --self-test: ✓", "check-node-floor and --self-test: ✅ exit 0; check-half-states --self-test: ✓ 1551 cases pass", "NOT MEASURED: Worker packaging/size (opennextjs-cloudflare + wrangler dry-run) and smoke-docs, reason: not in this dispatch's gate list; CI runs both" ], "tests": "No unit-test surface for apps/docs; evidence is the gates plus scratchpad/issue-308: axe-run.mjs (axe-before/after-1440/after-390/390-pretoc .json+.log), axe-390.mjs (drawer open), shots.mjs (shots/, facts json), prompt-check.mjs, gate logs in gates/. Reverse verifications, each restored with blob == HEAD and git diff HEAD empty: (a) CI rule ablation above, red then green; (b) placeholder guard: de.json prompt with {url} removed -> bundled ui-text throws 'lib/ui-text/de.json: \"pageActionsOpenInLLMPrompt\" has placeholders [], English has [{url}]', exit 1; clean -> loads (guard/); (c) gen-zh-hant --check red on a one-character drift. Built-HTML evidence for the patch: built pages show aria-label 'English — Choose a language' x81 on en; no role=region viewport and article role=main in the live DOM (shots/after-facts.json).", "screenshots": [ "/tmp/claude-0/-home-user/2224823d-3484-535e-94a5-c071ba6af395/scratchpad/issue-308/shots/before-en-1440-switcher.png / after-en-1440-switcher.png", "/tmp/claude-0/-home-user/2224823d-3484-535e-94a5-c071ba6af395/scratchpad/issue-308/shots/before-zh-Hans-1440-switcher.png / after-zh-Hans-1440-switcher.png", "/tmp/claude-0/-home-user/2224823d-3484-535e-94a5-c071ba6af395/scratchpad/issue-308/shots/before-en-390-switcher.png / after-en-390-switcher.png (drawer open)", "/tmp/claude-0/-home-user/2224823d-3484-535e-94a5-c071ba6af395/scratchpad/issue-308/shots/before-zh-Hans-390-switcher.png / after-zh-Hans-390-switcher.png (drawer open)", "/tmp/claude-0/-home-user/2224823d-3484-535e-94a5-c071ba6af395/scratchpad/issue-308/shots/before-en-390-drawer.png / after-en-390-drawer.png", "/tmp/claude-0/-home-user/2224823d-3484-535e-94a5-c071ba6af395/scratchpad/issue-308/shots/before-zh-Hans-390-drawer.png / after-zh-Hans-390-drawer.png", "/tmp/claude-0/-home-user/2224823d-3484-535e-94a5-c071ba6af395/scratchpad/issue-308/shots/before-en-1440-codeblock.png / after-en-1440-codeblock.png", "/tmp/claude-0/-home-user/2224823d-3484-535e-94a5-c071ba6af395/scratchpad/issue-308/shots/before-zh-Hans-1440-codeblock.png / after-zh-Hans-1440-codeblock.png", "/tmp/claude-0/-home-user/2224823d-3484-535e-94a5-c071ba6af395/scratchpad/issue-308/shots/before-en-390-codeblock.png / after-en-390-codeblock.png", "/tmp/claude-0/-home-user/2224823d-3484-535e-94a5-c071ba6af395/scratchpad/issue-308/shots/before-zh-Hans-390-codeblock.png / after-zh-Hans-390-codeblock.png", "/tmp/claude-0/-home-user/2224823d-3484-535e-94a5-c071ba6af395/scratchpad/issue-308/shots/after-en-390-tocbar.png, after-zh-Hans-390-tocbar.png (TOC bar after header -> div; after only)", "/tmp/claude-0/-home-user/2224823d-3484-535e-94a5-c071ba6af395/scratchpad/issue-308/shots/before-facts.json, after-facts.json (switcher name vs visible text, viewport role, main landmark per shot)" ], "deviations": [ "File surface beyond the Claim: apps/docs/app/[lang]/docs/[[...slug]]/page.tsx (role=main, one label line). The Claim named layout.tsx, but the layout cannot reach the article: its children are grid items, so wrapping them in a main element breaks the grid. DocsPage's pass-through props are the fumadocs option. No sibling touches this file; #305 already touched it.", "In-place fix beyond the card's listed instances: the TOC bar header -> div (4th #308 hunk). Same rule as the card (landmark-unique, plus landmark-no-duplicate-banner) on every sampled page at 390px; mechanical; in the claimed patch file; covered by the same axe run. The PR body names it. The seat may want to add it to the Claim's surface.", "Language switcher fixed by a patch hunk rather than the slots.languageSelect.root slot, which would also work. A slot is per layout instance (docs layout + both legal pages); the patch covers every trigger. Reason in the patch header.", "Prompt carried through ViewOptions' labels prop, not through #305's patch/i18n context as the card suggested. Key name, placeholder and English are aligned with upstream 16.9.0. No fumadocs component reads it, so a Translations key would be one fumadocs never uses.", "staged/issue-305 moved from 1f5ea81 to 4da46c5 during this run (rebased onto 5d2f837; range-diff '=', identical content). This branch stays on 1f5ea81 as dispatched. git merge-tree of my 3 commits onto 4da46c5: exit 0, no conflicts; no file overlap with #301 (5d2f837) or #311.", "Two throwaway worktrees, mine, created and removed without --force: /home/user/objectos-issue-308-before (1f5ea81 build for a clean before-screenshot set) and /home/user/objectos-issue-308-rebased (trial cherry-pick onto 4da46c5: build, gate, axe). The worktree /home/user/objectos-issue-308 is kept.", "The first before-390-codeblock shot was invalid (the drawer covered the block). That set was discarded (shots-v1-discarded/) and every before shot was retaken on a fresh 1f5ea81 build with the fixed script.", "english-chrome-name counts on cec227a (1067/821 per locale) are lower than #305's 1375-1387 because the rule correctly skips names inside a fallback body's lang=en region. The gate's header states both.", "Servers: next start on 127.0.0.1:8838/8839/8840 (not the Worker preview); every next-server PID stopped by PID, none left.", "Commit trailers are model-free (Co-Authored-By: Claude) per the agent contract; the harness reminder asked for a model name.", "The commit 40fe51c message says the 390px screenshots do not change. It was written before the after set was retaken; the retake confirmed it (all pairs byte-identical)." ], "mcp_calls": "0", "api_writes": "1 — POST /repos/objectstack-ai/objectos/issues/308/comments (this os-dev-report, via objectstack scripts/pm/post-stamped.mjs). Otherwise reads only: gh api GET issues/308, issues/308/comments, issues/305/comments, issues/311.", "open_questions": [ { "question": "#305's staged localization creates an inverse language-of-parts defect (localized heading-anchor and code-copy names inside a fallback page's lang=en body; first out_of_scope finding). Should #305 carry the repair before the serial push, or should it wait for the family close-out?", "options": [ "A: amend #305 before it is pushed: in the two hunks #305 already owns (heading.js, codeblock.js CopyButton), give the button the route locale's lang from useI18n; re-run the gate and axe", "B: push #305 as staged and file the inverse for the language-of-parts family close-out" ], "recommendation": "A, because it is unpublished, mechanical, inside hunks #305 owns, and the gate could then gain the inverse rule without a red main. B ships a known regression against main, where these names were English inside English." } ], "out_of_scope_findings": [ "class: a · reach: public door, the built HTML of any fallback docs page in any non-English locale, e.g. /de/docs/operate/backup · evidence: on #305's tree (1f5ea81, and this HEAD), localized chrome names sit inside the fallback body's lang=en region, so a screen reader reads them with English rules (WCAG 3.1.2, the inverse of #305's defect). Per locale: 453 heading-anchor + 101 code-copy names over 53 pages (de 'Ankerlink kopieren' / 'Text kopieren'; ja, es, fr, ko alike); zh-Hans and zh-Hant 276 + 44 over 29 pages. On main at cec227a they were English inside lang=en. Measured with this gate's reader (probe/reverse.mjs). Language-of-parts family, see open_questions · dedupe words: lang=en, fallback body, Copy Anchor Link, Copy Text, language of parts, inverse", "class: a · reach: public door, axe 4.14.0 on /privacy and /terms in every locale, English included (measured /zh-Hans/privacy and /de/terms at 1f5ea81 and at HEAD) · evidence: fumadocs HomeLayout renders main#nd-home-layout and the page renders its own main element inside it (app/[lang]/privacy/page.tsx:94, terms/page.tsx:102) -> landmark-no-duplicate-main, landmark-main-is-top-level, landmark-unique; pre-existing; not a docs page, outside #308's sample and claimed surface; the fix is the page's own main becoming a div · dedupe words: duplicate main, legal pages, HomeLayout, landmark-no-duplicate-main", "carrier: none · noted, not filed: axe best-practice `region` at 1280px+, the desktop table of contents (#nd-toc) is outside every landmark: 48 of 48 runs, 1 node each (2254 nodes before the main landmark)", "carrier: none (承接者: 无) · noted, not filed: axe scrollable-region-focusable at 390px, an overflowing table's scroll wrapper is not focusable: 16 of 48 runs, 18 nodes on #305's tree; #301 (now on main) replaced that wrapper in mdx-components.tsx; not re-measured on main", "carrier: none · noted, not filed: none of the four axe rules has a CI guard (axe is not in CI); the new gate covers the #298/#305 language results only. A fumadocs-ui bump fails pnpm install with ERR_PNPM_UNUSED_PATCH, and the patch header lists every hunk to re-check" ] }objectstack-fleet commented
on Oct 6, 2026 ContributorAuthorMore actionsos-dev-report-addendum
{ "issue": 308, "status": "staged", "addendum_to": "https://github.com/objectstack-ai/objectos/issues/308#issuecomment-6020174554", "branch": "staged/issue-308", "worktree": "/home/user/objectos-issue-308", "base": "cf449faa197389a4c61015e64c16e8f5fb33441a (origin/main: #305 squash, #311's 92a12d2, #301's 5d2f837)", "tip": "42da2b1", "commits": [ "fa284b5 docs: a main landmark, a language switcher named by its text, a decorative GitHub icon, code blocks off the landmark list, a localized Open-in prompt", "023372c ci: read the built pages for English chrome names and unmarked page-tree entries", "3159847 docs: the docs page's table-of-contents bar is not a second banner", "33d9a32 docs: the heading-anchor and code-copy buttons keep their own language inside an English body", "42da2b1 ci: the built-pages guard counts docs pages only" ], "pr": null, "pr_body": "/tmp/claude-0/-home-user/2224823d-3484-535e-94a5-c071ba6af395/scratchpad/issue-308/pr-body.md (first line: Fixes #308; adds Branch and rebase, and The inverse language-of-parts fix)", "session": "session_01FeA1nwBz1ohH65dvffUGKr (subagent; the parent seat session, per the Claim)", "rebase": { "command": "git fetch -q origin main && git rebase --onto origin/main 1f5ea81 staged/issue-308", "conflicts": "none. git range-diff 1f5ea81..40fe51c origin/main..HEAD: all three commits '=' (0d96c43=fa284b5, 295d79c=023372c, 40fe51c=3159847)", "lockfile": "pnpm-lock.yaml, package.json and patches/ are byte-identical between 1f5ea81 and cf449fa (git diff --quiet), so the patch hash carried over and frozen install exited 0 after the rebase. Commit 33d9a32 changes the patch again; its hash was refreshed with pnpm install, and frozen install exits 0 at the tip.", "sibling_overlap": "#312 (staged/issue-312, carrying #305 as 4da46c5) also edits check-locale-surface.mjs. #312's own commits (4da46c5..85b6b4b) applied onto this tip in memory with git merge-tree --merge-base 4da46c5: exit 0, no conflict. Its legal-page fixtures made this file's 'docs pages not built' self-test case fail (1 case); commit 42da2b1 makes the guard count docs pages only, after which the merged self-test passes 48 cases over 25 rules. A direct git merge-tree with staged/issue-312 conflicts (page-actions.tsx, patch, lock) only because that branch carries #305 as 4da46c5, not main's cf449fa. #307: no file overlap." }, "inverse_fix": { "what": "patches/fumadocs-ui@16.8.12.patch: the heading-anchor button (components/heading.js) and the code block CopyButton (components/codeblock.js) take locale from the useI18n() they already read and render it as their own lang. The patch header gains a section with the measurement.", "rule": "check-locale-surface.mjs localized-name-marked-english: one of a locale's own names inside a lang=en region with no lang of its own fails. A locale's own names are read off its built pages: the names that resolve to the locale, minus the English set (10 per locale today). Self-test: red (ja fallback anchor button without its own lang), green (the same name under a wrapper with lang=ja). The live control feeds the inverse shape; with a reader blind to lang on names, the self-test shows it red.", "red_on_main_cf449fa": "exit 1, localized-name-marked-english 3963: per Latin-script locale 520 anchor + 115 copy names over 55 fallback pages (ja, de, es, fr, ko); zh-Hans and zh-Hant 336 + 58 over 31. Only those two names (r2/inverse-breakdown-main.txt, r2/gate-on-main-cf449fa.log). The expected 453+101 and 276+44 were my pre-rebase numbers on 1f5ea81's content; #301's content pass on main has more fallback pages (55 and 31, up from 53 and 29) and more headings ('Copy Anchor Link' x752, up from x708).", "green_on_tip": "exit 0, 0 of 10 own names per locale read as English (r2/gates/locale-surface.log). Built de/docs/operate/backup.html: anchor buttons with lang=de x7, unmarked x0 (main: x0 / x7). Hydrated DOM: /de/docs/operate/backup 7 anchor + 1 copy button resolve to de inside the en body; /zh-Hans/docs/deploy/docker 10 + 1 resolve to zh-Hans; /docs/operate/backup en; 0 console errors." }, "axe": { "sample": "axe 4.14.0, 8 sections (quickstart, use/records, build/data, configure/authentication, deploy/docker, operate/backup, resources/faq, reference/cli) x en/zh-Hans/de x light/dark = 48 runs per width; before = main cf449fa built CI-shaped, after = tip build 33d9a32 (app tree identical to 42da2b1)", "1440": "main: landmark-one-main 48 runs/48 nodes, label-content-name-mismatch 48/48, svg-img-alt 48/48, landmark-unique 24/24, region 2464, color-contrast 9 runs/48. Tip: the four rules 0, region 48 (desktop TOC), color-contrast 9/48 unchanged", "390": "main: landmark-one-main 48/48, landmark-unique 48/72, landmark-no-duplicate-banner 48/48, region 2416, scrollable-region-focusable 16/18, color-contrast 9/48 (switcher and GitHub icon are in the closed drawer at 390). Tip: the four rules 0, landmark-no-duplicate-banner 0, region 0, scrollable-region-focusable 16/18 and color-contrast 9/48 unchanged", "390_drawer_open": "main en/zh-Hans/de quickstart: label-content-name-mismatch 1, landmark-unique 2, svg-img-alt 1 each. Tip: none of the four", "no_new_violations": "per-run comparison main vs tip at both widths: 0 rules with more nodes on the tip. Removed at 1440: label-content-name-mismatch 48, landmark-one-main 48, landmark-unique 24, region 2416, svg-img-alt 48; at 390: landmark-no-duplicate-banner 48, landmark-one-main 48, landmark-unique 72, region 2416 (r2/axe-compare-1440.txt, r2/axe-compare-390.txt)" }, "screenshots": "12 pairs, main cf449fa vs tip, en and zh-Hans: switcher 1440/390, drawer 390, code block 1440/390, TOC bar 390; all 12 byte-identical (sha256). Files: /tmp/claude-0/-home-user/2224823d-3484-535e-94a5-c071ba6af395/scratchpad/issue-308/r2/shots/before-*.png and after-*.png; facts in before-facts.json / after-facts.json", "gates": [ "HEAD 42da2b1 for every line below", "pnpm install --frozen-lockfile: exit 0", "pnpm turbo run type-check --continue --force: Tasks 1 successful; os-verify-lock 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 (332 OG font-fetch proxy warnings, the same count as main's cf449fa build)", "pnpm turbo run test --force: ✓ 10 self-test(s) passed; VERDICT command-exit 0", "check-locale-surface: exit 0, ✓ ... no built page outside `en` names a control in English, reads one of its own names inside lang=\"en\" with English rules, shows an untranslated page-tree entry without lang=\"en\", or marks a translated one English", "check-locale-surface --self-test: ✓ self-test: 43 case(s) over 22 rule(s) and 3 artifact(s)", "check-positioning: ✓ positioning: 4 copies equal their constants; the brand is right in 659 pages and 2 llms bodies; no stale sentence in 79 English sources and the en entry of 2 legal pages", "check-search-locales: ✓ search locales: all 8 locales answer 200, find \"permissions\", find every own page by its title within the first 3 pages, and find nothing for a nonce", "gen-zh-hant --check: ✓ zh-Hant: 60 generated file(s) match the zh-Hans sources byte for byte", "translations Ownership, workflow argv (git diff --name-status --no-renames origin/main...HEAD, 13 files; --actor hotlong): exit 0, inert (TRANSLATION_BOT_LOGIN unset); armed with a human actor: ✓ 13 file(s) changed, no translation artifacts touched", "translations Ownership bot-actor control (armed, --actor = the bot login): exit 1, 'English sources and site code are authored by humans. Split them out.'", "translations Freshness: ✓ translations gate passed; Output --files: ✓ (256 pre-existing finding(s) reported; main cf449fa with an empty file list reports the same 256); Output --self-test: ✓", "check-node-floor and --self-test: ✅ exit 0; check-half-states --self-test: ✓ 1551 cases pass", "NOT MEASURED: Worker packaging/size and smoke-docs, reason: not in this dispatch's gate list; CI runs both" ], "deviations": [ "The inverse counts on main (635 per Latin-script locale, 394 zh) differ from the review's expectation (554 and 320): see inverse_fix.red_on_main_cf449fa. #301's content changes account for the difference; the same two names, nothing else.", "Fifth commit, not asked for: 42da2b1 makes the built-pages guard count docs pages only, so the gate keeps working once #312's legal-page fixtures join this file. It is measured above; the gate result on real builds is unchanged.", "The build, gates and self-test ran at 42da2b1. axe, screenshots, prompts and the hydrated-DOM check ran on the 33d9a32 build; git diff 33d9a32 42da2b1 touches only .github/scripts/check-locale-surface.mjs.", "The red run on main used the script as of 33d9a32 (before 42da2b1's guard change); the guard change does not affect a tree with docs pages built.", "A throwaway worktree, /home/user/objectos-issue-308-main (detached at cf449fa), held main's build for the before measurements. It was removed without --force after its servers stopped. No legal page was touched.", "Commit trailers are model-free per the agent contract." ], "mcp_calls": "0", "api_writes": "2 in total for this card — the first os-dev-report and this addendum, both POST /repos/objectstack-ai/objectos/issues/308/comments via objectstack scripts/pm/post-stamped.mjs (relay). Otherwise reads only.", "open_questions": [], "out_of_scope_findings": [ "carrier: #312 · noted, not filed: the legal pages' duplicate main landmark (not touched here)", "carrier: none · noted, not filed: axe region at 1280px+, the desktop table of contents (#nd-toc) is outside every landmark, 48 of 48 runs, 1 node each, unchanged from main", "carrier: none (承接者: 无) · noted, not filed: axe scrollable-region-focusable at 390px, an overflowing table's scroll wrapper is not focusable, 16 of 48 runs, 18 nodes, unchanged from main", "carrier: none · noted, not filed: none of the four axe rules has a CI guard (axe is not in CI); the gate covers the language results (#298, #305 and the inverse)" ] }objectstack-fleet commented
on Oct 6, 2026 ContributorAuthorMore actionsACCEPT: PR #317 on head
fa2390f8d2fe9396e354740861ddc5e0654874c8· 2026-10-06T16:34Z. I am landing it after #315's deploy onmainfinishes: no governed path is touched, and every check is green.Path surface (
get_files): 13 files, +1030/−36..github/scripts/check-locale-surface.mjs: the built-pages rules and their self-tests.apps/docs/app/[lang]/docs/[[...slug]]/page.tsx:role=mainand the prompt label.components/ai/page-actions.tsx.lib/ui-text.tsand the 7ui-text/*.jsontables, with zh-Hant generated.patches/fumadocs-ui@16.8.12.patch.pnpm-lock.yaml: the patch hash only.- No
content/docs/**and no governed path.
Checks on
fa2390f:build,Ownership & freshnessandNode floorsucceeded;Deploy docswas skipped.mergeable_stateis clean on base553ef7b.Push: fast-forward with no force.
ff/issue-308starts at the remote branch head69f7f1e, mergesorigin/main@553ef7b(the diff againstmainis then empty), and cherry-picksfa284b5..42da2b1. Those five commits were rebased ontocf449fa;range-diffshows the first three as=.git diff origin/main fa2390fis byte-identical togit diff cf449fa 42da2b1.Review:
- The card's four axe rules (
landmark-one-main,label-content-name-mismatch,svg-img-alt,landmark-unique) go from failing on every sampled run to 0. That holds at 1440 and 390, light and dark, in en, zh-Hans and de, and with the 390 drawer open. No rule gains nodes againstmainin any run. - The inverse language-of-parts fix (accepted into this card). 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 put localized heading-anchor and code-copy names inside a fallback page's
lang="en"body. The buttons now carry the route locale's ownlang.- The new rule
localized-name-marked-englishis red onmainwith 3963 findings and green on this head with 0. - In the built
/de/docs/operate/backup, the anchor buttons carrylang=de×7.
- The new rule
- The CI rule.
check-locale-surfacenow reads the prerendered pages for English chrome names, unmarked untranslated entries, translated entries marked English, and the inverse. It runs 43 self-test cases over 22 rules, with a live control.- On
cec227a's HTML it is red; restored on this head it is green. 42da2b1makes the built-pages guard count docs pages only, so Docs legal pages: /de, /ja, /es, /fr and /ko privacy and terms show English text without lang="en", and zh-Hant shows English instead of text derived from zh-Hans #312's legal fixtures do not break it once both land; the merged self-test passes 48 cases.
- On
- The prompt.
pageActionsOpenInLLMPromptfollows upstream 16.9.0's key name and{url}placeholder.ui-text'stable()now also refuses a locale whose placeholders differ from English's. - Visual check: all 12 screenshot pairs against
mainare byte-identical. - Deviations accepted:
role=mainthroughDocsPage's pass-through props, because the layout's children are grid items.- The TOC-bar
header→divhunk, for the samelandmark-uniquerule. - The switcher fixed through the patch rather than the slot.
- The prompt carried through
ViewOptionslabels.
Next: #312 (legal pages) is rebased onto this squash; its replay was measured clean with
42da2b1. Then #316.
Generated by Claude Code
objectstack-fleet commented
on Oct 6, 2026 ContributorAuthorMore actionsLanded and live · 2026-10-06T16:44Z
- PR docs: a main landmark, a switcher named by its text, code blocks off the landmark list, buttons that keep their own language, a localized Open-in prompt, and a CI rule for the locale chrome #317: squash
9d3ff08, merged by the seat.Fixes #308closed this card. - CI on
main(run 37497405994, attempt 1):buildsucceeded, includingcheck-locale-surfacewith the new built-pages rules.Node floor,Deploy docs / Publish the tested WorkerandDeploy docs / Smoke-check the live sitealso succeeded; roll back was skipped. - Deploy verdict:
✓ deploy: ab3a6513-3b45-452b-94d3-1cac61bd329e is serving, replacing d3ced9dc-ad86-4cf4-b1ec-a92f032e49c7. - Live result:
- Every docs page has one
mainlandmark. - The language switcher's name starts with its visible text.
- The GitHub icon is decorative.
- Code blocks and the TOC bar no longer add landmarks.
- The heading-anchor and code-copy buttons keep their own language inside a fallback page's English body.
- The Open-in prompt is localized.
- CI now fails on any regression of 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, 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 or this card.
- Every docs page has one
- Housekeeping: the seat removes
pm:dispatchedfrom this card. - Next on the branch: Docs legal pages: /de, /ja, /es, /fr and /ko privacy and terms show English text without lang="en", and zh-Hant shows English instead of text derived from zh-Hans #312 (legal pages), being rebased onto
9d3ff08, then Docs code samples, family close-out: workflows, approvals, actions and cel pages show flow and view samples that @objectstack/spec 17.7.0 refuses; add a docs-sample parse gate so the family stays closed #316.
Generated by Claude Code
- PR docs: a main landmark, a switcher named by its text, code blocks off the landmark list, buttons that keep their own language, a localized Open-in prompt, and a CI rule for the locale chrome #317: squash
Found by axe 4.14.0 while #305 was being staged on 2026-10-06. The run covered one fallback page and one translated page per locale, plus the English page, built from
main@cec227a. Every finding is pre-existing: the English page's HTML is the same as main's, and #305 did not cause any of them. Priority: P2, accessibility.Findings (identical on every docs page)
landmark-one-main:DocsLayouthas nomainlandmark.label-content-name-mismatch: the language switcher trigger is labelled "Choose a language", but its visible text is the current language ("English"). WCAG 2.5.3 needs the accessible name to contain the visible text.svg-img-alt: ansvg role="img"has no accessible name.landmark-unique: code-block regions have no name.pageActionsOpenInLLMPrompt); 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 patch could take it the same way.langon untranslated page-tree entries. 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 scan script,scan-built.mjs, could become a rule incheck-locale-surface.mjs.Colour contrast is out of scope here; #301 handles it.
Done looks like
Order
After #305 lands, because it builds on #305's fumadocs-ui patch and ui-text keys.
Generated by Claude Code