fix(review-graph): one shared graph across worktrees, and say that semantic search degrades silently - #815
Merged
ViaJables merged 14 commits intoSep 8, 2026
Conversation
Documents, uploads and links showed as pills under the chat header while an attached knowledge base showed as a full-width bar above the composer, and a folder — which scopes the chat and is sent on every message — showed nothing anywhere. All three answer the same question about a conversation, so they share one row. The pill row was already the deliberate merge of chat uploads and file-browser selection; the KB bar was never brought along. This finishes that. - Type is icon + text tag + tint, never tint alone: --highlight-color is deploy-customisable and colour alone fails a colourblind reader. The KB and Folder tags preserve what the bar's "Knowledge Base: " prefix said. - Scope chips (KBs, capped at 3; folders) render first and always; the unbounded document tail collapses behind "+N more" past six, so a heavy library selection cannot push the knowledge base out of view. - FileBrowser reports selected folder titles alongside uuids, mirroring the document path, so a folder chip can name itself. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LKDBCBvedFaffXibXnFM2g
…feedback)
Six changes from a sit-down with a DGA working through the course:
1. Module 0 opens with the big picture — what AI is, why it's in research
administration, what the course delivers — before any terminology, and
teaches structured vs. unstructured data as the course's central idea
with no under-the-hood mechanics.
2. Jargon is defined in RA terms: JSON as "the fill-in-the-form format you
read, never write", Token via attachment-size limits, and Module 1's
pipeline lesson drops chunking/embedding/ChromaDB for "it reads the text
and builds an index, like the back of a book".
3. Every module now carries a worked example — eleven new walkthroughs
grounded in the modules' own sample documents.
4. Time estimates show everywhere they were missing: journey-map total,
locked cards, and the open module's header ("your place is saved").
5. An explicit pop-out button opens the course in its own browser window
for a second monitor, and the five cryptic mode icons get descriptive
tooltips with the pin group visually separated.
6. The Module 1 lab now says the Run button stays greyed out until a
document is ticked in the file browser — the exact stuck point.
Point 7 of the feedback (wrong duplicate-name message on import) is a
backend bug, fixed separately.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011tbi5K9tPm3DNKC5VJyXyQ
An Explore import told a user she "already had a workflow with the same name" while her personal library showed no such row — and she was right to disbelieve it. The uniqueness scope counts more than the personal library: a teammate's team-shared workflow, her own workflow filed under the Team tab, and a workflow whose library bookmark was removed while the object (and its name) lives on. The flat "already exists in your library" message claimed all of these were sitting in front of her. The 409 now resolves the conflicting workflow and names the case — team library (hers or a teammate's), or existing-but-unlisted — and quotes the stored name's exact capitalization, since the match is case-insensitive and "budget analyzer" is invisible to someone scanning for "Budget Analyzer". Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011tbi5K9tPm3DNKC5VJyXyQ
The recipe called `build`/`update` without `--repo`, so the tool discovered the repository from the working directory. Under `git worktree` that is the worktree, not the checkout holding the graph, so a run from one built a second index there containing only the files that worktree had touched -- and then answered every query from it reporting `"status": "ok"`. Measured on a working copy: four worktrees carried indexes of 7, 27, 26 and 6 files against a real 967. A partial index returns 0 for anything it never parsed and a true 0 is indistinguishable, so `callers_of` on a live symbol reports no callers, which reads as "safe to change". The first `git worktree list` entry is always the main working tree, so one graph now serves every worktree. ORIG_HEAD is read from that same tree, since that is the tree being indexed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KBMRynZ8iqpC7JKvwdBw9m
The page noted that semantic search "needs an extra" but not what happens without it. It does not fail: `search` returns `"status": "ok"` with `"search_mode": "fts"` -- keyword matching under a semantic name. A query whose terms appear literally still looks right, so the degradation is invisible until a conceptual query quietly returns nothing, which reads as "there is nothing there". The install line now takes `[embeddings]` and the setup adds the separate `embed` step, since building the graph does not populate vectors. Three `search_mode` values are documented -- `semantic`, `hybrid` (also correct) and the degraded `fts`. Also documents two traps that belong to the tool rather than the recipe: `detect-changes` reads its diff from the same `--repo` path it reads the graph from, so from a worktree it reports on main; and a symbol the graph never indexed is answered exactly like one with no callers, which only 2.3.8+ annotate with a `confidence` field. The network note now separates the local model -- downloaded once from Hugging Face, then run on-device, sending no repository content anywhere -- from the optional remote backends, which stay off for the reason already given. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KBMRynZ8iqpC7JKvwdBw9m
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KBMRynZ8iqpC7JKvwdBw9m
…ktree
Three things on top of the worktree fix, which is right.
`awk '/^worktree /{print $2}'` splits on whitespace, so a checkout at
"/Users/me/My Repos/vandalizer" resolved to "/Users/me/My". That is not a
cosmetic truncation: the graph.db test then fails, the recipe takes the
build branch, and `build --repo /Users/me/My` points the tool at a
directory outside the repository. `sed -n '1s/^worktree //p'` takes the
rest of the line whatever it contains.
The Setup block's `code-review-graph embed` carried no --repo, so it had
exactly the bug this PR fixes: run from a worktree it discovers the
worktree, creates a second graph there -- the directory the paragraph
below tells you to hunt down and delete -- and the shared graph never
gets vectors, so search keeps answering in fts mode with nothing saying
why. The PR's own defect survived its own fix.
One graph shared by every worktree means concurrent refreshes now contend
for one SQLite file, where per-worktree databases could not. This repo is
routinely worked from several worktrees at once, so that is the normal
case rather than an edge one. The recipe takes an flock where one exists;
macOS ships none, so the docs say to refresh one worktree at a time
there. `set --` builds the argument list once so the locked and unlocked
branches cannot drift.
The docs also now say what pinning --repo costs: the graph reflects the
main checkout's HEAD, so a symbol added on a worktree branch is absent --
and an absent node is indistinguishable from a real "no callers" answer,
which is the failure this tool is most likely to mislead a reviewer with.
Verified: the sed keeps "/Users/me/My Repos/vandalizer" whole where the
awk truncates it; the path survives as a single argument; make parses the
recipe; review-graph is still a prerequisite of nothing and CI still
never runs it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CtRYNNEdrq6SPyPt8PjH1v
…ot the workspace /certification is a redirect that opens the panel in whatever mode the origin window last persisted, so the 1080x860 pop-out showed the entire workspace with a floating panel. The pop-out button now asks for fullscreen via ?panel=fullscreen and the redirect honours it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qk88vvuDRJz5Nft2pRNpeY
… own workflow bookmark
LibraryItem.find_one({item_id}) matched any bookmark of that object,
including a teammate's team-library row, so a workflow the user had
removed from their library could still be reported as listed in it.
Filter by kind and by who added it.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qk88vvuDRJz5Nft2pRNpeY
…tes up front mkdir -p runs before the tool's first build, so the tool may never write its own .gitignore into the directory; without this the flock lock file shows up as untracked. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qk88vvuDRJz5Nft2pRNpeY
This was referenced Sep 8, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two defects in the optional
review-graphtarget from #704 / #706, both of whichfail by succeeding rather than by erroring — so neither is visible from
inside a normal review.
make review-graphbuilds a second, partial graph when run from a git worktree.The recipe called
build/updatewithout--repo, so the tool discovers therepository from the working directory. Under
git worktreethat is theworktree, not the checkout holding the graph.
The documented setup leaves semantic search silently switched off.
docs/review-graph.mdsaid semantic search "needs an extra" but not whathappens without it. It does not fail.
Changes
Makefile—review-graphnow pins--repoto the firstgit worktree listentry, which is always the main working tree, so one graph serves every worktree.
ORIG_HEADis read from that same tree, since that is the tree being indexed.The not-installed message now names the
[embeddings]extra.docs/review-graph.md—'code-review-graph[embeddings]'and adds the separatecode-review-graph embedstep, since building the graph does not populatevectors.
three normal
search_modevalues:semantic,hybrid(literal hits present,blended scoring — also correct), and
fts(degraded).like one that genuinely has no callers; 2.3.8+ annotate this with a
confidencefield, earlier versions return a barenot_found.detect-changesreads its diff from the same--repopath it reads the graph from — so from a worktree it reports on themain branch unless
GIT_DIR/GIT_WORK_TREEare set. Documented rather thanfixed, because it belongs to the tool.
Hugging Face, then run on-device, sending no repository content anywhere) from
the tool's optional remote embedding backends, which stay off for the
reason already given in that section.
CHANGELOG.md— one entry under Unreleased → Fixed.Why this matters
Both failures return
"status": "ok".A partial index returns
0for anything it never parsed, and a true0isindistinguishable —
callers_ofon a live symbol reports no callers, which readsas "safe to change". Measured on a working copy: four worktrees carried indexes
of 7, 27, 26 and 6 files against a real 967, every one answering
ok.Without embeddings,
searchreturns"search_mode": "fts"— keyword matchingunder a semantic name. A query whose terms appear literally still looks right, so
the degradation stays invisible until a conceptual query quietly returns nothing,
which reads as "there is nothing there" rather than "this search cannot do that".
Test Plan
make ci) — backend 4359 passed / 161 skipped, coverage 64.25% (gate 50%), tier-1 integration 14 passed;make frontend-cipasses. Note:frontend-typecheckfirst failed here withnpm: not foundbecause npm was not on my shellPATH; it passes once it is. Unrelated to this change.make release-check) — not run, and not applicable: this changes an optional developer target and its documentation. No packaging or deployment path is touched.make review-graphfrom a worktree with the fix and confirmed no.code-review-graph/is created there and the shared index is the one updated; confirmedsearch_modemoves fromftstosemantic/hybridafter installing the extra and runningembed; confirmed on 2.3.8 that an unindexed symbol returns theconfidencefield and on 2.3.7 that it does not.CHANGELOG.mdfor user-facing or operator-facing changesREADME.md,DEPLOY.md,OPERATIONS.md,RELEASE_CHECKLIST.md) if the install or release path changed — not applicable: none of those files mention the graph (verified: 0 matches).CONTRIBUTING.mdnames the target but defers the install line todocs/review-graph.md, which is updated here.Related Issues
No issue to close — this is a follow-up to #704 / #706, both already closed.
🤖 Generated with Claude Code
https://claude.ai/code/session_01KBMRynZ8iqpC7JKvwdBw9m