Journal
10 min readAn answer engine attributed our reviews to someone else.
An answer engine merged our client testimonials with an unrelated developer's profile. The entity schema we shipped, and what is still unresolved.
- entity disambiguation
- Person schema JSON-LD
- sameAs schema.org
- answer engine optimization
Searching "Sujal Thomas" developer returned an answer that was half right. The client testimonials in it were ours. The developer they were attached to was not.
The answer described a fullstack developer from India working in Prisma and ShadCN, then quoted praise written by our clients about our projects. Real reviews, wrong person. We observed this directly, in an answer engine, with our own name in the query.
Nothing was broken in the way tooling reports breakage. The site ranked #1-2 for its own brand name throughout. Ranking was never the problem. What failed was the question sitting underneath the ranking: which developer is this.
What exactly did the answer engine get wrong?
It answered a question about a person by assembling one person out of two.
The reviews are genuine and public. They live on a single Upwork profile, they name specific projects, the site quotes them with attribution. The biography wrapped around them came from somewhere else — different person, different country, different stack. Our builds run on Next.js, TypeScript and Postgres.
To a reader the answer looks credible, because every individual part of it is sourced. The join is the error. A join error is the hardest kind to catch, since nothing in the output reads as invented.
Why does a name collision break attribution?
Two collisions stacked, and neither one appears in a rank report.
The brand name collides badly. A search for the studio surfaced at least eight other design and software entities using "Hikari" — Hikari Design Studio, hikari-design.agency, Hikari Hub, Estudio Hikari, studiohikari, and others beyond those. The founder's first name collides too. Roughly nine other developer portfolios compete for it.
So the engine faced a crowded room with no name tags. It held reviews praising a developer, a studio name shared with eight companies, and a first name shared with nine other portfolios. The answer it produced attached the evidence to someone else.
This is the distinction worth carrying away. Ranking is a document problem — which page best answers this query. Entity resolution is an identity problem — which real-world thing this page is about. Conventional SEO measures the first. The failure we found lives entirely in the second, which is how a site can hold #1-2 for its own name and still lose its own reviews to a stranger.
Testimonials are the most exposed content on a studio site, because the proof lives somewhere other than the page. The quote sits on our site. The review it came from sits on Upwork, under a profile a machine has to reach for separately. Every hop is an opportunity for a machine to bind to the wrong node, and we had given it no reason to prefer the right one.
What did we actually ship?
A Person entity with a stable identifier, and a set of edges pointing at it.
| Signal | Before | After |
|---|---|---|
| Person entity | none | @id, jobTitle, knowsAbout, sameAs |
Person.url | none | /about, interim while the personal domain is down |
Person.sameAs | none | Upwork, GitHub, LinkedIn, personal site |
Organization.founder | absent | reference to the Person @id |
Organization.alternateName | absent | Hikari, Hikari Builds Studio, Hikari Builds — Sujal Thomas |
Blog post author | the Organization | the named founder |
Case study creator, eight pages | the Organization | the named founder |
The alternateName entry doing the real work is the third one. "Hikari" alone is shared with at least eight other studios. "Hikari Builds — Sujal Thomas" belongs to one, and pairing the studio name with the person is the string no other Hikari can claim.
How do you make a person resolvable to a machine?
Four moves. They apply to any site where a named practitioner is the proof — which is to say, any studio whose founder is the reason a client signs.
Give the person an identifier rather than a name. An @id is a URI, and URIs do not collide. Names do. Declare the Person once with a stable @id such as https://example.com/#founder, then reference that same @id everywhere the person appears instead of repeating a name string and hoping the strings get merged. The merge is exactly the step that went wrong for us.
Point sameAs at the places the proof actually lives. Not at every account you happen to own. The purpose of sameAs is corroboration, so each URL should be a profile a machine can fetch and cross-check against the claims on your page. Our set is Upwork, GitHub, LinkedIn, and the personal site — the four places where the reviews and the work can be checked independently.
Bind the person and the organisation in both directions. The Person carries worksFor pointing at the Organization @id, the Organization carries founder pointing back at the Person @id. A one-way edge is a claim. A round trip is a graph, and a graph is harder to misread.
Credit the work to the human. Blog posts and all eight case studies now name the founder as author and creator rather than the Organization. Eight creative works attributed to a person is a stronger disambiguation signal than eight attributed to a studio whose name is shared with eight other studios.
None of this is a ranking tactic. It moves no page up a results list. It tells a machine which subject the page is about, which is a different job, and in our case it was the one that had never been done.
What broke the fix on the same day?
The founder's personal domain had expired.
We found it while assembling the sameAs set, and the state was worse than a stale redirect. whois reported both redemptionPeriod and pendingDelete, with registry expiry of 2026-07-01. There was no A record. curl returned 000 — not a 404, not a timeout on a slow host, no connection at all.
That domain was the Person entity's url, and one of its sameAs entries. So the fix we had just written pointed the canonical address of our identity at nothing.
An entity whose canonical url resolves to nothing is worse than no entity. It asks a machine to resolve identity against an empty address, and an empty address answers nothing while still consuming the attempt.
The interim fix repoints url at /about, which resolves and describes the same person. The dead domain stays in sameAs, so the association survives for whenever the registration is restored. That is a deliberate trade rather than a tidy answer, and we will come back to it.
The sequencing lesson is the useful part. A type-check passes on a dead URL. A build passes on a dead URL. Structured data validators check shape, not reachability, so every automated gate in the pipeline was content to publish an identity claim addressed to a domain that had not resolved since at least its 2026-07-01 registry expiry.
What should you check on your own site?
Five checks. Two are shell commands, two are searches, and the last is a read of your own markup.
Search your own name in quotes with your discipline appended, in an answer engine rather than a search engine, and read the answer for whose biography your work has been attached to. Do it for the studio name as well.
Resolve every URL in every sameAs array you publish. curl -o /dev/null -s -w "%{http_code}\n" against each one takes seconds. Anything returning 000 is not slow, it is absent.
Run whois on every domain you claim as identity, including the ones you no longer actively use. Domain expiry does not appear in error tracking, nor in uptime checks pointed at your main site.
Confirm your @id values are stable and referenced rather than redeclared. Re-declaring a Person on every page with slightly different fields produces several weak entities instead of one strong one.
Count how many other organisations use your brand name in your category. If the answer is eight, no amount of on-page quality will disambiguate you on its own, and the pairing of brand plus person becomes the only reliable handle you have.
The checks are cheap. The failure is quiet, which is precisely why it survives — nobody gets an alert when a machine credits your reviews to a stranger.
Has any of this worked yet?
Unknown. The schema went out in the last deploy, and the re-measurement is set for 30 days after it.
Saying anything stronger would be dishonest. Answer engines re-crawl and re-synthesise on schedules we do not control, and structured data is an input to that process rather than an instruction to it. Ranking was never the problem here, so improved ranking cannot be the evidence.
What we will re-run is the same query, verbatim, and read whether the testimonials are still attached to someone else's biography. That is a binary outcome, not a metric, and it is the only outcome that matters for this defect. We will also check whether the eight case studies come back credited to the named founder.
We do not have a figure for how consistently the conflation reproduced across repeated prompts or across engines. [NEED: conflation rate across n repeated queries per engine, captured at baseline]. That gap is our fault, we found the defect and fixed it before measuring how often it fired.
Even a clean result 30 days out will not prove causation. Several things about this site changed in the same week.
What is still unresolved?
The domain. It sits in redemption, and until it is restored the Person entity's url points at /about — an interim, not a resolution.
Whether keeping a dead domain in sameAs helps or harms. A link to nothing may be ignored quietly, or it may weaken the credibility of the whole set. We chose continuity of association over a clean array, and we have no evidence either way. If the re-measurement comes back unchanged, that entry is the first thing we test by removing.
The other eight Hikaris are not going anywhere. Disambiguation against a crowded name is a standing condition, not a patch that ships once.
The profiles carrying our identity are third-party. Upwork, GitHub, and LinkedIn control their own markup, and none of them are obliged to point back at us. sameAs is an assertion we make, corroborated or not by hosts we do not own.
And the outcome itself. If the 30-day re-run still shows our testimonials under someone else's biography, the markup was not the binding constraint, and the next place to look is the third-party profiles — a worse problem, because we do not own those pages and cannot edit what they assert.