Skip to content

fix(search): point a match at where it actually is - #368

Open
fstubner wants to merge 1 commit into
mainfrom
fix/match-locator-offset
Open

fix(search): point a match at where it actually is#368
fstubner wants to merge 1 commit into
mainfrom
fix/match-locator-offset

Conversation

@fstubner

@fstubner fstubner commented Sep 9, 2026

Copy link
Copy Markdown
Owner

A search result rendered Match ${message_start_index}-${message_end_index} beside a tool whose parameter is documented as "Message offset for pagination". An agent auditing this project read that as an invitation and followed it. Those are not offsets.

getSessionDetail pages by position in a session's timestamp ordering. A retrieval unit stores the message_index values at its edges. The two agree only while numbering is dense and monotonic in time order — real transcripts are neither.

Measured on a live index:

windows whose range read backwards 430 of 9,728 (4.4%)
duplicate messages in the worst session 828
index/time order disagreements there 862

Match 5987-2108 was a real rendered line. Following it landed three weeks from the match.

The fix

Matches carry detail_offset, resolved on read, and the line now says what to do with it:

- Match (xtctx_session_detail offset=1274): …

Resolved on read rather than stored, because the stored edges are part of a window's id — changing them orphans every vector in every existing index and costs each user a full re-embed (measured: 92 minutes on the project that exposed this). At most three matches per session over five sessions, each an indexed count, is the cheaper side of that trade. A failed lookup omits the pointer and still returns the match; a signpost is an aid, not the answer.

Verification

Against the real index that exposed it: 75 of 75 matches now land on their window's own first message, checked through the public detail path.

The regression test builds a session whose indices deliberately don't ascend with time — the shape a /compact re-ingestion produces — so position and index can't be mistaken for one another.

mutation result
report the raw message_start_index again both assertions fail

708 tests pass, typecheck and lint clean.

The three orderings that must stay identical (selectSessionMessages, getSessionDetail, messageOffsetInSession) now name each other in their comments.

A search result rendered `Match ${message_start_index}-${message_end_index}`
beside a tool whose parameter is documented as "Message offset for
pagination". An agent auditing this project read that as an invitation and
followed it, which is the failure: those are not offsets.

`getSessionDetail` pages by POSITION in a session's timestamp ordering. A
retrieval unit stores the `message_index` VALUES at its edges. The two agree
only while numbering is dense and monotonic in time order, and real
transcripts are neither — on a live index 430 of 9,728 windows (4.4%) printed
a range that read backwards, `Match 5987-2108`, because one session carries
828 duplicate messages and 862 places where index order disagrees with time
order. Following one landed three weeks from the match.

Matches now carry `detail_offset`, resolved on read, and the line says what to
do with it: `- Match (xtctx_session_detail offset=1274): ...`.

Resolved on read rather than stored because the stored edges are part of a
window's id: changing them orphans every vector in every existing index and
costs each user a full re-embed, measured at 92 minutes on the project that
exposed this. At most three matches per session over five sessions, each an
indexed count, is the cheaper side of that trade. A lookup that fails leaves
the pointer off and returns the match anyway — a signpost is an aid, not the
answer.

Verified against the index that exposed it: 75 of 75 matches now land on their
window's own first message, checked through the public detail path. The
regression test builds a session whose indices deliberately do not ascend with
time — the shape a `/compact` re-ingestion produces — so position and index
cannot be mistaken for each other; reporting the raw index again turns both
assertions red.

The three orderings that must agree are now named in each other's comments:
`selectSessionMessages`, `getSessionDetail`, and the new
`messageOffsetInSession`.
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