Skip to content

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

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

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

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Part of #12237

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

TL;DR

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

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

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

1. Re-derived statistics

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

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

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

populationfilesmedianmax≤20 chars
all content/docs/**/*.mdx (the card)4031450325
references/** — generated, #122492141129207
releases/** — hard stop, #1225097288
editable surface (this card)1801850110

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.

consumerreadssource
SERP <title>page.data.title + %s | ObjectStackapp/[lang]/docs/[[...slug]]/page.tsxgenerateMetadata; app/layout.tsxmetadata.title.template
on-page <h1>page.data.titlesame page, <DocsTitle>{page.data.title}</DocsTitle>
sidebar labelpage.data.titlefumadocs-core page-tree builder, buildFile(): name: title ?? pathToName(basename(path, extname(path)))
llms.txt headingpage.data.titleapps/docs/lib/source.tsgetLLMText(): `# ${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.jsonpages 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.jsonpages 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.

pagecurrent titlesidebar shows instead
content/docs/index.mdxDocumentationdocs root — not in the tree at all (isRoot, and root meta.jsonpages omits it)
content/docs/protocol/objectql/index.mdxObjectQL: The Data Protocol"Data Protocol" from protocol/objectql/meta.json
content/docs/protocol/objectui/index.mdxObjectUI: 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.

filebeforeafterwhy it is safe today
content/docs/index.mdxDocumentation27Documentation — build apps from metadata54not in the page tree
content/docs/protocol/objectql/index.mdxObjectQL: The Data Protocol41ObjectQL — the data protocol specification56meta.json title wins
content/docs/protocol/objectui/index.mdxObjectUI: The UI Protocol39ObjectUI — the UI protocol specification54meta.json title wins
content/docs/protocol/objectui/record-alert.mdxrecord:alert — Conditional Banners on Record Pages64record:alert — banners on record pages52already 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.

medianminmaxin 50-60over 60
before32196421
after5450601800

content/docs/ai/

pagebeforenowafter (proposed)now
actions-as-tools.mdxActions as Tools30MCP tools — expose actions to AI agents53
agents.mdxAI Agents23AI agents — declare tools, model and prompt57
connect-mcp.mdxConnect an MCP Client35MCP client setup — Claude, Cursor and IDEs56
index.mdxAI Overview25AI features — MCP tools, agents and RAG53
knowledge-rag.mdxKnowledge & RAG29RAG — embeddings and knowledge retrieval54
natural-language-queries.mdxNatural Language Queries38Natural language queries over your data53
skills-reference.mdxAI Skills Reference33AI skill reference — every field explained56
skills.mdxAI Skills System30AI skills — reusable instructions for agents58
tools.mdxTool Records26Tool records — govern what an agent may call58

content/docs/api/

pagebeforenowafter (proposed)now
client-sdk.mdxClient SDK24Client SDK — typed JavaScript data access55
data-api.mdxData API22REST data API — CRUD over every object52
data-flow.mdxData Flow Diagrams32Request data flow — from HTTP to driver53
declarative-endpoints.mdxDeclarative Endpoints35Custom endpoints declared as metadata51
environment-routing.mdxEnvironment-Scoped Routing40Environment routing — dev, preview, prod54
error-catalog.mdxError Code Catalog32API error codes — the complete catalog52
error-handling-client.mdxClient-Side Error Handling40Client error handling — retries and codes55
error-handling-server.mdxServer-Side Error Handling40Server error handling — throw the envelope56
index.mdxAPI Overview26REST and GraphQL APIs — generated per object58
metadata-api.mdxMetadata & Package API36Metadata API — read and publish packages54
plugin-endpoints.mdxPlugin Endpoints30Plugin endpoints — add routes from a plugin57
wire-format.mdxWire Format & JSON Examples41API wire format — request and response JSON57

content/docs/automation/

pagebeforenowafter (proposed)now
approvals.mdxApproval workflow31Approval chains — multi-step sign-off rules57
connectors.mdxConnectors24Connectors — call external systems safely55
email-templates.mdxEmail Templates29Email templates — merge fields and layouts56
flows.mdxFlow Metadata27Flows — DAG automation as typed metadata54
hook-bodies.mdxHook & Action Bodies (L1 / L2)44Hook and action bodies — the L1/L2 rules54
hooks.mdxHooks19Record hooks — beforeInsert to afterDelete56
index.mdxAutomation24Automation — flows, triggers and schedules56
jobs.mdxScheduled Jobs28Scheduled jobs — cron automation metadata55
webhooks.mdxWebhook Delivery30Webhooks — outbound delivery and retries54
workflows.mdxWorkflow Metadata31Workflow rules — declarative record logic55

content/docs/

pagebeforenowafter (proposed)now
build-without-code.mdxBuild Without Code32Build business apps without writing code54

content/docs/capabilities/

pagebeforenowafter (proposed)now
ai.mdxAI Under Governance33AI under governance — permissioned agents55
analytics.mdxAnalytics & Dashboards36Analytics — dashboards, reports and charts56
approvals.mdxApprovals23Approvals — routing, queues and audit trail57
automation.mdxAutomation — Processes That Run Themselves56Automation — processes that run themselves56
data.mdxManage Business Data34Business data — objects, fields and records57
forms.mdxForms & Data Quality34Forms and data quality — validation rules55
index.mdxWhat Can It Do?29What can it do? — the capability overview55
integrations.mdxIntegrations & Everyday Work42Integrations — email, files and everyday work59
permissions.mdxPermissions — Who Sees What41Permissions — who sees what, enforced51
request-template.mdxHow to Request Features37Request a feature — how to describe it52
views.mdxViews — See Data Your Way39Views — lists, kanban, calendar and gantt55

content/docs/concepts/

pagebeforenowafter (proposed)now
architecture.mdxProtocol Architecture35Protocol architecture — the four layers53
design-principles.mdxDesign Principles31Design principles behind the metadata spec56
index.mdxCore Concepts27Core concepts — metadata, runtime, protocol57
metadata-driven.mdxMetadata-Driven Development41Metadata-driven development explained51
metadata-lifecycle.mdxMetadata Lifecycle & HMR38Metadata lifecycle — load, publish and HMR56
north-star.mdxNorth Star24North star — why this framework exists52

content/docs/data-modeling/

pagebeforenowafter (proposed)now
analytics.mdxAnalytics Datasets32Analytics datasets — modelling for reports56
drivers.mdxDatabase Drivers30Database drivers — Postgres, MySQL, Mongo55
external-datasources.mdxExternal Datasources (Federation)47External datasources — query without ETL54
field-type-decision-tree.mdxField Type Decision Tree38Which field type should I use? A decision tree60
field-types.mdxField Type Gallery32Field types — the complete visual gallery55
fields.mdxField Metadata28Field metadata — every option explained53
formulas.mdxExpressions (CEL)31Formulas — CEL expressions on records51
import-mappings.mdxImport Mappings29Import mappings — load external data files56
index.mdxData Modeling27Data modelling — objects, fields, relations57
indexing.mdxDatabase Indexing31Database indexes — declare and tune them54
object-extensions.mdxObject Extensions31Object extensions — extend without forking56
objects.mdxObject Metadata29Object metadata — define your data schema55
queries.mdxQuery Syntax Cheat Sheet38ObjectQL query syntax — a cheat sheet51
relationships.mdxRelationships & Lookups37Relationships — lookups, master-detail52
schema-design.mdxSchema Design27Schema design — model a business domain53
seed-data.mdxSeed Data & Fixtures34Seed data — fixtures and demo datasets52
validation-rules.mdxField Validation Rules36Validation rules — reject bad records51
validation.mdxValidation Metadata33Validation metadata — declare the checks54

content/docs/deployment/

pagebeforenowafter (proposed)now
backup-restore.mdxBackup & Restore30Backup and restore — protect tenant data54
cli.mdxCommand Line Interface36Command line interface — the os CLI reference59
environment-variables.mdxEnvironment Variables35Environment variables — the full list51
index.mdxDeployment Overview33Deployment — ship a runtime to production55
production-readiness.mdxProduction Readiness34Production readiness — the go-live list53
publish-and-preview.mdxPublish, Versioning & Preview43Publish, versioning and preview builds52
seed-tenancy-repair.mdxSeed Tenancy Repair33Repair seed tenancy after a bad import52
self-hosting.mdxSelf-Hosted Deployment36Self-hosting — run your own deployment52
single-project-mode.mdxSingle-Environment Mode37Single-environment mode — the simple setup56
tenancy-modes.mdxTenancy Postures & Membership43Tenancy postures and membership models52
troubleshooting.mdxTroubleshooting & FAQ35Troubleshooting — common errors and fixes55
validating-metadata.mdxValidating Metadata33Validate metadata before you deploy it52

content/docs/getting-started/

pagebeforenowafter (proposed)now
build-with-claude-code.mdxBuild with Claude Code36Build an app with Claude Code, step by step57
common-patterns.mdxCommon Patterns29Common patterns — proven metadata recipes55
examples.mdxExample Apps26Example apps — CRM, showcase and more51
glossary.mdxGlossary22Glossary — every metadata term defined52
how-ai-development-works.mdxHow AI Development Works38How AI-written app development works50
index.mdxWhat is ObjectStack?34What is ObjectStack? — a 5-minute intro53
quick-reference.mdxQuick Reference Guide35Quick reference — every metadata type51
quick-start.mdxAnatomy of an ObjectStack App43Anatomy of an app — your first metadata53
your-first-project.mdxYour First Project32Your first project — from zero to running55

content/docs/

pagebeforenowafter (proposed)now
index.mdxDocumentation27Documentation — build apps from metadata54

content/docs/kernel/

pagebeforenowafter (proposed)now
architecture.mdxArchitecture26Core architecture — kernel and services53
cluster.mdxCluster Semantics31Cluster semantics — multi-node runtimes53

content/docs/kernel/contracts/

pagebeforenowafter (proposed)now
auth-service.mdxIAuthService Contract35IAuthService — the authentication contract56
cache-service.mdxICacheService Contract36ICacheService — the cache service contract56
data-engine.mdxIDataEngine Contract34IDataEngine — the storage driver contract55
index.mdxService Contracts Overview40Service contracts — the kernel interfaces55
metadata-service.mdxIMetadataService Contract39IMetadataService — the metadata contract54
storage-service.mdxIStorageService Contract38IStorageService — the file storage contract57

content/docs/kernel/

pagebeforenowafter (proposed)now
events.mdxEvents & Hooks28Kernel events and hooks — the full list53
index.mdxKernel & Services31Kernel and services — the runtime core52

content/docs/kernel/runtime-services/

pagebeforenowafter (proposed)now
audit-service.mdxservices.audit28services.audit — the audit log service API56
data-service.mdxservices.data27services.data — the record CRUD service API57
email-service.mdxservices.email28services.email — the outbound mail API52
examples.mdxRuntime Service Examples38Runtime service examples — copyable code54
index.mdxRuntime Service APIs34Runtime service APIs — the services object56
queue-service.mdxservices.queue28services.queue — the job queue service API56
settings-service.mdxservices.settings31services.settings — the settings API50
sharing-service.mdxservices.sharing30services.sharing — the record share API53
sms-service.mdxservices.sms26services.sms — the text message service API57
storage-service.mdxservices.storage30services.storage — the file store API51
versioning.mdxRuntime Service API Versioning44Runtime service API versioning policy51

content/docs/kernel/

pagebeforenowafter (proposed)now
services-checklist.mdxKernel Services Checklist39Kernel services checklist for reviewers53
services.mdxService Registry30Service registry — resolve and override53

content/docs/permissions/

pagebeforenowafter (proposed)now
access-matrix.mdxAccess-Matrix Snapshot Gate41Access matrix snapshot gate explained51
access-recipes.mdxWho can see data / automation / interface55Access recipes — data, automation and UI54
administrator-guide.mdxAdministrator Guide33Administrator guide to permissions and roles58
attachments-access.mdxAttachments Access32Attachment access — who can read a file53
authentication.mdxAuthentication28Authentication — sessions, tokens, SSO52
authorization.mdxAuthorization Architecture40Authorization architecture — how it decides57
capabilities.mdxDeclaring Capabilities36Declaring capabilities in a permission set56
delegated-administration.mdxDelegated Administration38Delegated administration — scoped admin rights60
explain.mdxExplain Engine28Explain engine — why access was denied52
field-level-security.mdxField-Level Security34Field-level security — hide and mask fields57
index.mdxPermissions & Identity36Permissions and identity — the overview53
permission-metadata.mdxPermission Metadata33Permission metadata — every option explained58
permission-sets.mdxPermission Sets29Permission sets — grant access in bundles55
permissions-matrix.mdxSecurity Permissions Matrix41Security permissions matrix reference51
positions.mdxPositions23Positions — org hierarchy for sharing51
profiles.mdxProfiles (removed)32Profiles (removed) — use permission sets54
record-view-auditing.mdxRecord-View Auditing34Record view auditing — who read what50
rls.mdxRow-Level Security (RLS)38Row-level security (RLS) — filter by rule55
sharing-rules.mdxSharing Rules27Sharing rules and organization-wide defaults58
sso.mdxSocial & Enterprise SSO37SSO — social and enterprise sign-in setup55
system-context.mdxSystem Context (isSystem)39System context (isSystem) — bypass rules54

content/docs/plugins/

pagebeforenowafter (proposed)now
adding-a-metadata-type.mdxAdding a Metadata Type36Add a custom metadata type from a plugin54
anatomy.mdxPlugin Anatomy28Plugin anatomy — files, hooks and exports55
development.mdxPlugin Development32Plugin development — build and test one53
index.mdxPlugin System27Plugin system — extend the runtime safely55
packages.mdxPackage Overview30Package overview — what each one does51

content/docs/protocol/

pagebeforenowafter (proposed)now
backward-compatibility.mdxBackward Compatibility Policy43Backward compatibility policy for the spec56
diagram.mdxProtocol Relationship Diagram43Protocol relationship diagram explained53
index.mdxProtocol Specification36Protocol specification — the open format54

content/docs/protocol/kernel/

pagebeforenowafter (proposed)now
config-resolution.mdxConfiguration Resolution38Configuration resolution order and layers55
error-handling.mdxError Handling28Error handling — the response envelope52
http-protocol.mdxHTTP API22HTTP API protocol — routes and verbs50
i18n-standard.mdxInternationalization Standard43Internationalization standard for metadata56
index.mdxKernel: The System Protocol41Kernel — the system protocol specification56
lifecycle.mdxSystem Lifecycle30System lifecycle — from boot to ready state57
metadata-service.mdxMetadata Service30Metadata service protocol specification53
plugin-spec.mdxPlugin Package Specification42Plugin package specification and manifest55
realtime-protocol.mdxReal-Time Protocols33Real-time protocols — websockets, SSE51

content/docs/protocol/

pagebeforenowafter (proposed)now
knowledge.mdxKnowledge Protocol32Knowledge protocol — RAG as metadata50

content/docs/protocol/objectql/

pagebeforenowafter (proposed)now
index.mdxObjectQL: The Data Protocol41ObjectQL — the data protocol specification56
query-syntax.mdxQuery Syntax26ObjectQL query syntax — the full specification60
schema.mdxSchema Definition31ObjectQL schema definition specification54
security.mdxSecurity & Access Control39ObjectQL security and access control50
state-machine.mdxState Machine (Lifecycle)39State machine — record lifecycle spec51
types.mdxType System25ObjectQL type system — the specification54

content/docs/protocol/objectui/

pagebeforenowafter (proposed)now
actions.mdxAction Protocol29Action protocol — buttons as metadata51
concept.mdxUI as Data Concept32UI as data — the core ObjectUI concept52
index.mdxObjectUI: The UI Protocol39ObjectUI — the UI protocol specification54
layout-dsl.mdxLayout DSL24Layout DSL — arrange fields declaratively55
record-alert.mdxrecord:alert — Conditional Banners on Record Pages64record:alert — banners on record pages52
widget-contract.mdxWidget Contract29Widget contract — build a custom widget53

content/docs/ui/

pagebeforenowafter (proposed)now
actions.mdxActions21Actions — permission-checked UI buttons53
apps.mdxApp Metadata26App metadata — navigation and branding52
audience-based-interfaces.mdxAudience-based interfaces39Audience-based interfaces — one app, many55
create-vs-edit-form.mdxCreate form ≠ edit form37Create form vs edit form — the differences56
dashboards.mdxDashboard Metadata32Dashboard metadata — charts and KPIs50
doc-pages.mdxDoc Metadata26Doc metadata — in-app documentation pages55
field-grouping-and-order.mdxField grouping & order36Field grouping and order in generated forms57
forms.mdxForms (Public + Internal)39Forms — public and internal data entry52
index.mdxUI Engine23UI engine — render views from metadata52
pages.mdxPage Metadata27Page metadata — build custom app screens54
public-data-collection.mdxCollect data from the public42Collect data from the public with web forms57
react-pages.mdxReact Pages25React pages — escape hatch for custom UI54
reports.mdxReport Metadata29Report metadata — grouped, filtered data54
setup-app.mdxSetup App23Setup app — administer a running runtime54
translations.mdxTranslations26Translations — labels and UI text per locale58
views.mdxView Metadata27View metadata — list, kanban, calendar52

content/docs/

pagebeforenowafter (proposed)now
upgrading.mdxUpgrading23Upgrade guide — move between major versions57

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-zhuangos-zhuang added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 25, 2026
@github-actionsgithub-actionsBot 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 queueAug 26, 2026

@os-zhuangos-zhuang left a comment

Copy link
Copy Markdown
ContributorAuthor

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 e18c870Aug 26, 2026
35 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-12237-page-titles branch August 26, 2026 00:29
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/xsskip-changesetPR 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

@os-zhuang@hotlong