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

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
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 28, 2026
…hored pages, short nav labels kept via navTitle (objectstack-ai#20170)

Fixes objectstack-ai#12237
Clause-②: no

## Summary

Applies the maintainer-approved page-title rule to the authored docs
pages that still carried their pre-rule title, and keeps every
navigation label exactly as it was through `navTitle`.

- **175 approved rows applied verbatim** from the table in PR objectstack-ai#12312,
each with a `navTitle:` line holding the page's previous title.
- **1 approved row re-worded** because a required gate rejects the
approved text (`check:role-word`), and **1 authored page the table never
listed** authored per the rule. Both are listed under *Needs maintainer
voice review* below.
- **One gate premise moved, not loosened:**
`check:runtime-services-index` used to hold each
`kernel/runtime-services/NAME-service.mdx` to `title: services.NAME`. It
now reads that accessor premise from the page's `navTitle`, so the
chapter's 8 approved titles land verbatim with `navTitle:
services.NAME`. See *The runtime-services gate* below. (PM answer to the
round-1 open question, review comment `5852831074` on objectstack-ai#12237.)
- 177 docs files, frontmatter only: every one is `+2/-1` (`title:`
replaced, `navTitle:` added). No `description:`, no page body, nothing
under `references/**` or `releases/**`, nothing in `apps/docs/**` or any
`meta.json`. Plus one gate script,
`scripts/check-runtime-services-index.mjs` (file surface amended by the
seat for this round).

⛔ **Draft, and it stays open for maintainer review — do not self-merge**
(card body). The landing path after review is the seat's call.

## The rule

```
PRIMARY-KEYWORD — QUALIFIER
```

- The frontmatter `title:` string is **36–46 characters**, so the
rendered title with the 14-character ` | ObjectStack` suffix lands in
**50–60**; no rendered title over 60. (PM ruling on the card, comment
`5414218147`, Q2: the band is authoritative.)
- Separator ` — ` throughout; `ObjectStack` never inside a title,
because the suffix carries it; one declared exception,
`getting-started/index.mdx` keeps `What is ObjectStack?`. (Maintainer
ruling recorded in comment `5419555720`, verbatim: 「其他同意」 — the rule and
the full before/after table in PR objectstack-ai#12312 approved as they stand.)
- Scope: authored pages only. The generator-emitted pages are objectstack-ai#15403
(`domain:spec`), per the triage split in comment `5541938216`.
- Short navigation labels: `navTitle` (landed with PR objectstack-ai#14055). The
fallback to `title` lives only in `navLabel()` in
`apps/docs/lib/nav-title.ts`.

## What changes on the site, and what does not

Measured in `apps/docs` at this head. A page's `title` is read at **12
read points on 5 faces**, plus the site search index. All of these take
the longer title:

| face | read points |
|---|---|
| SERP / Open Graph / Twitter metadata |
`app/[lang]/docs/[[...slug]]/page.tsx` `generateMetadata`: `title`
(232), `openGraph.title` (237), OG image `alt` (248), `twitter.title`
(254) |
| on-page h1 | `page.tsx:171` `DocsTitle` |
| JSON-LD | `TechArticle` `headline` and `name` (`page.tsx:144,145`);
the `BreadcrumbList` leaf crumb (`page.tsx:116`) |
| `llms.txt` / `llms-full.txt` / `/llms.mdx/docs/…` |
`app/llms.txt/route.ts:10`; `lib/source.ts:66` `getLLMText` |
| Open Graph card image | `app/og/docs/[...slug]/route.tsx:18` |
| site search results | `app/api/search/route.ts`,
`createFromSource(source)` (fumadocs indexes `page.data.title`) |

**Unchanged, by construction:** the page tree, i.e. the sidebar entries
and the footer previous/next links. Every page this PR lengthens is a
page-tree node, and each one declares `navTitle:` with its previous
title verbatim (same YAML quoting), so `navLabel()` returns the old
string.

How "page-tree node" was measured, not guessed: replaying fumadocs-core
16.14.4 `buildFolder()` over every `meta.json` (all 19 authored
`meta.json` list `pages` explicitly, none uses a rest entry, none lists
`index`) gives **180 of 181 authored pages in the tree**: 162 sidebar
leaves and 18 folder index pages. A folder index page is not a sidebar
row (its folder label comes from `meta.json` `title`), but
`flattenTree()` puts `folder.index` into the footer previous/next list,
so it needs `navTitle` too. The one authored page outside the tree is
`content/docs/index.mdx`, the global root (landed by objectstack-ai#12312, not touched
here).

The JSON-LD breadcrumb does not pick up the short label: `docsTrail()`
calls `getBreadcrumbItems(…, { includePage: false })` and pushes
`page.data.title` itself, pinned by leg D of
`scripts/check-docs-nav-label.mjs` (run green below).

## Statistics, re-derived

Population: every `content/docs/**/*.mdx` outside `references/**` and
`releases/**`, **181 pages**. Rendered length = `title` + 14. Base
`84880f9`, head `c69d5c7`.

| | pages | median | min | max | in 50–60 | over 60 | under 50 |
duplicate titles | `navTitle` declared |
|---|--:|--:|--:|--:|--:|--:|--:|--:|--:|
| before | 181 | 32 | 19 | 56 | 6 | 0 | 175 | 0 | 0 |
| after | 181 | 54 | 50 | 60 | 181 | 0 | 0 | 0 | 177 |

Duplicates were also checked against the 225 generated `references/**` +
`releases/**` titles: no new title collides with any page.

**Premise check on the approved table** (PR objectstack-ai#12312 body, section 6): it
parses to **180 rows** — 4 landed with objectstack-ai#12312 and **176 not landed**. On
base `84880f9`, **176 of 176** pages still carry exactly the table's
*before*, **0** carry its *after*, **0** paths are missing. The
generated-page test reads the file head for a marker, never prose: no
authored page carries one (the nine prose hits of the word "generated"
in a page head, e.g. `api/index.mdx`, `ui/index.mdx`, are body text).
One authored page is absent from the table:
`permissions/tenant-audit-census.mdx` (added after objectstack-ai#12312; its
frontmatter is authored, only a body region is generated by
`scripts/tenant-audit-census.mjs`).

## Needs maintainer voice review — not covered by the approved table

Every row here is **applied in this diff**.

| page | before | approved *after* | applied | rendered | why |
|---|---|---|---|--:|---|
| `permissions/administrator-guide.mdx` | Administrator Guide |
Administrator guide to permissions and roles | **Administrator guide to
permissions and access** | 59 | the approved text fails the required
`check:role-word` gate: "role" is a reserved word (ADR-0090 D3).
Smallest change that clears it. |
| `permissions/tenant-audit-census.mdx` | Tenant-Audit Census | — (page
not in the table) | **Tenant-audit census — tenancy write call sites** |
60 | authored page the table never listed; written per the rule.
`navTitle: Tenant-Audit Census`. |

## The runtime-services gate

`scripts/check-runtime-services-index.mjs` checks first that each
chapter page declares the accessor its filename claims; the other five
enumerations rest on that premise. It used to read `title`. The title
rule lengthens `title` for the search-facing surfaces, so the premise
now reads `navTitle`, which is the page-tree label and, on these 8
pages, the bare accessor.

- Read from the page's **leading frontmatter block only**; one layer of
matching YAML quotes is syntax, not value. The `title` is not read at
all.
- Still red: `navTitle` absent, blank, present only in the body, or not
`services.NAME`. A page that still says `title: services.NAME` with no
`navTitle` is red too. Each finding names the fix (`add \`navTitle:
services.sms\` to the page's leading frontmatter block`).
- The header prose that stated the `title:` premise is rewritten to say
what the gate now does and why.
- `--self-test`: new floored battery *The accessor premise lives in
navTitle*, 7 cases (absent, wrong, old title-only spelling, body-only
line and blank are red; a long title and a quoted value are green).
Roster floor 8 to 9. The fixture's default `title` is now a long
search-intent string, so every existing green case is also a green with
a long title. 59 assertions.
- **Ablation, trapped:** `scripts/ablation-replace.mjs` set `navTitle:
services.sms` to `navTitle: services.text` in `sms-service.mdx` (anchor
1 to 0, blob `2001694b2859` to `67f9af393d4d`). The gate exited **1**:
`frontmatter navTitle is "services.text", expected "services.sms" to
match the filename -- set \`navTitle: services.sms\`, or rename the page
if the accessor really changed`. Restored under an EXIT/INT/TERM trap to
blob `2001694b2859`, which equals HEAD; `git diff HEAD` empty.
- The live tree before the 8 pages were edited: the new gate went red
with exactly 8 `declares no navTitle` findings.

**Other readers of these pages' `title`** (`git grep` over `scripts/`,
`packages/`, `apps/docs`, `.github/`, `content/docs`): no other gate or
script parses it. `apps/docs` reads it generically through
`page.data.title`, which is the intended change.
`content/docs/kernel/index.mdx` names the accessors in its services
table and does not read the page titles. One piece of stale prose: the
header of `scripts/check-docs-single-h1.mjs` (lines 72-73) uses
`audit-service.mdx` `title: services.audit` as a worked example. Its
logic is unaffected and it is green, but the example no longer matches
the page. That file is outside this PR's surface, so it is noted here
and not edited.

## The complete table

All 177 authored pages this PR changes. Rendered = `title` + ` |
ObjectStack`. `navTitle` = the page's previous title, declared verbatim.
✳️ = in the review section above.

| page | before | after | rendered | `navTitle` |
|---|---|---|--:|:-:|
| `ai/actions-as-tools.mdx` | Actions as Tools | MCP tools — expose
actions to AI agents | 53 | yes |
| `ai/agents.mdx` | AI Agents | AI agents — declare tools, model and
prompt | 57 | yes |
| `ai/connect-mcp.mdx` | Connect an MCP Client | MCP client setup —
Claude, Cursor and IDEs | 56 | yes |
| `ai/index.mdx` | AI Overview | AI features — MCP tools, agents and RAG
| 53 | yes |
| `ai/knowledge-rag.mdx` | Knowledge & RAG | RAG — embeddings and
knowledge retrieval | 54 | yes |
| `ai/natural-language-queries.mdx` | Natural Language Queries | Natural
language queries over your data | 53 | yes |
| `ai/skills-reference.mdx` | AI Skills Reference | AI skill reference —
every field explained | 56 | yes |
| `ai/skills.mdx` | AI Skills System | AI skills — reusable instructions
for agents | 58 | yes |
| `ai/tools.mdx` | Tool Records | Tool records — govern what an agent
may call | 58 | yes |
| `api/client-sdk.mdx` | Client SDK | Client SDK — typed JavaScript data
access | 55 | yes |
| `api/data-api.mdx` | Data API | REST data API — CRUD over every object
| 52 | yes |
| `api/data-flow.mdx` | Data Flow Diagrams | Request data flow — from
HTTP to driver | 53 | yes |
| `api/declarative-endpoints.mdx` | Declarative Endpoints | Custom
endpoints declared as metadata | 51 | yes |
| `api/environment-routing.mdx` | Environment-Scoped Routing |
Environment routing — dev, preview, prod | 54 | yes |
| `api/error-catalog.mdx` | Error Code Catalog | API error codes — the
complete catalog | 52 | yes |
| `api/error-handling-client.mdx` | Client-Side Error Handling | Client
error handling — retries and codes | 55 | yes |
| `api/error-handling-server.mdx` | Server-Side Error Handling | Server
error handling — throw the envelope | 56 | yes |
| `api/index.mdx` | API Overview | REST and GraphQL APIs — generated per
object | 58 | yes |
| `api/metadata-api.mdx` | Metadata & Package API | Metadata API — read
and publish packages | 54 | yes |
| `api/plugin-endpoints.mdx` | Plugin Endpoints | Plugin endpoints — add
routes from a plugin | 57 | yes |
| `api/wire-format.mdx` | Wire Format & JSON Examples | API wire format
— request and response JSON | 57 | yes |
| `automation/approvals.mdx` | Approval workflow | Approval chains —
multi-step sign-off rules | 57 | yes |
| `automation/connectors.mdx` | Connectors | Connectors — call external
systems safely | 55 | yes |
| `automation/email-templates.mdx` | Email Templates | Email templates —
merge fields and layouts | 56 | yes |
| `automation/flows.mdx` | Flow Metadata | Flows — DAG automation as
typed metadata | 54 | yes |
| `automation/hook-bodies.mdx` | Hook & Action Bodies (L1 / L2) | Hook
and action bodies — the L1/L2 rules | 54 | yes |
| `automation/hooks.mdx` | Hooks | Record hooks — beforeInsert to
afterDelete | 56 | yes |
| `automation/index.mdx` | Automation | Automation — flows, triggers and
schedules | 56 | yes |
| `automation/jobs.mdx` | Scheduled Jobs | Scheduled jobs — cron
automation metadata | 55 | yes |
| `automation/webhooks.mdx` | Webhook Delivery | Webhooks — outbound
delivery and retries | 54 | yes |
| `automation/workflows.mdx` | Workflow Metadata | Workflow rules —
declarative record logic | 55 | yes |
| `build-without-code.mdx` | Build Without Code | Build business apps
without writing code | 54 | yes |
| `capabilities/ai.mdx` | AI Under Governance | AI under governance —
permissioned agents | 55 | yes |
| `capabilities/analytics.mdx` | Analytics & Dashboards | Analytics —
dashboards, reports and charts | 56 | yes |
| `capabilities/approvals.mdx` | Approvals | Approvals — routing, queues
and audit trail | 57 | yes |
| `capabilities/automation.mdx` | Automation — Processes That Run
Themselves | Automation — processes that run themselves | 56 | yes |
| `capabilities/data.mdx` | Manage Business Data | Business data —
objects, fields and records | 57 | yes |
| `capabilities/forms.mdx` | Forms & Data Quality | Forms and data
quality — validation rules | 55 | yes |
| `capabilities/index.mdx` | What Can It Do? | What can it do? — the
capability overview | 55 | yes |
| `capabilities/integrations.mdx` | Integrations & Everyday Work |
Integrations — email, files and everyday work | 59 | yes |
| `capabilities/permissions.mdx` | Permissions — Who Sees What |
Permissions — who sees what, enforced | 51 | yes |
| `capabilities/request-template.mdx` | How to Request Features |
Request a feature — how to describe it | 52 | yes |
| `capabilities/views.mdx` | Views — See Data Your Way | Views — lists,
kanban, calendar and gantt | 55 | yes |
| `concepts/architecture.mdx` | Protocol Architecture | Protocol
architecture — the four layers | 53 | yes |
| `concepts/design-principles.mdx` | Design Principles | Design
principles behind the metadata spec | 56 | yes |
| `concepts/index.mdx` | Core Concepts | Core concepts — metadata,
runtime, protocol | 57 | yes |
| `concepts/metadata-driven.mdx` | Metadata-Driven Development |
Metadata-driven development explained | 51 | yes |
| `concepts/metadata-lifecycle.mdx` | Metadata Lifecycle & HMR |
Metadata lifecycle — load, publish and HMR | 56 | yes |
| `concepts/north-star.mdx` | North Star | North star — why this
framework exists | 52 | yes |
| `data-modeling/analytics.mdx` | Analytics Datasets | Analytics
datasets — modelling for reports | 56 | yes |
| `data-modeling/drivers.mdx` | Database Drivers | Database drivers —
Postgres, MySQL, Mongo | 55 | yes |
| `data-modeling/external-datasources.mdx` | External Datasources
(Federation) | External datasources — query without ETL | 54 | yes |
| `data-modeling/field-type-decision-tree.mdx` | Field Type Decision
Tree | Which field type should I use? A decision tree | 60 | yes |
| `data-modeling/field-types.mdx` | Field Type Gallery | Field types —
the complete visual gallery | 55 | yes |
| `data-modeling/fields.mdx` | Field Metadata | Field metadata — every
option explained | 53 | yes |
| `data-modeling/formulas.mdx` | Expressions (CEL) | Formulas — CEL
expressions on records | 51 | yes |
| `data-modeling/import-mappings.mdx` | Import Mappings | Import
mappings — load external data files | 56 | yes |
| `data-modeling/index.mdx` | Data Modeling | Data modelling — objects,
fields, relations | 57 | yes |
| `data-modeling/indexing.mdx` | Database Indexing | Database indexes —
declare and tune them | 54 | yes |
| `data-modeling/object-extensions.mdx` | Object Extensions | Object
extensions — extend without forking | 56 | yes |
| `data-modeling/objects.mdx` | Object Metadata | Object metadata —
define your data schema | 55 | yes |
| `data-modeling/queries.mdx` | Query Syntax Cheat Sheet | ObjectQL
query syntax — a cheat sheet | 51 | yes |
| `data-modeling/relationships.mdx` | Relationships & Lookups |
Relationships — lookups, master-detail | 52 | yes |
| `data-modeling/schema-design.mdx` | Schema Design | Schema design —
model a business domain | 53 | yes |
| `data-modeling/seed-data.mdx` | Seed Data & Fixtures | Seed data —
fixtures and demo datasets | 52 | yes |
| `data-modeling/validation-rules.mdx` | Field Validation Rules |
Validation rules — reject bad records | 51 | yes |
| `data-modeling/validation.mdx` | Validation Metadata | Validation
metadata — declare the checks | 54 | yes |
| `deployment/backup-restore.mdx` | Backup & Restore | Backup and
restore — protect tenant data | 54 | yes |
| `deployment/cli.mdx` | Command Line Interface | Command line interface
— the os CLI reference | 59 | yes |
| `deployment/environment-variables.mdx` | Environment Variables |
Environment variables — the full list | 51 | yes |
| `deployment/index.mdx` | Deployment Overview | Deployment — ship a
runtime to production | 55 | yes |
| `deployment/production-readiness.mdx` | Production Readiness |
Production readiness — the go-live list | 53 | yes |
| `deployment/publish-and-preview.mdx` | Publish, Versioning & Preview |
Publish, versioning and preview builds | 52 | yes |
| `deployment/seed-tenancy-repair.mdx` | Seed Tenancy Repair | Repair
seed tenancy after a bad import | 52 | yes |
| `deployment/self-hosting.mdx` | Self-Hosted Deployment | Self-hosting
— run your own deployment | 52 | yes |
| `deployment/single-project-mode.mdx` | Single-Environment Mode |
Single-environment mode — the simple setup | 56 | yes |
| `deployment/tenancy-modes.mdx` | Tenancy Postures & Membership |
Tenancy postures and membership models | 52 | yes |
| `deployment/troubleshooting.mdx` | Troubleshooting & FAQ |
Troubleshooting — common errors and fixes | 55 | yes |
| `deployment/validating-metadata.mdx` | Validating Metadata | Validate
metadata before you deploy it | 52 | yes |
| `getting-started/build-with-claude-code.mdx` | Build with Claude Code
| Build an app with Claude Code, step by step | 57 | yes |
| `getting-started/common-patterns.mdx` | Common Patterns | Common
patterns — proven metadata recipes | 55 | yes |
| `getting-started/examples.mdx` | Example Apps | Example apps — CRM,
showcase and more | 51 | yes |
| `getting-started/glossary.mdx` | Glossary | Glossary — every metadata
term defined | 52 | yes |
| `getting-started/how-ai-development-works.mdx` | How AI Development
Works | How AI-written app development works | 50 | yes |
| `getting-started/index.mdx` | What is ObjectStack? | What is
ObjectStack? — a 5-minute intro | 53 | yes |
| `getting-started/quick-reference.mdx` | Quick Reference Guide | Quick
reference — every metadata type | 51 | yes |
| `getting-started/quick-start.mdx` | Anatomy of an ObjectStack App |
Anatomy of an app — your first metadata | 53 | yes |
| `getting-started/your-first-project.mdx` | Your First Project | Your
first project — from zero to running | 55 | yes |
| `kernel/architecture.mdx` | Architecture | Core architecture — kernel
and services | 53 | yes |
| `kernel/cluster.mdx` | Cluster Semantics | Cluster semantics —
multi-node runtimes | 53 | yes |
| `kernel/contracts/auth-service.mdx` | IAuthService Contract |
IAuthService — the authentication contract | 56 | yes |
| `kernel/contracts/cache-service.mdx` | ICacheService Contract |
ICacheService — the cache service contract | 56 | yes |
| `kernel/contracts/data-engine.mdx` | IDataEngine Contract |
IDataEngine — the storage driver contract | 55 | yes |
| `kernel/contracts/index.mdx` | Service Contracts Overview | Service
contracts — the kernel interfaces | 55 | yes |
| `kernel/contracts/metadata-service.mdx` | IMetadataService Contract |
IMetadataService — the metadata contract | 54 | yes |
| `kernel/contracts/storage-service.mdx` | IStorageService Contract |
IStorageService — the file storage contract | 57 | yes |
| `kernel/events.mdx` | Events & Hooks | Kernel events and hooks — the
full list | 53 | yes |
| `kernel/index.mdx` | Kernel & Services | Kernel and services — the
runtime core | 52 | yes |
| `kernel/runtime-services/audit-service.mdx` | services.audit |
services.audit — the audit log service API | 56 | yes |
| `kernel/runtime-services/data-service.mdx` | services.data |
services.data — the record CRUD service API | 57 | yes |
| `kernel/runtime-services/email-service.mdx` | services.email |
services.email — the outbound mail API | 52 | yes |
| `kernel/runtime-services/examples.mdx` | Runtime Service Examples |
Runtime service examples — copyable code | 54 | yes |
| `kernel/runtime-services/index.mdx` | Runtime Service APIs | Runtime
service APIs — the services object | 56 | yes |
| `kernel/runtime-services/queue-service.mdx` | services.queue |
services.queue — the job queue service API | 56 | yes |
| `kernel/runtime-services/settings-service.mdx` | services.settings |
services.settings — the settings API | 50 | yes |
| `kernel/runtime-services/sharing-service.mdx` | services.sharing |
services.sharing — the record share API | 53 | yes |
| `kernel/runtime-services/sms-service.mdx` | services.sms |
services.sms — the text message service API | 57 | yes |
| `kernel/runtime-services/storage-service.mdx` | services.storage |
services.storage — the file store API | 51 | yes |
| `kernel/runtime-services/versioning.mdx` | Runtime Service API
Versioning | Runtime service API versioning policy | 51 | yes |
| `kernel/services-checklist.mdx` | Kernel Services Checklist | Kernel
services checklist for reviewers | 53 | yes |
| `kernel/services.mdx` | Service Registry | Service registry — resolve
and override | 53 | yes |
| `permissions/access-matrix.mdx` | Access-Matrix Snapshot Gate | Access
matrix snapshot gate explained | 51 | yes |
| `permissions/access-recipes.mdx` | Who can see data / automation /
interface | Access recipes — data, automation and UI | 54 | yes |
| `permissions/administrator-guide.mdx` ✳️ | Administrator Guide |
Administrator guide to permissions and access | 59 | yes |
| `permissions/attachments-access.mdx` | Attachments Access | Attachment
access — who can read a file | 53 | yes |
| `permissions/authentication.mdx` | Authentication | Authentication —
sessions, tokens, SSO | 52 | yes |
| `permissions/authorization.mdx` | Authorization Architecture |
Authorization architecture — how it decides | 57 | yes |
| `permissions/capabilities.mdx` | Declaring Capabilities | Declaring
capabilities in a permission set | 56 | yes |
| `permissions/delegated-administration.mdx` | Delegated Administration
| Delegated administration — scoped admin rights | 60 | yes |
| `permissions/explain.mdx` | Explain Engine | Explain engine — why
access was denied | 52 | yes |
| `permissions/field-level-security.mdx` | Field-Level Security |
Field-level security — hide and mask fields | 57 | yes |
| `permissions/index.mdx` | Permissions & Identity | Permissions and
identity — the overview | 53 | yes |
| `permissions/permission-metadata.mdx` | Permission Metadata |
Permission metadata — every option explained | 58 | yes |
| `permissions/permission-sets.mdx` | Permission Sets | Permission sets
— grant access in bundles | 55 | yes |
| `permissions/permissions-matrix.mdx` | Security Permissions Matrix |
Security permissions matrix reference | 51 | yes |
| `permissions/positions.mdx` | Positions | Positions — org hierarchy
for sharing | 51 | yes |
| `permissions/profiles.mdx` | Profiles (removed) | Profiles (removed) —
use permission sets | 54 | yes |
| `permissions/record-view-auditing.mdx` | Record-View Auditing | Record
view auditing — who read what | 50 | yes |
| `permissions/rls.mdx` | Row-Level Security (RLS) | Row-level security
(RLS) — filter by rule | 55 | yes |
| `permissions/sharing-rules.mdx` | Sharing Rules | Sharing rules and
organization-wide defaults | 58 | yes |
| `permissions/sso.mdx` | Social & Enterprise SSO | SSO — social and
enterprise sign-in setup | 55 | yes |
| `permissions/system-context.mdx` | System Context (isSystem) | System
context (isSystem) — bypass rules | 54 | yes |
| `permissions/tenant-audit-census.mdx` ✳️ | Tenant-Audit Census |
Tenant-audit census — tenancy write call sites | 60 | yes |
| `plugins/adding-a-metadata-type.mdx` | Adding a Metadata Type | Add a
custom metadata type from a plugin | 54 | yes |
| `plugins/anatomy.mdx` | Plugin Anatomy | Plugin anatomy — files, hooks
and exports | 55 | yes |
| `plugins/development.mdx` | Plugin Development | Plugin development —
build and test one | 53 | yes |
| `plugins/index.mdx` | Plugin System | Plugin system — extend the
runtime safely | 55 | yes |
| `plugins/packages.mdx` | Package Overview | Package overview — what
each one does | 51 | yes |
| `protocol/backward-compatibility.mdx` | Backward Compatibility Policy
| Backward compatibility policy for the spec | 56 | yes |
| `protocol/diagram.mdx` | Protocol Relationship Diagram | Protocol
relationship diagram explained | 53 | yes |
| `protocol/index.mdx` | Protocol Specification | Protocol specification
— the open format | 54 | yes |
| `protocol/kernel/config-resolution.mdx` | Configuration Resolution |
Configuration resolution order and layers | 55 | yes |
| `protocol/kernel/error-handling.mdx` | Error Handling | Error handling
— the response envelope | 52 | yes |
| `protocol/kernel/http-protocol.mdx` | HTTP API | HTTP API protocol —
routes and verbs | 50 | yes |
| `protocol/kernel/i18n-standard.mdx` | Internationalization Standard |
Internationalization standard for metadata | 56 | yes |
| `protocol/kernel/index.mdx` | Kernel: The System Protocol | Kernel —
the system protocol specification | 56 | yes |
| `protocol/kernel/lifecycle.mdx` | System Lifecycle | System lifecycle
— from boot to ready state | 57 | yes |
| `protocol/kernel/metadata-service.mdx` | Metadata Service | Metadata
service protocol specification | 53 | yes |
| `protocol/kernel/plugin-spec.mdx` | Plugin Package Specification |
Plugin package specification and manifest | 55 | yes |
| `protocol/kernel/realtime-protocol.mdx` | Real-Time Protocols |
Real-time protocols — websockets, SSE | 51 | yes |
| `protocol/knowledge.mdx` | Knowledge Protocol | Knowledge protocol —
RAG as metadata | 50 | yes |
| `protocol/objectql/query-syntax.mdx` | Query Syntax | ObjectQL query
syntax — the full specification | 60 | yes |
| `protocol/objectql/schema.mdx` | Schema Definition | ObjectQL schema
definition specification | 54 | yes |
| `protocol/objectql/security.mdx` | Security & Access Control |
ObjectQL security and access control | 50 | yes |
| `protocol/objectql/state-machine.mdx` | State Machine (Lifecycle) |
State machine — record lifecycle spec | 51 | yes |
| `protocol/objectql/types.mdx` | Type System | ObjectQL type system —
the specification | 54 | yes |
| `protocol/objectui/actions.mdx` | Action Protocol | Action protocol —
buttons as metadata | 51 | yes |
| `protocol/objectui/concept.mdx` | UI as Data Concept | UI as data —
the core ObjectUI concept | 52 | yes |
| `protocol/objectui/layout-dsl.mdx` | Layout DSL | Layout DSL — arrange
fields declaratively | 55 | yes |
| `protocol/objectui/widget-contract.mdx` | Widget Contract | Widget
contract — build a custom widget | 53 | yes |
| `ui/actions.mdx` | Actions | Actions — permission-checked UI buttons |
53 | yes |
| `ui/apps.mdx` | App Metadata | App metadata — navigation and branding
| 52 | yes |
| `ui/audience-based-interfaces.mdx` | Audience-based interfaces |
Audience-based interfaces — one app, many | 55 | yes |
| `ui/create-vs-edit-form.mdx` | Create form ≠ edit form | Create form
vs edit form — the differences | 56 | yes |
| `ui/dashboards.mdx` | Dashboard Metadata | Dashboard metadata — charts
and KPIs | 50 | yes |
| `ui/doc-pages.mdx` | Doc Metadata | Doc metadata — in-app
documentation pages | 55 | yes |
| `ui/field-grouping-and-order.mdx` | Field grouping & order | Field
grouping and order in generated forms | 57 | yes |
| `ui/forms.mdx` | Forms (Public + Internal) | Forms — public and
internal data entry | 52 | yes |
| `ui/index.mdx` | UI Engine | UI engine — render views from metadata |
52 | yes |
| `ui/pages.mdx` | Page Metadata | Page metadata — build custom app
screens | 54 | yes |
| `ui/public-data-collection.mdx` | Collect data from the public |
Collect data from the public with web forms | 57 | yes |
| `ui/react-pages.mdx` | React Pages | React pages — escape hatch for
custom UI | 54 | yes |
| `ui/reports.mdx` | Report Metadata | Report metadata — grouped,
filtered data | 54 | yes |
| `ui/setup-app.mdx` | Setup App | Setup app — administer a running
runtime | 54 | yes |
| `ui/translations.mdx` | Translations | Translations — labels and UI
text per locale | 58 | yes |
| `ui/views.mdx` | View Metadata | View metadata — list, kanban,
calendar | 52 | yes |
| `upgrading.mdx` | Upgrading | Upgrade guide — move between major
versions | 57 | yes |

## Verification

All at head `c69d5c7`, `git status --porcelain` empty. `origin/main` was
merged in twice during the rework round (now at merge base `e2c4e12`);
neither merge touched this PR's files.

Gate list derived with `node scripts/pm/dispatch-gates.mjs --repo
objectstack-ai/objectstack --commands` (178 paths vs merge base
`e2c4e12`, three-dot; 640 changed lines, under the 5,000 human-merge
threshold), then reconciled: `--ran` answered `74 derived famil(ies)
accounted for — 74 run, 0 NOT-MEASURED (a DERIVED zero — all 74 recorded
an exit code and none of them is 3)`. Each command's exit code was
captured before any pipe:

```
exit 0  node scripts/check-ci-filter-parity.mjs
exit 0  node scripts/check-closing-keyword-parity.mjs
exit 0  node scripts/check-closing-keyword-parity.mjs --self-test
exit 0  node scripts/check-comment-mask-corpus.mjs
exit 0  node scripts/check-declaration-mirrors.mjs
exit 0  node scripts/check-declaration-mirrors.mjs --self-test
exit 0  node scripts/check-doc-frontmatter.mjs
exit 0  node scripts/check-doc-frontmatter.mjs --self-test
exit 0  node scripts/check-doc-route-spelling.mjs --advisory
exit 0  node scripts/check-doc-route-spelling.mjs --self-test
exit 0  node scripts/check-docs-section-name.mjs
exit 0  node scripts/check-docs-section-name.mjs --self-test
exit 0  node scripts/check-scripts-symbol-anchors.mjs
exit 0  node scripts/check-scripts-symbol-anchors.mjs --self-test
exit 0  node scripts/check-section-landing-index.mjs
exit 0  node scripts/check-section-landing-index.mjs --self-test
exit 0  node scripts/check-self-test-wired.mjs
exit 0  node scripts/check-self-test-wired.mjs --self-test
exit 0  node scripts/check-self-test-workflow-commands.mjs
exit 0  node scripts/check-self-test-workflow-commands.mjs --self-test
exit 0  node scripts/check-system-context-census.mjs
exit 0  node scripts/check-system-context-census.mjs --self-test
exit 0  node scripts/check-tenant-audit-census.mjs
exit 0  node scripts/check-tenant-audit-census.mjs --self-test
exit 0  node scripts/check-whole-set-label-write.mjs
exit 0  node scripts/check-whole-set-label-write.mjs --self-test
exit 0  node scripts/docs-audit/check-drift-comment.mjs
exit 0  node scripts/pm/bare-root-worklist.mjs --self-test
exit 0  pnpm --filter @objectstack/lint run check:doc-formula-expressions
exit 0  pnpm --filter @objectstack/lint run check:doc-security-posture
exit 0  pnpm --filter @objectstack/spec run check:docs
exit 0  pnpm --filter @objectstack/spec run check:empty-state
exit 0  pnpm --filter @objectstack/spec run check:liveness
exit 0  pnpm --filter @objectstack/spec run check:skill-docs
exit 0  pnpm --filter @objectstack/spec run check:skill-examples
exit 0  pnpm --filter @objectstack/spec run check:strictness-ledger
exit 0  pnpm --filter @objectstack/spec run check:variant-docs
exit 0  pnpm --filter @objectstack/spec run check:yaml-examples
exit 0  pnpm check:agent-test-spelling
exit 0  pnpm check:bash32-floor
exit 0  pnpm check:cli-command-ids
exit 0  pnpm check:cli-examples-parity
exit 0  pnpm check:corpus-claim-drift
exit 0  pnpm check:cross-package-test-inputs
exit 0  pnpm check:doc-anchors
exit 0  pnpm check:doc-authoring
exit 0  pnpm check:docs-audit-scope
exit 0  pnpm check:docs-image-tag
exit 0  pnpm check:docs-redirects
exit 0  pnpm check:docs-single-h1
exit 0  pnpm check:docs-spec-enumerations
exit 0  pnpm check:docs-transcript-drift
exit 0  pnpm check:driver-memory-census
exit 0  pnpm check:entry-guard
exit 0  pnpm check:error-status-conformance
exit 0  pnpm check:gitlink-declared
exit 0  pnpm check:lockstep-package-count
exit 0  pnpm check:merge-driver
exit 0  pnpm check:nul-bytes
exit 0  pnpm check:overlay-whitelist-table
exit 0  pnpm check:parse-guard
exit 0  pnpm check:pm-dispatch-gates
exit 0  pnpm check:pm-widening-tells
exit 0  pnpm check:pnpm-filter-targets
exit 0  pnpm check:published-readme-links
exit 0  pnpm check:quick-reference-counts
exit 0  pnpm check:ratchet-remedy-authority
exit 0  pnpm check:react-page-adapter-contract
exit 0  pnpm check:refd-timer-probe
exit 0  pnpm check:role-word
exit 0  pnpm check:runtime-services-index
exit 0  pnpm check:skill-identifier-liveness
exit 0  pnpm check:vendor-version-stamps
exit 0  pnpm check:watch-hint-literal
exit 0  node scripts/check-docs-nav-label.mjs
exit 0  node scripts/check-docs-nav-label.mjs --self-test
```

Verdict lines:
- `check-doc-anchors: 376 internal #fragment link(s) across 411 source
file(s) all resolve to a real heading` (the card's acceptance gate)
- `check-doc-frontmatter: 2 content root(s) verified, each against its
own floor — content/docs 406, content/blog 3.`
- `check-docs-nav-label`: `navTitle` named in code by 2 file(s) only;
resolver executed; plugin wired into the docs loader; JSON-LD breadcrumb
leg green. It reads `apps/docs/**` only, so a `navTitle:` frontmatter
line is not judged by it; run as a guard that the mechanism this PR
relies on is intact.
- `check-runtime-services-index --self-test: 59 assertions …` and
`check-runtime-services-index: 8 chapter page(s) vs meta.json "pages" …`
both green, with the 8 approved titles applied.

For the record, round 1: on the verbatim table `check:role-word` and
`check:runtime-services-index` exited **1**. Both were real findings:
the first is handled by the re-worded row, the second by the gate change
above. `check:skill-examples` exited **3** (`PREREQUISITE NOT MET`,
stale `client-react` declarations, nothing measured) until
`@objectstack/client-react` was built. All green at this head.

**NOT MEASURED locally:** the card's "link checker" is
`.github/workflows/check-links.yml` (lychee, advisory lane); lychee is
not installed in this container. It runs on this PR in CI. This diff
changes no link, link text or file path, only two frontmatter keys. A
full `next build` of `apps/docs` was not run locally; `fumadocs-mdx`
regenerated the source index at exit 0, and `check-doc-frontmatter`
parses every page with the `yaml` version the build resolves.

`pnpm lint` is not owed: `.mdx` is outside the ESLint population and no
other file type is touched.

No changeset: docs content only, publishes nothing. `skip-changeset`
applied.

## Acceptance notes

- **30 approved rows have no ` — ` separator** (for example
`ai/natural-language-queries.mdx` → `Natural language queries over your
data`), although the ruling says "separator ` — ` throughout". The
ruling approves the table "as they stand", so they are applied verbatim
(PM answer in review comment `5852831074`); re-wording them is the
maintainer's call.
- **The two folder-index pages objectstack-ai#12312 lengthened show the long title in
the footer previous/next links**: `protocol/objectql/index.mdx` and
`protocol/objectui/index.mdx` carry no `navTitle`, and `flattenTree()`
puts a folder index into that list. objectstack-ai#12312 judged them "not in the page
tree / `meta.json` title wins", which holds for the sidebar only. Left
as they are, per the PM answer in review comment `5852831074`.
- The objectstack-ai#12236 trap note (76 demoted body H1s as a mapping-table input) is
moot for the applied rows: the table was approved as it stands.
- Pre-existing duplicate titles exist among the generated
`references/**` pages (e.g. `Mapping`, `Sharing`, `Plugin`); that is the
generator half, objectstack-ai#15403.

---
_Generated by [Claude
Code](https://claude.ai/code/session_018bR89KaSkoZVqgBnUYXtyD)_

---------

Co-authored-by: Claude <noreply@anthropic.com>
veigajoao pushed a commit to veigajoao/objectstack that referenced this pull request Sep 29, 2026
…ebar labels kept via navTitle (objectstack-ai#20401)

Fixes objectstack-ai#15403
Clause-②: no

## Summary

The generated reference pages under `content/docs/references/**` now
carry the docs page-title rule, emitted by the generator itself, and
every one keeps its sidebar label byte-identical through `navTitle`.

- **One pure function holds the rule:**
`packages/spec/scripts/lib/page-title.ts`. It builds each title from
data the generator already holds (the module display name and the
category's declared title) plus fixed words, picks the longest candidate
inside the band, and refuses (never truncates) when no candidate fits.
- **Three emission sites use it:** module pages and category index pages
in `packages/spec/scripts/build-docs.ts`, and the root index in
`packages/spec/scripts/lib/root-index.ts`.
- **`navTitle` = the page's previous `title`, verbatim**, so
`navLabel()` in `apps/docs/lib/nav-title.ts` returns the same string as
before. The page tree is unchanged (replayed below, with a control).
- **211 generated pages regenerated**, frontmatter only: each is `+2/-1`
(the `title:` line replaced, a `navTitle:` line added). No
`description:`, no body, no `meta.json`.

## The rule, and how the emitter expresses it

The rule is the maintainer's, recorded on objectstack-ai#12237 in comment
`5419555720`, verbatim 「其他同意」. Its text as landed is the `## The rule`
section of PR objectstack-ai#20170:

- shape `PRIMARY-KEYWORD — QUALIFIER`, separator ` — `;
- frontmatter `title` is 36–46 characters, so the rendered title with
the 14-character ` | ObjectStack` suffix is 50–60, none over 60;
- `ObjectStack` never inside a title;
- short navigation labels go in `navTitle`, whose fallback to `title`
lives only in `navLabel()`.

The generator holds only the module display name (3–29 characters on
this tree), the category's declared title (11–20 for categories with
pages) and fixed words. No single template lands every page inside an
11-character band, so each page kind has a short ladder, longest first,
and the title is the first candidate inside the band:

| page kind | candidate (longest first) | length |
|---|---|--:|
| module page | `NAME schema — CATEGORY property reference`, offered
only when the page renders a `### Properties` table | name + category +
29 |
| | `NAME schema — CATEGORY reference` | name + category + 20 |
| | `NAME — CATEGORY reference` | name + category + 13 |
| | `NAME — CATEGORY` | name + category + 3 |
| category index | `CATEGORY — complete schema reference` | category +
28 |
| | `CATEGORY — schema reference` | category + 19 |
| root index | `Protocol reference — every schema by module` | 43 |

The module rungs overlap end to end and cover every name + category
length from 7 to 43; the longest pair on the tree is 41 (`Schemaless
Node Config` in `Automation Protocol`). The first module rung is
conditional: `property reference` is offered only when at least one of
the page's schemas renders a `### Properties` table
(`rendersPropertiesTable` in `lib/schema-section.ts`, built on
`declaresProperties`, the one expression the section renderer itself
branches on). A page without one, such as the enum-only `data/feed`,
starts at the second rung; without the first rung the ladder covers name
+ category lengths 16 to 43, and the shortest property-less pair on the
tree is 17 (`Feed` in `Data Protocol`). The category rungs cover
category titles of 8 to 27 characters; the longest declared title is 25.
The category in every module title keeps same-named modules apart
(`Plugin` is both a `kernel` and a `studio` page). A page no rung fits
stops `gen:docs` with a message naming the page and every candidate with
its length.

Rung usage on this tree: module pages 18 / 103 / 60 / 15 (rungs 1–4),
category index pages 11 / 3, root index 1. All 18 rung-1 pages render a
`### Properties` table; the 14 module pages that render none are all on
rungs 2–4.

## Measured, before and after

Population re-derived at base `862b6ce8` and at head:
`content/docs/**/*.mdx` = **406**; pages carrying the generator's
`AUTO-GENERATED — DO NOT EDIT` banner = **211**, all under
`content/docs/references/**` (196 module pages, 14 category index pages,
1 root index). Rendered = `title` + 14.

| | pages | min | median | max | in 36–46 | outside | rendered over 60 |
with ` — ` | with `ObjectStack` | duplicate titles | `navTitle` |
|---|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|
| before (`origin/main`) | 211 | 3 | 11 | 29 | 0 | 211 | 0 | 0 | 0 | 6 |
0 |
| after (this head) | 211 | 37 | 43 | 46 | 211 | 0 | 0 | 211 | 0 | 0 |
211 |

After: rendered 51–60. Duplicate titles across the whole corpus (406
pages, authored and generated): **0**.

**The card body's 225 of 405 does not reproduce, and the difference is
not generator output.** The generated set is 211; the 14 others in that
count are `content/docs/releases/**` pages, which carry no generator
banner and are release-owned (compiled by hand at release time). No
other emitter writes a `title:` into `content/docs/**`:
`build-skill-docs.ts` rewrites only the region between its markers in
`content/docs/ai/skills-reference.mdx`, whose authored title already
follows the rule (PR objectstack-ai#20170). The only `title:` emissions in the repo's
generators are the three this PR changes (`git grep` over `scripts/`,
`packages/*/scripts/`, `apps/docs/scripts/`).

## The page tree is unchanged

Replayed with the site's own machinery: `fumadocs-core` 16.14.4
`loader()` (the `apps/docs` dependency) with the site's `i18n` settings
and the site's `navTitlePlugin()`, over every `.mdx` frontmatter and
`meta.json` under `content/docs`, once from `origin/main` and once from
this head. Serialized page tree (folders, separators, pages, folder
index pages): **449 nodes before, 449 after, `diff` exit 0.**

Control, so the replay can see a title change at all: the same replay of
this head WITHOUT `navTitlePlugin()` differs from `origin/main` in
**388** nodes, exactly the 211 generated pages plus the 177 authored
pages PR objectstack-ai#20170 gave a `navTitle`.

Folder labels come from each category's `meta.json` `title`, which this
PR does not touch. The category and root `index.mdx` pages carry
`navTitle` anyway because the footer previous/next list walks folder
index pages.

## Examples

| page | before | after | chars / rendered |
|---|---|---|--:|
| `references/ai/mcp.mdx` | Mcp | Mcp schema — AI Protocol property
reference | 43 / 57 |
| `references/ai/agent.mdx` | Agent | Agent schema — AI Protocol
property reference | 45 / 59 |
| `references/data/feed.mdx` | Feed | Feed schema — Data Protocol
reference | 37 / 51 |
| `references/data/object.mdx` | Object | Object schema — Data Protocol
reference | 39 / 53 |
| `references/automation/flow.mdx` | Flow | Flow schema — Automation
Protocol reference | 43 / 57 |
| `references/kernel/plugin.mdx` | Plugin | Plugin schema — Kernel
Protocol reference | 41 / 55 |
| `references/studio/plugin.mdx` | Plugin | Plugin schema — Studio
Protocol reference | 41 / 55 |
| `references/kernel/plugin-registry.mdx` | Plugin Registry | Plugin
Registry — Kernel Protocol reference | 43 / 57 |
| `references/kernel/metadata-protection.mdx` | Metadata Protection |
Metadata Protection — Kernel Protocol | 37 / 51 |
| `references/ui/expression-bindable-text-keys.mdx` | Expression
Bindable Text Keys | Expression Bindable Text Keys — UI Protocol | 43 /
57 |
| `references/automation/schemaless-node-config.mdx` | Schemaless Node
Config | Schemaless Node Config — Automation Protocol | 44 / 58 |
| `references/ai/index.mdx` | AI Protocol | AI Protocol — complete
schema reference | 39 / 53 |
| `references/automation/index.mdx` | Automation Protocol | Automation
Protocol — schema reference | 38 / 52 |
| `references/identity/index.mdx` | Identity Protocol | Identity
Protocol — complete schema reference | 45 / 59 |
| `references/index.mdx` | Protocol Reference | Protocol reference —
every schema by module | 43 / 57 |

The complete table is at the end.

## Wording for maintainer voice review

The rule is approved; the fixed words the generator adds are new public
text and are not in the approved table of PR objectstack-ai#12312: `schema`, `property
reference`, `reference`, `complete schema reference`, `schema
reference`, and the root title `Protocol reference — every schema by
module`. Rewording any of them is a one-line change in
`lib/page-title.ts` plus `gen:docs`; the band, the unit test and
`check:docs` hold the result either way. Non-blocking: the seat answered
land-as-shipped in comment 5865474678 on objectstack-ai#15403, and `property
reference` is now claimed only by pages that render a property table.

## File surface

- `packages/spec/scripts/lib/page-title.ts` (new): the rule, the
ladders, the refusal, the frontmatter lines.
- `packages/spec/scripts/page-title.test.ts` (new): the pin (`local`
vitest project; reads nothing outside the package).
- `packages/spec/scripts/build-docs.ts`: the two emission sites (module
pages, category index pages), located by symbol.
- `packages/spec/scripts/lib/schema-section.ts`: `declaresProperties()`
(an object that declares properties, the one spelling of that condition,
now also used by the renderer's root and union-arm branches) and
`rendersPropertiesTable()` (whether a schema renders at least one `###
Properties` table), read by the module-page title. Rendered output
unchanged: regenerating moved exactly one page, `data/feed.mdx`, and
only its `title:` line.
- `packages/spec/scripts/lib/root-index.ts`: **one site beyond the two
the dispatch named.** It is the generator's third `title:` emission (the
root `references/index.mdx`, previously `title: Protocol Reference`, 18
characters). Leaving it would have left one generated page outside the
rule.
- `content/docs/references/**`: 211 pages, regenerated by `gen:docs`,
never hand-edited.
- Not touched: authored pages, the `description:` emission,
`check-generated.ts` (it regenerates and compares; nothing in it reads
the title line), `build-skill-docs.ts` (emits no title).

## Changeset: `skip-changeset`

Rule applied: a changeset is owed when a package's published `files[]`
content moves. `@objectstack/spec` publishes `dist`, `json-schema`,
`liveness`, `prompts`, `llms.txt`, `README.md`, `src/**/*.zod.ts`,
`CHANGELOG.md`, `api-surface`, `spec-changes.json`; `scripts/` is not
among them, and `apps/docs` (which renders `content/docs`) is private.
Measured after the build: the new symbols (`titleFrontmatter`,
`navTitle`, `property reference`) have **0** hits across all ten of
those paths, while the positive control `ObjectSchema` hits in 9 of the
10 (all but `json-schema`).

## Verification

Head `1ba9d84c`, REWORK round 1: `41b0f683` (the conditional rung),
`0ea42f45` (`data/feed.mdx` regenerated), then a merge of `origin/main`
at `df3ba164` whose two deferred pages, `security/{permission,rls}.mdx`,
were regenerated on the merged tree in `1ba9d84c`. Round 0 was head
`a61335f9`.

- `pnpm --filter @objectstack/spec test`: 561 files, 16546 passed, 1
todo.
- `pnpm --filter @objectstack/spec test:repo`: 35 files, 634 passed.
- Type check: `check:scripts-typecheck` (`tsc -p tsconfig.scripts.json`,
the program holding every file this diff edits, `page-title.test.ts`
included) exit 0 at `1ba9d84c`. The full spec `typecheck` was green
locally at round 0's pre-merge head `0a241893`, and `Type Check ·
workspace` ran it green in CI on `a61335f9`.
- The new files are in the scripts tsc program (`tsc -p
tsconfig.scripts.json --listFiles` lists `lib/page-title.ts` and
`page-title.test.ts`).
- Gates derived by `node scripts/pm/dispatch-gates.mjs --commands` over
this diff at `1ba9d84c` (216 paths, the same 89 families as round 0),
each exit code recorded, then `--ran`: **89 derived, 85 run (all exit
0), 4 NOT MEASURED, 0 UNRUN.** Among the 85: `check:docs`,
`check:generated` (all 15 generated artifacts up to date),
`check:doc-frontmatter`, `check:docs-single-h1`, `check:doc-anchors`,
`check:nul-bytes`, `check:quick-reference-counts`.
- NOT MEASURED, prerequisite refused (exit 3): `check:skill-examples`
(needs the 36-package `@objectstack/client-react` closure built),
`check:dual-build-cjs-loads` (needs every package built),
`check:type-check-debt` (needs a 30-package closure built).
- NOT MEASURED, timed out: `check:pm-dispatch-gates`. Its self-test of
`scripts/pm/dispatch-gates.mjs`, which this diff does not edit, did not
finish in a 420 s and then a 560 s window on the shared box. Not re-run
in round 1; `Lint & Repo Gates` ran it green in CI on `a61335f9`.
- **Ablation 1, the unit test can fail:** `scripts/ablation-replace.mjs`
changed `TITLE_MAX = 46` to `60` in `lib/page-title.ts` (anchor 1 to 0,
blob `3fe85286f82d` to `ee55509927c8`). `page-title.test.ts` went **12
failed / 13 passed**. Restored under an EXIT/INT/TERM trap to
`3fe85286f82d` = HEAD blob, `git diff HEAD` empty.
- **Ablation 2, `check:docs` sees the emission:** the first module
rung's `property reference` changed to `field reference` (blob
`3fe85286f82d` to `b8f4c6d26269`). `check:docs` exited **1** with **49**
pages out of date: the 19 rung-1 pages, plus the pages the shorter word
lets onto rung 1. Restored the same way.
- **Ablation 3, the rung condition is pinned:** `...(documentsProperties
? [` changed to `...(true ? [` in `lib/page-title.ts` (anchor 1 to 0,
blob `38277b23f728` to `90e0cbe18d3b`). `page-title.test.ts` went **3
failed / 38 passed**: the `data/feed` row, the condition pin, and the
short property-less refusal. Restored to `38277b23f728` = HEAD blob,
`git diff HEAD` empty.
- **The predicate agrees with the renderer on every published schema:**
over all 1523 JSON Schemas under `packages/spec/json-schema/`,
`rendersPropertiesTable(name, schema)` equals whether
`renderSchemaSection(name, schema)` emits a `### Properties` heading:
1219 true, **0 disagreements**. The unit pin holds the same agreement
shape by shape (11 JSON Schema shapes covering every renderer branch).

## Acceptance notes

- **Module display names are title-cased from the file slug, so
abbreviations come out wrong:** `Mcp`, `Rls`, `Scim`, `Odata`, `Http`,
`I18n`, `Driver Sql`, `Cli Extension`, `Io Node Config`, `Bpmn Interop`,
`Package Api`, `Rest Server`, `Events Dlq`, and others. This is the `Qa
Protocol` defect class that `lib/category-title.ts` fixed for category
titles, one level down, and it predates this PR (these strings were the
whole title before; they are the `navTitle` now, and the primary keyword
of the new title). Out of scope here: fixing it changes nav labels,
which this card keeps byte-identical. Noted, not filed.
- The card body's 225 / 405 reading included 14 `releases/**` pages (see
*Measured*); nothing here depends on them.
- `api/protocol.mdx` renders 6 `### Properties` tables with no rows
(object schemas declaring an empty `properties`). Pre-existing renderer
output; that page is on rung 2 and carries non-empty tables as well.
Noted, not filed.
- The optional routing of `rootIndexTitle()` through `pageTitleOrExit`
was not taken: `pageTitleOrExit` lives in `build-docs.ts`, so routing it
means passing the title into `renderRootIndex` (its input type and the
`root-index.test.ts` fixtures), more than one line. The root title is a
fixed 43-character string pinned by the unit test.

## The complete table

All 211 generated pages. `navTitle` = before, verbatim, on every row.

| page | before | after | chars / rendered | `navTitle` |
|---|---|---|--:|---|
| `references/index.mdx` | Protocol Reference | Protocol reference —
every schema by module | 43 / 57 | = before |
| `references/ai/index.mdx` | AI Protocol | AI Protocol — complete
schema reference | 39 / 53 | = before |
| `references/ai/agent.mdx` | Agent | Agent schema — AI Protocol
property reference | 45 / 59 | = before |
| `references/ai/build-progress.mdx` | Build Progress | Build Progress
schema — AI Protocol reference | 45 / 59 | = before |
| `references/ai/conversation.mdx` | Conversation | Conversation schema
— AI Protocol reference | 43 / 57 | = before |
| `references/ai/embedding.mdx` | Embedding | Embedding schema — AI
Protocol reference | 40 / 54 | = before |
| `references/ai/knowledge-document.mdx` | Knowledge Document |
Knowledge Document — AI Protocol reference | 42 / 56 | = before |
| `references/ai/knowledge-source.mdx` | Knowledge Source | Knowledge
Source — AI Protocol reference | 40 / 54 | = before |
| `references/ai/mcp.mdx` | Mcp | Mcp schema — AI Protocol property
reference | 43 / 57 | = before |
| `references/ai/model-registry.mdx` | Model Registry | Model Registry
schema — AI Protocol reference | 45 / 59 | = before |
| `references/ai/skill.mdx` | Skill | Skill schema — AI Protocol
property reference | 45 / 59 | = before |
| `references/ai/solution-blueprint.mdx` | Solution Blueprint | Solution
Blueprint — AI Protocol reference | 42 / 56 | = before |
| `references/ai/tool.mdx` | Tool | Tool schema — AI Protocol property
reference | 44 / 58 | = before |
| `references/ai/usage.mdx` | Usage | Usage schema — AI Protocol
property reference | 45 / 59 | = before |
| `references/api/index.mdx` | API Protocol | API Protocol — complete
schema reference | 40 / 54 | = before |
| `references/api/analytics.mdx` | Analytics | Analytics schema — API
Protocol reference | 41 / 55 | = before |
| `references/api/auth-endpoints.mdx` | Auth Endpoints | Auth Endpoints
schema — API Protocol reference | 46 / 60 | = before |
| `references/api/auth.mdx` | Auth | Auth schema — API Protocol property
reference | 45 / 59 | = before |
| `references/api/automation-api.mdx` | Automation Api | Automation Api
schema — API Protocol reference | 46 / 60 | = before |
| `references/api/batch.mdx` | Batch | Batch schema — API Protocol
property reference | 46 / 60 | = before |
| `references/api/contract.mdx` | Contract | Contract schema — API
Protocol reference | 40 / 54 | = before |
| `references/api/discovery.mdx` | Discovery | Discovery schema — API
Protocol reference | 41 / 55 | = before |
| `references/api/dispatcher.mdx` | Dispatcher | Dispatcher schema — API
Protocol reference | 42 / 56 | = before |
| `references/api/documentation.mdx` | Documentation | Documentation
schema — API Protocol reference | 45 / 59 | = before |
| `references/api/endpoint.mdx` | Endpoint | Endpoint schema — API
Protocol reference | 40 / 54 | = before |
| `references/api/error-code-ledger.mdx` | Error Code Ledger | Error
Code Ledger — API Protocol reference | 42 / 56 | = before |
| `references/api/errors.mdx` | Errors | Errors schema — API Protocol
reference | 38 / 52 | = before |
| `references/api/events.mdx` | Events | Events schema — API Protocol
reference | 38 / 52 | = before |
| `references/api/export.mdx` | Export | Export schema — API Protocol
reference | 38 / 52 | = before |
| `references/api/http-cache.mdx` | Http Cache | Http Cache schema — API
Protocol reference | 42 / 56 | = before |
| `references/api/metadata.mdx` | Metadata | Metadata schema — API
Protocol reference | 40 / 54 | = before |
| `references/api/misc.mdx` | Misc | Misc schema — API Protocol property
reference | 45 / 59 | = before |
| `references/api/odata.mdx` | Odata | Odata schema — API Protocol
property reference | 46 / 60 | = before |
| `references/api/package-api-assembled.mdx` | Package Api Assembled |
Package Api Assembled — API Protocol reference | 46 / 60 | = before |
| `references/api/package-api.mdx` | Package Api | Package Api schema —
API Protocol reference | 43 / 57 | = before |
| `references/api/package-lifecycle.mdx` | Package Lifecycle | Package
Lifecycle — API Protocol reference | 42 / 56 | = before |
| `references/api/plugin-rest-api.mdx` | Plugin Rest Api | Plugin Rest
Api — API Protocol reference | 40 / 54 | = before |
| `references/api/protocol.mdx` | Protocol | Protocol schema — API
Protocol reference | 40 / 54 | = before |
| `references/api/query-adapter.mdx` | Query Adapter | Query Adapter
schema — API Protocol reference | 45 / 59 | = before |
| `references/api/realtime-shared.mdx` | Realtime Shared | Realtime
Shared — API Protocol reference | 40 / 54 | = before |
| `references/api/realtime.mdx` | Realtime | Realtime schema — API
Protocol reference | 40 / 54 | = before |
| `references/api/rest-server.mdx` | Rest Server | Rest Server schema —
API Protocol reference | 43 / 57 | = before |
| `references/api/router.mdx` | Router | Router schema — API Protocol
reference | 38 / 52 | = before |
| `references/api/sortability.mdx` | Sortability | Sortability schema —
API Protocol reference | 43 / 57 | = before |
| `references/api/storage.mdx` | Storage | Storage schema — API Protocol
reference | 39 / 53 | = before |
| `references/api/versioning.mdx` | Versioning | Versioning schema — API
Protocol reference | 42 / 56 | = before |
| `references/api/websocket.mdx` | Websocket | Websocket schema — API
Protocol reference | 41 / 55 | = before |
| `references/automation/index.mdx` | Automation Protocol | Automation
Protocol — schema reference | 38 / 52 | = before |
| `references/automation/approval.mdx` | Approval | Approval —
Automation Protocol reference | 40 / 54 | = before |
| `references/automation/bpmn-interop.mdx` | Bpmn Interop | Bpmn Interop
— Automation Protocol reference | 44 / 58 | = before |
| `references/automation/builtin-node-config.mdx` | Builtin Node Config
| Builtin Node Config — Automation Protocol | 41 / 55 | = before |
| `references/automation/control-flow.mdx` | Control Flow | Control Flow
— Automation Protocol reference | 44 / 58 | = before |
| `references/automation/execution.mdx` | Execution | Execution —
Automation Protocol reference | 41 / 55 | = before |
| `references/automation/flow-function.mdx` | Flow Function | Flow
Function — Automation Protocol reference | 45 / 59 | = before |
| `references/automation/flow.mdx` | Flow | Flow schema — Automation
Protocol reference | 43 / 57 | = before |
| `references/automation/io-node-config.mdx` | Io Node Config | Io Node
Config — Automation Protocol reference | 46 / 60 | = before |
| `references/automation/node-executor.mdx` | Node Executor | Node
Executor — Automation Protocol reference | 45 / 59 | = before |
| `references/automation/schedule-organization.mdx` | Schedule
Organization | Schedule Organization — Automation Protocol | 43 / 57 | =
before |
| `references/automation/schemaless-node-config.mdx` | Schemaless Node
Config | Schemaless Node Config — Automation Protocol | 44 / 58 | =
before |
| `references/automation/state-machine.mdx` | State Machine | State
Machine — Automation Protocol reference | 45 / 59 | = before |
| `references/automation/time-relative-trigger.mdx` | Time Relative
Trigger | Time Relative Trigger — Automation Protocol | 43 / 57 | =
before |
| `references/automation/webhook.mdx` | Webhook | Webhook schema —
Automation Protocol reference | 46 / 60 | = before |
| `references/data/index.mdx` | Data Protocol | Data Protocol — complete
schema reference | 41 / 55 | = before |
| `references/data/analytics.mdx` | Analytics | Analytics schema — Data
Protocol reference | 42 / 56 | = before |
| `references/data/context-tokens.mdx` | Context Tokens | Context Tokens
— Data Protocol reference | 40 / 54 | = before |
| `references/data/data-engine.mdx` | Data Engine | Data Engine schema —
Data Protocol reference | 44 / 58 | = before |
| `references/data/datasource.mdx` | Datasource | Datasource schema —
Data Protocol reference | 43 / 57 | = before |
| `references/data/date-macros.mdx` | Date Macros | Date Macros schema —
Data Protocol reference | 44 / 58 | = before |
| `references/data/document.mdx` | Document | Document schema — Data
Protocol reference | 41 / 55 | = before |
| `references/data/driver-common.mdx` | Driver Common | Driver Common
schema — Data Protocol reference | 46 / 60 | = before |
| `references/data/driver-memory.mdx` | Driver Memory | Driver Memory
schema — Data Protocol reference | 46 / 60 | = before |
| `references/data/driver-mongo.mdx` | Driver Mongo | Driver Mongo
schema — Data Protocol reference | 45 / 59 | = before |
| `references/data/driver-mysql.mdx` | Driver Mysql | Driver Mysql
schema — Data Protocol reference | 45 / 59 | = before |
| `references/data/driver-nosql.mdx` | Driver Nosql | Driver Nosql
schema — Data Protocol reference | 45 / 59 | = before |
| `references/data/driver-postgres.mdx` | Driver Postgres | Driver
Postgres — Data Protocol reference | 41 / 55 | = before |
| `references/data/driver-sql.mdx` | Driver Sql | Driver Sql schema —
Data Protocol reference | 43 / 57 | = before |
| `references/data/driver-sqlite.mdx` | Driver Sqlite | Driver Sqlite
schema — Data Protocol reference | 46 / 60 | = before |
| `references/data/driver-turso.mdx` | Driver Turso | Driver Turso
schema — Data Protocol reference | 45 / 59 | = before |
| `references/data/driver.mdx` | Driver | Driver schema — Data Protocol
reference | 39 / 53 | = before |
| `references/data/external-catalog.mdx` | External Catalog | External
Catalog — Data Protocol reference | 42 / 56 | = before |
| `references/data/feed.mdx` | Feed | Feed schema — Data Protocol
reference | 37 / 51 | = before |
| `references/data/field-value.mdx` | Field Value | Field Value schema —
Data Protocol reference | 44 / 58 | = before |
| `references/data/field.mdx` | Field | Field schema — Data Protocol
reference | 38 / 52 | = before |
| `references/data/filter.mdx` | Filter | Filter schema — Data Protocol
reference | 39 / 53 | = before |
| `references/data/hook-body.mdx` | Hook Body | Hook Body schema — Data
Protocol reference | 42 / 56 | = before |
| `references/data/hook.mdx` | Hook | Hook schema — Data Protocol
property reference | 46 / 60 | = before |
| `references/data/mapping.mdx` | Mapping | Mapping schema — Data
Protocol reference | 40 / 54 | = before |
| `references/data/object.mdx` | Object | Object schema — Data Protocol
reference | 39 / 53 | = before |
| `references/data/query.mdx` | Query | Query schema — Data Protocol
reference | 38 / 52 | = before |
| `references/data/seed-loader.mdx` | Seed Loader | Seed Loader schema —
Data Protocol reference | 44 / 58 | = before |
| `references/data/seed.mdx` | Seed | Seed schema — Data Protocol
property reference | 46 / 60 | = before |
| `references/data/validation.mdx` | Validation | Validation schema —
Data Protocol reference | 43 / 57 | = before |
| `references/identity/index.mdx` | Identity Protocol | Identity
Protocol — complete schema reference | 45 / 59 | = before |
| `references/identity/eval-user.mdx` | Eval User | Eval User schema —
Identity Protocol reference | 46 / 60 | = before |
| `references/identity/identity.mdx` | Identity | Identity schema —
Identity Protocol reference | 45 / 59 | = before |
| `references/identity/organization.mdx` | Organization | Organization —
Identity Protocol reference | 42 / 56 | = before |
| `references/identity/position.mdx` | Position | Position schema —
Identity Protocol reference | 45 / 59 | = before |
| `references/identity/scim.mdx` | Scim | Scim schema — Identity
Protocol reference | 41 / 55 | = before |
| `references/integration/index.mdx` | Integration Protocol |
Integration Protocol — schema reference | 39 / 53 | = before |
| `references/integration/connector.mdx` | Connector | Connector —
Integration Protocol reference | 42 / 56 | = before |
| `references/kernel/index.mdx` | Kernel Protocol | Kernel Protocol —
complete schema reference | 43 / 57 | = before |
| `references/kernel/cli-extension.mdx` | Cli Extension | Cli Extension
— Kernel Protocol reference | 41 / 55 | = before |
| `references/kernel/cluster.mdx` | Cluster | Cluster schema — Kernel
Protocol reference | 42 / 56 | = before |
| `references/kernel/context.mdx` | Context | Context schema — Kernel
Protocol reference | 42 / 56 | = before |
| `references/kernel/dependency-resolution.mdx` | Dependency Resolution
| Dependency Resolution — Kernel Protocol | 39 / 53 | = before |
| `references/kernel/events-bus.mdx` | Events Bus | Events Bus schema —
Kernel Protocol reference | 45 / 59 | = before |
| `references/kernel/events-core.mdx` | Events Core | Events Core schema
— Kernel Protocol reference | 46 / 60 | = before |
| `references/kernel/events-dlq.mdx` | Events Dlq | Events Dlq schema —
Kernel Protocol reference | 45 / 59 | = before |
| `references/kernel/events-handlers.mdx` | Events Handlers | Events
Handlers — Kernel Protocol reference | 43 / 57 | = before |
| `references/kernel/events-integrations.mdx` | Events Integrations |
Events Integrations — Kernel Protocol | 37 / 51 | = before |
| `references/kernel/events-queue.mdx` | Events Queue | Events Queue —
Kernel Protocol reference | 40 / 54 | = before |
| `references/kernel/execution-context.mdx` | Execution Context |
Execution Context — Kernel Protocol reference | 45 / 59 | = before |
| `references/kernel/manifest.mdx` | Manifest | Manifest schema — Kernel
Protocol reference | 43 / 57 | = before |
| `references/kernel/metadata-loader.mdx` | Metadata Loader | Metadata
Loader — Kernel Protocol reference | 43 / 57 | = before |
| `references/kernel/metadata-plugin.mdx` | Metadata Plugin | Metadata
Plugin — Kernel Protocol reference | 43 / 57 | = before |
| `references/kernel/metadata-protection.mdx` | Metadata Protection |
Metadata Protection — Kernel Protocol | 37 / 51 | = before |
| `references/kernel/package-artifact.mdx` | Package Artifact | Package
Artifact — Kernel Protocol reference | 44 / 58 | = before |
| `references/kernel/package-registry.mdx` | Package Registry | Package
Registry — Kernel Protocol reference | 44 / 58 | = before |
| `references/kernel/package-upgrade.mdx` | Package Upgrade | Package
Upgrade — Kernel Protocol reference | 43 / 57 | = before |
| `references/kernel/plugin-capability.mdx` | Plugin Capability | Plugin
Capability — Kernel Protocol reference | 45 / 59 | = before |
| `references/kernel/plugin-lifecycle-advanced.mdx` | Plugin Lifecycle
Advanced | Plugin Lifecycle Advanced — Kernel Protocol | 43 / 57 | =
before |
| `references/kernel/plugin-loading.mdx` | Plugin Loading | Plugin
Loading — Kernel Protocol reference | 42 / 56 | = before |
| `references/kernel/plugin-registry.mdx` | Plugin Registry | Plugin
Registry — Kernel Protocol reference | 43 / 57 | = before |
| `references/kernel/plugin-security-advanced.mdx` | Plugin Security
Advanced | Plugin Security Advanced — Kernel Protocol | 42 / 56 | =
before |
| `references/kernel/plugin-security.mdx` | Plugin Security | Plugin
Security — Kernel Protocol reference | 43 / 57 | = before |
| `references/kernel/plugin-structure.mdx` | Plugin Structure | Plugin
Structure — Kernel Protocol reference | 44 / 58 | = before |
| `references/kernel/plugin-validator.mdx` | Plugin Validator | Plugin
Validator — Kernel Protocol reference | 44 / 58 | = before |
| `references/kernel/plugin-versioning.mdx` | Plugin Versioning | Plugin
Versioning — Kernel Protocol reference | 45 / 59 | = before |
| `references/kernel/plugin.mdx` | Plugin | Plugin schema — Kernel
Protocol reference | 41 / 55 | = before |
| `references/kernel/service-registry.mdx` | Service Registry | Service
Registry — Kernel Protocol reference | 44 / 58 | = before |
| `references/kernel/startup-orchestrator.mdx` | Startup Orchestrator |
Startup Orchestrator — Kernel Protocol | 38 / 52 | = before |
| `references/marketplace/index.mdx` | Marketplace Protocol |
Marketplace Protocol — schema reference | 39 / 53 | = before |
| `references/marketplace/marketplace.mdx` | Marketplace | Marketplace —
Marketplace Protocol reference | 44 / 58 | = before |
| `references/marketplace/package-version.mdx` | Package Version |
Package Version — Marketplace Protocol | 38 / 52 | = before |
| `references/marketplace/package.mdx` | Package | Package — Marketplace
Protocol reference | 40 / 54 | = before |
| `references/marketplace/template-manifest.mdx` | Template Manifest |
Template Manifest — Marketplace Protocol | 40 / 54 | = before |
| `references/qa/index.mdx` | QA Protocol | QA Protocol — complete
schema reference | 39 / 53 | = before |
| `references/qa/testing.mdx` | Testing | Testing schema — QA Protocol
reference | 38 / 52 | = before |
| `references/security/index.mdx` | Security Protocol | Security
Protocol — complete schema reference | 45 / 59 | = before |
| `references/security/explain.mdx` | Explain | Explain schema —
Security Protocol reference | 44 / 58 | = before |
| `references/security/misc.mdx` | Misc | Misc schema — Security
Protocol reference | 41 / 55 | = before |
| `references/security/permission.mdx` | Permission | Permission —
Security Protocol reference | 40 / 54 | = before |
| `references/security/rls.mdx` | Rls | Rls schema — Security Protocol
reference | 40 / 54 | = before |
| `references/security/sharing.mdx` | Sharing | Sharing schema —
Security Protocol reference | 44 / 58 | = before |
| `references/shared/index.mdx` | Shared Protocol | Shared Protocol —
complete schema reference | 43 / 57 | = before |
| `references/shared/duration.mdx` | Duration | Duration schema — Shared
Protocol reference | 43 / 57 | = before |
| `references/shared/enums.mdx` | Enums | Enums schema — Shared Protocol
reference | 40 / 54 | = before |
| `references/shared/epoch.mdx` | Epoch | Epoch schema — Shared Protocol
reference | 40 / 54 | = before |
| `references/shared/expression.mdx` | Expression | Expression schema —
Shared Protocol reference | 45 / 59 | = before |
| `references/shared/http.mdx` | Http | Http schema — Shared Protocol
reference | 39 / 53 | = before |
| `references/shared/identifiers.mdx` | Identifiers | Identifiers schema
— Shared Protocol reference | 46 / 60 | = before |
| `references/shared/mapping.mdx` | Mapping | Mapping schema — Shared
Protocol reference | 42 / 56 | = before |
| `references/shared/metadata-types.mdx` | Metadata Types | Metadata
Types — Shared Protocol reference | 42 / 56 | = before |
| `references/shared/protection.mdx` | Protection | Protection schema —
Shared Protocol reference | 45 / 59 | = before |
| `references/shared/value-domain.mdx` | Value Domain | Value Domain —
Shared Protocol reference | 40 / 54 | = before |
| `references/studio/index.mdx` | Studio Protocol | Studio Protocol —
complete schema reference | 43 / 57 | = before |
| `references/studio/flow-builder.mdx` | Flow Builder | Flow Builder —
Studio Protocol reference | 40 / 54 | = before |
| `references/studio/object-designer.mdx` | Object Designer | Object
Designer — Studio Protocol reference | 43 / 57 | = before |
| `references/studio/plugin.mdx` | Plugin | Plugin schema — Studio
Protocol reference | 41 / 55 | = before |
| `references/system/index.mdx` | System Protocol | System Protocol —
complete schema reference | 43 / 57 | = before |
| `references/system/app-install.mdx` | App Install | App Install schema
— System Protocol reference | 46 / 60 | = before |
| `references/system/auth-config.mdx` | Auth Config | Auth Config schema
— System Protocol reference | 46 / 60 | = before |
| `references/system/book.mdx` | Book | Book schema — System Protocol
reference | 39 / 53 | = before |
| `references/system/cache.mdx` | Cache | Cache schema — System Protocol
reference | 40 / 54 | = before |
| `references/system/collaboration.mdx` | Collaboration | Collaboration
— System Protocol reference | 41 / 55 | = before |
| `references/system/core-services.mdx` | Core Services | Core Services
— System Protocol reference | 41 / 55 | = before |
| `references/system/deploy-bundle.mdx` | Deploy Bundle | Deploy Bundle
— System Protocol reference | 41 / 55 | = before |
| `references/system/dev-login.mdx` | Dev Login | Dev Login schema —
System Protocol reference | 44 / 58 | = before |
| `references/system/disaster-recovery.mdx` | Disaster Recovery |
Disaster Recovery — System Protocol reference | 45 / 59 | = before |
| `references/system/doc.mdx` | Doc | Doc schema — System Protocol
reference | 38 / 52 | = before |
| `references/system/email-config.mdx` | Email Config | Email Config —
System Protocol reference | 40 / 54 | = before |
| `references/system/email-template.mdx` | Email Template | Email
Template — System Protocol reference | 42 / 56 | = before |
| `references/system/encryption.mdx` | Encryption | Encryption schema —
System Protocol reference | 45 / 59 | = before |
| `references/system/environment-artifact.mdx` | Environment Artifact |
Environment Artifact — System Protocol | 38 / 52 | = before |
| `references/system/http-server.mdx` | Http Server | Http Server schema
— System Protocol reference | 46 / 60 | = before |
| `references/system/job.mdx` | Job | Job schema — System Protocol
reference | 38 / 52 | = before |
| `references/system/license.mdx` | License | License schema — System
Protocol reference | 42 / 56 | = before |
| `references/system/logging.mdx` | Logging | Logging schema — System
Protocol reference | 42 / 56 | = before |
| `references/system/metadata-persistence.mdx` | Metadata Persistence |
Metadata Persistence — System Protocol | 38 / 52 | = before |
| `references/system/metrics.mdx` | Metrics | Metrics schema — System
Protocol reference | 42 / 56 | = before |
| `references/system/migration.mdx` | Migration | Migration schema —
System Protocol reference | 44 / 58 | = before |
| `references/system/notification.mdx` | Notification | Notification —
System Protocol reference | 40 / 54 | = before |
| `references/system/object-storage.mdx` | Object Storage | Object
Storage — System Protocol reference | 42 / 56 | = before |
| `references/system/registry-config.mdx` | Registry Config | Registry
Config — System Protocol reference | 43 / 57 | = before |
| `references/system/search-engine.mdx` | Search Engine | Search Engine
— System Protocol reference | 41 / 55 | = before |
| `references/system/security-context.mdx` | Security Context | Security
Context — System Protocol reference | 44 / 58 | = before |
| `references/system/settings-client.mdx` | Settings Client | Settings
Client — System Protocol reference | 43 / 57 | = before |
| `references/system/settings-manifest.mdx` | Settings Manifest |
Settings Manifest — System Protocol reference | 45 / 59 | = before |
| `references/system/stack-server.mdx` | Stack Server | Stack Server —
System Protocol reference | 40 / 54 | = before |
| `references/system/supplier-security.mdx` | Supplier Security |
Supplier Security — System Protocol reference | 45 / 59 | = before |
| `references/system/tenant.mdx` | Tenant | Tenant schema — System
Protocol reference | 41 / 55 | = before |
| `references/system/tracing.mdx` | Tracing | Tracing schema — System
Protocol reference | 42 / 56 | = before |
| `references/system/translation.mdx` | Translation | Translation schema
— System Protocol reference | 46 / 60 | = before |
| `references/system/worker.mdx` | Worker | Worker schema — System
Protocol reference | 41 / 55 | = before |
| `references/ui/index.mdx` | UI Protocol | UI Protocol — complete
schema reference | 39 / 53 | = before |
| `references/ui/action-params.mdx` | Action Params | Action Params
schema — UI Protocol reference | 44 / 58 | = before |
| `references/ui/action.mdx` | Action | Action schema — UI Protocol
property reference | 46 / 60 | = before |
| `references/ui/app.mdx` | App | App schema — UI Protocol property
reference | 43 / 57 | = before |
| `references/ui/bulk-action.mdx` | Bulk Action | Bulk Action schema —
UI Protocol reference | 42 / 56 | = before |
| `references/ui/chart.mdx` | Chart | Chart schema — UI Protocol
property reference | 45 / 59 | = before |
| `references/ui/component.mdx` | Component | Component schema — UI
Protocol reference | 40 / 54 | = before |
| `references/ui/dashboard.mdx` | Dashboard | Dashboard schema — UI
Protocol reference | 40 / 54 | = before |
| `references/ui/dataset.mdx` | Dataset | Dataset schema — UI Protocol
reference | 38 / 52 | = before |
| `references/ui/expression-bindable-text-keys.mdx` | Expression
Bindable Text Keys | Expression Bindable Text Keys — UI Protocol | 43 /
57 | = before |
| `references/ui/i18n.mdx` | I18n | I18n schema — UI Protocol property
reference | 44 / 58 | = before |
| `references/ui/notification.mdx` | Notification | Notification schema
— UI Protocol reference | 43 / 57 | = before |
| `references/ui/page.mdx` | Page | Page schema — UI Protocol property
reference | 44 / 58 | = before |
| `references/ui/report.mdx` | Report | Report schema — UI Protocol
property reference | 46 / 60 | = before |
| `references/ui/responsive.mdx` | Responsive | Responsive schema — UI
Protocol reference | 41 / 55 | = before |
| `references/ui/sharing.mdx` | Sharing | Sharing schema — UI Protocol
reference | 38 / 52 | = before |
| `references/ui/view.mdx` | View | View schema — UI Protocol property
reference | 44 / 58 | = before |

---
_Generated by [Claude
Code](https://claude.ai/code/session_01ARcDurZ5j34RdqsGgc4jgH)_

---------

Co-authored-by: Claude <noreply@anthropic.com>
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