Skip to content

Fix homepage structured data: add review, correct brand type, add validFrom - #120

Merged
vpetersson merged 2 commits into
sbomify:masterfrom
vpetersson-bot:fix/product-schema-review
Aug 1, 2026
Merged

Fix homepage structured data: add review, correct brand type, add validFrom#120
vpetersson merged 2 commits into
sbomify:masterfrom
vpetersson-bot:fix/product-schema-review

Conversation

@vpetersson-bot

Copy link
Copy Markdown
Contributor

Search Console reported two non-critical Product snippet issues on sbomify.com: missing review and missing aggregateRating.

What this adds

review on the homepage Product entity, using the Atsign testimonial that is already rendered in the "Trusted By" section of that same page:

"review": {
  "@type": "Review",
  "author": { "@type": "Person", "name": "Chris Swan",
              "affiliation": { "@type": "Organization", "name": "Atsign" } },
  "datePublished": "2024-11-22",
  "reviewBody": "Using sbomify has hugely accelerated our work on NTIA compliance. …",
  "url": "https://sbomify.com/case-studies/atsign/"
}

Structured data must reflect content the visitor can actually see, and this markup sits on the homepage — so reviewBody matches the visible blockquote verbatim. I asserted that at build time rather than eyeballing it:

reviewBody matches the visible blockquote: True

What this deliberately does not add

No reviewRating. Chris never gave a score — the testimonial is prose. A Review is valid schema without one, and attaching a number nobody chose would be fabricated data.

No aggregateRating. That property summarises multiple ratings, and none have been collected. This warning will therefore remain reported, which is the honest outcome: Google's review policy requires structured-data reviews to reflect genuine sentiment, so the fix for that one is a real review programme (G2, Capterra, or on-site ratings), not markup.

So this closes one of the two issues properly and leaves the other visible rather than papering over it.

One thing worth your judgement

Chris Swan is described in data/authors.yml as "an sbomify advisor" as well as an engineer at Atsign. The testimonial is genuine and already published, but Google's review policy discourages reviews carrying a conflict of interest. I do not think an advisor-cum-customer quote crosses the line — he is speaking as Atsign, a real user — but you are better placed to judge that than I am, and it is easy to revert if you would rather not attach his name to review markup specifically.

Verification

  • Hugo builds; all three homepage JSON-LD blocks parse as valid JSON
  • reviewBody matches the rendered blockquote exactly
  • No reviewRating / aggregateRating present, asserted
  • dprint clean; site-wide link audit: 0 broken internal links

Search Console reports two non-critical Product snippet issues on
sbomify.com: missing "review" and missing "aggregateRating".

Adds `review` using the Atsign testimonial that is already rendered in the
"Trusted By" section of the homepage. Structured data has to reflect what a
visitor can actually see, and this markup sits on the homepage, so the
reviewBody matches that blockquote verbatim — asserted at build time rather
than assumed.

Deliberately NOT added:

- `reviewRating` — no rating was ever given. A Review is valid without one,
  and attaching a number nobody chose would be fabricated data.
- `aggregateRating` — that summarises multiple ratings, and none have been
  collected. This one stays reported until a real review programme exists.

Google's review policy requires structured-data reviews to reflect genuine
sentiment, so the remaining warning is the honest outcome rather than
something to engineer away.

Verified: all three homepage JSON-LD blocks parse, reviewBody matches the
visible blockquote exactly, and no rating fields are present.
Search Console reports four non-critical Merchant listing issues. Two are
real defects and are fixed here; two are retail concepts that do not apply
to a SaaS and are deliberately left alone.

Fixed:

- "Invalid object type for field brand" — brand was typed Organization.
  Google's merchant listing spec expects Brand. Genuine markup bug.
- "Missing field validFrom (in offers)" — added to both offers. The site
  rebuilds nightly, so "valid from this build" is an accurate statement
  rather than an invented date, and it mirrors the priceValidUntil that was
  already generated the same way. priceValidUntil now covers the Community
  offer too, which it previously omitted.

Not fixed, on purpose:

- "Missing field shippingDetails (in offers)" — nothing is shipped. There
  is no honest value.
- "Missing field hasMerchantReturnPolicy (in offers)" — a real refund
  policy exists in terms.md, but MerchantReturnPolicy wants machine-readable
  return windows, fees and methods. Those would have to be invented, and
  publishing invented return commitments is a legal statement, not a markup
  detail.

Both remaining warnings stem from Product + offers causing Google to
evaluate the page as an e-commerce listing. A B2B SaaS will not qualify for
merchant listings regardless, so they are permanent unless the page stops
modelling itself as a Product.

Verified in the built output: brand is Brand, both offers carry validFrom
and priceValidUntil, all JSON-LD parses.
@vpetersson-bot vpetersson-bot changed the title Add the Atsign review to the homepage Product structured data Fix homepage structured data: add review, correct brand type, add validFrom Aug 1, 2026
@vpetersson-bot

Copy link
Copy Markdown
Contributor Author

Extended to cover the second Search Console email (Merchant listings, 4 non-critical issues) as well as the Product snippets one.

Fixed

Issue Fix
Missing field review Atsign testimonial already visible on the homepage
Invalid object type for brand was Organization, Google's merchant spec wants Brand — a genuine markup bug
Missing field validFrom (in offers) added to both offers

validFrom uses the build date. That is accurate rather than invented: the site rebuilds nightly, so "valid from this build" is true, and it mirrors the priceValidUntil already generated the same way. That also fixes an inconsistency — priceValidUntil was on the Business offer but not Community; both now carry it.

Verified in the built output:

brand: {'@type': 'Brand', 'name': 'sbomify'}
  Community  validFrom=2026-08-01 priceValidUntil=2026-09-01
  Business   validFrom=2026-08-01 priceValidUntil=2026-09-01

Deliberately not fixed

aggregateRating — summarises multiple ratings; none have been collected.

shippingDetails — nothing is shipped. There is no honest value to supply.

hasMerchantReturnPolicy — this one I looked into properly. A real refund policy does exist in content/terms.md (UK consumer protection law, no partial refunds, written requests). But MerchantReturnPolicy wants machine-readable return windows, fees and methods, and I would have to invent those. Publishing invented return commitments is a legal statement, not a markup detail — so that needs your call, not mine. If you tell me the actual terms I will encode them.

The pattern worth noticing

All four merchant-listing warnings exist because Product + offers makes Google evaluate the homepage as an e-commerce listing — hence shipping and returns. A B2B SaaS will not qualify for merchant listings regardless; those target retail goods and generally expect GTIN/MPN too.

So this class of warning is permanent unless the page stops modelling itself as a Product. This PR fixes everything that can be fixed honestly; if the remaining noise bothers you, the real lever is dropping the Product entity and expressing pricing as Organization.makesOffer, which I sketched earlier. That is an SEO decision rather than a correctness one, so I have not done it.

@vpetersson-bot

Copy link
Copy Markdown
Contributor Author

@cpswan — looping you in because this PR uses your Atsign testimonial as schema.org review markup on the homepage, which puts your name and affiliation into structured data Google may surface in search results.

Nothing new is being said: the quote is the one already rendered in the "Trusted By" section, reproduced verbatim (the build asserts the markup matches the visible text). No rating is attached — you gave prose, not a score, so inventing a number would have been fabricated data.

"review": {
  "@type": "Review",
  "author": { "@type": "Person", "name": "Chris Swan",
              "affiliation": { "@type": "Organization", "name": "Atsign" } },
  "datePublished": "2024-11-22",
  "reviewBody": "Using sbomify has hugely accelerated our work on NTIA compliance. …",
  "url": "https://sbomify.com/case-studies/atsign/"
}

Two things worth your eye:

  1. Consent to the machine-readable use. A visible testimonial and a structured-data review are not quite the same thing — the latter is explicitly a review of the product, attributed to you, in a format Google indexes.
  2. You are listed in data/authors.yml as an sbomify advisor as well as an Atsign engineer. Google discourages review markup carrying a conflict of interest. I do not think it crosses the line, since you are speaking as a real user, but you are better placed to judge than I am.

If either gives you pause, say so and I will drop the review block — the rest of the PR (a genuine brand type bug and missing validFrom) stands on its own.

(Requested by @vpetersson. I could not add you as a formal reviewer — this account only has read access on this repo.)

@vpetersson
vpetersson merged commit 8e1d527 into sbomify:master Aug 1, 2026
2 checks passed
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.

2 participants