Skip to content

feat(brainbar): humanize injections tab (item F-2)#336

Merged
EtanHey merged 5 commits into
mainfrom
feat/brainbar-item-F-injections-coverage
May 26, 2026
Merged

feat(brainbar): humanize injections tab (item F-2)#336
EtanHey merged 5 commits into
mainfrom
feat/brainbar-item-F-injections-coverage

Conversation

@EtanHey
Copy link
Copy Markdown
Owner

@EtanHey EtanHey commented May 26, 2026

Summary

Implements Item F Fix 2 from /tmp/codex-mandate-item-F.md: humanize the BrainBar Injections tab so retrieval cards are based on retrieved chunk content instead of source-prompt plumbing.

Changes

  • Enriches InjectionEvent objects with chunk metadata from chunks during listInjectionEvents.
  • Adds content-first injection titles from chunk summary/content, with query text demoted to Triggered by: subtitle.
  • Adds source/type classification with glyphs and labels for checkpoints, realtime capture, stored memory, digests, video, chat, tool sessions, quick capture, and other chunks.
  • Adds persisted type filter chips: All, Memory, Stored, Realtime, Checkpoint, Video, Chat, Tool.
  • Adds source-type legend/tooltips for hit bars and human-readable expanded chunk rows.
  • Lets the conversation modal title reflect the chunk type, e.g. Memory Checkpoint instead of always Conversation.

Tests

  • swift test --package-path brain-bar --filter 'InjectionEventTests|DatabaseTests/testListInjectionEventsLoadsChunkDisplayMetadata'
  • swift test --package-path brain-bar --filter 'InjectionPresentationTests|InjectionStoreTests|InjectionEventTests|DatabaseTests/testListInjectionEventsLoadsChunkDisplayMetadata'
  • swift test --package-path brain-bar — 478 tests, 0 failures
  • git diff --check
  • Local CodeRabbit: cr review --plain --type uncommitted — no findings
  • Pre-push hook passed:
    • Python: 2186 passed, 9 skipped, 75 deselected, 1 xfailed
    • MCP tool registration: 3 passed
    • isolated eval/hook routing: 36 passed
    • bun stale index: 1 pass
    • test_fts5_determinism.sh: pass

Visual Gate

Per Etan addendum, this PR still requires post-merge rebuild + relaunch + Computer Use screenshots of Dashboard, Injections, and Graph before TASK_DONE_FIX_2.


Note

Medium Risk
Each injection list now runs an extra chunks query per event (read-path cost), and classification/filter behavior affects what users see in a core BrainBar surface; changes are localized to UI/read models with broad test coverage.

Overview
Humanizes the BrainBar Injections tab by loading retrieved chunk rows when listing injection events and driving cards from summary/content instead of raw query text, with the query shown as a “Triggered by:” line.

Adds InjectionChunk, InjectionKind (glyphs, labels, source-based classification), and InjectionTypeFilter with persisted filter chips (All, Memory, Stored, Realtime, etc.). InjectionPresentation.snapshot applies the type filter and extends text search to display titles and chunk sources; burst previews can show deduplicated chunk previews with kind glyphs.

InjectionFeedView updates burst/event rows, hit-bar colors by kind (not chunk-ID hash), legends/tooltips, and InjectionConversationSelection so ChunkConversationSheet / overlay use chunk-type modal titles instead of a fixed “Conversation”.

InjectionEvent row parsing no longer throws on malformed chunk_ids JSON—it falls back to an empty array. Events with chunk IDs but missing loaded metadata still pass non-All type filters so rows are not hidden incorrectly.

Reviewed by Cursor Bugbot for commit dca7b71. Bugbot is set up for automated code reviews on this repo. Configure here.

Summary by CodeRabbit

  • New Features

    • Type-filter chips in the event feed (persisted) to filter injections by kind.
    • Enriched chunk display: summaries, sources, tags, and kind-based glyphs/labels.
    • Conversation sheets/overlays accept a custom title.
  • Bug Fixes

    • Chunk previews and collapsed bursts show actual chunk content (not just IDs).
    • Event filtering/search respects type filter and chunk metadata.
  • Tests

    • Added unit tests covering decoding, kind classification, presentation, and selection behavior.

Review Change Stack

Note

Add semantic kind classification and type filtering to the BrainBar injections tab

  • Introduces InjectionKind (with glyphs, labels, and palette indices) and InjectionTypeFilter to classify injection events by source type (memory, stored, realtime, checkpoint, video, chat, tool)
  • InjectionEvent and InjectionChunk now carry chunk metadata (content, summary, source, tags) fetched from the database, enabling display-ready titles and kind classification in the UI
  • InjectionFeedView is reworked to show kind glyphs, display titles, 'Triggered by' query lines, and a persistent type-filter chip row; chunk ribbon colors are now stable per kind rather than hashed per chunk ID
  • InjectionPresentation.snapshot and filter accept a typeFilter to narrow results by type and match against display titles and chunk sources
  • Malformed chunk_ids JSON in InjectionEvent row initializer no longer throws — it silently falls back to an empty array

Macroscope summarized dca7b71.

Copy link
Copy Markdown

@greptile-apps greptile-apps Bot left a comment

Choose a reason for hiding this comment

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

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 26, 2026

📝 Walkthrough

Walkthrough

This PR enriches injection events with detailed chunk data throughout the stack: DB loads chunk rows into events; the model adds chunk/kind/filter types; presentation adds type filtering and chunk previews; the feed UI renders kind-based glyphs, previews, and persisted filters; and components/tests were updated accordingly.

Changes

Injection Event Chunk Enrichment

Layer / File(s) Summary
Injection chunk and kind data model
brain-bar/Sources/BrainBar/InjectionEvent.swift
Introduces InjectionChunk value type with content/summary/source metadata, displayText eliding, and tag decoding. Adds InjectionKind enum with classification logic from source/sourceFile/tags/content patterns, and InjectionTypeFilter for kind-based filtering. Updates InjectionEvent to include [InjectionChunk] payload and computed display helpers (primaryKind, displayTitle, triggeredByText, modalTitle) derived from the first chunk.
Database chunk loading and event enrichment
brain-bar/Sources/BrainBar/BrainDatabase.swift
listInjectionEvents now fetches associated chunk details from the chunks table for each event's chunkIDs and constructs enriched InjectionEvent objects with populated chunks array. New injectionChunkDetails(ids:) helper performs the chunk lookup while preserving input order and dropping missing IDs.
Presentation type filtering and previewing
brain-bar/Sources/BrainBar/InjectionPresentation.swift
snapshot(...) accepts new typeFilter parameter and pre-filters events by type before text search. Added previewChunks(for:limit:) to dedupe and return first-seen chunk previews. Filtering when searching includes displayTitle and joined chunk source values.
Feed UI chunk display, filtering, and kind visualization
brain-bar/Sources/BrainBar/InjectionFeedView.swift
Adds persisted typeFilter and filter chips; burst headers and collapsed previews prefer chunk-derived glyph + displayText; event rows use displayTitle, triggered-by text, and kind chips; chunk ribbon/list show kind legend, help text, and kind-based colors; "Open" actions set overlay title from chunk/event modal titles.
Conversation sheet title parameterization
brain-bar/Sources/BrainBar/Views/Components/ChunkConversationSheet.swift
ChunkConversationSheet and ChunkConversationOverlay add a configurable title (default "Conversation") and pass it to the sheet header; onClose is made escaping on the overlay.
Test coverage for chunk loading, display, filtering, and previewing
brain-bar/Tests/BrainBarTests/*
Adds tests that verify DB enrichment of chunk metadata into events, InjectionEvent decoding and presentation behavior, InjectionKind classification and palette ordering, InjectionPresentation.snapshot filtering by type, previewChunks deduplication and ordering, and conversation selection close behavior.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • EtanHey/brainlayer#159: Touches BrainDatabase.listInjectionEvents and previous injection event plumbing; related to this PR's DB enrichment work.
  • EtanHey/brainlayer#326: Modifies InjectionFeedView.swift and feed rendering; overlaps with UI changes here.
  • EtanHey/brainlayer#322: Adjusts InjectionPresentation.snapshot burst logic; closely related to presentation changes in this PR.

"🐰 I hop through chunks and name them kind by kind,
I trim and order IDs until the right ones bind,
Glyphs and titles bloom for bursts that used to hide,
Filters chip and previews pick the first that’s tried,
Conversation sheets wear titles, clean and kind."

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.44% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title "feat(brainbar): humanize injections tab (item F-2)" directly reflects the PR's main objective of humanizing the BrainBar Injections tab by displaying chunk content instead of raw queries.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ 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/brainbar-item-F-injections-coverage

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.

@EtanHey
Copy link
Copy Markdown
Owner Author

EtanHey commented May 26, 2026

@codex review
@cursor review
@BugBot review

Requesting AP_BL_7 review for Item F Fix 2. Please focus on injection event metadata loading, type classification/filtering, modal title routing, and whether the UI can misrepresent retrieved chunk source/type.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ee8bb71314

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

let trimmed = needle.trimmingCharacters(in: .whitespacesAndNewlines)
guard !trimmed.isEmpty else { return events }
let typedEvents = events.filter { event in
typeFilter.contains(event.primaryKind)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Match type filter against all retrieved chunk kinds

The new type filter only checks event.primaryKind, which is derived from the first chunk, so mixed-source retrievals are filtered incorrectly. If an event includes a selected type in any non-first chunk (for example a Video hit after a Stored hit), selecting that chip will hide the event even though it contains matching retrieved chunks. This makes the filter semantics inconsistent with the per-chunk UI and can mislead operators about what sources were actually injected.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fixed in 6b3207d: InjectionEvent now exposes allKinds/matches(typeFilter:), and InjectionPresentation filters with event.matches(typeFilter:) so non-first retrieved chunks are included. Added testSnapshotTypeFilterMatchesAnyRetrievedChunkKind.

]
if let event = try? InjectionEvent(row: row) {
if var event = try? InjectionEvent(row: row) {
let details = try injectionChunkDetails(ids: event.chunkIDs)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Prevent one bad chunk lookup from failing entire feed

Inside listInjectionEvents, chunk metadata loading is now a throwing call in the main row loop; if injectionChunkDetails fails for any single event (for example due malformed/oversized chunk_ids input), the whole query throws and the Injections feed degrades instead of returning the other valid events. This is a regression in read-path resilience introduced by metadata enrichment and can blank the tab for an otherwise recoverable per-event problem.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fixed in 6b3207d: listInjectionEvents now wraps injectionChunkDetails in try? and falls back to [] for that event, preserving the rest of the feed if one metadata lookup fails.

Copy link
Copy Markdown

@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: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@brain-bar/Sources/BrainBar/InjectionFeedView.swift`:
- Around line 610-613: The color(for:) function uses kind.rawValue.hashValue
which is unstable across launches; replace that with a deterministic mapping
from InjectionKind.rawValue to an index into accentPalette (e.g., if rawValue is
an Int use Int(rawValue) % accentPalette.count, or if it’s a String compute a
stable numeric value such as summing unicodeScalars (unicodeScalars.map {
Int($0.value) }.reduce(0,+)) and then take abs(...) % accentPalette.count) so
that color(for:) always returns the same palette index for a given InjectionKind
across runs.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 64292945-d66e-4427-8232-f25f9dd24eca

📥 Commits

Reviewing files that changed from the base of the PR and between 56ee32f and ee8bb71.

📒 Files selected for processing (7)
  • brain-bar/Sources/BrainBar/BrainDatabase.swift
  • brain-bar/Sources/BrainBar/InjectionEvent.swift
  • brain-bar/Sources/BrainBar/InjectionFeedView.swift
  • brain-bar/Sources/BrainBar/InjectionPresentation.swift
  • brain-bar/Sources/BrainBar/Views/Components/ChunkConversationSheet.swift
  • brain-bar/Tests/BrainBarTests/DatabaseTests.swift
  • brain-bar/Tests/BrainBarTests/InjectionEventTests.swift
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: test (3.13)
  • GitHub Check: test (3.11)
  • GitHub Check: test (3.12)
  • GitHub Check: Macroscope - Correctness Check
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2026-03-18T00:12:08.774Z
Learnt from: EtanHey
Repo: EtanHey/brainlayer PR: 87
File: brain-bar/Sources/BrainBar/BrainBarServer.swift:118-129
Timestamp: 2026-03-18T00:12:08.774Z
Learning: In Swift files under brain-bar/Sources/BrainBar, enforce that when a critical dependency like the database is nil due to startup ordering (socket before DB), any tool handler that accesses the database must throw an explicit error (e.g., ToolError.noDatabase) instead of returning a default/empty value. Do not allow silent defaults (e.g., guard let db else { return ... }). Flag patterns that silently return defaults when db is nil, as this masks startup timing issues. This guidance applies broadly to similar Swift files in the BrainBar module, not just this one location.

Applied to files:

  • brain-bar/Sources/BrainBar/Views/Components/ChunkConversationSheet.swift
  • brain-bar/Sources/BrainBar/BrainDatabase.swift
  • brain-bar/Sources/BrainBar/InjectionPresentation.swift
  • brain-bar/Sources/BrainBar/InjectionFeedView.swift
  • brain-bar/Sources/BrainBar/InjectionEvent.swift
📚 Learning: 2026-03-29T18:45:40.988Z
Learnt from: EtanHey
Repo: EtanHey/brainlayer PR: 133
File: brain-bar/Sources/BrainBar/BrainDatabase.swift:0-0
Timestamp: 2026-03-29T18:45:40.988Z
Learning: In the BrainBar module’s Swift database layer (notably BrainDatabase.swift), ensure that the `search()` function’s `unreadOnly=true` path orders results by the delivery frontier cursor so the watermark `maxRowID` stays contiguous. Specifically, when `unreadOnly` is enabled, the query must include `ORDER BY c.rowid ASC` (e.g., via `let orderByClause = unreadOnly ? "c.rowid ASC" : "f.rank"`). Do not replace the unread-only ordering with relevance-based sorting (e.g., `f.rank`) unconditionally or for the unread-only path, as it can introduce gaps in the watermark and incorrectly mark unseen rows as delivered. Flag any future change to the `ORDER BY` clause in this function that makes relevance sorting apply to the unread-only case.

Applied to files:

  • brain-bar/Sources/BrainBar/BrainDatabase.swift
  • brain-bar/Sources/BrainBar/InjectionPresentation.swift
  • brain-bar/Sources/BrainBar/InjectionFeedView.swift
  • brain-bar/Sources/BrainBar/InjectionEvent.swift
🔇 Additional comments (7)
brain-bar/Tests/BrainBarTests/DatabaseTests.swift (1)

654-685: LGTM!

brain-bar/Tests/BrainBarTests/InjectionEventTests.swift (1)

37-63: LGTM!

Also applies to: 65-73, 75-126

brain-bar/Sources/BrainBar/InjectionEvent.swift (1)

3-289: LGTM!

brain-bar/Sources/BrainBar/BrainDatabase.swift (1)

4551-4561: LGTM!

Also applies to: 4568-4604

brain-bar/Sources/BrainBar/InjectionPresentation.swift (1)

98-105: LGTM!

Also applies to: 173-190, 334-335

brain-bar/Sources/BrainBar/InjectionFeedView.swift (1)

69-114: LGTM!

Also applies to: 130-590, 721-747

brain-bar/Sources/BrainBar/Views/Components/ChunkConversationSheet.swift (1)

5-16: LGTM!

Also applies to: 23-23, 99-110, 119-119

Comment thread brain-bar/Sources/BrainBar/InjectionFeedView.swift
Copy link
Copy Markdown

@greptile-apps greptile-apps Bot left a comment

Choose a reason for hiding this comment

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

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

@EtanHey
Copy link
Copy Markdown
Owner Author

EtanHey commented May 26, 2026

@codex review\n@cursor @BugBot re-review\n@coderabbitai review\n\nUpdated PR branch at 6b3207d with fixes for prior review comments: type filtering now matches all retrieved chunk kinds, chunk detail lookup failure falls back per event, and kind colors use deterministic declaration-order palette indices. Please re-review current head.

Comment thread brain-bar/Sources/BrainBar/InjectionFeedView.swift Outdated
Comment thread brain-bar/Sources/BrainBar/InjectionFeedView.swift
Comment thread brain-bar/Sources/BrainBar/InjectionEvent.swift Outdated
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 26, 2026

Tip

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Get the PR diff and key file contents
gh pr view 336 --repo EtanHey/brainlayer --json title,state,headRefName,baseRefName,commits

echo "=== Recent commits ==="
gh pr view 336 --repo EtanHey/brainlayer --json commits --jq '.commits[-5:]'

echo "=== PR files changed ==="
gh pr diff 336 --repo EtanHey/brainlayer --name-only

Length of output: 2810


🏁 Script executed:

#!/bin/bash
echo "=== InjectionEvent.swift ==="
cat brain-bar/Sources/BrainBar/InjectionEvent.swift

echo ""
echo "=== InjectionPresentation.swift ==="
cat brain-bar/Sources/BrainBar/InjectionPresentation.swift

echo ""
echo "=== BrainDatabase.swift (injection section) ==="
grep -n -A 60 "listInjectionEvents\|injectionChunkDetails" brain-bar/Sources/BrainBar/BrainDatabase.swift | head -200

echo ""
echo "=== InjectionFeedView.swift ==="
cat brain-bar/Sources/BrainBar/InjectionFeedView.swift

echo ""
echo "=== ChunkConversationSheet.swift ==="
cat brain-bar/Sources/BrainBar/Views/Components/ChunkConversationSheet.swift

Length of output: 65526


🏁 Script executed:

#!/bin/bash
echo "=== InjectionEventTests.swift ==="
cat brain-bar/Tests/BrainBarTests/InjectionEventTests.swift

echo ""
echo "=== DatabaseTests.swift ==="
cat brain-bar/Tests/BrainBarTests/DatabaseTests.swift

Length of output: 58052



@EtanHey: Re-reviewed at 6b3207d8. All three stated fixes are correctly implemented and well-covered by the new tests. Here's the full picture:


✅ Verified fixes

Fix Location Verdict
Type filtering matches any retrieved chunk kind InjectionEvent.matches(typeFilter:) using allKinds + new testSnapshotTypeFilterMatchesAnyRetrievedChunkKind ✅ Correct
Per-event chunk lookup fallback (try? injectionChunkDetails(ids: event.chunkIDs)) ?? [] — event always appended, empty chunks on failure ✅ Correct
Deterministic palette indices kind.paletteIndex = declaration-order allCases.firstIndex mod palette count ✅ Deterministic

🔍 Remaining observations

1. SQL variable limit in injectionChunkDetails (low risk / latent)

The IN (?,?,…) clause creates one bind per chunk ID. SQLite's default SQLITE_MAX_VARIABLE_NUMBER is 999. If an event somehow has ≥999 chunk IDs, sqlite3_prepare_v2 will fail; the try? silently catches it and the event gets empty chunks rather than an error. Likely never triggered in practice, but easy to guard:

// Chunk injectionChunkDetails into batches of, say, 500
private static let maxChunkQueryBatch = 500

private func injectionChunkDetails(ids: [String]) throws -> [InjectionChunk] {
    guard let db else { throw DBError.notOpen }
    let orderedIDs = ids.filter { !$0.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty }
    guard !orderedIDs.isEmpty else { return [] }

    var detailsByID: [String: InjectionChunk] = [:]
    for batch in stride(from: 0, to: orderedIDs.count, by: Self.maxChunkQueryBatch).map({
        Array(orderedIDs[$0..<min($0 + Self.maxChunkQueryBatch, orderedIDs.count)])
    }) {
        // existing prepare/step logic per batch...
    }
    return orderedIDs.compactMap { detailsByID[$0] }
}

2. id = 0 fallback in InjectionEvent.init(row:) (low risk)

If a row's id cannot be cast to Int or Int64, the event silently gets id = 0. Two such events would have the same Identifiable id, causing SwiftUI ForEach to misbehave. A throw or at least a unique sentinel (e.g., negative hash of the row) would be safer.

3. Palette color collisions in the legend (cosmetic)

With 10 InjectionKind cases and a 5-slot palette, pairs of semantically unrelated kinds share the same dot color:

Color index Kind pair
0 memoryCheckpointchat
1 realtimeCapturetoolSession
2 storedMemoryquickCapture
3 dailyDigestcheckpoint
4 videoKnowledgeother

Since the legend shows a colored dot per kind with a text label, users can still distinguish them by label; but the dots alone are ambiguous. Not a blocking issue — just consider expanding accentPalette to 10 entries if the legend ever becomes the primary disambiguation mechanism.

4. .other kind is filter-invisible (design note)

Events whose chunk metadata couldn't be loaded (chunks = []) resolve to allKinds == [.other]. No InjectionTypeFilter case covers .other except .all, so these events only appear under the "All" chip. This is defensible (no metadata → no classification), but worth a short code comment so future maintainers don't think it's a bug.


Overall

The PR is well-structured and the injection humanization works correctly end-to-end. Issues 1 and 2 are worth a follow-up, but none are blocking for merge.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6b3207d8c7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


private static func topicOrSource(for event: InjectionEvent) -> String {
let trimmed = event.query.trimmingCharacters(in: .whitespacesAndNewlines)
let trimmed = event.displayTitle.trimmingCharacters(in: .whitespacesAndNewlines)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Keep burst grouping keyed on trigger query

Using event.displayTitle as topicOrSource makes burst grouping depend on the first retrieved chunk’s summary/content instead of the triggering query. In practice, repeated injections from the same session and prompt often return different top chunks, so the same retrieval flow gets split into multiple bursts, inflating burstCount and fragmenting the feed. This regresses the burst semantics validated elsewhere in InjectionPresentationTests (group-by session + topic) and should continue grouping by the trigger text while using display titles only for presentation.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fixed in 03ed083. The burst topic/source path is keyed from the trigger query again instead of displayTitle, preserving burst aggregation across multiple chunks with distinct content summaries. Added InjectionPresentationTests/testBurstAggregationUsesTriggerQueryWhenChunkSummariesDiffer; it failed before the change with two groups, then passed after.

@EtanHey
Copy link
Copy Markdown
Owner Author

EtanHey commented May 26, 2026

@cursor @BugBot re-review\n@codex review\n@coderabbitai review\n\nUpdated current head 6f9ec2f for Cursor Bugbot findings: row Open title now uses the actually opened chunk or generic fallback, burst preview chunks are de-duplicated before ForEach, and typed filters keep events with chunk IDs visible when metadata failed to load. Local gates: focused regressions passed, affected Injection* tests passed, full BrainBar Swift suite passed (483 tests), pre-push BrainLayer gate passed.

Copy link
Copy Markdown

@greptile-apps greptile-apps Bot left a comment

Choose a reason for hiding this comment

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

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

@cursor
Copy link
Copy Markdown

cursor Bot commented May 26, 2026

You need to increase your spend limit or enable usage-based billing to run background agents. Go to Cursor

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 26, 2026

Tip

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
echo "=== InjectionFeedView.swift ==="
cat brain-bar/Sources/BrainBar/InjectionFeedView.swift

echo ""
echo "=== InjectionPresentation.swift ==="
cat brain-bar/Sources/BrainBar/InjectionPresentation.swift

echo ""
echo "=== InjectionEvent.swift (filter/kind section) ==="
grep -n "matches\|typeFilter\|allKinds\|chunkIDs\|id = 0\|paletteIndex\|other\|other\b" brain-bar/Sources/BrainBar/InjectionEvent.swift | head -80

echo ""
echo "=== BrainDatabase.swift (injectionChunkDetails) ==="
grep -n -A 50 "injectionChunkDetails" brain-bar/Sources/BrainBar/BrainDatabase.swift | head -120

echo ""
echo "=== Recent commits at new head ==="
gh pr view 336 --repo EtanHey/brainlayer --json commits --jq '.commits[-5:] | .[] | {oid: .oid[0:8], message: .messageHeadline}'

Length of output: 50990



@EtanHey: Re-reviewed at 6f9ec2f9. All three stated fixes are correctly implemented.


✅ Fix verification

Fix Code evidence Verdict
Open title uses actually-opened chunk eventRow: event.openingModalTitle(forChunkID: firstChunk); chunkList: chunk?.kind.modalTitle ?? event.modalTitle ✅ Correct
Burst preview chunks de-duplicated previewChunks(for:limit:) walks events.flatMap(\.chunks) with a seen: Set<String> guard before appending ✅ Correct
Typed filters keep metadata-failed events matches(typeFilter:) early-returns true when chunks.isEmpty && !chunkIDs.isEmpty && typeFilter != .all ✅ Correct

Bonus: Fix 3 also resolves the prior observation about .other events being filter-invisible — events whose chunk metadata failed to load now surface under every type-filter chip, not just "All".


📌 Still-open latent items (non-blocking)

The two low-risk observations from the previous round remain:

  1. SQL variable limit (injectionChunkDetails): No batching yet; if an event ever has ≥ 999 chunk IDs, sqlite3_prepare_v2 silently fails and the event gets empty chunks. Low probability in practice.
  2. id = 0 silent fallback (InjectionEvent.init(row:) line 287): Two rows that fail id parsing get the same Identifiable id, which can cause SwiftUI ForEach key collisions.

Would you like me to open GitHub issues for these two so they don't get lost?

Comment thread brain-bar/Sources/BrainBar/InjectionEvent.swift
Comment thread brain-bar/Sources/BrainBar/InjectionEvent.swift
@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Already looking forward to the next diff.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Copy link
Copy Markdown

@greptile-apps greptile-apps Bot left a comment

Choose a reason for hiding this comment

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

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

@EtanHey
Copy link
Copy Markdown
Owner Author

EtanHey commented May 26, 2026

@coderabbitai review

@EtanHey
Copy link
Copy Markdown
Owner Author

EtanHey commented May 26, 2026

@cursor @BugBot re-review

@EtanHey
Copy link
Copy Markdown
Owner Author

EtanHey commented May 26, 2026

@codex review

@cursor
Copy link
Copy Markdown

cursor Bot commented May 26, 2026

You need to increase your spend limit or enable usage-based billing to run background agents. Go to Cursor

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 26, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 2c05eb3. Configure here.

Comment thread brain-bar/Sources/BrainBar/InjectionPresentation.swift Outdated
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2c05eb3800

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +146 to +148
if normalizedSource == "mcp" || normalizedSource == "brain_store" {
return .storedMemory
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Include manual source in stored-memory classification

InjectionKind.classify only maps mcp and brain_store to .storedMemory, but stored memories in this repo are also written with source="manual" (see brain_store tests and ingestion paths). With the current mapping, those chunks fall into .other, so they get the wrong glyph/label and are excluded by the new Stored type filter, which makes the injections view misclassify a major class of retrieved memories.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fixed in a803a30. InjectionKind.classify now maps source="manual" to .storedMemory, so manually stored brain_store chunks get the Stored Memory glyph/label and remain visible under the Stored type filter. Added InjectionEventTests/testManualSourceClassifiesAsStoredMemory; it failed before the classifier change and passes after. Also addressed a local pre-commit review finding by resetting the conversation title on overlay close with InjectionConversationSelectionTests/testCloseClearsConversationAndResetsTitle.

Copy link
Copy Markdown

@greptile-apps greptile-apps Bot left a comment

Choose a reason for hiding this comment

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

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

@EtanHey
Copy link
Copy Markdown
Owner Author

EtanHey commented May 26, 2026

@cursor @BugBot re-review

@EtanHey
Copy link
Copy Markdown
Owner Author

EtanHey commented May 26, 2026

@coderabbitai review

@EtanHey
Copy link
Copy Markdown
Owner Author

EtanHey commented May 26, 2026

@codex review

@cursor
Copy link
Copy Markdown

cursor Bot commented May 26, 2026

You need to increase your spend limit or enable usage-based billing to run background agents. Go to Cursor

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 26, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 03ed083d8c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +158 to +159
if ["cursor", "codex_cli", "codex"].contains(normalizedSource) {
return .toolSession
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Include cursor_cli and gemini_cli in tool-session mapping

InjectionKind.classify only treats cursor, codex_cli, and codex as tool sessions, so chunks from the canonical CLI sources cursor_cli and gemini_cli fall through to .other. In practice this makes Tool-filtered views miss/mislabel real tool-origin injections (these source names are already first-class in src/brainlayer/pipeline/enrichment_tiers.py), which undermines the new type chips and modal labeling for common data.

Useful? React with 👍 / 👎.

VStack(alignment: .leading, spacing: 8) {
ForEach(event.chunkIDs, id: \.self) { chunkID in
Button {
selectedConversationTitle = chunk(for: chunkID, event: event)?.kind.modalTitle ?? event.modalTitle
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3 Badge Use unknown-title fallback for chunk list modal opens

When a chunk in the expanded hit list has no loaded metadata, this path falls back to event.modalTitle instead of the generic unknown fallback used elsewhere (openingModalTitle(... ) returns "Conversation"). That means selecting an unmapped chunk can display an incorrect contextual title (for example, "Other" or another chunk’s kind) even though the selected chunk type is unknown.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@greptile-apps greptile-apps Bot left a comment

Choose a reason for hiding this comment

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

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

@EtanHey
Copy link
Copy Markdown
Owner Author

EtanHey commented May 26, 2026

@cursor @BugBot re-review

@EtanHey
Copy link
Copy Markdown
Owner Author

EtanHey commented May 26, 2026

@codex review

@EtanHey
Copy link
Copy Markdown
Owner Author

EtanHey commented May 26, 2026

@coderabbitai review

@cursor
Copy link
Copy Markdown

cursor Bot commented May 26, 2026

You need to increase your spend limit or enable usage-based billing to run background agents. Go to Cursor

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 26, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a803a30946

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +155 to +159
if normalizedSource == "whatsapp" {
return .chat
}
if ["cursor", "codex_cli", "codex"].contains(normalizedSource) {
return .toolSession
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Map known source values to the intended injection kinds

InjectionKind.classify currently recognizes chat only for whatsapp and tool sessions only for cursor/codex_cli/codex, so chunks from other known first-class sources (for example telegram, cursor_cli, gemini_cli) will be classified as .other. That causes the new type chips and labels to silently mis-filter/mislabel real events whenever those sources appear in chunk.source, even though the broader codebase already treats them as explicit source categories.

Useful? React with 👍 / 👎.

@EtanHey EtanHey force-pushed the feat/brainbar-item-F-injections-coverage branch from a803a30 to dca7b71 Compare May 26, 2026 22:25
Copy link
Copy Markdown

@greptile-apps greptile-apps Bot left a comment

Choose a reason for hiding this comment

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

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

@EtanHey
Copy link
Copy Markdown
Owner Author

EtanHey commented May 26, 2026

@coderabbitai review

@EtanHey
Copy link
Copy Markdown
Owner Author

EtanHey commented May 26, 2026

@codex review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 26, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: dca7b71b2d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

]
if let event = try? InjectionEvent(row: row) {
if var event = try? InjectionEvent(row: row) {
let details = (try? injectionChunkDetails(ids: event.chunkIDs)) ?? []
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Batch chunk detail reads instead of querying per injection event

listInjectionEvents now calls injectionChunkDetails inside the row loop, which turns one feed refresh into N+1 SQL statements (1 + event_count). In this repo, InjectionStore polls every 250ms with a default limit of 50 events, so this path can execute ~51 queries per refresh and significantly increase read-path cost and lock pressure on the shared DB during normal UI use. Fetching all needed chunk IDs in one batched query would avoid this regression.

Useful? React with 👍 / 👎.

Comment on lines +158 to +159
if ["cursor", "codex_cli", "codex"].contains(normalizedSource) {
return .toolSession
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Map cursor_cli and gemini_cli sources to tool sessions

Tool-session classification only matches cursor, codex_cli, and codex, so chunks with source cursor_cli or gemini_cli are classified as .other and won't appear under the Tool filter despite being agent/tool sources in this codebase (see src/brainlayer/pipeline/enrichment_tiers.py). This causes incorrect type chips, legends, and modal titles for those injections.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
brain-bar/Sources/BrainBar/InjectionEvent.swift (1)

286-293: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

id = 0 fallback risks duplicate Identifiable ids in SwiftUI.

When row id parsing fails, multiple events will share id = 0, violating Identifiable uniqueness. SwiftUI ForEach may exhibit undefined behavior (missing/duplicated rows, incorrect animations).

Consider throwing an error or generating a unique sentinel (e.g., negative random id) instead.

🐛 Proposed fix using a unique sentinel
     init(row: [String: Any]) throws {
         if let intID = row["id"] as? Int {
             id = Int64(intID)
         } else if let intID = row["id"] as? Int64 {
             id = intID
         } else {
-            id = 0
+            id = Int64.random(in: Int64.min..<0)
         }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@brain-bar/Sources/BrainBar/InjectionEvent.swift` around lines 286 - 293, The
init(row:) currently falls back to id = 0 which creates duplicate Identifiable
ids; update the InjectionEvent initializer to avoid using 0 as a fallback by
either throwing a parsing error (propagate by making init(row:) throw a
descriptive error when id is missing/invalid) or assign a unique sentinel (e.g.,
generate a negative random/hashed Int64 or derive from UUID/timestamp) to the id
property; modify the init(row:) branch that now sets id = 0 to implement one of
these two options and ensure any callers handle the thrown error or that the
sentinel generation uses Int64 and is deterministic enough to avoid collisions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@brain-bar/Sources/BrainBar/BrainDatabase.swift`:
- Around line 4568-4604: injectionChunkDetails currently builds one IN(...)
statement which will fail silently for large id arrays due to SQLite's
SQLITE_MAX_VARIABLE_NUMBER limit; modify injectionChunkDetails to batch
orderedIDs into smaller chunks (e.g., 500 IDs per batch) and for each batch
build the placeholders, call sqlite3_prepare_v2, bindText for that batch, step
through rows to populate detailsByID, finalize the statement, then continue with
the next batch; after all batches are processed, return orderedIDs.compactMap {
detailsByID[$0] } so order is preserved and no metadata is dropped when the
variable limit is reached.

---

Outside diff comments:
In `@brain-bar/Sources/BrainBar/InjectionEvent.swift`:
- Around line 286-293: The init(row:) currently falls back to id = 0 which
creates duplicate Identifiable ids; update the InjectionEvent initializer to
avoid using 0 as a fallback by either throwing a parsing error (propagate by
making init(row:) throw a descriptive error when id is missing/invalid) or
assign a unique sentinel (e.g., generate a negative random/hashed Int64 or
derive from UUID/timestamp) to the id property; modify the init(row:) branch
that now sets id = 0 to implement one of these two options and ensure any
callers handle the thrown error or that the sentinel generation uses Int64 and
is deterministic enough to avoid collisions.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 8b3f104d-ec36-4a95-840d-ce6ec6085bd8

📥 Commits

Reviewing files that changed from the base of the PR and between ee8bb71 and dca7b71.

📒 Files selected for processing (9)
  • brain-bar/Sources/BrainBar/BrainDatabase.swift
  • brain-bar/Sources/BrainBar/InjectionEvent.swift
  • brain-bar/Sources/BrainBar/InjectionFeedView.swift
  • brain-bar/Sources/BrainBar/InjectionPresentation.swift
  • brain-bar/Sources/BrainBar/Views/Components/ChunkConversationSheet.swift
  • brain-bar/Tests/BrainBarTests/DatabaseTests.swift
  • brain-bar/Tests/BrainBarTests/InjectionConversationSelectionTests.swift
  • brain-bar/Tests/BrainBarTests/InjectionEventTests.swift
  • brain-bar/Tests/BrainBarTests/InjectionPresentationTests.swift
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: test (3.12)
  • GitHub Check: test (3.13)
  • GitHub Check: test (3.11)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2026-03-18T00:12:08.774Z
Learnt from: EtanHey
Repo: EtanHey/brainlayer PR: 87
File: brain-bar/Sources/BrainBar/BrainBarServer.swift:118-129
Timestamp: 2026-03-18T00:12:08.774Z
Learning: In Swift files under brain-bar/Sources/BrainBar, enforce that when a critical dependency like the database is nil due to startup ordering (socket before DB), any tool handler that accesses the database must throw an explicit error (e.g., ToolError.noDatabase) instead of returning a default/empty value. Do not allow silent defaults (e.g., guard let db else { return ... }). Flag patterns that silently return defaults when db is nil, as this masks startup timing issues. This guidance applies broadly to similar Swift files in the BrainBar module, not just this one location.

Applied to files:

  • brain-bar/Sources/BrainBar/BrainDatabase.swift
  • brain-bar/Sources/BrainBar/Views/Components/ChunkConversationSheet.swift
  • brain-bar/Sources/BrainBar/InjectionEvent.swift
  • brain-bar/Sources/BrainBar/InjectionFeedView.swift
  • brain-bar/Sources/BrainBar/InjectionPresentation.swift
📚 Learning: 2026-03-29T18:45:40.988Z
Learnt from: EtanHey
Repo: EtanHey/brainlayer PR: 133
File: brain-bar/Sources/BrainBar/BrainDatabase.swift:0-0
Timestamp: 2026-03-29T18:45:40.988Z
Learning: In the BrainBar module’s Swift database layer (notably BrainDatabase.swift), ensure that the `search()` function’s `unreadOnly=true` path orders results by the delivery frontier cursor so the watermark `maxRowID` stays contiguous. Specifically, when `unreadOnly` is enabled, the query must include `ORDER BY c.rowid ASC` (e.g., via `let orderByClause = unreadOnly ? "c.rowid ASC" : "f.rank"`). Do not replace the unread-only ordering with relevance-based sorting (e.g., `f.rank`) unconditionally or for the unread-only path, as it can introduce gaps in the watermark and incorrectly mark unseen rows as delivered. Flag any future change to the `ORDER BY` clause in this function that makes relevance sorting apply to the unread-only case.

Applied to files:

  • brain-bar/Sources/BrainBar/BrainDatabase.swift
  • brain-bar/Sources/BrainBar/InjectionEvent.swift
  • brain-bar/Sources/BrainBar/InjectionFeedView.swift
  • brain-bar/Sources/BrainBar/InjectionPresentation.swift
🪛 SwiftLint (0.63.2)
brain-bar/Tests/BrainBarTests/InjectionConversationSelectionTests.swift

[Warning] 4-4: Classes should have an explicit deinit method

(required_deinit)

🔇 Additional comments (15)
brain-bar/Sources/BrainBar/Views/Components/ChunkConversationSheet.swift (2)

5-5: LGTM!

Also applies to: 9-17, 23-23


99-99: LGTM!

Also applies to: 102-110, 119-119

brain-bar/Sources/BrainBar/InjectionPresentation.swift (1)

98-105: LGTM!

Also applies to: 140-151, 186-203

brain-bar/Sources/BrainBar/InjectionFeedView.swift (1)

68-70: LGTM!

Also applies to: 109-114, 153-177, 258-279, 319-343, 378-397, 419-424, 441-467, 481-493, 617-619, 732-754, 757-771

brain-bar/Sources/BrainBar/InjectionEvent.swift (6)

3-49: LGTM!


51-72: LGTM!


74-166: LGTM!


168-211: LGTM!


299-307: Graceful handling of malformed chunk_ids JSON.

Using try? to silently fall back to an empty array on JSON decode failure is appropriate here—per PR objectives, malformed JSON no longer throws.


309-313: LGTM!

brain-bar/Sources/BrainBar/BrainDatabase.swift (1)

4551-4563: Event enrichment with graceful fallback on chunk lookup failure.

The try? pattern correctly ensures events are still returned (with empty chunks) when chunk metadata lookup fails, matching the PR's robustness objective.

brain-bar/Tests/BrainBarTests/DatabaseTests.swift (1)

654-685: LGTM!

brain-bar/Tests/BrainBarTests/InjectionConversationSelectionTests.swift (1)

1-26: LGTM!

brain-bar/Tests/BrainBarTests/InjectionEventTests.swift (1)

37-261: LGTM!

brain-bar/Tests/BrainBarTests/InjectionPresentationTests.swift (1)

116-145: LGTM!

Also applies to: 261-293, 300-323

Comment on lines +4568 to +4604
private func injectionChunkDetails(ids: [String]) throws -> [InjectionChunk] {
guard let db else { throw DBError.notOpen }
let orderedIDs = ids.filter { !$0.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty }
guard !orderedIDs.isEmpty else { return [] }

let placeholders = Array(repeating: "?", count: orderedIDs.count).joined(separator: ",")
let sql = """
SELECT id, content, summary, source, source_file, tags, content_type
FROM chunks
WHERE id IN (\(placeholders))
"""
var stmt: OpaquePointer?
guard sqlite3_prepare_v2(db, sql, -1, &stmt, nil) == SQLITE_OK else {
throw DBError.prepare(sqlite3_errcode(db))
}
defer { sqlite3_finalize(stmt) }

for (offset, id) in orderedIDs.enumerated() {
bindText(id, to: stmt, index: Int32(offset + 1))
}

var detailsByID: [String: InjectionChunk] = [:]
while sqlite3_step(stmt) == SQLITE_ROW {
let row: [String: Any] = [
"id": columnText(stmt, 0) as Any,
"content": columnText(stmt, 1) as Any,
"summary": columnText(stmt, 2) as Any,
"source": columnText(stmt, 3) as Any,
"source_file": columnText(stmt, 4) as Any,
"tags": columnText(stmt, 5) as Any,
"content_type": columnText(stmt, 6) as Any,
]
let detail = InjectionChunk(row: row)
detailsByID[detail.id] = detail
}
return orderedIDs.compactMap { detailsByID[$0] }
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

SQL variable limit could silently drop chunk metadata for high-cardinality events.

SQLite's default SQLITE_MAX_VARIABLE_NUMBER is 999. For events with ≥999 chunkIDs, sqlite3_prepare_v2 will fail and the caller's try? fallback returns empty chunks—silently degrading the event.

Consider batching queries (e.g., 500 IDs per batch) to avoid hitting the limit.

♻️ Suggested batching approach
 private func injectionChunkDetails(ids: [String]) throws -> [InjectionChunk] {
     guard let db else { throw DBError.notOpen }
     let orderedIDs = ids.filter { !$0.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty }
     guard !orderedIDs.isEmpty else { return [] }

+    let batchSize = 500
+    var detailsByID: [String: InjectionChunk] = [:]
+
+    for batchStart in stride(from: 0, to: orderedIDs.count, by: batchSize) {
+        let batchEnd = min(batchStart + batchSize, orderedIDs.count)
+        let batch = Array(orderedIDs[batchStart..<batchEnd])
+        let placeholders = Array(repeating: "?", count: batch.count).joined(separator: ",")
+        let sql = """
+            SELECT id, content, summary, source, source_file, tags, content_type
+            FROM chunks
+            WHERE id IN (\(placeholders))
+        """
+        var stmt: OpaquePointer?
+        guard sqlite3_prepare_v2(db, sql, -1, &stmt, nil) == SQLITE_OK else {
+            throw DBError.prepare(sqlite3_errcode(db))
+        }
+        defer { sqlite3_finalize(stmt) }
+
+        for (offset, id) in batch.enumerated() {
+            bindText(id, to: stmt, index: Int32(offset + 1))
+        }
+
+        while sqlite3_step(stmt) == SQLITE_ROW {
+            let row: [String: Any] = [
+                "id": columnText(stmt, 0) as Any,
+                "content": columnText(stmt, 1) as Any,
+                "summary": columnText(stmt, 2) as Any,
+                "source": columnText(stmt, 3) as Any,
+                "source_file": columnText(stmt, 4) as Any,
+                "tags": columnText(stmt, 5) as Any,
+                "content_type": columnText(stmt, 6) as Any,
+            ]
+            let detail = InjectionChunk(row: row)
+            detailsByID[detail.id] = detail
+        }
+    }
+
+    return orderedIDs.compactMap { detailsByID[$0] }
-    let placeholders = Array(repeating: "?", count: orderedIDs.count).joined(separator: ",")
-    // ... rest of current implementation
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@brain-bar/Sources/BrainBar/BrainDatabase.swift` around lines 4568 - 4604,
injectionChunkDetails currently builds one IN(...) statement which will fail
silently for large id arrays due to SQLite's SQLITE_MAX_VARIABLE_NUMBER limit;
modify injectionChunkDetails to batch orderedIDs into smaller chunks (e.g., 500
IDs per batch) and for each batch build the placeholders, call
sqlite3_prepare_v2, bindText for that batch, step through rows to populate
detailsByID, finalize the statement, then continue with the next batch; after
all batches are processed, return orderedIDs.compactMap { detailsByID[$0] } so
order is preserved and no metadata is dropped when the variable limit is
reached.

@EtanHey EtanHey merged commit cffb5ae into main May 26, 2026
7 checks passed
@EtanHey EtanHey deleted the feat/brainbar-item-F-injections-coverage branch May 26, 2026 22:37
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