Skip to content

related_notes: the notes around a note, from the tree's links and one search - #2197

Merged
jewunetie merged 7 commits into
mainfrom
feat/related-notes
Oct 26, 2026
Merged

jewunetie merged 7 commits into
mainfrom
feat/related-notes

Conversation

@jewunetie

Copy link
Copy Markdown
Collaborator

An agent can now ask what is around a note: fetch <note> { related: true, limit } (or related_notes { path, limit } by name). Each line names a note and why: linked_from_here (this note links to it), links_here (it links to this note), related_text (its text is like this note's title and headings). Linked notes rank first, then by text rank. This replaces the path-keyed link graph on the link-graph/* branches, which no longer fit id-keyed notes.

How

  • Links and backlinks come from the tree's nodeLinks through a new ask: "neighbors" on /gateway/tree/facts (nodeFacts.neighbors, lib/nodes/neighbors.ts). Names resolve the way the map resolves them, among visible notes only. A queued move is folded in.
  • Similar text is one searchVisibleNotes call, so it reads the search database where there is one. It is skipped for an encrypted seed.
  • The seed is opened by read_note itself, which now also hands the note it opened to the caller as a non-enumerable field.
  • Decision record: docs/decisions/search/workspace-map.md, "Related notes are the map's links for one note, plus one search".

Privacy (each has a test and a sabotage record)

  • A hidden seed answers exactly what an absent one does, on the gateway and in Convex.
  • Hidden notes never appear, and never change a team reader's answer, however many link to the seed. Each side reads at most 4,000 rows and a side past that answers like any other, because a reported cap would count hidden rows.
  • complete depends only on the seed's own links having been read and on moves among notes the reader can see; a move inside a private folder changes nothing.

Known limits

  • A note with more than 4,000 links on one side loses the rest silently.
  • A path link the map resolves only through the file-name fallback is an outgoing link here, not a backlink.
  • Shared targets, shared URLs, folder nearness and meaning search are not used yet.

Evidence (local)

  • Gateway (apps/mcp, node test/run.mjs): all 7 entry points pass; 2174 tests, 0 failures, 7 skipped.
  • Convex (npx vitest run): 549 files, 6873 passed, 3 skipped.
  • pnpm architecture: 0 errors. apps/convex typecheck passes; apps/mobile typecheck has the same 113 errors as main (no mobile files changed).
  • Not run: CI, staging.

🤖 Generated with Claude Code

https://claude.ai/code/session_01GqYLoY9HLUG3EJ2xKySxMF

jewunetie and others added 7 commits October 26, 2026 00:29
`ask: "neighbors"` on /gateway/tree/facts reads the note's nodeLinks rows on
both sides and resolves them the way the map does: a path, else the one
visible note with that file name; a bare [[name]] beside the linking note
first, else the one visible note carrying it. Every path passes canSee at the
reader's clearance, a seed they may not see answers what an absent one does,
and the answer says it may be short when the seed's links are unread or a
side passed 200 rows.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GqYLoY9HLUG3EJ2xKySxMF
A 200-row cap whose overflow marked the answer short counted rows from notes
the reader may not see, telling a team reader when many hidden notes link to
a note they can. Every row is read, as candidates reads them, and `complete`
depends only on the seed's own links having been read.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GqYLoY9HLUG3EJ2xKySxMF
…y other

An unbounded read could fail at Convex's query limit only for a note many
hidden notes link to, which a reader could tell from the refusal. Each side
reads at most 4,000 rows and a side past that answers normally; a hub that
large loses the rest of its links silently.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GqYLoY9HLUG3EJ2xKySxMF
…d notes like it

Reached as fetch <note> { related: true, limit } and by name. The seed is
opened by read_note itself, so a note the caller may not see is refused as an
absent one is, before the tree is asked about it. Links and backlinks come
from the tree's facts (ask: "neighbors") and are judged again by the gateway's
canSee; similar text is one search over the seed's title and headings through
searchVisibleNotes, skipped for an encrypted seed. Linked notes rank first,
then by text rank, then by path; a part that cannot answer leaves the rest
and the answer ends "may be incomplete". Meaning search is not used yet.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GqYLoY9HLUG3EJ2xKySxMF
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GqYLoY9HLUG3EJ2xKySxMF
…ructurally

From self-review: a note mid-move was looked up at a key its link rows do not
use, so its answer came back empty and called itself complete. Link rows are
now read at the names the tree shows (a queued move folded in, as the map
does) and any queued move marks the answer may-be-short. Path links are
percent-decoded as the map decodes them. read_note hands the note it opened
(path, text, encryption) to related_notes as a non-enumerable field, so a
path holding U+2028 is no longer cut short by parsing the printed header.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GqYLoY9HLUG3EJ2xKySxMF
Marking every answer short while any move was queued told a team reader that
a move was under way somewhere they cannot see, such as inside a private
folder. Only a move of the seed, or of a link row whose note this reader sees
at its new name, marks the answer short now.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GqYLoY9HLUG3EJ2xKySxMF
@jewunetie
jewunetie merged commit 1865d1a into main Oct 26, 2026
36 checks passed
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