`rw-common`'s `rootClaimOf` is `host ?? unscoped` (`attribution.ts:53`), and the search collator uses it (`RwDocsCollatorFactory.ts:152`). But `runScan` deliberately persists only the `host` claim under the site-ref sentinel (`runScan.ts:60-67`), and `if (links.size > 0)` (`runScan.ts:68`) means a site documented only by an unscoped pointer gets no ownership rows at all.
So for such a site, `effectiveOwnership.ts:52` computes `entity_ref = siteRef` while the collator computes `entity_ref = ` — the two surfaces attribute the same page to different entities. This is the class of bug `rw-common/attribution` exists to prevent.
Surfaced while implementing #108 (read-path entity-read enforcement). It is not a hole there — the read path falls back to the site entity, which is a real permission check — but it does mean search and read can disagree about which entity gates an unscoped-only site's pages: a caller who can read the site entity but not the unscoped pointer entity can fetch pages that search hides from them.
Fixing it means changing what the sentinel row means, and its host-only semantics also drive comment routing and notification ownership — hence deferred out of #108 rather than folded in.
`rw-common`'s `rootClaimOf` is `host ?? unscoped` (`attribution.ts:53`), and the search collator uses it (`RwDocsCollatorFactory.ts:152`). But `runScan` deliberately persists only the `host` claim under the site-ref sentinel (`runScan.ts:60-67`), and `if (links.size > 0)` (`runScan.ts:68`) means a site documented only by an unscoped pointer gets no ownership rows at all.
So for such a site, `effectiveOwnership.ts:52` computes `entity_ref = siteRef` while the collator computes `entity_ref = ` — the two surfaces attribute the same page to different entities. This is the class of bug `rw-common/attribution` exists to prevent.
Surfaced while implementing #108 (read-path entity-read enforcement). It is not a hole there — the read path falls back to the site entity, which is a real permission check — but it does mean search and read can disagree about which entity gates an unscoped-only site's pages: a caller who can read the site entity but not the unscoped pointer entity can fetch pages that search hides from them.
Fixing it means changing what the sentinel row means, and its host-only semantics also drive comment routing and notification ownership — hence deferred out of #108 rather than folded in.