Skip to content

feat: surface session search hits in browser and preview#75

Closed
awsl233777 wants to merge 2 commits intomainfrom
feat/session-search-highlights
Closed

feat: surface session search hits in browser and preview#75
awsl233777 wants to merge 2 commits intomainfrom
feat/session-search-highlights

Conversation

@awsl233777
Copy link
Copy Markdown
Collaborator

@awsl233777 awsl233777 commented Apr 6, 2026

Summary

  • surface session search hit snippets in the sessions browser list
  • jump to the first matched message and highlight it in preview
  • add focused unit coverage for search-hit normalization and navigation

Testing

  • node tests/unit/run.mjs

Summary by CodeRabbit

  • New Features

    • Session list shows hit counts and snippet previews; matched messages are highlighted in previews.
    • Viewing a session auto-navigates to the matched message when applicable.
  • Tests

    • Added tests covering session search normalization, message-key matching, UI decoration, and the load-and-jump flow.
    • Improved parity-drift test logic to better report unexpected/missing keys.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 6, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 94ef67c8-dfa5-4452-bf4b-50ccf121c53f

📥 Commits

Reviewing files that changed from the base of the PR and between 425b992 and 89f5bee.

📒 Files selected for processing (1)
  • tests/unit/web-ui-behavior-parity.test.mjs
📜 Recent review details
🔇 Additional comments (3)
tests/unit/web-ui-behavior-parity.test.mjs (3)

332-337: Good parity-drift assertion hardening for data keys.

Using unexpected* filtered sets keeps the non-HEAD check strict on true regressions while avoiding brittle exact-list matching.


353-356: Method-key drift check update looks good.

This keeps non-HEAD validation focused on unexpected extras while preserving missing-key protection.


373-376: Computed-key parity check is improved.

The filtered unexpected assertion pattern is consistent and reduces false failures from allowed drift.


📝 Walkthrough

Walkthrough

Adds session search/match support: helpers to normalize match payloads and locate matched messages, decorates computed session list with match metadata, marks and jumps to matched messages when loading session details, and updates UI/templates/styles and tests to surface matches.

Changes

Cohort / File(s) Summary
Session Match Logic
web-ui/logic.sessions.mjs
Added normalizeSessionMatch, buildSessionMessageSearchText, and findSessionMessageMatchKey to extract/clean snippets, normalize message text, and resolve stable record keys for matched snippets.
Session Computed Properties
web-ui/modules/app.computed.session.mjs
sortedSessionsList now decorates sessions with a match property (from normalizeSessionMatch) for UI consumption.
Session Browser Methods
web-ui/modules/app.methods.session-browser.mjs
Integrated match handling into loadActiveSessionDetail: reset pendingSessionMatchKey, compute nextMatchKey, mark matched message (isSearchMatch/matchSnippet), set pendingSessionMatchKey, and schedule jumpToSessionTimelineNode(nextMatchKey).
Session List UI & Styles
web-ui/partials/index/panel-sessions.html, web-ui/styles/sessions-list.css
Render hit-count badge and primary snippet per session when present; added .session-item-match and .session-match-count CSS rules.
Session Message Preview UI & Styles
web-ui/partials/index/panel-sessions.html, web-ui/styles/sessions-preview.css
Mark matched messages with .session-msg-content-match and show .session-msg-match-chip; added corresponding styles.
Tests
tests/unit/session-search-ui.test.mjs, tests/unit/web-ui-behavior-parity.test.mjs
New test suite verifying normalization, key resolution, computed decoration, and load/detail jump behavior; parity test updated to compute unexpected extra/missing keys via Set-based filtering.

Sequence Diagram

sequenceDiagram
    participant UI as User/UI Layer
    participant SB as Session Browser
    participant Logic as Session Logic
    participant Nav as Timeline Navigation

    UI->>SB: select active session
    SB->>SB: reset pendingSessionMatchKey
    SB->>SB: load session messages helper
    SB->>Logic: normalizeSessionMatch(activeSession)
    Logic-->>SB: { primarySnippet, count, snippets }
    SB->>Logic: findSessionMessageMatchKey(messages, snippets)
    Logic-->>SB: nextMatchKey
    SB->>SB: pendingSessionMatchKey = nextMatchKey
    alt match found
        SB->>SB: mark message isSearchMatch / matchSnippet
        SB->>SB: $nextTick...
        SB->>Nav: jumpToSessionTimelineNode(nextMatchKey)
        Nav-->>UI: focus/scroll to record
    end
    SB-->>UI: return helper result
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

Suggested reviewers

  • ymkiux

Poem

🐰 I hopped through snippets, tidy and neat,
Found the match where messages meet,
I tagged the line and gave a cheer,
Then leapt the timeline—the match is clear! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: surfacing session search hits in both the browser and preview UI components.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/session-search-highlights

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
web-ui/partials/index/panel-sessions.html (1)

349-349: Consider adding match fields to v-memo dependencies.

The v-memo on line 349 tracks [msg.text, msg.timestamp, msg.roleLabel, msg.normalizedRole] but doesn't include msg.isSearchMatch or msg.matchSnippet. If match status changes independently of these fields, the message won't re-render.

In practice this may not be an issue if messages are fully replaced when loading, but adding these fields would be more robust.

💡 Optional: Add match fields to v-memo
-v-memo="[msg.text, msg.timestamp, msg.roleLabel, msg.normalizedRole]"
+v-memo="[msg.text, msg.timestamp, msg.roleLabel, msg.normalizedRole, msg.isSearchMatch, msg.matchSnippet]"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web-ui/partials/index/panel-sessions.html` at line 349, The v-memo dependency
list on the message element currently includes [msg.text, msg.timestamp,
msg.roleLabel, msg.normalizedRole] but misses search-related fields so a change
in match state won't trigger re-render; update the v-memo array used in the
message rendering (the v-memo attribute) to also include msg.isSearchMatch and
msg.matchSnippet so messages re-evaluate when match status or snippet content
changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/unit/session-search-ui.test.mjs`:
- Around line 60-121: The test file is orphaned and will never run and also
misses a required mock helper: when calling createSessionBrowserMethods you must
provide loadActiveSessionDetailHelper (the implementation expected by
loadActiveSessionDetail) in the test's mock options; to fix either (A) register
the test file in the test runner entry so it executes and update the test's mock
creation to pass loadActiveSessionDetailHelper (implement a simple stub matching
the real helper signature) alongside api, or (B) if the test is no longer
desired, remove the orphaned test file; ensure the test that remains calling
methods.loadActiveSessionDetail uses createSessionBrowserMethods with the
loadActiveSessionDetailHelper stub and then verify pendingSessionMatchKey,
activeSessionMessages matchSnippet/isSearchMatch and jumpToSessionTimelineNode
behavior.

In `@web-ui/modules/app.methods.session-browser.mjs`:
- Around line 439-465: Before awaiting loadActiveSessionDetailHelper in
loadActiveSessionDetail, capture the identity/state used to compute the match
(e.g., const currentSession = this.activeSession or const seq =
this.sessionDetailRequestSeq) and after the await, verify that the captured
value still matches (compare currentSession === this.activeSession or seq ===
this.sessionDetailRequestSeq) before computing match and mutating
this.activeSessionMessages/pendingSessionMatchKey/jumpToSessionTimelineNode; if
the check fails, skip applying the match annotations to avoid the race. Ensure
you reference loadActiveSessionDetail, loadActiveSessionDetailHelper,
this.activeSession, and sessionDetailRequestSeq when implementing the guard.

---

Nitpick comments:
In `@web-ui/partials/index/panel-sessions.html`:
- Line 349: The v-memo dependency list on the message element currently includes
[msg.text, msg.timestamp, msg.roleLabel, msg.normalizedRole] but misses
search-related fields so a change in match state won't trigger re-render; update
the v-memo array used in the message rendering (the v-memo attribute) to also
include msg.isSearchMatch and msg.matchSnippet so messages re-evaluate when
match status or snippet content changes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: de5425d2-e1c4-45f0-ad5f-28481657a4f4

📥 Commits

Reviewing files that changed from the base of the PR and between f22b427 and 425b992.

📒 Files selected for processing (7)
  • tests/unit/session-search-ui.test.mjs
  • web-ui/logic.sessions.mjs
  • web-ui/modules/app.computed.session.mjs
  • web-ui/modules/app.methods.session-browser.mjs
  • web-ui/partials/index/panel-sessions.html
  • web-ui/styles/sessions-list.css
  • web-ui/styles/sessions-preview.css
📜 Review details
🔇 Additional comments (15)
web-ui/logic.sessions.mjs (3)

61-80: LGTM!

The normalizeSessionMatch function correctly normalizes session match data with proper defensive handling for missing/invalid inputs. The coercion of count to a non-negative integer and the filtering/trimming of snippets are well implemented.


82-105: LGTM!

The buildSessionMessageSearchText function comprehensively extracts searchable text from multiple possible message formats (text, content, and parts array with both string and object entries). Good defensive coding with proper null/type checks.


107-130: LGTM!

The findSessionMessageMatchKey function correctly implements case-insensitive snippet matching with proper fallback key generation. The key format matches getRecordRenderKey from the relevant code snippet (record-${recordLineIndex} or record-fallback-${idx}-${timestamp}), ensuring consistency.

web-ui/modules/app.methods.session-browser.mjs (3)

1-8: LGTM!

The new imports for findSessionMessageMatchKey and normalizeSessionMatch are correctly added to support the search-hit functionality.


328-338: LGTM!

The normalizeSessionMessage method now correctly extracts and normalizes matchSnippet, deriving isSearchMatch from its presence. This aligns with the UI template's conditional rendering of match indicators.


386-386: LGTM!

Resetting pendingSessionMatchKey when selecting a new session prevents stale jump callbacks from executing after session changes.

web-ui/modules/app.computed.session.mjs (2)

4-6: LGTM!

The new imports for isSessionQueryEnabled and normalizeSessionMatch are correctly added.


34-54: LGTM!

The sortedSessionsList computed property now correctly decorates each session with normalized match data for both pinned and non-pinned paths. The normalizeSessionMatch function is lightweight (no I/O, simple object transformations), so the per-session overhead is acceptable.

web-ui/partials/index/panel-sessions.html (2)

255-259: LGTM!

The session list item correctly displays the match count badge and primary snippet when search results are available. The conditional rendering with v-if guards and the tooltip via :title are well implemented.


359-361: LGTM!

The message-level match highlighting with the conditional session-msg-content-match class and the "搜索命中" chip are correctly implemented.

web-ui/styles/sessions-preview.css (1)

252-268: LGTM!

The new CSS classes for match highlighting are well-implemented:

  • .session-msg-content-match provides subtle visual emphasis with a gradient background
  • .session-msg-match-chip follows the existing pill/badge pattern with consistent brand colors

Both classes integrate well with the existing design system.

web-ui/styles/sessions-list.css (1)

381-405: LGTM!

The new CSS classes for session list match display are well-designed:

  • .session-item-match provides proper snippet styling with 2-line clamping and word-break handling
  • .session-match-count follows the existing badge pattern with consistent brand colors

Both classes integrate seamlessly with the existing session list styling.

tests/unit/session-search-ui.test.mjs (3)

1-16: LGTM!

The test setup correctly uses dynamic imports with pathToFileURL to load the modules under test. This ESM-compatible approach works well for Node.js test environments.


18-43: LGTM!

Good unit test coverage for the core helper functions:

  • normalizeSessionMatch test verifies snippet trimming, empty filtering, and derived fields
  • findSessionMessageMatchKey test verifies case-insensitive partial matching and correct key generation

45-58: LGTM!

The sortedSessionsList computed property test correctly verifies that match data is normalized and included in the output, validating the integration between the computed property and normalizeSessionMatch.

Comment on lines +60 to +121
test('loadActiveSessionDetail annotates matched message and schedules jump', async () => {
const methods = createSessionBrowserMethods({
api: async () => ({
messages: [
{ recordLineIndex: 1, role: 'user', text: 'hello world' },
{ recordLineIndex: 2, role: 'assistant', text: 'needle is right here in the answer' }
],
clipped: false,
messageLimit: 80,
totalMessages: 2
})
});

const jumps = [];
const context = {
activeSession: {
source: 'codex',
sessionId: 's1',
filePath: '/tmp/demo.jsonl',
match: {
hit: true,
count: 1,
snippets: ['needle is right here']
}
},
mainTab: 'sessions',
sessionPreviewRenderEnabled: true,
sessionDetailRequestSeq: 0,
sessionDetailInitialMessageLimit: 80,
sessionDetailMessageLimit: 80,
sessionPreviewPendingVisibleCount: 0,
sessionPreviewVisibleCount: 0,
sessionMessageRefMap: Object.create(null),
sessionMessageRefBinderMap: Object.create(null),
resetSessionDetailPagination() {},
resetSessionPreviewMessageRender() {},
primeSessionPreviewMessageRender() {
this.sessionPreviewVisibleCount = 2;
},
cancelSessionTimelineSync() {},
clearSessionTimelineRefs() {},
syncActiveSessionMessageCount() {},
invalidateSessionTimelineMeasurementCache() {},
$nextTick(fn) { fn(); },
jumpToSessionTimelineNode(key) { jumps.push(key); },
getRecordKey(message) {
return String(message.recordLineIndex);
},
getRecordRenderKey(message, idx) {
return `record-${message.recordLineIndex || idx}`;
},
normalizeSessionMessage: methods.normalizeSessionMessage,
pendingSessionMatchKey: ''
};

await methods.loadActiveSessionDetail.call(context);

assert.strictEqual(context.pendingSessionMatchKey, 'record-2');
assert.strictEqual(context.activeSessionMessages[1].isSearchMatch, true);
assert.strictEqual(context.activeSessionMessages[1].matchSnippet, 'needle is right here');
assert.deepStrictEqual(jumps, ['record-2']);
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify if the test actually runs successfully
cd tests/unit
node --test session-search-ui.test.mjs 2>&1 || echo "Test execution failed"

Repository: SakuraByteCore/codexmate

Length of output: 620


🏁 Script executed:

cat -n tests/unit/session-search-ui.test.mjs | head -70

Repository: SakuraByteCore/codexmate

Length of output: 3112


🏁 Script executed:

# Find where createSessionBrowserMethods is defined
rg -l "createSessionBrowserMethods" --type js --type mjs

Repository: SakuraByteCore/codexmate

Length of output: 95


🏁 Script executed:

# Look for loadActiveSessionDetail in production code
rg -l "loadActiveSessionDetail" --type js --type mjs

Repository: SakuraByteCore/codexmate

Length of output: 95


🏁 Script executed:

# Find the actual file without mjs type filter
find . -name "app.methods.session-browser.mjs" -type f

Repository: SakuraByteCore/codexmate

Length of output: 116


🏁 Script executed:

# Search for loadActiveSessionDetailHelper without mjs type
rg "loadActiveSessionDetailHelper" -A 5

Repository: SakuraByteCore/codexmate

Length of output: 2121


🏁 Script executed:

# Check if there's a test setup file or package.json with test runner config
cat package.json | head -50

Repository: SakuraByteCore/codexmate

Length of output: 1461


🏁 Script executed:

# Read the test runner to understand how tests work
cat tests/unit/run.mjs

Repository: SakuraByteCore/codexmate

Length of output: 3842


🏁 Script executed:

# Read createSessionBrowserMethods in detail
sed -n '1,50p' web-ui/modules/app.methods.session-browser.mjs

Repository: SakuraByteCore/codexmate

Length of output: 1630


🏁 Script executed:

# Look for the function definition and what it returns
rg "export.*createSessionBrowserMethods|function createSessionBrowserMethods" -A 30

Repository: SakuraByteCore/codexmate

Length of output: 2512


🏁 Script executed:

# Verify session-search-ui.test.mjs is not in the test runner imports
grep "session-search-ui" tests/unit/run.mjs

Repository: SakuraByteCore/codexmate

Length of output: 50


🏁 Script executed:

# List all test files that ARE being run
grep "await import" tests/unit/run.mjs | grep -oE "'[^']+\.test\.mjs'" | sort

Repository: SakuraByteCore/codexmate

Length of output: 1218


Test file is not registered in the test suite and will never execute.

The file session-search-ui.test.mjs is not imported in tests/unit/run.mjs, so this test is dead code. Either add it to the test runner or remove the file. Additionally, if this test were to run, it would fail because createSessionBrowserMethods destructures loadActiveSessionDetailHelper from options (line 12 of app.methods.session-browser.mjs), but the test only passes api on line 61. To fix: add the test to tests/unit/run.mjs and provide the missing helper in the mock setup, or delete the orphaned test file.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/unit/session-search-ui.test.mjs` around lines 60 - 121, The test file
is orphaned and will never run and also misses a required mock helper: when
calling createSessionBrowserMethods you must provide
loadActiveSessionDetailHelper (the implementation expected by
loadActiveSessionDetail) in the test's mock options; to fix either (A) register
the test file in the test runner entry so it executes and update the test's mock
creation to pass loadActiveSessionDetailHelper (implement a simple stub matching
the real helper signature) alongside api, or (B) if the test is no longer
desired, remove the orphaned test file; ensure the test that remains calling
methods.loadActiveSessionDetail uses createSessionBrowserMethods with the
loadActiveSessionDetailHelper stub and then verify pendingSessionMatchKey,
activeSessionMessages matchSnippet/isSearchMatch and jumpToSessionTimelineNode
behavior.

Comment on lines 439 to 465
async loadActiveSessionDetail(options = {}) {
return loadActiveSessionDetailHelper.call(this, api, options);
const result = await loadActiveSessionDetailHelper.call(this, api, options);
const match = normalizeSessionMatch(this.activeSession);
const nextMatchKey = findSessionMessageMatchKey(this.activeSessionMessages, match.snippets);
this.pendingSessionMatchKey = nextMatchKey;
if (nextMatchKey) {
this.activeSessionMessages = Object.freeze(
this.activeSessionMessages.map((message, index) => {
const key = this.getRecordRenderKey(message, index);
if (key !== nextMatchKey) {
return message;
}
return Object.freeze({
...message,
matchSnippet: match.primarySnippet
});
})
);
this.$nextTick(() => {
if (this.pendingSessionMatchKey !== nextMatchKey) return;
if (typeof this.jumpToSessionTimelineNode === 'function') {
this.jumpToSessionTimelineNode(nextMatchKey);
}
});
}
return result;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Potential race condition between match computation and async helper result.

The match is computed from this.activeSession after loadActiveSessionDetailHelper completes, but this.activeSession may have changed if the user selected a different session while the async call was in-flight. The helper uses sessionDetailRequestSeq to guard its writes, but this code path doesn't verify that the active session is still the same one that was loaded.

Consider capturing this.activeSession before the await and comparing after, or checking sessionDetailRequestSeq consistency before applying match annotations.

🛡️ Suggested guard
 async loadActiveSessionDetail(options = {}) {
+    const sessionSnapshot = this.activeSession;
     const result = await loadActiveSessionDetailHelper.call(this, api, options);
+    // Guard against session change during async load
+    if (this.activeSession !== sessionSnapshot) {
+        return result;
+    }
     const match = normalizeSessionMatch(this.activeSession);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async loadActiveSessionDetail(options = {}) {
return loadActiveSessionDetailHelper.call(this, api, options);
const result = await loadActiveSessionDetailHelper.call(this, api, options);
const match = normalizeSessionMatch(this.activeSession);
const nextMatchKey = findSessionMessageMatchKey(this.activeSessionMessages, match.snippets);
this.pendingSessionMatchKey = nextMatchKey;
if (nextMatchKey) {
this.activeSessionMessages = Object.freeze(
this.activeSessionMessages.map((message, index) => {
const key = this.getRecordRenderKey(message, index);
if (key !== nextMatchKey) {
return message;
}
return Object.freeze({
...message,
matchSnippet: match.primarySnippet
});
})
);
this.$nextTick(() => {
if (this.pendingSessionMatchKey !== nextMatchKey) return;
if (typeof this.jumpToSessionTimelineNode === 'function') {
this.jumpToSessionTimelineNode(nextMatchKey);
}
});
}
return result;
}
async loadActiveSessionDetail(options = {}) {
const sessionSnapshot = this.activeSession;
const result = await loadActiveSessionDetailHelper.call(this, api, options);
// Guard against session change during async load
if (this.activeSession !== sessionSnapshot) {
return result;
}
const match = normalizeSessionMatch(this.activeSession);
const nextMatchKey = findSessionMessageMatchKey(this.activeSessionMessages, match.snippets);
this.pendingSessionMatchKey = nextMatchKey;
if (nextMatchKey) {
this.activeSessionMessages = Object.freeze(
this.activeSessionMessages.map((message, index) => {
const key = this.getRecordRenderKey(message, index);
if (key !== nextMatchKey) {
return message;
}
return Object.freeze({
...message,
matchSnippet: match.primarySnippet
});
})
);
this.$nextTick(() => {
if (this.pendingSessionMatchKey !== nextMatchKey) return;
if (typeof this.jumpToSessionTimelineNode === 'function') {
this.jumpToSessionTimelineNode(nextMatchKey);
}
});
}
return result;
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web-ui/modules/app.methods.session-browser.mjs` around lines 439 - 465,
Before awaiting loadActiveSessionDetailHelper in loadActiveSessionDetail,
capture the identity/state used to compute the match (e.g., const currentSession
= this.activeSession or const seq = this.sessionDetailRequestSeq) and after the
await, verify that the captured value still matches (compare currentSession ===
this.activeSession or seq === this.sessionDetailRequestSeq) before computing
match and mutating
this.activeSessionMessages/pendingSessionMatchKey/jumpToSessionTimelineNode; if
the check fails, skip applying the match annotations to avoid the race. Ensure
you reference loadActiveSessionDetail, loadActiveSessionDetailHelper,
this.activeSession, and sessionDetailRequestSeq when implementing the guard.

@SurviveM
Copy link
Copy Markdown
Collaborator

SurviveM commented Apr 6, 2026

@coderabbitai if I merge this PR directly, will it introduce any regression risk?

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 6, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

Let me inspect the actual code changes to give you a thorough answer.


🏁 Script executed:

#!/bin/bash
cat web-ui/logic.sessions.mjs | tail -120
echo "---SEPARATOR---"
cat web-ui/modules/app.computed.session.mjs
echo "---SEPARATOR---"
cat web-ui/modules/app.methods.session-browser.mjs
echo "---SEPARATOR---"
cat tests/unit/web-ui-behavior-parity.test.mjs

Length of output: 69684


@SurviveM Overall the PR is low-to-medium regression risk, but there are two specific points worth checking before merging:


🟡 Risk 1 — Object.freeze on activeSessionMessages

In loadActiveSessionDetail, when a search-match key is found, the code does:

this.activeSessionMessages = Object.freeze(
    this.activeSessionMessages.map((message, index) => { ... })
);

Object.freeze prevents any later in-place mutations (.push(), direct index writes). For sessions that have no search match, this path is never taken, so the common case is safe. However, if any "load more messages" or pagination code downstream does this.activeSessionMessages.push(...) rather than full reassignment, it would silently fail (or throw in strict mode) only for sessions with match data.

Check: confirm that all code that appends/extends activeSessionMessages after the initial load does so by reassigning the whole array (this.activeSessionMessages = [...]), not by mutating it in place.


🟡 Risk 2 — pendingSessionMatchKey must be a declared data() property

The $nextTick stale-guard relies on this.pendingSessionMatchKey being reactive:

this.$nextTick(() => {
    if (this.pendingSessionMatchKey !== nextMatchKey) return;  // stale check
    if (typeof this.jumpToSessionTimelineNode === 'function') {
        this.jumpToSessionTimelineNode(nextMatchKey);
    }
});

If pendingSessionMatchKey is not declared in Vue's data(), Vue 3 will still set it but it won't be reactive — meaning the guard could fail to detect a rapid session switch and execute a stale jump. Additionally, the parity test's allowedExtraCurrentKeys list does not include pendingSessionMatchKey, so if it is a brand-new data key in this PR, the behavior-parity test would fail.

Check: confirm pendingSessionMatchKey: '' exists in the app's data() definition, and add it to allowedExtraCurrentKeys in tests/unit/web-ui-behavior-parity.test.mjs if it's new.


✅ No-risk items

  • All new CSS classes and HTML template additions are additive and conditional on new fields with false/'' defaults — normal sessions are unaffected.
  • normalizeSessionMessage changes are additive (matchSnippet: '', isSearchMatch: false defaults).
  • No exported/public API signatures were changed.
  • sortedSessionsList returning new wrapper objects instead of raw session references is safe for Vue template rendering.

If you can confirm the two items above, the merge risk is low.


🧠 Learnings used
Learnt from: SurviveM
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-03-29T02:42:32.785Z
Learning: The repository maintainer SurviveM (GitHub: SurviveM, repo: SakuraByteCore/codexmate) prefers that CodeRabbit review suggestions do not introduce breaking changes. Reviews should focus on correctness, safety, and quality without recommending refactors or changes that would break existing APIs, interfaces, or behavior.

Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-03-31T12:37:51.023Z
Learning: In PR `#59` for SakuraByteCore/codexmate, github-actions[bot] repeatedly flagged that CodeRabbit was introducing breaking changes. The maintainer's absolute constraint is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.

Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-03-30T04:47:15.352Z
Learning: In PR `#55` for SakuraByteCore/codexmate, github-actions[bot] repeatedly flagged that CodeRabbit was introducing breaking changes. The maintainer's absolute constraint is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.

Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-04-04T01:36:26.964Z
Learning: In PR `#68` for SakuraByteCore/codexmate, github-actions[bot] again flagged that CodeRabbit was introducing breaking changes. The maintainer's absolute constraint (reinforced across PRs `#55`, `#59`, `#67`, `#68`) is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.

Learnt from: github-actions[bot]
Repo: SakuraByteCore/codexmate PR: 0
File: :0-0
Timestamp: 2026-04-03T10:42:22.368Z
Learning: In PR `#67` for SakuraByteCore/codexmate, github-actions[bot] flagged that CodeRabbit was again introducing breaking changes. The maintainer's absolute constraint (reinforced across PRs `#55`, `#59`, `#67`) is: no breaking changes in any review suggestion; limit all feedback to correctness, safety, and quality only within the existing design, without refactoring or altering APIs, interfaces, or behavior.

@ymkiux ymkiux closed this Apr 6, 2026
@ymkiux ymkiux deleted the feat/session-search-highlights branch April 6, 2026 14:09
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.

3 participants