Skip to content

docs(content): propose a search-intent title rule, land it on the four pages with no sidebar cost - #12312

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-12237-page-titles
Aug 26, 2026
Merged

docs(content): propose a search-intent title rule, land it on the four pages with no sidebar cost#12312
os-zhuang merged 1 commit into
mainfrom
claude/issue-12237-page-titles

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Part of #12237

Draft on purpose. This awaits the maintainer's judgement on the rule — please do not merge or queue it. Titles are maintainer-voice copy, so the rule and the complete before/after table are below and the diff is deliberately four files.

TL;DR

Three things the card did not know, all re-derived rather than inherited:

  1. The editable surface is 180 files, not 403. 214 pages live under content/docs/references/** (generated, docs content: the 38 generated reference pages still render two <h1> — the heading comes from a spec JSDoc header via build-docs.ts #12249) and 9 under content/docs/releases/** (CLAUDE.md hard stop, docs content: 4 pages under content/docs/releases/ still render two or three <h1> — three of them need a cascading demotion, not a mechanical one #12250). Both are barred, and together they are 55% of the population. The card's headline median of 14 is dominated by the pages nobody may edit here.
  2. There is no separate short sidebar label, and there cannot be one without a code change. The frontmatter title is simultaneously the SERP <title>, the on-page <h1>, the sidebar nav label and the llms.txt heading. The card made settling this a precondition for mass-editing. It settles negative.
  3. Two of the card's three worked examples breach the card's own 60-character box. "50–60 characters including the suffix" leaves a 36–46 character budget for the frontmatter string; the examples were written to 50–60 excluding it.

Because of (2), this PR lands the rule only on the four pages where it demonstrably costs the navigation nothing, and leaves the other 176 as a table to be judged.

1. Re-derived statistics

The card's numbers came from the same session and the same method that produced #12236's wrong 205/129/76, so everything here was measured again — frontmatter-aware, reading only the leading --- block so a title: line inside a body code fence can never be counted.

The card's own figures hold. A naive grep '^title:' over whole files returns n=405 median=14 max=50 <=20: 327; the frontmatter-aware read returns n=403 median=14 max=50 <=20: 325 — exactly the card. The two extra hits are title: lines inside YAML fences. Unlike #12236, the method error did not move the headline. That is reported as a null result rather than dressed up as a catch.

What the card's framing does hide is the split:

population files median max ≤20 chars
all content/docs/**/*.mdx (the card) 403 14 50 325
references/** — generated, #12249 214 11 29 207
releases/** — hard stop, #12250 9 7 28 8
editable surface (this card) 180 18 50 110

Corroborated independently by two gates on this branch: check-doc-frontmatter reports content/docs 403, and check-docs-single-h1 reports 180 page(s) under content/docs/ (2 subtree(s) excluded) — the same 180.

Other measured facts:

  • Zero duplicate titles among the 180. Sidebar disambiguation is not a driver here.
  • One page already breaches the card's 60-char box today: protocol/objectui/record-alert.mdx at 64. Fixed in this PR.
  • The proposed shape is not novel — 4 of the 180 already use X — Y and 3 use X: Y.

2. The sidebar-label finding (the card's stated precondition)

Note the Sidebar title and the SEO title do not have to be the same string — check whether content/docs/**/meta.json or the frontmatter can carry a separate short label for navigation before lengthening every sidebar entry.

Checked against this repo's actual configuration (fumadocs-core@16.14.4, apps/docs/source.config.ts, apps/docs/lib/source.ts). The answer is no.

consumer reads source
SERP <title> page.data.title + %s | ObjectStack app/[lang]/docs/[[...slug]]/page.tsx generateMetadata; app/layout.tsx metadata.title.template
on-page <h1> page.data.title same page, <DocsTitle>{page.data.title}</DocsTitle>
sidebar label page.data.title fumadocs-core page-tree builder, buildFile(): name: title ?? pathToName(basename(path, extname(path)))
llms.txt heading page.data.title apps/docs/lib/source.ts getLLMText(): `# ${page.data.title}`

One string, four consumers. And no second field exists to split them:

  • pageSchema (fumadocs-core/dist/source/schema.js) declares exactly title, description, icon, full, _openapi — and compiles to z.core.$strip, so an invented sidebarTitle: in frontmatter is silently dropped, not rejected. source.config.ts uses pageSchema unextended (the blog collection does extend it, so the mechanism exists and is simply unused for docs).
  • meta.json (metaSchema) has a title, but buildFolder() uses it for the folder's own label (node.name = metadata.title ?? node.index?.name). It cannot name a child page.
  • meta.json pages does accept a [Label](url) form, but resolveLink() emits a bare link node with no $ref — it drops description, icon and the page↔tree binding, and the real page would need !-excluding to avoid appearing twice. That is a link mechanism, not a label mechanism.

The supported fix is code, roughly ten lines: pageSchema.extend({ sidebarTitle: z.string().optional() }) in apps/docs/source.config.ts, plus a pageTree.transformers entry in apps/docs/lib/source.ts whose file(node) prefers it. That is inside epic #12243's territory but outside this card's declared file surface, and apps/docs/lib/source.ts is contended by cards in flight — so it is filed rather than taken here.

The three-page exemption this PR uses instead

buildFolder() sets a folder's label to metadata.title ?? node.index?.name, and excludes the index page from children unless meta.json pages names "index" explicitly. So for a folder whose meta.json carries a title and whose pages omits "index", that index page's frontmatter title never appears in the sidebar.

Enumerated across all 19 editable folders: exactly three pages qualify. 16 folders list "index" in pages, which puts the page in the tree as an ordinary child.

page current title sidebar shows instead
content/docs/index.mdx Documentation docs root — not in the tree at all (isRoot, and root meta.json pages omits it)
content/docs/protocol/objectql/index.mdx ObjectQL: The Data Protocol "Data Protocol" from protocol/objectql/meta.json
content/docs/protocol/objectui/index.mdx ObjectUI: The UI Protocol "UI Protocol" from protocol/objectui/meta.json

Those last two are worth noticing on their own: the repo already demonstrates the split the card is asking for — a short nav label beside a longer page title — using the one mechanism that happens to work for folder index pages.

3. The proposed rule

<primary keyword> — <qualifier>
  • 36–46 characters for the frontmatter string, so that with the 14-character | ObjectStack suffix the rendered title lands in the 50–60 band. (The card said "50–60 including the suffix" but wrote its examples to 50–60 excluding it; ObjectStack documentation: metadata-driven app framework is 70 rendered and Object metadata: define objects, fields and relationships is 71. Only Expose actions as MCP tools for AI agents, at 55, obeys the box.)
  • The primary keyword leads — what a developer types, not our internal noun.
  • Separator is , matching the rule as written and the 4 titles already using it. Where the qualifier is a genuine restatement, : also reads fine; the table uses throughout for one pattern.
  • Never repeat "ObjectStack" — the suffix already carries it. One declared exception: getting-started/index.mdx keeps What is ObjectStack? because the product name is the search query for that page.
  • Vocabulary: AI-written / AI-authored / AI-generated only, no fourth spelling. AI-written is the anchor for titles because the homepage title landed in docs(site): lead the homepage title with the category, cut the 614-char description to 152 #12284 as Metadata framework for AI-written apps.
    • ⚠️ Correction to a fact carried into this card: AI-built is not zero. It appears 35 times across 18 files repo-wide (packages/objectql, packages/metadata-protocol, docs/adr, …). It is zero under content/docs/** and apps/docs/**, which is presumably the surface that was measured. The ruling still stands — no fourth spelling in titles — but it stands on taste, not on absence.

Applied to all 180 rows the rule yields: median 54, min 50, max 60, 180/180 in the 50–60 band, zero over 60, zero duplicates.

4. What this PR actually changes: 4 files

Every file in the diff either has no sidebar exposure or shortens its sidebar label. Zero navigation regression, by construction rather than by judgement.

file before after why it is safe today
content/docs/index.mdx Documentation 27 Documentation — build apps from metadata 54 not in the page tree
content/docs/protocol/objectql/index.mdx ObjectQL: The Data Protocol 41 ObjectQL — the data protocol specification 56 meta.json title wins
content/docs/protocol/objectui/index.mdx ObjectUI: The UI Protocol 39 ObjectUI — the UI protocol specification 54 meta.json title wins
content/docs/protocol/objectui/record-alert.mdx record:alert — Conditional Banners on Record Pages 64 record:alert — banners on record pages 52 already over 60; label gets shorter

Every file is 1+/1-. Frontmatter description is untouched (#12238's card, same block, same files). Nothing under references/** or releases/**.

content/docs/index.mdx also takes the free input from #12236: its demoted ## ObjectStack Documentation confirmed the page had a better wording available than Documentation.

5. Acceptance boxes

  • one written rule, in the PR body, with the before/after table — §3 and §6
  • sidebar navigation still reads cleanly (short labels preserved, by whatever mechanism) — ⛔ not satisfiable from this card's file surface for 176 of 180 pages; see §2. Satisfied for the 4 that ship.
  • no title exceeds 60 characters including the site suffix — the one existing breach (64) is fixed; nothing added goes over
  • pnpm check:doc-anchors green — check-doc-anchors: 278 internal #fragment link(s) across 408 source file(s) all resolve to a real heading

6. Rule applied to all 180 — the complete before/after table

Rows: 180. Lengths include the | ObjectStack suffix (14 chars) the root layout appends.
marks the 4 rows this PR actually lands; the rest are proposals awaiting the ruling.

median min max in 50-60 over 60
before 32 19 64 2 1
after 54 50 60 180 0

content/docs/ai/

page before now after (proposed) now
actions-as-tools.mdx Actions as Tools 30 MCP tools — expose actions to AI agents 53
agents.mdx AI Agents 23 AI agents — declare tools, model and prompt 57
connect-mcp.mdx Connect an MCP Client 35 MCP client setup — Claude, Cursor and IDEs 56
index.mdx AI Overview 25 AI features — MCP tools, agents and RAG 53
knowledge-rag.mdx Knowledge & RAG 29 RAG — embeddings and knowledge retrieval 54
natural-language-queries.mdx Natural Language Queries 38 Natural language queries over your data 53
skills-reference.mdx AI Skills Reference 33 AI skill reference — every field explained 56
skills.mdx AI Skills System 30 AI skills — reusable instructions for agents 58
tools.mdx Tool Records 26 Tool records — govern what an agent may call 58

content/docs/api/

page before now after (proposed) now
client-sdk.mdx Client SDK 24 Client SDK — typed JavaScript data access 55
data-api.mdx Data API 22 REST data API — CRUD over every object 52
data-flow.mdx Data Flow Diagrams 32 Request data flow — from HTTP to driver 53
declarative-endpoints.mdx Declarative Endpoints 35 Custom endpoints declared as metadata 51
environment-routing.mdx Environment-Scoped Routing 40 Environment routing — dev, preview, prod 54
error-catalog.mdx Error Code Catalog 32 API error codes — the complete catalog 52
error-handling-client.mdx Client-Side Error Handling 40 Client error handling — retries and codes 55
error-handling-server.mdx Server-Side Error Handling 40 Server error handling — throw the envelope 56
index.mdx API Overview 26 REST and GraphQL APIs — generated per object 58
metadata-api.mdx Metadata & Package API 36 Metadata API — read and publish packages 54
plugin-endpoints.mdx Plugin Endpoints 30 Plugin endpoints — add routes from a plugin 57
wire-format.mdx Wire Format & JSON Examples 41 API wire format — request and response JSON 57

content/docs/automation/

page before now after (proposed) now
approvals.mdx Approval workflow 31 Approval chains — multi-step sign-off rules 57
connectors.mdx Connectors 24 Connectors — call external systems safely 55
email-templates.mdx Email Templates 29 Email templates — merge fields and layouts 56
flows.mdx Flow Metadata 27 Flows — DAG automation as typed metadata 54
hook-bodies.mdx Hook & Action Bodies (L1 / L2) 44 Hook and action bodies — the L1/L2 rules 54
hooks.mdx Hooks 19 Record hooks — beforeInsert to afterDelete 56
index.mdx Automation 24 Automation — flows, triggers and schedules 56
jobs.mdx Scheduled Jobs 28 Scheduled jobs — cron automation metadata 55
webhooks.mdx Webhook Delivery 30 Webhooks — outbound delivery and retries 54
workflows.mdx Workflow Metadata 31 Workflow rules — declarative record logic 55

content/docs/

page before now after (proposed) now
build-without-code.mdx Build Without Code 32 Build business apps without writing code 54

content/docs/capabilities/

page before now after (proposed) now
ai.mdx AI Under Governance 33 AI under governance — permissioned agents 55
analytics.mdx Analytics & Dashboards 36 Analytics — dashboards, reports and charts 56
approvals.mdx Approvals 23 Approvals — routing, queues and audit trail 57
automation.mdx Automation — Processes That Run Themselves 56 Automation — processes that run themselves 56
data.mdx Manage Business Data 34 Business data — objects, fields and records 57
forms.mdx Forms & Data Quality 34 Forms and data quality — validation rules 55
index.mdx What Can It Do? 29 What can it do? — the capability overview 55
integrations.mdx Integrations & Everyday Work 42 Integrations — email, files and everyday work 59
permissions.mdx Permissions — Who Sees What 41 Permissions — who sees what, enforced 51
request-template.mdx How to Request Features 37 Request a feature — how to describe it 52
views.mdx Views — See Data Your Way 39 Views — lists, kanban, calendar and gantt 55

content/docs/concepts/

page before now after (proposed) now
architecture.mdx Protocol Architecture 35 Protocol architecture — the four layers 53
design-principles.mdx Design Principles 31 Design principles behind the metadata spec 56
index.mdx Core Concepts 27 Core concepts — metadata, runtime, protocol 57
metadata-driven.mdx Metadata-Driven Development 41 Metadata-driven development explained 51
metadata-lifecycle.mdx Metadata Lifecycle & HMR 38 Metadata lifecycle — load, publish and HMR 56
north-star.mdx North Star 24 North star — why this framework exists 52

content/docs/data-modeling/

page before now after (proposed) now
analytics.mdx Analytics Datasets 32 Analytics datasets — modelling for reports 56
drivers.mdx Database Drivers 30 Database drivers — Postgres, MySQL, Mongo 55
external-datasources.mdx External Datasources (Federation) 47 External datasources — query without ETL 54
field-type-decision-tree.mdx Field Type Decision Tree 38 Which field type should I use? A decision tree 60
field-types.mdx Field Type Gallery 32 Field types — the complete visual gallery 55
fields.mdx Field Metadata 28 Field metadata — every option explained 53
formulas.mdx Expressions (CEL) 31 Formulas — CEL expressions on records 51
import-mappings.mdx Import Mappings 29 Import mappings — load external data files 56
index.mdx Data Modeling 27 Data modelling — objects, fields, relations 57
indexing.mdx Database Indexing 31 Database indexes — declare and tune them 54
object-extensions.mdx Object Extensions 31 Object extensions — extend without forking 56
objects.mdx Object Metadata 29 Object metadata — define your data schema 55
queries.mdx Query Syntax Cheat Sheet 38 ObjectQL query syntax — a cheat sheet 51
relationships.mdx Relationships & Lookups 37 Relationships — lookups, master-detail 52
schema-design.mdx Schema Design 27 Schema design — model a business domain 53
seed-data.mdx Seed Data & Fixtures 34 Seed data — fixtures and demo datasets 52
validation-rules.mdx Field Validation Rules 36 Validation rules — reject bad records 51
validation.mdx Validation Metadata 33 Validation metadata — declare the checks 54

content/docs/deployment/

page before now after (proposed) now
backup-restore.mdx Backup & Restore 30 Backup and restore — protect tenant data 54
cli.mdx Command Line Interface 36 Command line interface — the os CLI reference 59
environment-variables.mdx Environment Variables 35 Environment variables — the full list 51
index.mdx Deployment Overview 33 Deployment — ship a runtime to production 55
production-readiness.mdx Production Readiness 34 Production readiness — the go-live list 53
publish-and-preview.mdx Publish, Versioning & Preview 43 Publish, versioning and preview builds 52
seed-tenancy-repair.mdx Seed Tenancy Repair 33 Repair seed tenancy after a bad import 52
self-hosting.mdx Self-Hosted Deployment 36 Self-hosting — run your own deployment 52
single-project-mode.mdx Single-Environment Mode 37 Single-environment mode — the simple setup 56
tenancy-modes.mdx Tenancy Postures & Membership 43 Tenancy postures and membership models 52
troubleshooting.mdx Troubleshooting & FAQ 35 Troubleshooting — common errors and fixes 55
validating-metadata.mdx Validating Metadata 33 Validate metadata before you deploy it 52

content/docs/getting-started/

page before now after (proposed) now
build-with-claude-code.mdx Build with Claude Code 36 Build an app with Claude Code, step by step 57
common-patterns.mdx Common Patterns 29 Common patterns — proven metadata recipes 55
examples.mdx Example Apps 26 Example apps — CRM, showcase and more 51
glossary.mdx Glossary 22 Glossary — every metadata term defined 52
how-ai-development-works.mdx How AI Development Works 38 How AI-written app development works 50
index.mdx What is ObjectStack? 34 What is ObjectStack? — a 5-minute intro 53
quick-reference.mdx Quick Reference Guide 35 Quick reference — every metadata type 51
quick-start.mdx Anatomy of an ObjectStack App 43 Anatomy of an app — your first metadata 53
your-first-project.mdx Your First Project 32 Your first project — from zero to running 55

content/docs/

page before now after (proposed) now
index.mdx Documentation 27 Documentation — build apps from metadata 54

content/docs/kernel/

page before now after (proposed) now
architecture.mdx Architecture 26 Core architecture — kernel and services 53
cluster.mdx Cluster Semantics 31 Cluster semantics — multi-node runtimes 53

content/docs/kernel/contracts/

page before now after (proposed) now
auth-service.mdx IAuthService Contract 35 IAuthService — the authentication contract 56
cache-service.mdx ICacheService Contract 36 ICacheService — the cache service contract 56
data-engine.mdx IDataEngine Contract 34 IDataEngine — the storage driver contract 55
index.mdx Service Contracts Overview 40 Service contracts — the kernel interfaces 55
metadata-service.mdx IMetadataService Contract 39 IMetadataService — the metadata contract 54
storage-service.mdx IStorageService Contract 38 IStorageService — the file storage contract 57

content/docs/kernel/

page before now after (proposed) now
events.mdx Events & Hooks 28 Kernel events and hooks — the full list 53
index.mdx Kernel & Services 31 Kernel and services — the runtime core 52

content/docs/kernel/runtime-services/

page before now after (proposed) now
audit-service.mdx services.audit 28 services.audit — the audit log service API 56
data-service.mdx services.data 27 services.data — the record CRUD service API 57
email-service.mdx services.email 28 services.email — the outbound mail API 52
examples.mdx Runtime Service Examples 38 Runtime service examples — copyable code 54
index.mdx Runtime Service APIs 34 Runtime service APIs — the services object 56
queue-service.mdx services.queue 28 services.queue — the job queue service API 56
settings-service.mdx services.settings 31 services.settings — the settings API 50
sharing-service.mdx services.sharing 30 services.sharing — the record share API 53
sms-service.mdx services.sms 26 services.sms — the text message service API 57
storage-service.mdx services.storage 30 services.storage — the file store API 51
versioning.mdx Runtime Service API Versioning 44 Runtime service API versioning policy 51

content/docs/kernel/

page before now after (proposed) now
services-checklist.mdx Kernel Services Checklist 39 Kernel services checklist for reviewers 53
services.mdx Service Registry 30 Service registry — resolve and override 53

content/docs/permissions/

page before now after (proposed) now
access-matrix.mdx Access-Matrix Snapshot Gate 41 Access matrix snapshot gate explained 51
access-recipes.mdx Who can see data / automation / interface 55 Access recipes — data, automation and UI 54
administrator-guide.mdx Administrator Guide 33 Administrator guide to permissions and roles 58
attachments-access.mdx Attachments Access 32 Attachment access — who can read a file 53
authentication.mdx Authentication 28 Authentication — sessions, tokens, SSO 52
authorization.mdx Authorization Architecture 40 Authorization architecture — how it decides 57
capabilities.mdx Declaring Capabilities 36 Declaring capabilities in a permission set 56
delegated-administration.mdx Delegated Administration 38 Delegated administration — scoped admin rights 60
explain.mdx Explain Engine 28 Explain engine — why access was denied 52
field-level-security.mdx Field-Level Security 34 Field-level security — hide and mask fields 57
index.mdx Permissions & Identity 36 Permissions and identity — the overview 53
permission-metadata.mdx Permission Metadata 33 Permission metadata — every option explained 58
permission-sets.mdx Permission Sets 29 Permission sets — grant access in bundles 55
permissions-matrix.mdx Security Permissions Matrix 41 Security permissions matrix reference 51
positions.mdx Positions 23 Positions — org hierarchy for sharing 51
profiles.mdx Profiles (removed) 32 Profiles (removed) — use permission sets 54
record-view-auditing.mdx Record-View Auditing 34 Record view auditing — who read what 50
rls.mdx Row-Level Security (RLS) 38 Row-level security (RLS) — filter by rule 55
sharing-rules.mdx Sharing Rules 27 Sharing rules and organization-wide defaults 58
sso.mdx Social & Enterprise SSO 37 SSO — social and enterprise sign-in setup 55
system-context.mdx System Context (isSystem) 39 System context (isSystem) — bypass rules 54

content/docs/plugins/

page before now after (proposed) now
adding-a-metadata-type.mdx Adding a Metadata Type 36 Add a custom metadata type from a plugin 54
anatomy.mdx Plugin Anatomy 28 Plugin anatomy — files, hooks and exports 55
development.mdx Plugin Development 32 Plugin development — build and test one 53
index.mdx Plugin System 27 Plugin system — extend the runtime safely 55
packages.mdx Package Overview 30 Package overview — what each one does 51

content/docs/protocol/

page before now after (proposed) now
backward-compatibility.mdx Backward Compatibility Policy 43 Backward compatibility policy for the spec 56
diagram.mdx Protocol Relationship Diagram 43 Protocol relationship diagram explained 53
index.mdx Protocol Specification 36 Protocol specification — the open format 54

content/docs/protocol/kernel/

page before now after (proposed) now
config-resolution.mdx Configuration Resolution 38 Configuration resolution order and layers 55
error-handling.mdx Error Handling 28 Error handling — the response envelope 52
http-protocol.mdx HTTP API 22 HTTP API protocol — routes and verbs 50
i18n-standard.mdx Internationalization Standard 43 Internationalization standard for metadata 56
index.mdx Kernel: The System Protocol 41 Kernel — the system protocol specification 56
lifecycle.mdx System Lifecycle 30 System lifecycle — from boot to ready state 57
metadata-service.mdx Metadata Service 30 Metadata service protocol specification 53
plugin-spec.mdx Plugin Package Specification 42 Plugin package specification and manifest 55
realtime-protocol.mdx Real-Time Protocols 33 Real-time protocols — websockets, SSE 51

content/docs/protocol/

page before now after (proposed) now
knowledge.mdx Knowledge Protocol 32 Knowledge protocol — RAG as metadata 50

content/docs/protocol/objectql/

page before now after (proposed) now
index.mdx ObjectQL: The Data Protocol 41 ObjectQL — the data protocol specification 56
query-syntax.mdx Query Syntax 26 ObjectQL query syntax — the full specification 60
schema.mdx Schema Definition 31 ObjectQL schema definition specification 54
security.mdx Security & Access Control 39 ObjectQL security and access control 50
state-machine.mdx State Machine (Lifecycle) 39 State machine — record lifecycle spec 51
types.mdx Type System 25 ObjectQL type system — the specification 54

content/docs/protocol/objectui/

page before now after (proposed) now
actions.mdx Action Protocol 29 Action protocol — buttons as metadata 51
concept.mdx UI as Data Concept 32 UI as data — the core ObjectUI concept 52
index.mdx ObjectUI: The UI Protocol 39 ObjectUI — the UI protocol specification 54
layout-dsl.mdx Layout DSL 24 Layout DSL — arrange fields declaratively 55
record-alert.mdx record:alert — Conditional Banners on Record Pages 64 record:alert — banners on record pages 52
widget-contract.mdx Widget Contract 29 Widget contract — build a custom widget 53

content/docs/ui/

page before now after (proposed) now
actions.mdx Actions 21 Actions — permission-checked UI buttons 53
apps.mdx App Metadata 26 App metadata — navigation and branding 52
audience-based-interfaces.mdx Audience-based interfaces 39 Audience-based interfaces — one app, many 55
create-vs-edit-form.mdx Create form ≠ edit form 37 Create form vs edit form — the differences 56
dashboards.mdx Dashboard Metadata 32 Dashboard metadata — charts and KPIs 50
doc-pages.mdx Doc Metadata 26 Doc metadata — in-app documentation pages 55
field-grouping-and-order.mdx Field grouping & order 36 Field grouping and order in generated forms 57
forms.mdx Forms (Public + Internal) 39 Forms — public and internal data entry 52
index.mdx UI Engine 23 UI engine — render views from metadata 52
pages.mdx Page Metadata 27 Page metadata — build custom app screens 54
public-data-collection.mdx Collect data from the public 42 Collect data from the public with web forms 57
react-pages.mdx React Pages 25 React pages — escape hatch for custom UI 54
reports.mdx Report Metadata 29 Report metadata — grouped, filtered data 54
setup-app.mdx Setup App 23 Setup app — administer a running runtime 54
translations.mdx Translations 26 Translations — labels and UI text per locale 58
views.mdx View Metadata 27 View metadata — list, kanban, calendar 52

content/docs/

page before now after (proposed) now
upgrading.mdx Upgrading 23 Upgrade guide — move between major versions 57

Verification

All at pushed head ed3afdfda, git status --porcelain empty.

Gate family re-derived from the actual diff, not from the dispatch list: node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (4 paths vs merge base, three-dot). Its first run warned STALE TREE — 3 commit(s) behind origin/main and named .github/workflows/lint.yml as changed in that range, so origin/main was merged in and the derivation re-run before anything was trusted.

25 gate families run, every exit code captured before any pipe (each redirected to its own file, $? read immediately), all exit=0, re-run in full at the final commit ed3afdfda:

check:doc-anchors · check:doc-authoring · check:docs-audit-scope · check:docs-redirects · check:docs-single-h1 · check:published-readme-links · check:react-page-adapter-contract · check:release-notes · check:role-word · check:cross-package-test-inputs · check:nul-bytes · check-ci-filter-parity · check-cross-package-test-inputs · check-doc-frontmatter · check-doc-route-spelling · check-docs-section-name · check-section-landing-index · @objectstack/lint check:doc-formula-expressions · @objectstack/lint check:doc-security-posture · @objectstack/spec check:docs · @objectstack/spec check:skill-examples · @objectstack/spec check:empty-state · @objectstack/spec check:liveness · @objectstack/spec check:strictness-ledger · @objectstack/spec check:variant-docs

Verdict lines quoted rather than exit codes:

  • ✅ check-doc-anchors: 278 internal #fragment link(s) across 408 source file(s) all resolve to a real heading
  • ✓ check-docs-single-h1: 180 page(s) under content/docs/ carry no body-level # heading (2 subtree(s) excluded)
  • ✓ check-doc-frontmatter: 2 content root(s) verified, each against its own floor — content/docs 403, content/blog 3.

⚠️ Four of those 25 first reported exit=1 and are NOT recorded as failures — each printed PREREQUISITE NOT MET or a missing-build-artifact banner and, in the gates' own words, "Nothing was measured … It is NOT a finding." They were re-run green after pnpm exec turbo run build --filter=@objectstack/{formula,lint,spec,client,client-react} and pnpm --filter @objectstack/spec gen:schema. Recorded here so the first reading is not mistaken for a red that got quietly dropped.

Two declared narrowings:

  1. os-verify-lock.sh ran UNLOCKED. Every heavy command went through the entry point, which reports on this host: VERDICT command-exit 0 · UNLOCKED (declared) · no usable flock on this host, so the shared verify lock was NEVER taken and NOTHING was serialized · declare it in the PR body. macOS ships no flock; the script's own disclosure is pasted rather than paraphrased.
  2. pnpm lint narrowed to the diff, with all three required measurements:
    • population read from ESLint itself: eslint --print-config content/docs/index.mdx prints undefined.mdx is outside the lint population, and that is ESLint's answer, not an assumption;
    • file count from --format json: 4 files, 0 errors, 4 warnings, and all four warnings are File ignored because no matching configuration was supplied;
    • invariance for untouched files: eslint.config.mjs states at line 328 that this repo "never enables type-aware linting (no parserOptions.project, no typed @typescript-eslint rules) for ANY file", so a frontmatter change cannot move any untouched file's verdict.

No changeset — docs content only, publishing nothing. skip-changeset applied.

Out of scope, filed

Generated by Claude Code

The frontmatter title is the SERP <title>, the on-page <h1>, the sidebar
label and the llms.txt heading — one string, four consumers. Lengthening
it for search shortens nothing else, so this lands the rule only where it
costs the navigation nothing:

- content/docs/index.mdx, protocol/objectql/index.mdx and
  protocol/objectui/index.mdx have no sidebar entry of their own — their
  folder's meta.json title is what the tree shows.
- protocol/objectui/record-alert.mdx was 64 characters with the site
  suffix, already over the 60-character budget; the new title is 52 and
  its sidebar label gets shorter, not longer.

The rule and the full 180-row before/after table are in the PR body for
the maintainer to judge. Nothing else is rewritten.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@os-zhuang os-zhuang added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 25, 2026
@github-actions github-actions Bot added the documentation Improvements or additions to documentation label Aug 25, 2026
@os-zhuang
os-zhuang marked this pull request as ready for review August 26, 2026 00:08
@os-zhuang
os-zhuang added this pull request to the merge queue Aug 26, 2026

@os-zhuang os-zhuang left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

同意

Merged via the queue into main with commit e18c870 Aug 26, 2026
35 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-12237-page-titles branch August 26, 2026 00:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/xs skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants