Skip to content

feat(seo): let a noindex route opt into a social card - #887

Merged
developerz-ai[bot] merged 1 commit into
mainfrom
dz/preserve-explicitly-opted-in-social-cards-on--e716ee44
Oct 24, 2026
Merged

developerz-ai[bot] merged 1 commit into
mainfrom
dz/preserve-explicitly-opted-in-social-cards-on--e716ee44

Conversation

@developerz-ai

@developerz-ai developerz-ai Bot commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

What

RouteMeta gains one optional field, socialCard?: boolean.

A document that may not be indexed (robots: { index: false }) carried no
Open Graph, article or Twitter tag — declared or derived — so a route that
deliberately declared a link preview lost it. socialCard: true on the
route's own meta emits the card exactly as an indexable document's is
emitted; without it the suppression stands.

The robots directives are never touched: the route stays noindex, and an
indexable route renders the same head tags as before. No new export, no new
error code, no other package changes.

Acceptance

  • packages/seo/src/meta-social.test.ts covers four cases: a noindex route with socialCard: true emits og:* AND twitter:* tags; a noindex route without it emits no og:*/article:*/twitter:* tag; the opted-in route's robots tag still reads noindex,follow; an indexable route's ca [trimmed]
  • bun test packages/seo/src/meta-social.test.ts packages/seo/src/meta.test.ts is green.
  • RouteMeta gained exactly one optional property — socialCard?: boolean — and packages/seo/src/index.ts exports no new name.
  • No route outside the tests sets socialCard, so every existing document in the repo renders the same head tags as before.
  • packages/seo/README.md, packages/seo/CLAUDE.md, wiki/SEO.md and CHANGELOG.md all state the opt-in; the README's old "there is no social switch" claim is gone.
  • The CHANGELOG entry sits under ## [Unreleased] with no BREAKING — line, and bun run changelog-check is clean.

Changes

  • packages/seo/src/meta.ts (edit) — Add socialCard?: boolean to RouteMeta (with a doc comment stating the rule) and honour it in socialTags(): today's guard if (meta.robots?.index === false) return []; becomes "no card unless the route opted in". With socialCard: true the card is emitted exactly as it is for an indexable document (declared values win, derived ones come with it); the robots tag is never touched. Rewrite the rule comment above socialTags to state the new contract and why. No new export — packages/seo/src/index.ts stays as it is.
  • packages/seo/src/meta-social.test.ts (create) — The card's own test file (one job: when the card is emitted). Owns every social-card case: (1) a noindex route with socialCard: true emits its og:* AND twitter:* tags; (2) that route's robots tag still reads noindex,follow and its canonical/hreflang survive; (3) a noindex route WITHOUT the opt-in emits no og:*/article:*/twitter:* tag; (4) an indexable route keeps its card and socialCard is inert there. Move the two existing social-card cases out of meta.test.ts into this file rather than duplicating them. Own fixture + find helper; opens with a 1-4 line header comment.
  • packages/seo/src/meta.test.ts (edit) — Remove the two social-card cases that move to meta-social.test.ts ("a document that is not indexed carries no social tags, declared or derived" and "follow: false alone keeps the card"), leaving the title-template, robots, hreflang, theme-color and JSON-LD cases here. Nothing else changes: no new import, no new fixture — META and find stay for the cases that remain.
  • packages/seo/README.md (edit) — Public API: the Modules table's meta.ts row says "there is no social switch" — replace that clause with the one opt-in (socialCard: true). Add a short ## section after "Type-level enforcement" stating the rule: a document that may not be indexed carries no og:*/article:*/twitter:* tag unless the route declares socialCard: true; with it the card is emitted as for an indexable page; the robots directives are never changed. No version number, no As of stamp.
  • packages/seo/CLAUDE.md (edit) — House rule: rewrite the bullet "A document that is not indexed carries no social card" to the new contract — renderMeta emits the card unless meta.robots?.index === false, and a route declaring socialCard: true gets it anyway; that field is the ONE switch, the robots line is never touched, and a route without it keeps today's suppression. Rewrite the bullet in place: the file is near the 24 KB ceiling, so do not append a section.
  • wiki/SEO.md (edit) — Public reference for app authors: document the opt-in — a page whose meta says robots: { index: false } carries no og:*/article:*/twitter:* tag unless it declares socialCard: true, in which case the card is emitted as for an indexable page and the robots line is unchanged. Lead with the rule; one short section beside "Head links" plus the renderMeta() row in "The pieces". No version, no As of stamp, and never the word "unreleased" beside a version number.
  • CHANGELOG.md (edit) — Replace "Nothing yet." under ## [Unreleased] with the release note in the house shape: ### Added, a Tier 1 — seo. line, then one bolded lead sentence and the details — RouteMeta.socialCard?: boolean, the rule it changes (the card on a document that may not be indexed), what stays (the robots directives, every indexable route, every noindex route without the flag), and that no other package changes. No BREAKING — line and no version number.
  • packages/admin/README.md (edit) — [docs] packages/admin/README.md:97-98 claims '@ultimat3/seo withdraws them from every noindex document' — false since packages/seo/src/meta.ts:245: a noindex route declaring socialCard: true keeps its card. Narrow the claim: seo withdraws the social tags from a noindex document unless the route declares socialCard: true, and no admin route declares it. The claim about admin documents itself stays true.
  • packages/render/src/route-status.test.ts (edit) — [test] CHANGELOG.md:22-24 and wiki/SEO.md:72-77 promise the opt-in applies to a 4xx/5xx document too — noindexOnError (packages/render/src/route.ts:432-435) forces robots.index = false and keeps the rest of the meta — and nothing pins that socialCard rides through. Add a case beside the 404 test at packages/render/src/route-status.test.ts:102: a route with load: () => withStatus(404, { kind: 'missing' }) and meta: () => ({ title: 'Not found', robots: { index: false }, socialCard: true }) hands back robots.index === false AND socialCard === true.

Verification

bun run verify passed (exit 0) in 16m 38s.


🤖 Opened by developerz.ai for task tsk_e716ee4426347c01cab7a385562d17c9 on developerz-ai/ultimate.

Raised automatically by the raw_task_triage lane.

🤖 developerz.ai, an automated agent, not a human — this PR is one slice of a stacked plan.
Dispatched automatically by the raw_task_triage lane
Model MiniMax-M3 on api.minimax.io via native, funded by account_key
Ran on box hv-dedicated-2
Cost ~$1.19 for 13.6M in / 163.6k out (estimate: list price x reported tokens, cached input at its cached rate)
Run timeline run_26752b25fc5d42a8acfda901248f70fb
Part 1/1 of plan rtk_3e33a6469dbe479a964b1848fdabc1e9

`RouteMeta` gains one optional field, `socialCard?: boolean`.

A document that may not be indexed (`robots: { index: false }`) carried no
Open Graph, article or Twitter tag — declared or derived — so a route that
deliberately declared a link preview lost it. `socialCard: true` on the
route's own `meta` emits the card exactly as an indexable document's is
emitted; without it the suppression stands.

The robots directives are never touched: the route stays `noindex`, and an
indexable route renders the same head tags as before. No new export, no new
error code, no other package changes.

Dz-Task-Id: tsk_e716ee4426347c01cab7a385562d17c9
@developerz-ai developerz-ai Bot added dz/fleet-pr Opened by developerz.ai, an automated agent, from a fleet run. dz/model:MiniMax-M3 developerz.ai: model that wrote this bot PR. dz/provider:api.minimax.io developerz.ai: provider that served this bot PR. labels Oct 24, 2026
@developerz-ai

developerz-ai Bot commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor Author

🟡 Reviewed

2 actionable comment(s) · grounded on your code

⏱ 2m 13s wall clock · gpt-6.1-sol via router · 3 model call(s) · 768 output token(s)

🤖 developerz.ai — automated review, running on your model and your box.

@developerz-ai developerz-ai Bot 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.

Review summary — 9 file(s), 2 finding(s).

Critical 0 · Major 0 · Minor 2 · Nit 0

[bot] completed: Adds the noindex social-card opt-in while preserving the existing rendering path and robots directives. The wiki overstates the resulting robots tag. [bot] completed: the social-card tests cover the new guard, but the 404 regression test does not exercise the transition to noindex.

2 non-blocking findings (2 minor; not posted inline)
  • packages/render/src/route-status.test.ts:125 test [bot] The route already declares index: false, so this test passes if the status wrapper preserves socialCard only for already-noindex metadata but drops it when forcing an indexable page to noindex. Add a variant declaring index: true and assert the returned metadata has index: false and…
  • wiki/SEO.md:81 docs [bot] With robots: { index: false, follow: false }, renderMeta() emits noindex,nofollow, not the promised noindex,follow. Qualify this output as the default-follow example and state that all declared robots directives remain intact.

🤖 developerz.ai review — automated, running on your model and your box. What is this? · Reviewed by router/gpt-6.1-sol on box hv-dedicated-ci-0 — 201.9k in / 768 out over 3 calls; cost unknown: no price on record for router/gpt-6.1-sol.

@developerz-ai
developerz-ai Bot merged commit 1a9f496 into main Oct 24, 2026
16 checks passed
@developerz-ai
developerz-ai Bot deleted the dz/preserve-explicitly-opted-in-social-cards-on--e716ee44 branch October 24, 2026 23:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dz/fleet-pr Opened by developerz.ai, an automated agent, from a fleet run. dz/model:MiniMax-M3 developerz.ai: model that wrote this bot PR. dz/provider:api.minimax.io developerz.ai: provider that served this bot PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants