Pond-wide link graph endpoint #111

Closed
opened 2026-07-14 09:32:44 +02:00 by fable-5 · 1 comment
Collaborator

Context

The knowledge graph needs one pond-scoped read of nodes and wikilink edges. Backlinks and phantom links already live in apps/api/src/links/ with per-page permission filtering — this is an extension of that service, not a new module.

Scope

  • GET /ponds/:pondId/links (links.controller.ts, @RequiresPondRole('reader')) returning:
    • nodes: readable, non-trashed pages (id, title, slug, labelIds),
    • edges: resolved page_links rows as { from, to } — an edge is included only when both endpoints are readable,
    • phantoms: { targetSlug, referencedBy: pageId[] } with unreadable referrers dropped (a phantom disappears entirely when no readable referrer remains).
  • Filter through the same permissions.filterPages path the existing backlinks()/phantomLinks() use.
  • Shared PondGraphView types in packages/shared/src/links.ts.
  • DB tests including permission slicing (label-restricted reader sees only their slice; no edge or phantom leaks an unreadable page's existence) and trashed-page exclusion.

Acceptance criteria

  • Response contains exactly the caller-readable subgraph; trashed pages and their links are absent.
  • A label-scoped reader gets no edge, node, or phantom referencing pages outside their scope.
  • Payload size is one query round-trip per collection (no N+1 per page).

Technical notes

  • page_links already has everything (from_page_id, nullable to_page_id, target_slug); no schema change.
  • Independent of the page-hierarchy issues — can be built in parallel.

Dependencies

None.

Size

~0.5–1 day.

## Context The knowledge graph needs one pond-scoped read of nodes and wikilink edges. Backlinks and phantom links already live in `apps/api/src/links/` with per-page permission filtering — this is an extension of that service, not a new module. ## Scope - `GET /ponds/:pondId/links` (`links.controller.ts`, `@RequiresPondRole('reader')`) returning: - `nodes`: readable, non-trashed pages (`id`, `title`, `slug`, `labelIds`), - `edges`: resolved `page_links` rows as `{ from, to }` — an edge is included only when **both** endpoints are readable, - `phantoms`: `{ targetSlug, referencedBy: pageId[] }` with unreadable referrers dropped (a phantom disappears entirely when no readable referrer remains). - Filter through the same `permissions.filterPages` path the existing `backlinks()`/`phantomLinks()` use. - Shared `PondGraphView` types in `packages/shared/src/links.ts`. - DB tests including permission slicing (label-restricted reader sees only their slice; no edge or phantom leaks an unreadable page's existence) and trashed-page exclusion. ## Acceptance criteria - [ ] Response contains exactly the caller-readable subgraph; trashed pages and their links are absent. - [ ] A label-scoped reader gets no edge, node, or phantom referencing pages outside their scope. - [ ] Payload size is one query round-trip per collection (no N+1 per page). ## Technical notes - `page_links` already has everything (`from_page_id`, nullable `to_page_id`, `target_slug`); no schema change. - Independent of the page-hierarchy issues — can be built in parallel. ## Dependencies None. ## Size ~0.5–1 day.
fable-5 added this to the M12 — Page hierarchy & knowledge graph milestone 2026-07-14 09:32:44 +02:00
fable-5 added the
backend
label 2026-07-14 09:32:44 +02:00
Author
Collaborator

Implemented in e957c2a. GET /ponds/:pondId/links returns the caller's readable slice in one read: nodes (id/title/slug/labelIds), resolved edges deduplicated per direction (rename leftovers collapse), and phantoms with their referrer ids. An edge survives only when both endpoints are readable; a phantom disappears with its last readable referrer; trashed pages are excluded. DB tests cover the owner graph, the label-DENY reader slice, dedupe, and trash exclusion.

Implemented in e957c2a. `GET /ponds/:pondId/links` returns the caller's readable slice in one read: nodes (id/title/slug/labelIds), resolved edges deduplicated per direction (rename leftovers collapse), and phantoms with their referrer ids. An edge survives only when both endpoints are readable; a phantom disappears with its last readable referrer; trashed pages are excluded. DB tests cover the owner graph, the label-DENY reader slice, dedupe, and trash exclusion.
Sign in to join this conversation.
No project
No Assignees
1 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: stwaidele/dorfteich#111
No description provided.