Skip to content

Split inbound links out of Page Detail into listInboundLinks (#235) - #243

Merged
YusukeHirao merged 5 commits into
devfrom
worktree-inherited-tickling-swan
Jul 29, 2026
Merged

Split inbound links out of Page Detail into listInboundLinks (#235)#243
YusukeHirao merged 5 commits into
devfrom
worktree-inherited-tickling-swan

Conversation

@YusukeHirao

Copy link
Copy Markdown
Member

Summary

  • Split a page's inbound links (referrers) out of getPageDetail into a dedicated, cursor-paginated listInboundLinks query — a page's referrer count can reach the hundreds of thousands on a large site, too large to embed in a single response (issue ページ詳細の inboundLinks が巨大サイトで実用に耐えない件(要検討: 見せ方から) #235).
  • listInboundLinks reads exclusively from the viewer_anchor_facts read model (no legacy fallback) and throws an actionable viewer-build error when the read model is missing or stale, via a new requireViewerReadModel guard.
  • viewer_anchor_facts gains a first_text_id column (references text_refs, read-model schema v21→v22) so anchor text resolves without a second anchor_edges round-trip.
  • viewer: new /api/pages/inbound-links route + dedicated /pages/inbound-links view (MPA and virtual-scroll). Page Detail now shows only the referrer count with a link to the full list. In stub mode (live crawl), the route responds { available: false } instead of throwing, since the read model can never exist there.
  • CLI: new inbound-links query sub-command. MCP: new list_inbound_links tool; get_page_detail's description no longer claims to include inbound links.
  • ARCHITECTURE.md, docs/query.md, and mcp-server/README.md updated to match.

Breaking change: PageDetail.inboundLinks is removed. Package is at 0.14.0 (0.x), so this is a normal breaking change — no migration guide required.

Test plan

  • yarn lint:check (no --fix) — clean
  • yarn build — all 13 packages build
  • yarn test — 561 files / 3963 tests pass
  • Viewer Playwright e2e — all 5 suites green (shared fixture 52, stub 4, directory-tree 12, template-clusters 7, new dedicated inbound-links 5 — MPA pagination, empty state, nonexistent URL, virtual scroll)
  • Reviewed via /qa-engineer, /product-manager, and /code-review medium; all actionable findings fixed (a few findings verified as pre-existing patterns shared with sibling viewer_* cursor functions, out of this PR's scope)

🤖 Generated with Claude Code

…ndLinks

A page's referrer count can reach the hundreds of thousands on a large
site, too large to embed in a single getPageDetail response — this
split lets callers page through referrers instead of loading them all
at once.

listInboundLinks reads viewer_anchor_facts (the read model) exclusively,
with no legacy fallback: answering "who links here" without the
dest_page_id index would require the exact full anchor_edges scan this
function exists to avoid. It throws an actionable error when the read
model is missing or stale, via the new requireViewerReadModel guard.

viewer_anchor_facts gains a first_text_id column (references text_refs,
schema v21->v22) so anchor text resolves without a second anchor_edges
round-trip. The new viewer-inbound-links-cursor/ module is a dedicated
per-table cursor (not a reuse of viewer-anchor-facts-cursor, whose
filterKey is scoped to status for broken/external link listing, not
destPageId) supporting forward/backward keyset pagination plus a direct
offset read for MPA page-number jumps, and a limit: 0 short-circuit for
count-only reads.

BREAKING CHANGE: PageDetail no longer has an inboundLinks field. Use
listInboundLinks(accessor, { url }) instead.
Mirrors the query package's split of inbound links out of
get_page_detail: exposes listInboundLinks as its own cursor-paginated
tool instead of embedding referrers (which can number in the hundreds
of thousands) in the page-detail payload. get_page_detail's
description no longer claims to include inbound links.
/api/pages/inbound-links (register-inbound-links-route.ts) exposes
listInboundLinks with no legacy fallback — in stub mode (a live crawl),
where viewer_anchor_facts can never exist, it responds
{ available: false } instead of calling a query that would only throw.

Page Detail no longer embeds the full inbound-links list: it fetches
only the referrer count (limit: 0) and links to the new
/pages/inbound-links view for the full, paginated list (MPA and
virtual-scroll both supported). Retry is disabled on both hooks since a
missing-read-model error won't resolve on retry.

Adds a dedicated e2e fixture/config (generate-inbound-links-fixture.mjs,
playwright.inbound-links.config.ts) since the shared fixture
deliberately never builds the viewer read model.
Mirrors get_page_detail's split: exposes listInboundLinks via the
existing query CLI, with --url required and --limit/--offset/--cursor/
--direction for pagination, matching resource-referrers/duplicates/
mismatches' flag conventions. Documents the sub-command and backfills
the --cursor/--direction rows the full-option table was missing for
those existing cursor-paginated sub-commands.
…e step

Updates the alias_of_id caller list, the inbound-link counting-grain
invariant (now listInboundLinks instead of getPageDetail.inboundLinks),
and adds the no-legacy-fallback / stub-mode-unavailable invariants for
listInboundLinks alongside directory-tree's existing precedent. Also
registers the new dedicated inbound-links Playwright suite as its own
step in the Viewer E2E workflow.
@YusukeHirao
YusukeHirao merged commit 756de13 into dev Jul 29, 2026
10 checks passed
@YusukeHirao
YusukeHirao deleted the worktree-inherited-tickling-swan branch July 29, 2026 05:40
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