Skip to content

fix(search): break intent ties on identity instead of node id - #110

Merged
tae2089 merged 2 commits into
mainfrom
ticket-106
Aug 11, 2026
Merged

fix(search): break intent ties on identity instead of node id#110
tae2089 merged 2 commits into
mainfrom
ticket-106

Conversation

@tae2089

@tae2089 tae2089 commented Aug 11, 2026

Copy link
Copy Markdown
Owner

Closes #106.

What changed

  • internal/domain/graph/identity.go (new): Identity and CompareIdentity — file path, qualified name, kind, namespace, start line. Those five are also the node uniqueness index, so the key is total.
  • rank.compareIdentity now delegates to it; the reranker's order is unchanged.
  • intentrank.Doc carries the identity fields and the score-tie sort uses graph.CompareIdentity instead of node id.
  • Both MatchIntent queries join nodes to carry that identity, which also drops reason rows whose node is gone instead of spending a candidate slot on one that cannot be scored.

Why

The same declarations re-indexed under different ids answered in a different order, and rerank and intent scoring disagreed about what "same score" means.

Measurement

Comparing the two tie-breaks over every golden question against the live graphs:

corpus reasons questions answered different order same top hit
ccg 1740 91 78 19 18 of 19
context-diary 110 21 18 0

The golden report does not move, and cannot: intent_candidates.json freezes the post-Rank answer, so make search-eval never runs intentrank.Rank. ccg stays at ALL 0.747 (127/170) 48 62 0.649, ANSWERABLE 0.840.

Deliberately out of scope

Retrieval's matchRows keeps its own key order (rank, qualified name, file path, id). Flipping it decides which tied rows survive the LIMIT, so it moves measured ranking numbers and needs a -capture-golden recapture plus a re-judgment landing as its own change. Both comments now say what this change did and what that later one still owes.

Verification

  • CGO_ENABLED=1 go test -tags "fts5" ./... -count=1 — green
  • go vet -tags fts5 ./... — clean
  • gofmt -l . — only the two pre-existing testdata/binding_gap/go fixtures
  • make search-eval — unchanged on all four corpora

🤖 Generated with Claude Code

tae2089 and others added 2 commits August 11, 2026 16:39
seedTiedIntentFixture always wrote the tied declarations in ascending
order, which makes file order and id order the same sequence. A test that
wants to prove the answer does not depend on which id a declaration got
needs to seed the same corpus the other way round.

Split the loop out as seedTiedIntentNodes(indexes) and keep the ascending
sequence as tiedIntentNames(count). No test changes behaviour.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two recorded reasons that score the same were ordered by node id, so the
same declarations re-indexed under different ids answered in a different
order. Rerank already ordered by identity — file path, qualified name,
kind, namespace, start line — so the two layers of one answer disagreed
about what "same score" means.

Move that key into the domain as graph.Identity and graph.CompareIdentity,
have rank.compareIdentity delegate to it, and widen intentrank.Doc with the
identity fields so intent scoring breaks its ties the same way. Both
MatchIntent queries now join nodes to carry the identity, which also drops
reason rows whose node is gone rather than spending a candidate slot on one
that cannot be scored.

Measured on the live graphs, comparing the two tie-breaks over every golden
question: ccg (1740 recorded reasons) answers 78 of 91 questions, 19 of
those in a different order, 18 of the 19 keeping the same top hit;
context-diary (110 reasons) answers 18 of 21, none of them differently.
The golden report does not move, and cannot: intent_candidates.json freezes
the post-Rank answer, so `make search-eval` never runs intentrank.Rank.
ccg stays at ALL 0.747 (127/170) 48 62 0.649, ANSWERABLE 0.840.

Retrieval's matchRows keeps its own key order on purpose — flipping it
decides which tied rows survive the LIMIT and so moves measured numbers.
Both comments now say what #106 did and what a later change still has to
recapture and re-judge.

Closes #106

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@tae2089
tae2089 merged commit d06174e into main Aug 11, 2026
2 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.

search: intent ranking breaks ties by node id, so answers depend on insertion order

1 participant