Skip to content

fix(review-graph): one shared graph across worktrees, and say that semantic search degrades silently - #815

Merged
ViaJables merged 14 commits into
ui-insight:mainfrom
arhyneRWU:fix/review-graph-worktree-and-search-mode
Sep 8, 2026
Merged

fix(review-graph): one shared graph across worktrees, and say that semantic search degrades silently#815
ViaJables merged 14 commits into
ui-insight:mainfrom
arhyneRWU:fix/review-graph-worktree-and-search-mode

Conversation

@arhyneRWU

Copy link
Copy Markdown
Contributor

Summary

Two defects in the optional review-graph target from #704 / #706, both of which
fail by succeeding rather than by erroring — so neither is visible from
inside a normal review.

  1. make review-graph builds a second, partial graph when run from a git worktree.
    The recipe called build/update without --repo, so the tool discovers the
    repository from the working directory. Under git worktree that is the
    worktree, not the checkout holding the graph.

  2. The documented setup leaves semantic search silently switched off.
    docs/review-graph.md said semantic search "needs an extra" but not what
    happens without it. It does not fail.

Changes

Makefilereview-graph now pins --repo to the first git worktree list
entry, which is always the main working tree, so one graph serves every worktree.
ORIG_HEAD is 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

  • Setup installs 'code-review-graph[embeddings]' and adds the separate
    code-review-graph embed step, since building the graph does not populate
    vectors.
  • New What it gets wrong entry for the silent degradation, documenting all
    three normal search_mode values: semantic, hybrid (literal hits present,
    blended scoring — also correct), and fts (degraded).
  • New entry noting that a symbol the graph never indexed is answered exactly
    like one that genuinely has no callers; 2.3.8+ annotate this with a
    confidence field, earlier versions return a bare not_found.
  • A worktree note, including that detect-changes reads its diff from the same
    --repo path it reads the graph from — so from a worktree it reports on the
    main branch unless GIT_DIR/GIT_WORK_TREE are set. Documented rather than
    fixed, because it belongs to the tool.
  • The network note now separates the local model (downloaded once from
    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 0 for anything it never parsed, and a true 0 is
indistinguishable — callers_of on a live symbol reports no callers, which reads
as "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, search returns "search_mode": "fts" — keyword matching
under 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

  • Shared checks pass (make ci) — backend 4359 passed / 161 skipped, coverage 64.25% (gate 50%), tier-1 integration 14 passed; make frontend-ci passes. Note: frontend-typecheck first failed here with npm: not found because npm was not on my shell PATH; it passes once it is. Unrelated to this change.
  • Release check passes if packaging or deployment changed (make release-check) — not run, and not applicable: this changes an optional developer target and its documentation. No packaging or deployment path is touched.
  • Manually tested the affected feature(s) — ran make review-graph from a worktree with the fix and confirmed no .code-review-graph/ is created there and the shared index is the one updated; confirmed search_mode moves from fts to semantic/hybrid after installing the extra and running embed; confirmed on 2.3.8 that an unindexed symbol returns the confidence field and on 2.3.7 that it does not.
  • Updated CHANGELOG.md for user-facing or operator-facing changes
  • Updated deploy/release docs (README.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.md names the target but defers the install line to docs/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

ViaJables and others added 7 commits September 1, 2026 11:18
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
ViaJables and others added 7 commits September 8, 2026 12:24
…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
@ViaJables
ViaJables merged commit 5050020 into ui-insight:main Sep 8, 2026
11 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.

2 participants