Docs
See what a decision touches before you change it
RepoOps parses every cross-reference in your repository brain and shows both directions per file: what links to it, what it links to, each with the source line and a snippet. No model call, so the answer is the same every time.
For: the engineer about to edit a decision or an error entry, and the owner who wants to know which knowledge nobody cites
What it does, and why it helps
The walk reads three directories in order: the brain root, then pull-requests/, then sessions/. In each markdown file it looks at one line at a time and captures three kinds of reference: a markdown link [text](file.md), a [[wikilink]], and a bare file.md mention in prose. A target resolves by base name against the files the walk actually read, so a reference to something it did not read is dropped rather than drawn as a dangling edge. Self-links are skipped, and one source line contributes one edge per target however many times it names it.
Each reference carries its line number and the trimmed line as a snippet, so an inbound row links to /brain/{repo}/{from}#L{line} and lands on the sentence that wrote the link. A file with no inbound and no outbound reference is flagged as an orphan. The walk stops at 2,000 files per repository and reads the first 500 KB of each file, and the tab says so: anything past those bounds is unread, so the totals are a floor and a file can read as an orphan when the file linking to it was never opened.
The pain. You are about to rewrite a decision, and the file itself does not say who is standing on it. Grepping the brain finds the string, not the direction, and it tells you nothing about the files that no longer matter to anyone.
The point of view. Knowledge is a graph whether or not anyone draws it. Read who cites a file before you change it, and treat a file nobody cites as a question rather than a verdict.
What gets easier. Two questions in one open: what does this file depend on, and what depends on it. Each answer is a row with a file, a line and a snippet, and the inbound rows are links, so following a citation back is a click.
When it helps. Editing or retiring a brain file, auditing brain hygiene after a long run of sessions, or handing a repository to someone who has not read it.
Its limits. It reads prose references only, never source code, so it is not a code impact graph. It is per repository: there are no cross-repo edges. Targets resolve by base name, so two files with the same base name in different directories collapse into one target. The bounds are fixed in the module and the totals are a floor, never a count.
Understand it in 30 seconds
Read the narration
- 0:00 You are about to change a decision.
- 0:02 Who else is standing on it?
- 0:06 RepoOps reads every link in the brain and shows both directions.
- 0:10 No model, no guess.
- 0:13 Each row names the file, the line and the snippet.
- 0:16 Files past the caps go unread, so the totals are a floor.
- 0:23 Read who cites it, then change it.
- 0:25 The guide covers the caps.
Synthetic example. Read the guide
Where to find it
Where to find it
- Desktop:
localhost:4000, then Memory in the sidebar, then Relationships under All tools, in the Decisions and errors group. - Hosted:
repoops.ai/team/relationships, from Memory in the sidebar, then Relationships under All tools, in the Decisions and errors group. - Keyboard: ⌘ K, then type “Relationships”.
When to use it
Before you rewrite a decision
Situation. A convention in decisions.md is about to change, and several sessions and pull request records were written against it.
What you do. Open Relationships, type the file name into the filter box, and expand the card. Read the inbound group, then follow a row to the line that cited it.
What you see. The card head shows the counts as pills, one for inbound and one for outbound. Every inbound row names the citing file, its line, and the trimmed line as a snippet, and links to that line in the brain viewer.
What it establishes. You know which records you are invalidating, and you know it is a lower bound: files past the 2,000-file walk or past the first 500 KB of a file were not read.
Finding knowledge nobody cites
Situation. The brain has grown for months and some of it may be dead. You want the files that no other file references and that reference nothing.
What you do. Open the tab with no filter and read the sub line for the repository totals, then look for the cards carrying the orphan pill.
What you see. The sub line reads as repos, brain files scanned with the 2,000 cap named, edges, and orphans. An orphan card is drawn with a dashed border and its body shows both empty-state sentences.
What it establishes. A list of candidates to fold into a better-referenced file or delete. An orphan is a prompt to look, not proof the file is unused: the reference may sit in a file the walk did not read.
Before you start
- Supported versions
- RepoOps desktop v0.3.1, the release this guide was read against. Nothing else needs installing: the walk is plain file reading and string parsing in the aggregator process.
- Where it runs
- Local: Memory, then Relationships under All tools, with Relationship graph as its subtab; the palette and its own URL reach it too. Hosted: Memory, then Relationships under All tools (repoops.ai/team/relationships), over your team's published or GitHub-pulled brain snapshot.
- Permissions
- Local: none. The tab reads the mirrored brain directory of every repository in repos.config.json. Hosted: a signed-in team member, narrowed by per-member repository access and by the rule that a personal repository never rolls up to a team.
- Connections
- Local: none, and no API key. Hosted: either the Brain snapshot publisher turned on with this device bound, or a GitHub repository connected so RepoOps pulls its brain server-side.
- Plan
- The local tab is free. Publishing a brain snapshot needs a paid plan: the ingest route refuses a free or lapsed team with 402 and the message that a paid plan (Solo Hosted or above) is required to publish a brain snapshot.
Configure it
- Open it from Memory.
Memory lists Relationships under All tools, and the link opens it on the repository in view. The palette (type Relationships) and its own URL open it too. For a permanent sidebar row, turn on Show power-user tabs in Settings under Advanced.
- Choose the repositories you want graphed.
The shell opens the tab with repo and repos both set to the repository you have selected, so you see one repository at a time. Open /brain-links.html directly with no query and the endpoint graphs every tracked repository, one section each.
- Open a file and read both directions.
Click a card head to expand it. The body has two groups: what links to this file, and what this file links to. Each row is the other file, its line, and the snippet. Inbound rows link to that line; outbound rows link to the target file.
- Filter by name when the list is long.
The filter box is placeholdered Filter by file name... and matches anywhere in the path, case-insensitively. Cards you have expanded stay expanded while you type, because the open set is kept across re-renders.
- Publish the brain only if the team page needs it.
Settings, the section headed What this machine publishes to your team, then the Brain snapshot switch. It is off by default and also needs a bound device and cloud sync. Turning it on sends the redacted top-level brain markdown, which is what the hosted graph is built from.
| Setting | Where | A sensible choice | Why it matters |
|---|---|---|---|
?repos=<id,id> | the Relationships tab URL, set by the dashboard shell | whatever the shell sets (the selected repository) | Comma separated. Omit it and the endpoint graphs every tracked repository; ?repo= takes a single id. |
Show power-user tabs | Settings, Advanced | on if you want a permanent sidebar row | Off means the row is drawn only while the tab is active. The palette and the direct URL work regardless. |
Brain snapshot | Settings, What this machine publishes to your team | off unless a team page should show the graph | The only switch that moves brain content off the machine. It needs a bound device and a paid plan. |
maxFiles | lib/brain-link-graph.mjs, no control | 2000 (the default the endpoint uses) | The walk stops there, in directory order, so files past it are unread and the totals are a floor. |
maxBytes | lib/brain-link-graph.mjs, no control | 500000 (the default the endpoint uses) | Only the first 500 KB of a file is parsed, so a link written past that point contributes no edge. |
MAX_BRAIN_FILES | the hosted page module, no control | 2000 | The hosted read budget. A read that fills it shows Files and Links as lower bounds and withholds the orphan count. |
?repo=<key> | repoops.ai/team/relationships | omit it | Without it the hosted page shows the most connected repository; with it you pick one, and an unknown or unpermitted key falls back to the default. |
REPOOPS_PREWARM | the data directory's .env | unset (prewarm on) | 0 skips the boot warm of /api/brain-graph, so the first open after a restart pays the cold walk instead of reading a warm cache. |
What you should see
A brain well under the bounds
Configuration. One tracked repository, fewer than 2,000 markdown files across the brain root, pull-requests/ and sessions/.
Expect. The sub line names the repository count, the files scanned with the 2,000 cap, the edge count and the orphan count. Cards are sorted by inbound count, highest first, then by path.
Verify. Expand the most-cited card and check one inbound row against the file it names: the line number should land on the sentence in the snippet. The freshness line under the heading reads updated just now on a fresh compute.
A brain past the bounds
Configuration. A repository whose brain root, pull-requests/ and sessions/ hold more than 2,000 markdown files, or files larger than 500 KB.
Expect. The scan stops at 2,000 files in that directory order, so sessions/ is the part that gets cut first. Counts are lower bounds and some files read as orphans because the files linking to them were not opened.
Verify. Compare the scanned count in the sub line with the number of markdown files on disk. The legend beside the filter box states the bounds and that the totals are a floor.
The hosted page after a first publish
Configuration. Brain snapshot on, device bound, paid plan, one repository published.
Expect. A smaller graph than the desktop shows. The publisher sends top-level brain markdown only, not sessions/ or pull-requests/, so every edge that ran through those records is absent. Files with no link at all are counted as orphans but not listed as cards.
Verify. Read the three figures at the top of the hosted page. If the read filled its 2,000-file budget, Files and Links are prefixed with at least and Orphans reads not counted, with the reason printed underneath.
Data and cost
- What is captured
- Nothing new. The graph is derived on each request from the markdown already in the repository's .claude/brain directory, and nothing is written back to the brain.
- Who can see it
- Local by default. The desktop tab reads your own mirrored files over localhost. The hosted page shows only a team's own snapshot rows, bounded by team id and narrowed by per-member repository access, with personal repositories excluded before the read.
- How long it is kept
- No retention setting, because the feature stores no history. The last successful response is kept in the per-machine SQLite store (data.db in the data directory, rows prefixed apilkg:), skipped above 2,000,000 bytes and capped at 200 rows across all cached endpoints, so a restart paints the previous answer while a fresh walk runs. In memory the graph is memoized per brain directory, 64 directories at most, invalidated by the modification time and entry count of the three scanned directories. Hosted, a published file stays in the brain snapshot store until a later version of that repository supersedes it.
- What leaves the machine
- None from the local tab. The walk, the parse and the render happen on your machine and no model is called. The Brain snapshot publisher is the only path that moves this content, and it is off by default: it sends the redacted top-level brain markdown, at most 500 files, 1 MiB per file and 5 MiB per snapshot, and the server re-runs the same redaction before storing.
- What it costs
- No model call, no API key, no metered spend. The cost is file reading and string parsing, which is why the result is identical on every run over the same files.
When the result differs
| Symptom | Likely cause | Next action |
|---|---|---|
| There is no Relationships row in the sidebar. | It is registered in the power-user subgroup, which the shell holds back. | Open Memory and choose Relationships under All tools, open it from the palette, or turn on Show power-user tabs in Settings under Advanced. |
| A file you know cites this one is missing from the inbound group. | The citing file was past the 2,000-file walk, the citation sat past the first 500 KB of it, or it lives outside the brain root, pull-requests/ and sessions/. | Read the sub line for the scanned count, and treat the group as a floor. The empty-state sentence names the same three directories and the cap. |
| A file reads as an orphan although it clearly has links. | Its targets were not among the files the walk read, so the edges were dropped rather than left dangling. A markdown link inside backticks is also skipped, because inline code spans are blanked before the markdown-link pass. | Check whether the target file was scanned, and move a real citation out of the code span. |
| A path-style mention such as .claude/brain/decisions.md produces no edge. | The bare-mention pattern needs a line start or a space immediately before the file name, so a mention inside a path does not match. | Write it as a markdown link or a wikilink, which both resolve by base name. |
| An edge points at a file with the right name in the wrong place. | Targets resolve by base name, so two files sharing a base name in different directories collapse into one target. | Read the from and to paths on the row, and rename one of the two files if the collision matters. |
| The hosted page says No snapshot yet. | The team has no published or GitHub-pulled brain for any repository you can see. | Turn on the Brain snapshot switch on a bound device with a paid plan, or connect a GitHub repository so RepoOps pulls the brain server-side. |
| The hosted graph is much smaller than the desktop one. | The publisher sends top-level brain markdown only, never sessions/ or pull-requests/, so every edge through those records is absent. | Expected. Use the desktop tab when you need the session and pull request citations. |
- Disable
- Turn Show power-user tabs off to hide the row, and the Brain snapshot switch off to stop brain content leaving the machine. The local computation itself has no switch.
- Roll back
- Not provided, and there is nothing to roll back: the feature reads files and writes none. The markdown it reads is versioned by git in your own repository.
- Revoke access
- Turn the Brain snapshot switch off, or disconnect the device in the desktop app, which forgets the binding and revokes the device token on the server. Hosted visibility is narrowed further by per-member repository access and by the personal-repository exclusion, both applied before the read.
- Delete
- Not provided. No route deletes a stored graph or a published snapshot. The local cached response is a row prefixed apilkg: in data.db in the data directory; the hosted copy is rows in the brain snapshot store, replaced when a later version of that repository publishes, and pruned for a GitHub-sourced repository when a file disappears upstream.
Related tasks
Maintenance evidence
- Feature id
brain-links(spine leafrelationships)- Owner
- Brain intelligence program, P3 (the brain link graph and backlinks), with the hosted surface under Hosted UI parity W2 and Cloud-hosted localapp phases B.1 and B.2. Guide: LDG-0717.
- Supported product version
- RepoOps v0.3.1
- Last verified
- 2026-09-15, read against origin/main at 52366bb6d; labels read from the served tab source (public/brain-links.html, public/settings.html) and from the hosted page module, not from a running instance. The caps, the cache bounds and the publish gate were read from the modules cited above.
- Example fixtures
- No fixture file. lib/brain-link-graph.test.mjs (14 cases) pins the parser over in-memory corpora: the three reference kinds, base-name resolution, dropped dangling references, per-line dedupe, orphan counting, sort order and snippet trimming. lib/brain-link-graph.memo.test.mjs (2 cases) pins the stat-fingerprint memo against a temporary brain directory. website/lib/brain-link-graph.test.ts (3 cases) holds the hosted port to the same contract, and website/lib/github/brain-link-index.integration.test.ts (11 cases) covers the cached indexer, tenant isolation and the push-webhook refresh against a real in-process Postgres.
- Source references
lib/brain-link-graph.mjs,lib/markdown-links.mjs,lib/routes/brain.mjs,lib/canonical-tabs.mjs,lib/api-cache.mjs,lib/api-cache-disk.mjs,lib/prewarm.mjs,lib/brain-publisher.mjs,lib/routes/publish-toggles.mjs,public/brain-links.html,public/settings.html,website/app/team/(home)/relationships/page.tsx,website/lib/github/brain-link-index.ts,website/app/api/brain/ingest/route.ts- Documentation review
- Independent review requested on the slice pull request; not yet recorded.
- Video review
- Narrated story rendered and published 2026-09-26 (render d5a484a7b111, LDG-1014) with the breadcrumb Memory, which lists the feature under Moved here, checked against main at 7aab4cd82 with LDG-1014 part 1. Six frames, the captions and the transcript were reviewed by the authoring agent, not an independent reviewer; the audio was not listened to by a person. Narration is the provisional Windows voice until LDG-0721.
Last updated