Repository navigation
docs: contrast, phone table affordance, FAQ headings, title-weighted per-locale search, consistency pass (#301) - #310
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
This was referenced Oct 6, 2026
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 #301
Docs polish from the 2026-10-06 audit, plus the two comments on the card (search cold start and post-deploy smoke from #296; three
configure/aicitations and the empty Releases line from #297). Every item is fixed below, or answered in one line where it is not.1. Contrast
--color-fd-muted-foregroundis set tohsl(0 0% 40%)in an@themeblock inapps/docs/app/global.css. The neutral theme's dark value is a plain.darkrule, so it is untouched.colorReplacementsinapps/docs/source.config.ts, spread over fumadocs' ownrehypeCodeDefaultOptions.#6a737dbecomes#8b949ein github-dark and#57606ain github-light.#6a737dis the comment colour in both themes, and it colours nothing else in either.How these were measured: Chromium, each element's computed colour composited over its real background, on
/docs/quickstartand/docs/configure/aiat 1440 and 390.Light-mode code comments: the card does not name them. They measured 4.26, the same defect class, fixed by the same mechanism on the same line, so they are fixed in place here.
2. License Editions table on phones
mdx-components.tsxoverridestable. It keeps fumadocs' own wrapper and classes and adds adocs-tableclass.global.cssgives that class two affordances:::-webkit-scrollbarand deliberately sets noscrollbar-widthorscrollbar-color, because Chrome 121+ drops::-webkit-scrollbarwhen either is set. Firefox getsscrollbar-width: thinunder@supports not selector(::-webkit-scrollbar).Measured under mobile emulation:
calc(100% - 40px).100%, so nothing is faded.3. FAQ formatting
The FAQ page has 35 questions and the License FAQ has 7. Each question is now a
###heading, with its answer as a paragraph of its own. TheQ:/A:prefixes are gone. The questions are now linkable and appear in the table of contents.After normalising the formatting, the only text changes are the license-validation sentence in one answer on each page (item 5).
4. Search (
apps/docs/app/api/search/route.ts)Ranking. Orama ranks a page's title, its headings and its paragraphs on one shared
contentfield. A per-propertyboostcannot tell them apart, so a customsortByweights the hits instead: title 4, heading 2, text 1./docs/configure/permissionsnow ranks first. Before, it was outside the top 8./docs/deploy/air-gappednow ranks first. Before, it was third.A side finding. Before this change, title-versus-heading ties were broken by indexing order. fumadocs-mdx does not keep that order stable between builds: two builds of one tree ranked the same ties differently, in 27 of 142 responses.
Cold start. The index for a locale is now built on that locale's first search; before, the first search in any locale built all eight. This is the "one locale lazily" option from the #296 comment. These are first-search times after boot, measured interleaved on one box:
main@ 601bb37next start, ennext start, jaopennextjs-cloudflare preview), enwrangler deploy --dry-run): 47016.28 KiB, against a budget of 61440 KiB.Why the index is not built at build time. fumadocs' build-time export (
staticGET) is client-side search:/api/searchbecomes a static file that no longer answers?query=, and bothcheck-search-locales.mjsand the smoke test call that;Intl.SegmenterCJK tokenizer would have to be rebuilt in the browser bundle;type: 'static'set onRootProviderinroot-provider.tsx.Per-locale lazy indexing gets most of the cold-start win from
route.tsalone.Parity. The per-locale index is built from public fumadocs APIs (
createSearchAPI,findPath). Breadcrumbs are built the same waycreateFromSourcebuilds them. With all weights set to 1, it was compared byte for byte againstcreateFromSourcein one process:Gate.
check-search-locales.mjsgains a rule,own-title-buried: a page's own title must rank it within the first 3 pages. The self-test has 11 cases, and all 7 rules can fire.main's built route: red, with 56own-title-buriedfindings.Post-deploy smoke.
smoke-docs.mjsnow sendsGET /api/search?locale=…&query=permissionsfor every locale inlib/i18n.ts. It reads that file as text, so the smoke stays install-free. Each response must be a 200 JSON array and must not be empty.search-negative-control-passed). Without it, a cache that ignored the query string would read as green.HEAD's.The
ci.ymlcomment that counted "four fetches" now names the search requests.5. Consistency
reference/security.mdx#data-residency. It gains the rows only the other copies had: the compiled app definition, the runtime itself, and "secrets".index.mdxandarchitecture.mdxnow link to it.index.mdx,architecture.mdx, the License FAQ and the FAQ: "Self-managed ObjectOS validates its license online; Enterprise air-gapped licenses validate offline."security.mdxkeeps its own phrasing, in context, as the canonical page.defineObject. It no longer appears anywhere in the English docs.reference/rest-api.mdxandconfigure/permissions/record-access.mdxnow useObjectSchema.create(from@objectstack/spec/data).8a399b2b,content/docs/data-modeling/objects.mdx:15(ObjectSchema.create, 58 uses in 31 pages) andpackages/spec/src/kernel/manifest.zod.ts:386. The latter says the platform "deliberately does NOT provide" a genericdefineObjectfactory.apiMethodsandapiEnablednow sit underenable. That is whereObjectSchemadeclares them, andObjectSchema.createrejects unknown top-level keys.get,list,create,update,delete,bulk. It says the eight older values are retired and are stripped at parse with a warning (object.zod.ts:25-41).build/interface/views.mdx. The two declaration samples useddefineObjectwith an object-levelview: { list, form, listViews, formViews }. They now show the form ObjectStack documents: views declared beside the object in adefineViewcontainer, registered on the stack withviews: [...].list, namedlistViews, a defaultformand namedformViews. Each view is bound throughdata: { provider: 'object', object: … }, and every list view keeps a top-levelcolumns.8a399b2b,content/docs/ui/views.mdx:9-73, whose example isos:check-compiled there, together withpackages/spec/src/ui/view.zod.ts:4749(ViewSchema) and:4845(defineView).KanbanConfigSchema.columnsis required (view.zod.ts:1809).@objectstack/spec17.6.0 from npm.viewkey is refused byObjectSchema.create("viewis not an ObjectSchema field"); there is nodefineObjectexport; top-levelapiMethodsis refused; a list view withoutcolumnsis refused. DeclaringapiMethods: ['get','list','search','export']parses to['get','list']with one warning.AGENTS.mdhas no spelling rule. The English corpus is American on every other pair (organization 83:1, behavior 26:7, catalog 35:1), and the page title, URL and env vars all say "license".deploy/docker#licencebecame#license, and its one inbound link is updated. Three more headings changed slug inair-gapped,kubernetesandenvironment-variables. No inbound link to any of them was found in this repo, www or objectstack.configure/index.mdxtitle changes from "Administration" to "Configure". That matches the sidebar group, the URL, the 7 existing[Configure](/docs/configure)links, and howoperate/names its index page. The three "Administration" link texts are updated.configure/permissions/positions. The editions are left until [Decision] One name for the self-managed edition: "ObjectOS Enterprise" (positioning, 8 pages) vs "ObjectOS Self-Managed" with Business single-node and Enterprise under it (License & Pricing, Deploy) #295./docs,/zh-Hans/docs, …). A docs reader clicking it expects the docs start, not an English marketing page.www.objectos.aistays one click away as an icon link beside GitHub; its label is a host name, so it needs noui-textentry.llms.txt. The Other Languages example is now derived: the first page in navigation order that has a real translation in the first other locale. Today that is/zh-Hans/docs/use, which renders 使用 ObjectOS;/zh-Hans/docs/quickstartwas an English fallback.configure/aicitations.build/automation/flows.mdx: theai_callbullet is removed. ObjectStack'sFlowNodeActionhas no such node.reference/security.mdx: theredactparenthetical is removed. No public AI redact config exists; objectstack'sprotocol/knowledge.mdx:410lists aredactstep as "Deferred".build/ai-builder.mdx: the provider list now reads OpenAI / Anthropic / Google / DeepSeek, from the publicOS_AI_PROVIDERlist (configure/ai.mdx; objectstackdeployment/environment-variables.mdx:179).zh-Hantfiles thatgen-zh-hantpruned. That is 14 files. Every sibling carried the removed claim.resources/support.mdxnow points at Release notes and atobjectstack-ai/objectstack→ Releases, which publishes (@objectstack/runtime@17.6.0is there).objectstack-ai/objectoshas 0 releases.resources/changelog.mdxpointed readers at the same empty feed twice, so it gets the same fix in place.Branch and rebase
Head:
ff/issue-301@911a174. It fast-forwards from the seat branchclaude/pm-dispatch-objectos-ju9td1@7b4fd19(#309's head):origin/main@a24b39d, whose tree is identical (git diff origin/mainis empty after the merge), followed by this PR's four commits, cherry-picked.origin/mainis byte-identical to the diff ofstaged/issue-301@2e474fd, the same four commits rebased ontoa24b39d(sha256043d7c07…for both). Both tips have the tree263e3709….Rebasing onto #167 (
a24b39d) produced the two predicted conflicts. Both lines were kept in each, and #167'sseoTitlevalues were aligned to this PR:configure/index.mdx:title: Configure+seoTitle: "Configure: Users, Access and Settings"(was "Administration: …").deploy/air-gapped.mdx:seoTitle: "Air-gapped Deployment: License Mode and Settings"(was "Licence") + the description with "license".Re-checked: no other English
seoTitlecontains "Licence" or "Administration", and no English page contains "licence". "Licensed" and "Licensing" are spelled the same in both variants.Acceptance notes
Observations. None of these is filed by this PR.
build/interface/views.mdx. The per-type fragments further down the page (Kanban, Calendar, Gantt, …) still omit the top-levelcolumnsthat every list view requires. The "Common list options" fragment setsfontWeight: 600, where the spec wants a string. Wrapped as list views withdata, each of these is refused by@objectstack/spec17.6.0. They are not touched here.build/automation/flows.mdxsamples. They useaction: 'send_email', and the Email bullet calls it "thesend_emailaction".FlowNodeActionhas no such value. This is a read-only inference; it is not verified which schema theactionkey belongs to.AGENTS.mdcoverage count. It says "62 of 79" Simplified pages.mainhas 50, and this branch has 48.next start -H 127.0.0.1loops forever (307) on prefix-less English routes, because the middleware's rewrite tolocalhostis treated as an external proxy. Bind without-Hwhen verifying locally.Verification (at 2e474fd;
ff/issue-301@ 911a174 has the identical tree)pnpm turbo run type-check --continue --forceNEXT_PRIVATE_STANDALONE=true pnpm turbo run build --forcepnpm turbo run test --forcegen-zh-hant.mjs --checkcheck-locale-surface.mjscheck-positioning.mjscheck-search-locales.mjscheck-translation-ownership.mjs, workflow argv (git diff --name-status --no-renames origin/main...HEAD), actor hotlong, bot login setcheck-translations.mjsmain@a24b39d: zh-Hans 49, the other locales 26. On this branch: zh-Hans 47, the other locales 24check-translation-output.mjs --files …main@a24b39dreports 261 on its own; most come from #167's localeseoTitlelines. This branch reports 5 fewer, because of the deleted siblings, and adds the non-blocking fence-count differences in the views siblingscheck-node-floor.mjs· half-states--self-testsmoke-docs.mjs --baseagainst a localopennextjs-cloudflare preview(run at 8dc4d10; later commits change MDX content and frontmatter only)Screenshots, before and after, light and dark, at 1440 and 390: a page with sidebar and TOC (
quickstart-*), the License table (license-*, plus 360 and scrolled-to-end variants), the FAQ (faq-*), the search dialog with "permissions" (search-permissions-*), and code comments (code-comments-*). Also the mobile-emulated table bottom with the drawn scrollbar (license-*-table-bottom-mobile). The beforesearch-permissions-light-1440shot caught the dialog still empty 2.5 s into the first search: that is the cold start, before this change.Generated by Claude Code