Schema.org JSON-LD audit · 2026-05-18
Author: Simon Azoulay
Scope: site-wide structured-data audit + per-page-type coverage. Follow-up
to the author/E-E-A-T workstream switching blog bylines from
Organization "Sigma Research" to Person Simon Azoulay.
1. Author entity
Introduced src/lib/seo/author.ts. Single source of truth for:
SIMON_AUTHOR—Person,@id = /about/#simon, LinkedIn as bothurlandsameAs,jobTitle = "Founder & Lead Researcher",worksForpointing to the org.PUBLISHER_ORG—Organizationwith stable@id = /#organization, matching the layout-level Organization node so Google can dedupe.resolveArticleAuthor(name)— keeps explicit guest bylines if ever set, otherwise returnsSIMON_AUTHOR.
Wired into:
/blog/[slug](Article.author, wasSigma Research)/about(rootPerson+Organization.founder)/layout.tsxrootOrganization.founder(was missing;sameAswas[])
2. Visible byline
Blog hero now reads By [Simon Azoulay] · DATE · MIN read, with the name
wrapped in a <Link href="/about/">. This builds the on-page author link
Google expects to walk for E-E-A-T evidence.
3. Per-page-type coverage
| Page type | Route | Schema types | Status |
|---|---|---|---|
| Root entity | /, every page (layout) |
Organization, WebSite+SearchAction |
Existed. Added founder + populated sameAs with LinkedIn. |
| Founder | /about |
Person, Organization, BreadcrumbList |
NEW. Linked from every blog byline. |
| Article | /blog/[slug] |
Article, BreadcrumbList |
Existed. Added articleSection, keywords, isPartOf, mentions (already had), swapped author → Simon. |
| Blog hub by category | /blog/category/[key] |
BreadcrumbList, ItemList |
NEW (was bare). |
| Company | /company/[slug] |
Organization, BreadcrumbList |
Org existed. Breadcrumb NEW. |
| Insider | /insider/[slug] |
Person, BreadcrumbList |
Person existed. Breadcrumb NEW. |
| Company hub paginated | /companies/, /companies/page/[n] |
BreadcrumbList |
NEW via _shared.tsx. |
| Insider hub paginated | /insiders/, /insiders/page/[n] |
BreadcrumbList |
NEW via _shared.tsx. |
| Pricing | /pricing |
FAQPage, BreadcrumbList |
NEW. 5 EN + 5 FR Q/A pairs. |
| Performance | /performance |
Dataset, BreadcrumbList |
NEW. Dataset CC-BY 4.0, free, links to /api/backtest/summary. |
| Backtest | /backtest |
Dataset, BreadcrumbList |
NEW. Dynamic temporalCoverage + transaction count. |
| Compare vendor | /compare/[vendor] |
— | Skipped (each vendor is a hand-written page; covered by root layout Organization+WebSite). |
| Docs | /docs |
— | Skipped this pass; FAQ section to be migrated to FAQPage later. |
| Recommendations | /recommendations |
— | Skipped (1100-line file, multiple return paths; needs a refactor before injecting ItemList). |
4. Article schema completeness
Current Article payload now ships:
@context, @type, headline, description, inLanguage,
datePublished, dateModified, wordCount, image,
articleSection, keywords, author (Person), publisher (Organization),
mainEntityOfPage, isPartOf (#website), mentions[]
Validated by mental SDTT model: all required Article properties present
when image is set. Articles without heroImageUrl will still validate
as a generic Article (image is recommended, not required).
5. Org graph consistency
@id strategy:
- Organization:
${BASE}/#organization(layout + publisher refs + datasetcreator) - Website:
${BASE}/#website(layout + articleisPartOf) - Founder:
${BASE}/about/#simon(layoutfounder+ about page Person)
This lets Google merge the three references into one node in the knowledge graph instead of treating each as a separate orphan entity.
6. Known gaps (not addressed this pass)
/recommendations— needs anItemListof the top N reco cards. Skipped because the page has 16 separatereturn (branches; deserves a dedicated refactor./compare/*— five vendor comparison pages, each could carry aComparisonTable(no schema exists, would useWebPagewithmainEntityof twoSoftwareApplication/Product)./docsFAQ section — should be promoted to its ownFAQPage./insider/[slug]jobTitle— usesprimaryRoleviatranslateRole, could be enriched withworksForend-date whenEmployeeRoleis known.
7. Lint / typecheck
npx tsc --noEmit passes after all edits.