Skip to content

docs(site): lead the homepage title with the category, cut the 614-char description to 152 - #12284

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-12239-homepage-keywords
Aug 25, 2026
Merged

docs(site): lead the homepage title with the category, cut the 614-char description to 152#12284
os-zhuang merged 1 commit into
mainfrom
claude/issue-12239-homepage-keywords

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Fixes#12239

One file, apps/docs/app/[lang]/page.tsx, +2 −4, all of it inside the exported
metadata object. The h1 is byte-identical and the uppercase eyebrow above it is
untouched — both proved from rendered HTML below, not from the diff.

The two strings, before and after

Emitted by a running dev server on / — which the proxy rewrites to /en, so this is
app/[lang]/page.tsx doing the rendering, not the shadowed app/page.tsx (finding #12255).

Title element

before 53 chars ObjectStack — Apps small enough for AI to hold whole.
after 52 chars Metadata framework for AI-written apps | ObjectStack

Meta description

before 614 chars
ObjectStack turns the whole app — data model, UI, workflows, permissions — into typed
metadata: a complete CRM in under 150k tokens, one context window. Agents read it whole,
reason it whole, refactor it whole. The business logic alone — every object, workflow and
permission — is under 100k tokens; the UI adds just 50k more. That metadata is your
business ontology — an open, versioned definition you own. Strict TypeScript, Zod, and a
validation gate catch the agent's mistakes at authoring time, and the runtime derives the
database, REST API, UI, and MCP server, enforcing permissions and audit on every call.
after 152 chars
ObjectStack turns the whole app — data model, UI, workflows, permissions — into typed
metadata: a complete CRM in under 150k tokens, one context window.

Why these words

The description is a cut, not a rewrite — every word is already yours. It is
sentence one of the old description, verbatim, and it is a complete sentence at 152
characters with nothing added. Sentences two through five were the rest of the landing
page pasted into a meta tag; they are still on the page, where they belong. A snippet
that matches the opening paragraph a visitor then reads is the shape you want anyway.

The title needed assembling, and the vocabulary came from the repo. No existing
string was a keyword-shaped title, so this one is built rather than cut — but
AI-written is lifted from the site's own site-wide description in app/layout.tsx
("the open target format and runtime for AI-written business apps"), not invented.
The repo spells this idea three ways already (AI-written, AI-authored,
AI-generated); AI-built, the phrasing the card suggested, appears nowhere in the
tree, so it would have been a fourth.

Dropping absolute is the whole title mechanism.app/layout.tsx already declares
title.template: '%s | ObjectStack'. The old value opted out of it with absolute, for
a good reason — it began with the brand, and ObjectStack — … | ObjectStack reads
badly. Now that the title leads with the category, the template is simply the right way
to get the brand back, so the page hands it a plain string and the site's own convention
appends the rest. The homepage stops being the one route that bypasses the site title
shape. If the template ever stops applying, this degrades to Metadata framework for AI-written apps (38 chars) — still inside every bound the card sets.

What was left out on purpose.open source and business apps did not fit under 60
characters alongside the brand. business apps is carried in the description by
a complete CRM, a concrete instance of the category; open source is already on the
page in the eyebrow (Open protocol & runtime · Apache-2.0) and in the Apache-2.0 badge.
Neither was worth stuffing.

Acceptance, measured on rendered HTML

criterionresult
title element ≤ 60 characters52
title names the category, not only the brandleads with Metadata framework
meta description 120–155 characters152
h1 byte-identical to todayidentical — 346 bytes of markup, both renders

The h1 check is a byte comparison of the h1 markup extracted from the before and
after HTML, not an eyeball: identical, including the class list and the gradient span.
One h1 on the page, before and after.

How it was verified

The change was committed first, so both legs are recoverable from git rather than from
the working tree:

  1. git checkout HEAD~1 -- apps/docs/app/[lang]/page.tsx → blob on disk hashed and compared to the base
    blob: 243e21aa1 both sides, so the revert provably landed.
  2. Dev server started on a private high port, / fetched (HTTP 200, 81 950 bytes),
    server torn down by the PID that was started. → the before numbers above.
  3. git checkout HEAD -- apps/docs/app/[lang]/page.tsxgit diff HEAD empty, blob 7f20305e5 matching
    HEAD. → restore provably landed.
  4. Same capture again on a second port (HTTP 200, 81 003 bytes). → the after numbers.

Both servers confirmed gone afterwards (lsof -ti tcp:PORT → 0 listeners on each).

Gates

All run on the pushed head 1e45188fa, each exit code captured before any pipe, each
line below quoted from the gate's own verdict:

[0] pnpm --filter @objectstack/docs typecheck ✓ Types generated successfully
[0] pnpm check:published-files ✓ 69 publishable package(s) … admits no test …
[0] pnpm check:test-source-alias check-test-source-alias OK — 72 packages with tests scanned
[0] pnpm check:type-source-resolution check-type-source-resolution OK — 93 tsc program(s) across 77 packages
[0] pnpm check:nul-bytes OK (scanned 6783 text file(s) … no raw ASCII control bytes)
[0] pnpm lint full repo, eslint . --no-inline-config, 26s, clean

The first three are what node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack
derives for this diff; check:nul-bytes is owed by any edit. pnpm lint is the whole
repo, not a narrowed subset, so no narrowing needs declaring for it.

The typecheck claim is measured rather than assumed: tsc --noEmit --listFiles puts
apps/docs/app/[lang]/page.tsx in the program (1 of 1 236 files), so "typecheck clean"
really does cover the edited file.

Declared narrowing — verification ran UNLOCKED.scripts/pm/os-verify-lock.sh
could not take the shared verify lock on this host: no usable flock. The shared
verify lock is declared Linux-only (flock is util-linux, and a stock macOS does
not ship it), so the command below was run directly, without the lock —
a declared narrowing, not a silent one. No serialization guarantee held for this
run, nor for any sibling agent in this container while it ran.

Every command in the table above ran through that entry point and carries that
disclosure.

Corrections to the card

  • The description is 614 characters, not 615. 615 counts the source literal including
    the backslash in agent\'s; the string that reaches the meta tag is 614. Confirmed on
    the rendered HTML, not just in the source. Everything else the card measured holds
    exactly: title 53 characters, and the served homepage really is app/[lang]/page.tsx.
  • PR feat(docs-site): lead the homepage with the 90-second overview, link the YouTube channel #12218 did not touch this metadata block. It reflowed the hero paragraph only,
    so the bytes measured here are the same ones the card measured before that merge.

Landing notes

  • No changeset: docs-site copy, nothing published. skip-changeset label applied.
  • Neither .github/workflows/lint.yml nor package.json is touched — no new gate here.
  • Card docs site: homepage hero cover is a 406 KB PNG served at ~1024 px #12242 is queued behind this one on the same file; this diff is six lines inside
    the metadata object, nowhere near the hero poster, so its rebase should be clean.

Generated by Claude Code

…-char description
The homepage `<title>` was `ObjectStack — Apps small enough for AI to hold whole.`
— the maintainer's hero line, but as a `<title>` it is 53 characters carrying no
term anyone searches for. The meta description was 614 characters, roughly four
times what a search result renders.
Title: drop the `absolute` wrapper so the root layout's existing `%s | ObjectStack`
template supplies the brand, and lead with the category instead of the slogan.
Description: keep sentence one of the existing copy verbatim and cut the rest —
it is already a complete sentence at 152 characters.
The `<h1>`, the eyebrow, and every other line on the page are untouched.
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 size/xs documentation Improvements or additions to documentation labels Aug 25, 2026
@os-zhuang
os-zhuang marked this pull request as ready for review August 25, 2026 16:30
@os-zhuang
os-zhuang added this pull request to the merge queueAug 25, 2026
Merged via the queue into main with commit af58a6fAug 25, 2026
29 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-12239-homepage-keywords branch August 25, 2026 16:57
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.

docs site: homepage <title> and description are not keyword-shaped (description is 615 characters)

1 participant

@os-zhuang