Skip to content

seo: complete the social share image on home (dogfood d87d715be3d0) - #31

Merged
nish3451 merged 3 commits into
mainfrom
fix/social-share-og-image-home
Aug 9, 2026
Merged

seo: complete the social share image on home (dogfood d87d715be3d0)#31
nish3451 merged 3 commits into
mainfrom
fix/social-share-og-image-home

Conversation

@nish3451

@nish3451 nish3451 commented Aug 9, 2026

Copy link
Copy Markdown
Collaborator

What

Closes dogfood finding d87d715be3d0 — "Social share image incomplete on home" (audit run 20260808T074205Z-msk2fl3n).

The leak audit this site sells flags a homepage whose served HTML cannot tell a social platform what to show when the page is shared — the share card comes back with no image, or a scraped guess. The dogfood run found exactly that on our own home page: public/index.html served zero og:/twitter: tags even though public/og-image.png (1200×630) exists and is allow-listed in the worker (src/worker.js).

Change

  1. All five public pages (index.html home, audit.html, agents.html, pricing.html, specimen.html) now carry a complete, per-page share set in <head>:
    • og:title, og:description, og:type=website, og:url (each page's own absolute URL), og:image = https://tinystudio.io/og-image.png with og:image:width=1200, og:image:height=630, og:image:alt
    • Twitter Card mirror: twitter:card=summary_large_image, twitter:title/description/image
    • og:description mirrors each page's meta description; nothing revives the retired "Agent Desk"/"The Tiny Studio" framing (checked by the existing stale-identity guard).
  2. Social share image regenerated — the original og-image.png (PR Add HSTS, social image, touch icon, and structured data #6) was built while the root page was the retired TinyStudio Agent Desk, so its copy advertised the legacy product. The new card uses the current site's palette (index.css cream/ink/brass), the existing favicon.svg mark, and copy verbatim from the current home page headline + description. scripts/generate-og-image.mjs renders it in real Chromium at exactly 1200×630 and refuses to write unless fonts load, elements fit the canvas, and pixels contain the cream background / ink text / brass accents.
  3. CI guard in scripts/check-site.mjs ("Social share tags" section, modeled on the meta-descriptions guard from PR SEO: add truthful page meta descriptions #21): each tag exactly once inside <head>; og: tags must use property=, twitter: tags must use name= (no data-property pass-through); og:description must equal the page's meta description; og:image must be the absolute og-image.png URL; declared dimensions must match the actual PNG header bytes.

Verification

  • npm run check — passes; mutation-tested (wrong og:image URL fails; malformed name="og:title" fails; restored fixes pass)
  • npm test — 68/68 pass (site checks + 53 worker + 15 UI)
  • npm run check:render-blocking — all six pages PASS under the production CSP
  • Real Chromium: all five pages parse exactly 12 social tags in document.head, og:image resolves to the absolute URL with correct 1200×630 dimensions
  • New image verified on-disk: PNG signature, 1200×630, pixel-sampled cream/ink/brass
  • scripts/generate-og-image.mjs is deterministic (same bytes on rerun)

The leak audit this site sells flags a homepage whose served HTML cannot
tell a social platform what to show when shared. The dogfood audit run
20260808T074205Z-msk2fl3n found exactly that on this site's own home page
(finding d87d715be3d0, 'Social share image incomplete on home'):
public/index.html served zero og:/twitter: tags even though
public/og-image.png exists and is allow-listed in the worker.

Add the complete Open Graph + Twitter Card set (og:title, og:description,
og:type, og:url, og:image with width/height/alt, twitter:card
summary_large_image, twitter:title/description/image) to the heads of all
five public pages, mirroring each page's meta description and pointing
og:image at the absolute https://tinystudio.io/og-image.png (1200x630,
matching the PNG header).

Add a source-string CI guard in scripts/check-site.mjs (social share tags
section) so the fault cannot return: each page must carry each tag exactly
once inside <head>, og:description must equal the page's meta description,
og:image must be the absolute og-image.png URL, and the declared dimensions
must match the actual PNG bytes.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

nish3451 has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Five public pages now include Open Graph and Twitter Card metadata. scripts/check-site.mjs validates the required tags, page-specific values, shared preview image, and image dimensions.

Changes

Social metadata

Layer / File(s) Summary
Add page social metadata
public/agents.html, public/audit.html, public/index.html, public/pricing.html, public/specimen.html
Each page adds Open Graph and Twitter Card tags with titles, descriptions, canonical URLs, preview image details, and large-image card configuration.
Validate social metadata
scripts/check-site.mjs
Site checks require one correctly placed set of tags per page and validate page-specific values plus the shared 1200×630 PNG image.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title describes a real social-sharing metadata change, but it focuses on the homepage while the pull request updates five pages and adds validation.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/social-share-og-image-home

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 69bb89aa5d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread public/index.html
<meta property="og:description" content="TinyStudio: the free leak audit of high-ticket service homepages. Each fault named in order of what it costs you, with the fix beside it. Six a month.">
<meta property="og:type" content="website">
<meta property="og:url" content="https://tinystudio.io/">
<meta property="og:image" content="https://tinystudio.io/og-image.png">

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Replace the legacy Agent Desk share image

When any of these pages is shared, this new tag renders the existing public/og-image.png, whose visible copy advertises “TinyStudio Agent Desk” and “Self-serve AI agents.” That directly contradicts the current Website Appraisal offer and public/offer.md, which identifies the Agent Desk as a legacy product, so every newly enabled social card promotes the wrong service; replace the asset with artwork for the current offer before pointing all five pages at it.

AGENTS.md reference: AGENTS.md:L2-L3

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
scripts/check-site.mjs (1)

976-988: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Assert page-specific title values.

The empty expected values for og:title and twitter:title only enforce non-empty content. A copied title or a mismatch between the two card formats passes. Add explicit expected titles, or compare both values with the canonical page title after HTML entity decoding.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/check-site.mjs` around lines 976 - 988, Update the expected metadata
assertions in the page-check logic around the expected Map so og:title and
twitter:title are validated against the canonical page title after HTML entity
decoding, ensuring both values are page-specific and consistent rather than
merely non-empty.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/check-site.mjs`:
- Around line 961-964: Update shareTagIn to select the required metadata
attribute based on the key prefix: use property for og:* keys and name for
twitter:* keys. Anchor the regex at a valid attribute boundary so data-property
or data-name attributes cannot match, while preserving case-insensitive
matching.
- Around line 973-1005: Validate that the page description parsed in the
socialSharePages loop is non-empty before constructing the expected Map; record
a failure for the page and skip its social-tag checks when missing, so
og:description and twitter:description cannot pass as empty expected values.

---

Nitpick comments:
In `@scripts/check-site.mjs`:
- Around line 976-988: Update the expected metadata assertions in the page-check
logic around the expected Map so og:title and twitter:title are validated
against the canonical page title after HTML entity decoding, ensuring both
values are page-specific and consistent rather than merely non-empty.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9a0182ca-52b1-4aa3-bae7-6c2de235a28e

📥 Commits

Reviewing files that changed from the base of the PR and between a76dba4 and 69bb89a.

📒 Files selected for processing (6)
  • public/agents.html
  • public/audit.html
  • public/index.html
  • public/pricing.html
  • public/specimen.html
  • scripts/check-site.mjs

Comment thread scripts/check-site.mjs Outdated
Comment thread scripts/check-site.mjs
Comment on lines +973 to +1005
for (const [pageName, pageHtml, pageUrl] of socialSharePages) {
const head = pageHtml.match(/<head\b[\s\S]*?<\/head>/i)?.[0] ?? "";
const description = pageHtml.match(/<meta\b[^>]*\bname="description"[^>]*>/i)?.[0]?.match(/\bcontent="([^"]*)"/i)?.[1] ?? "";
const expected = new Map([
["og:title", ""], // non-empty, per page
["og:description", description],
["og:type", "website"],
["og:url", pageUrl],
["og:image", SOCIAL_IMAGE_URL],
["og:image:width", "1200"],
["og:image:height", "630"],
["og:image:alt", ""], // non-empty, per page
["twitter:card", "summary_large_image"],
["twitter:title", ""], // non-empty, per page
["twitter:description", description],
["twitter:image", SOCIAL_IMAGE_URL]
]);
for (const [key, expectedContent] of expected) {
const inDoc = shareTagIn(pageHtml, key);
if (inDoc.length !== 1) {
failures.push(`Social share tag ${key} must appear exactly once on ${pageName} (found ${inDoc.length}).`);
continue;
}
const inHead = shareTagIn(head, key);
if (inHead.length !== 1) {
failures.push(`Social share tag ${key} on ${pageName} must sit inside <head>.`);
continue;
}
const content = inHead[0].match(/\bcontent="([^"]*)"/i)?.[1] ?? "";
if (expectedContent === "" && !content.trim()) {
failures.push(`Social share tag ${key} on ${pageName} must not be empty.`);
} else if (expectedContent !== "" && content !== expectedContent) {
failures.push(`Social share tag ${key} on ${pageName} must be ${JSON.stringify(expectedContent)} (found ${JSON.stringify(content)}).`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Fail when the page description is missing.

If line 975 cannot parse name="description", description becomes an empty string. The expected values for og:description and twitter:description then become empty. Lines 1002-1003 check only that the social descriptions are non-empty. A page without a parseable meta description can therefore pass without matching page metadata.

Fail on an empty description before building expected.

Proposed fix
   const description = pageHtml.match(/<meta\b[^>]*\bname="description"[^>]*>/i)?.[0]?.match(/\bcontent="([^"]*)"/i)?.[1] ?? "";
+  if (!description.trim()) {
+    failures.push(`Page ${pageName} must have a non-empty meta description.`);
+  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
for (const [pageName, pageHtml, pageUrl] of socialSharePages) {
const head = pageHtml.match(/<head\b[\s\S]*?<\/head>/i)?.[0] ?? "";
const description = pageHtml.match(/<meta\b[^>]*\bname="description"[^>]*>/i)?.[0]?.match(/\bcontent="([^"]*)"/i)?.[1] ?? "";
const expected = new Map([
["og:title", ""], // non-empty, per page
["og:description", description],
["og:type", "website"],
["og:url", pageUrl],
["og:image", SOCIAL_IMAGE_URL],
["og:image:width", "1200"],
["og:image:height", "630"],
["og:image:alt", ""], // non-empty, per page
["twitter:card", "summary_large_image"],
["twitter:title", ""], // non-empty, per page
["twitter:description", description],
["twitter:image", SOCIAL_IMAGE_URL]
]);
for (const [key, expectedContent] of expected) {
const inDoc = shareTagIn(pageHtml, key);
if (inDoc.length !== 1) {
failures.push(`Social share tag ${key} must appear exactly once on ${pageName} (found ${inDoc.length}).`);
continue;
}
const inHead = shareTagIn(head, key);
if (inHead.length !== 1) {
failures.push(`Social share tag ${key} on ${pageName} must sit inside <head>.`);
continue;
}
const content = inHead[0].match(/\bcontent="([^"]*)"/i)?.[1] ?? "";
if (expectedContent === "" && !content.trim()) {
failures.push(`Social share tag ${key} on ${pageName} must not be empty.`);
} else if (expectedContent !== "" && content !== expectedContent) {
failures.push(`Social share tag ${key} on ${pageName} must be ${JSON.stringify(expectedContent)} (found ${JSON.stringify(content)}).`);
for (const [pageName, pageHtml, pageUrl] of socialSharePages) {
const head = pageHtml.match(/<head\b[\s\S]*?<\/head>/i)?.[0] ?? "";
const description = pageHtml.match(/<meta\b[^>]*\bname="description"[^>]*>/i)?.[0]?.match(/\bcontent="([^"]*)"/i)?.[1] ?? "";
if (!description.trim()) {
failures.push(`Page ${pageName} must have a non-empty meta description.`);
}
const expected = new Map([
["og:title", ""], // non-empty, per page
["og:description", description],
["og:type", "website"],
["og:url", pageUrl],
["og:image", SOCIAL_IMAGE_URL],
["og:image:width", "1200"],
["og:image:height", "630"],
["og:image:alt", ""], // non-empty, per page
["twitter:card", "summary_large_image"],
["twitter:title", ""], // non-empty, per page
["twitter:description", description],
["twitter:image", SOCIAL_IMAGE_URL]
]);
for (const [key, expectedContent] of expected) {
const inDoc = shareTagIn(pageHtml, key);
if (inDoc.length !== 1) {
failures.push(`Social share tag ${key} must appear exactly once on ${pageName} (found ${inDoc.length}).`);
continue;
}
const inHead = shareTagIn(head, key);
if (inHead.length !== 1) {
failures.push(`Social share tag ${key} on ${pageName} must sit inside <head>.`);
continue;
}
const content = inHead[0].match(/\bcontent="([^"]*)"/i)?.[1] ?? "";
if (expectedContent === "" && !content.trim()) {
failures.push(`Social share tag ${key} on ${pageName} must not be empty.`);
} else if (expectedContent !== "" && content !== expectedContent) {
failures.push(`Social share tag ${key} on ${pageName} must be ${JSON.stringify(expectedContent)} (found ${JSON.stringify(content)}).`);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/check-site.mjs` around lines 973 - 1005, Validate that the page
description parsed in the socialSharePages loop is non-empty before constructing
the expected Map; record a failure for the page and skip its social-tag checks
when missing, so og:description and twitter:description cannot pass as empty
expected values.

The original og-image.png (PR #6) was built while the root page was the
retired TinyStudio Agent Desk, so its copy advertised the legacy product —
sharing the home page after the tag fix would render a card for the wrong
offer. Rebuild it with the current site's palette (index.css cream/ink/
brass), the existing favicon.svg mark, and copy verbatim from the current
home page headline and description. No new claims.

scripts/generate-og-image.mjs renders the card in real Chromium at exactly
1200x630 and refuses to write unless the Google Fonts load, every element
sits inside the canvas, and the pixels contain the cream background, ink
text, and brass accents. The check-site.mjs social guard keeps all five
pages pointed at this file with dimensions matching its PNG header.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

nish3451 has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

nish3451 has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 08f2c19436

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +26 to +28
const HEADLINE = "Most of them leave <em>before they ever get in touch.</em>";
const DESCRIPTION =
"TinyStudio: the free leak audit of high-ticket service homepages. Each fault named in order of what it costs you, with the fix beside it. Six a month.";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve a separate share card for the Agent Desk

When someone shares the still-public /agent-desk route (allow-listed in src/worker.js), its existing metadata continues to advertise “The Tiny Studio Agent Desk” but points to /og-image.png; regenerating that shared asset with this Website Appraisal-only copy therefore produces a contradictory card. Give the Agent Desk its own matching image or update that page's social metadata as part of replacing the global asset.

Useful? React with 👍 / 👎.

@nish3451
nish3451 merged commit eae1d87 into main Aug 9, 2026
3 checks passed
nish3451 added a commit that referenced this pull request Aug 9, 2026
Resolve the drift between fix/apple-touch-icon-home (finding 98a7bf8e08fc)
and origin/main after #28 (heading hierarchy) and #31 (social share tags)
landed. Both changes touched the same head regions and check-site.mjs;
the merged result keeps the apple-touch-icon link and its regression guard
alongside main's og:/twitter: share blocks on all five public pages.
nish3451 added a commit that referenced this pull request Aug 13, 2026
…current main and live (#168)

Judges the lane-1 item for dogfood finding d87d715be3d0 ("Social share image
incomplete on home", audit 20260808T074205Z-msk2fl3n). The finding was fixed
by PR #31 (eae1d87), verified live by PR #76, and engine-rerun-confirmed by
PR #122; this receipt re-verifies on the current head (5209ec7) and the live
deployment that the twelve-tag share set, the CI guard, and the reachable
1200x630 og-image.png all still hold. Nothing further to change; item closes
with a reason.

Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
nish3451 added a commit that referenced this pull request Aug 14, 2026
…live (#175)

Dogfood finding d87d715be3d0 ("Social share image incomplete on home")
was fixed by PR #31 and closed on 2026-08-11/12/13. This lane re-verifies
the fix on the current origin/main head (afb5d49) and the live deployment:
the twelve-tag share set is intact in public/index.html, the CI guard
passes, and the live site serves the complete set with a reachable
1200x630 og-image.png byte-identical to the committed file. No code
change required.

Co-authored-by: nish3451 <nish3451@users.noreply.github.com>
Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
nish3451 added a commit that referenced this pull request Aug 15, 2026
…live (#221) (#221)

Lane-1 re-verification of dogfood finding d87d715be3d0 (social share
image incomplete on home). The fix (PR #31, eae1d87) is present on the
current origin/main head 9944fec, the source guard passes, and the live
deployment serves the complete twelve-tag share set with a reachable
1200x630 og-image.png byte-identical to the committed file.

Co-authored-by: nish3451 <nish3451@users.noreply.github.com>
Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant