Skip to content

docs: remove locale pages that call ObjectOS Apache-2.0, fix search in CJK locales, fix the Quickstart anchor - #302

Merged
hotlong merged 3 commits into
mainfrom
claude/pm-dispatch-objectos-ju9td1
Oct 6, 2026
Merged

hotlong merged 3 commits into
mainfrom
claude/pm-dispatch-objectos-ju9td1

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #296

This PR makes three fixes from the seat's site audit of main @ b754364. The branch is rebased onto 6b0e9a5 (#294) with no conflicts; the head is dd17bd2.

1. Delete 12 locale pages that call ObjectOS Apache-2.0

The English resources/license.mdx and resources/support.mdx say ObjectOS is a commercial product with no open-source edition. They say only the ObjectStack framework is Apache-2.0. These 12 locale siblings still said the opposite. They are deleted under ruling 5989567068 on #256 (letter A): when an English correction removes or reverses a capability assertion, that page's locale siblings are deleted. The routes now fall back to the current English body.

I re-measured each claim on origin/main @ b754364 before deleting it:

Deleted file What it says on main
resources/license.de.mdx :10 ## ObjectOS-Runtime — Apache-2.0 · :60 **Kostenlos.** Für immer. Keine Plätze, keine Nutzungsstufe, kein Lizenzserver, kein Schlüssel. · :95 A: Nein. Es gibt keine Telemetrie, keine Lizenzprüfung, keinen Update-Ping.
resources/license.es.mdx :10 ## Runtime de ObjectOS — Apache-2.0 · :60 **Gratis.** Para siempre. Sin asientos, sin niveles de uso, sin servidor de licencias, sin clave. · :95 R: No. No hay telemetría, ni comprobación de licencia, ni ping de actualización.
resources/license.fr.mdx :10 ## Runtime ObjectOS — Apache-2.0 · :60-61 **Gratuit.** Pour toujours. Pas de sièges, pas de palier d'utilisation, pas de serveur de licences, pas de clé. · :96 R : Non. Il n'y a aucune télémétrie, aucune vérification de licence, aucun ping de mise à jour.
resources/license.ja.mdx :10 ## ObjectOS ランタイム — Apache-2.0 · :59 **無料。** 永久に。シート課金なし、使用量ティアなし、ライセンスサーバーなし、キーなし。 · :94 A: いいえ。テレメトリも、ライセンスチェックも、アップデートの ping もありません。
resources/license.ko.mdx :10 ## ObjectOS 런타임 — Apache-2.0 · :60 **무료입니다.** 영원히. 좌석도, 사용량 등급도, 라이선스 서버도, 키도 없습니다. · :96 A: 아니요. 텔레메트리, 라이선스 검사, 업데이트 핑이 없습니다.
resources/support.de.mdx :67 ObjectOS steht unter Apache-2.0 und nimmt Beiträge an:
resources/support.es.mdx :67 ObjectOS usa la licencia Apache-2.0 y acepta contribuciones:
resources/support.fr.mdx :67 ObjectOS est sous licence Apache-2.0 et accepte les contributions :
resources/support.ja.mdx :67 ObjectOS は Apache-2.0 ライセンスであり、コントリビューションを受け付けています:
resources/support.ko.mdx :67 ObjectOS는 Apache-2.0이며 기여를 받습니다:
resources/support.zh-Hans.mdx :66 ObjectOS 是 Apache-2.0,接受贡献:
resources/support.zh-Hant.mdx :67 ObjectOS 是 Apache-2.0,接受貢獻: (generated from the zh-Hans file, so it goes with it)

The English support.mdx:66 says only "The underlying ObjectStack framework is Apache-2.0 and accepts contributions".

  • license.zh-Hans.mdx and license.zh-Hant.mdx are correct (## ObjectOS 是商业产品), so they stay.
  • No other content file changes. The locale meta.LOCALE.json files list pages by slug, and a slug with no locale file falls back to English, so no sidebar or registry entry needed editing.
  • The sidebar still lists both pages in every locale.

Served from the build (next start, port 3296, for the screenshot check):

  • /de/docs/resources/license returns 200. Its h1 is License & Pricing and its first h2 is ObjectOS is a commercial product.
  • /zh-Hans/docs/resources/support returns 200 with the h1 Support.
  • /es, /fr, /ja, /ko/docs/resources/license and /zh-Hant, /de, /ko/docs/resources/support were checked too.
  • On all ten routes, none of the deleted Apache-2.0 sentences appear.

2. Search answers in all 8 locales

Before (b754364, built and served):

en 200 · zh-Hans 500 · zh-Hant 500 · ja 500 · de 200 · es 200 · fr 200 · ko 500
LANGUAGE_NOT_SUPPORTED

Cause. createFromSource builds one Orama index per locale. It takes each index's tokenizer from the locale code: de becomes german, while zh-Hans, ja and ko stay as they are. Orama has no tokenizer for Chinese, Japanese or Korean. The top-level language: 'english' was never consulted, because every locale's own mapping overrides it.

Fix (apps/docs/app/api/search/route.ts): an explicit localeMap with one entry per locale in lib/i18n.ts.

  • A satisfies clause keyed by every Locale makes a missing entry a type error.
  • en, de, es and fr keep the language they already resolved to, so their behaviour is unchanged.
  • zh-Hans, zh-Hant, ja and ko get a tokenizer built on Intl.Segmenter, through Orama's components.tokenizer extension point.
    • It is built into V8, so it adds no dependency, and package.json and the lockfile are untouched.
    • I measured that it works in both runtimes this route runs in: Node 22.22.2, and workerd 1.20260526.1 through miniflare.
    • Those four locales search with threshold: 0, tolerance: 0, as fumadocs recommends for CJK tokenizers.
  • Orama's English tokenizer was not used as the fallback. It splits on every non-Latin character, so a Chinese query tokenizes to nothing and finds nothing. The ablation below measures this.

After, GET /api/search?locale=L&query=permissions:

locale next start local workerd preview (opennextjs-cloudflare preview)
en 200 · 141 hits 200 · 141 hits
zh-Hans 200 · 55 200 · 56
zh-Hant 200 · 55 200 · 56
ja 200 · 83 200 · 84
de 200 · 86 200 · 86
es 200 · 86 200 · 86
fr 200 · 144 200 · 144
ko 200 · 83 200 · 84

Queries in each locale's own script, both runtimes:

locale query hits first result
zh-Hans 权限 147 /zh-Hans/docs/build/agents
zh-Hant 許可權 149 /zh-Hant/docs/build/agents
ja 権限 74 /ja/docs/build/agents
ko 권한 88 /ko/docs/operate/observability

OpenCC s2twp renders 权限 as 許可權, which is why the zh-Hant probe uses that word.

Browser check. I opened the search dialog in Chromium and typed a query, in zh-Hans (权限), ja (権限), zh-Hant (許可權) and ko (권한). I did this on next start, and again for zh-Hans and ja on the workerd preview. Each /api/search request answered 200. The dialog listed results with the term highlighted inside Chinese, Japanese and Korean text. I looked at each screenshot.

The test: .github/scripts/check-search-locales.mjs

The route is dynamic, so the build output records nothing about it. Only a request finds this defect. The gate therefore loads the BUILT handler (apps/docs/.next/server/app/api/search/route.js, routeModule.userland.GET) and calls it in-process for every locale lib/i18n.ts declares. Its rules:

  • threw / status / not-array: each locale must answer 200 with a JSON array. A throw is what Next serves as a 500.
  • no-results: permissions must find something.
  • own-title-missed: every page with a source file in that locale must be found by its own title. That is what proves the locale's script is tokenized, because English fallback pages make permissions match everywhere.
  • negative-control-passed: a nonce query must find nothing, in the same run against the same handler.

It is wired the same way as Locale surface and Positioning:

  • a ci.yml step after build, with shell: bash for pipefail;
  • its --self-test (8 fake-handler cases, all 6 rules able to fire) listed in tools/ci-scripts/run-self-tests.mjs.

Proof that it can fail:

run result
On the unfixed build (b754364) exit 1: (threw) zh-Hans ?query=permissions: the handler threw LANGUAGE_NOT_SUPPORTED, and the same for zh-Hant, ja and ko. 4 findings.
Ablation, mutation leg: CJK locales set to 'english' Done with ablation-replace.mjs: anchor 1 to 0, blob 789cb3f5 to acac90d6. Rebuilt. The bundle carries "zh-Hans":"english" and has no Intl.Segmenter. exit 1: 115 own-title-missed (zh-Hans 40, zh-Hant 40, ja 19, ko 16) and no threw, which is the predicted direction. Example: its title "审批流程" does not find /zh-Hans/docs/build/automation/approvals (0 hits).
Ablation, restore leg Blob equals HEAD and git diff HEAD is empty. Rebuilt. The marker is gone and Intl.Segmenter is back. The gate is green again.
Type pin: ko entry deleted type-check exit 2: error TS1360 ... Property 'ko' is missing. Restored, and git diff HEAD is empty.

The ablations ran on commits 36560e6/329b437. The final route commit differs from those only in two comments; the code is the same.

3. Quickstart anchor

quickstart.mdx:20 now links #path-a--os-start-operator--first-time-evaluator, which is the id the heading generates in the built HTML.

I crawled every internal href on /LOCALE/docs/quickstart in all 8 locales: 224 hrefs, 96 of them with fragments, 0 broken. The crawler's negative control is the b754364 English page, and it flags no element with id "path-a--os-start-operator-first-time-evaluator".

Verification

On the rebased head dd17bd2 (onto 6b0e9a5; turbo Cached: 0 cached, 1 total for each task):

gate verdict line
pnpm turbo run type-check --continue --force Tasks: 1 successful, 1 total
NEXT_PRIVATE_STANDALONE=true pnpm turbo run build --force Tasks: 1 successful, 1 total
pnpm turbo run test --force ✓ 10 self-test(s) passed
check-positioning.mjs ✓ positioning: 3 copies equal the constant; 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.mjs ✓ search locales: all 8 locales answer 200, find "permissions", find every own page by its title, and find nothing for a nonce (own pages: en 79/79, zh-Hans 53/53, zh-Hant 53/53, ja/de/es/fr/ko 29/29 each)
check-locale-surface.mjs ✓ every advertised URL has a source file and every source file is advertised; …
gen-zh-hant.mjs --check ✓ zh-Hant: 64 generated file(s) match the zh-Hans sources byte for byte.
check-translations.mjs ✓ translations gate passed
check-translation-ownership.mjs, enforced (diff against origin/main) ✓ 17 file(s) changed: 12 translation artifact(s) deleted, none added or modified.

Before the rebase, on d44440a (base b754364), the full set:

gate verdict line
pnpm turbo run type-check --continue --force Tasks: 1 successful, 1 total
NEXT_PRIVATE_STANDALONE=true pnpm turbo run build --force Tasks: 1 successful, 1 total
pnpm turbo run test --force ✓ 10 self-test(s) passed
gen-zh-hant.mjs --check ✓ zh-Hant: 64 generated file(s) match the zh-Hans sources byte for byte.
check-locale-surface.mjs ✓ every advertised URL has a source file and every source file is advertised; …
check-positioning.mjs ✓ positioning: 3 copies equal the constant; the brand is right in 659 pages and 2 llms bodies; no stale sentence in 79 English sources
check-search-locales.mjs ✓ search locales: all 8 locales answer 200, find "permissions", find every own page by its title, and find nothing for a nonce (own pages: en 79/79, zh-Hans 53/53, zh-Hant 53/53, ja/de/es/fr/ko 29/29 each)
check-translations.mjs ✓ translations gate passed
check-translation-output.mjs --files (PR diff) ✓ translation output gate passed (100 pre-existing finding(s) reported)
check-translation-ownership.mjs --actor … --files (git diff --name-status --no-renames), enforced ✓ 17 file(s) changed: 12 translation artifact(s) deleted, none added or modified.
the same, with TRANSLATION_BOT_LOGIN unset ⚠ TRANSLATION_BOT_LOGIN is not set — ownership is not enforced yet. (exit 0)
check-node-floor.mjs (+ --self-test) ✅ Every declared floor clears what the dependency tree requires, and the declarations agree.
scripts/pm/check-half-states.mjs --self-test ✓ check-half-states self-test: 1551 cases pass.
smoke-docs.mjs --base (local workerd preview) ✓ smoke: 4 page(s) rendered against http://127.0.0.1:8796, negative control demonstrated red
wrangler deploy --dry-run Total Upload: 51033.71 KiB, under the 61440 KiB budget

Risk and cost

  • First search is slower. fumadocs builds every locale's index on the first search in any locale. Four of those indexes used to throw at once and now get built.
    • Measured in Node with three cold runs each (a shared box, so read the ratio): the first search took 2.7–3.0 s wall (3.5–3.8 s CPU) before and 7.2–7.4 s wall (8.2–8.5 s CPU) after.
    • In the workerd preview the first search took 6.2 s. Warm searches take 30–60 ms.
    • Segmenting the corpus itself costs about 100 ms per CJK locale. Most of the rest is index size: four more full indexes, each holding that locale's translations plus the English fallback pages.
    • wrangler.jsonc sets no CPU limit, so the Workers paid default of 30 s CPU applies.
    • Nothing here measures CPU on the deployed Worker.
  • Rollback: revert the route commit. Search in the four CJK locales goes back to 500, and the new gate turns red, as intended.

Acceptance notes


Generated by Claude Code

claude added 3 commits October 6, 2026 08:50
…Apache-2.0, fix the Quickstart anchor

Deletes content/docs/resources/license.{de,es,fr,ja,ko}.mdx and
support.{de,es,fr,ja,ko,zh-Hans,zh-Hant}.mdx. Each still asserted that
ObjectOS itself is Apache-2.0 (License: "ObjectOS-Runtime — Apache-2.0",
free forever, no licence server; Support: "ObjectOS is Apache-2.0 and
accepts contributions"), which the English sources reversed. Per ruling
5989567068 on #256, letter A, those routes now fall back to the current
English body. license.zh-Hans.mdx and license.zh-Hant.mdx are correct and
stay; support.zh-Hant.mdx goes with its zh-Hans source, and
gen-zh-hant --check passes with 64 files.

The Quickstart's Path A link now targets the heading's generated id,
path-a--os-start-operator--first-time-evaluator.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeA1nwBz1ohH65dvffUGKr
createFromSource built one Orama index per locale and took each one's
tokenizer from the locale code. Orama has none for Chinese, Japanese or
Korean, so those four indexes threw LANGUAGE_NOT_SUPPORTED and
/api/search answered 500, which the dialog shows as an empty list.

The route now passes a localeMap with one entry per locale in
lib/i18n.ts. A `satisfies` makes a missing entry a type error. en, de, es
and fr keep the Orama language they already resolved to. The four CJK
locales get an Intl.Segmenter word tokenizer, which is built into V8 (Node
and workerd), so it adds no dependency. Orama's English tokenizer is not
used as a fallback for them, because it drops every CJK character and a
Chinese query would find nothing. Those locales search with threshold 0
and tolerance 0.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeA1nwBz1ohH65dvffUGKr
check-search-locales.mjs loads the built /api/search handler out of
apps/docs/.next/ and calls its GET in-process for every locale that
lib/i18n.ts declares. Each locale must answer 200 with a JSON array and
find hits for "permissions". Every page with a source file in that locale
must be found by its own title. A nonce query must find nothing, which is
the run's live negative control. It runs as a ci.yml step after the
build, next to Locale surface and Positioning. Its --self-test drives
every rule with fake handlers and joins run-self-tests.mjs.

Measured on the unfixed build (b754364): 4 findings, zh-Hans, zh-Hant, ja
and ko, each `threw LANGUAGE_NOT_SUPPORTED`. On this branch: 8 of 8
locales answer 200, and 330 of 330 own pages are found by title.

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

Labels

None yet

Projects

None yet

2 participants