Skip to content

feat(brainbar): show graph + injections panels with degradation resilience#315

Merged
EtanHey merged 6 commits into
mainfrom
feat/brainbar-show-graph-injections-no-degradation
May 23, 2026
Merged

feat(brainbar): show graph + injections panels with degradation resilience#315
EtanHey merged 6 commits into
mainfrom
feat/brainbar-show-graph-injections-no-degradation

Conversation

@EtanHey
Copy link
Copy Markdown
Owner

@EtanHey EtanHey commented May 22, 2026

Summary

Etan TOP PRIORITY ("IMPROVE BRAINBAR AND MAKE IT SHOW GRAPH AND INJESTIONS ALREADY, WITHOUT DEGRATION!").

Commit c25dc760 (Etan-authored) adds graph + injections UI surfaces with degradation resilience — the BrainBar UI now shows the KG graph view + injections panel without getting stuck on partial/degraded daemon state.

Diff

6 files changed, +229 / -13:

  • brain-bar/Sources/BrainBar/BrainBarWindowRootView.swift (+52/-1) — graph + injections panel wiring
  • brain-bar/Sources/BrainBar/DegradationState.swift (NEW, +28) — degradation state model
  • brain-bar/Sources/BrainBar/InjectionStore.swift (+8) — degradation hooks
  • brain-bar/Sources/BrainBar/KnowledgeGraph/KGViewModel.swift (+39/-3) — graph view-model degradation handling
  • brain-bar/Sources/BrainBarDaemon/DegradationState.swift (+1) — daemon-side mirror
  • brain-bar/Tests/BrainBarTests/DegradationStateTests.swift (NEW, +114) — regression test suite

Test plan

  • BrainBarTests/DegradationStateTests.swift — 114 lines of regression coverage
  • CI: test (3.11/3.12/3.13), lint, CodeRabbit, Cursor Bugbot, Macroscope - Correctness Check

Provenance

Branch was prepared by brainlayerCodex worker spawned via cmux MCP earlier in this session. Codex pushed the commit before its surface was lost in a cmux topology shift; no PR was opened by codex. PR created here by orc gen-6 per A1 Row 1 (Etan-authored + autonomous-mode declared).

Related

  • PR fix(brainbar): wire database + injection store on UI startup #314 (warming-memory wireRuntime fix, merged at 14:32Z) — predecessor for runtime install
  • Issue 1b investigation (docs.local/audits/2026-05-22-issue-1b-fts5-kg-investigation.md) — identified KG forward-only as the real "degradation without recovery" root cause; this PR addresses UI-side resilience, KG-ingest-side fix remains separate follow-up

Note

Medium Risk
Touches live data-refresh logic for the knowledge graph and injection feed, adding retry/polling and new state transitions that could impact UI update frequency and error recovery behavior.

Overview
Adds a shared DegradationState model and a small DegradationBadge UI indicator, and wires the badge into both the Graph and Injections tabs so users see when data may be stale instead of the surface blanking.

Makes the Graph tab resilient to transient SQLite read failures by retrying loads, continuously polling via loadGraphRepeatedly, and keeping last-known nodes/edges while degraded; also stabilizes layout on refresh/resize by preserving node positions/velocities and reseeding based on the actual drawable canvas size (KGCanvasMetrics).

Updates InjectionStore to publish degradationState and to only clear degraded status after a successful listInjectionEvents read (not just dataVersion), with added test coverage for the new degradation and recovery behavior.

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

Note

Show degradation badges on graph and injection panels in BrainBar with recovery resilience

  • Adds a DegradationBadge overlay (orange pill with exclamation icon) to both the graph and injection tabs in BrainBar, shown when the respective view model or store reports a degraded state.
  • Introduces a DegradationState enum (healthy / degraded(reason:)) shared across BrainBar and BrainBarDaemon via a symlink.
  • InjectionStore now publishes degradationState and stays degraded until an event query succeeds — a clean dataVersion read alone no longer clears the badge.
  • KGViewModel retries graph loads once on failure, preserves existing node positions/velocities across reloads, and polls repeatedly via loadGraphRepeatedly; node seeding uses the actual drawable canvas size via KGCanvasMetrics.
  • Adds tests covering degradation state semantics, recovery sequences, node position preservation, and canvas metrics calculations.

Macroscope summarized b06b74d.

Summary by CodeRabbit

Release Notes

New Features

  • Added visual degradation badge to indicate system health status in graph and injection views

Improvements

  • Features now gracefully handle transient read issues while maintaining last valid state and automatic recovery

Review Change Stack

…ience

Etan-mandate 2026-05-22: "IMPROVE BRAINBAR AND MAKE IT SHOW GRAPH AND
INJESTIONS ALREADY, WITHOUT DEGRATION!" + addendum: "AUTO MERGE AFTER
/pr-loop, not sloppy."

PR #314 wired the runtime database + InjectionStore so the "Warming
memory…" placeholder cleared and BrainBarGraphTab + BrainBarInjectionTab
get non-nil dependencies. But two failure modes remained:

1. When KG queries failed (typically transient ReadOnly / busy / locked
   from writer-pidfile contention with the Python enrich-supervisor +
   drain — PR #309), `KGViewModel.loadGraph` silently set nodes=[]/
   edges=[] and returned false. The UI had no way to distinguish "empty
   graph" from "queries failing," so the canvas blanked.
2. Same pattern in `InjectionStore.refresh`: failures only `NSLog`'d and
   the panel held stale or empty events with no user-visible signal.

This change adds a small `DegradationState` enum (`.healthy` /
`.degraded(reason:)`) published on both view-models. `loadGraph` retries
once on a 200ms backoff before reporting degraded, then keeps last-known-
good nodes/edges so the user sees prior data rather than a blank canvas.
`InjectionStore.refresh` flips state on failure and clears it on the next
successful poll cycle.

A small `DegradationBadge` (orange capsule with an exclamation-triangle
icon) overlays the top-right of each tab when its state is degraded, with
the underlying error in the SwiftUI .help tooltip. Per Etan's mandate:
degraded ≠ hidden.

Tests cover the enum surface, KG load-failure → degraded transition, KG
success → healthy transition, and InjectionStore initial-healthy state.
387/387 tests pass, swift build clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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 22, 2026

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.00% 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 accurately summarizes the main change: introducing degradation resilience to the graph and injections panels through a new DegradationState model, badge UI, and retry/recovery logic.
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-show-graph-injections-no-degradation

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.

Comment thread brain-bar/Sources/BrainBar/InjectionStore.swift
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: 2

🤖 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/Tests/BrainBarTests/DegradationStateTests.swift`:
- Around line 45-60: Add a test that verifies a degraded→healthy recovery by
first forcing a failing load to set KGViewModel.degradationState to .degraded
(e.g., make vm.loadGraph() fail or simulate DB error), then fix the underlying
data/state (insert the two entities and relation as in
testLoadGraphSuccessKeepsDegradationStateHealthy) and call vm.loadGraph() again;
assert the second call returns true and vm.degradationState is .healthy and
nodes/edges are populated. Reference KGViewModel, loadGraph(), degradationState,
nodes, and edges when locating where to add the test.
- Line 5: The KGDegradationStateTests test class (and the other two test classes
flagged by SwiftLint) lack an explicit deinit which triggers the required_deinit
lint rule; add a simple explicit deinit { } to each test class (for example in
KGDegradationStateTests) to satisfy the rule—locate the class declarations
(e.g., class KGDegradationStateTests: XCTestCase) and add an empty deinitializer
method in each class body.
🪄 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: 7fd1448d-1bcf-4492-b515-88b12a1e0896

📥 Commits

Reviewing files that changed from the base of the PR and between 379d65a and c25dc76.

📒 Files selected for processing (6)
  • brain-bar/Sources/BrainBar/BrainBarWindowRootView.swift
  • brain-bar/Sources/BrainBar/DegradationState.swift
  • brain-bar/Sources/BrainBar/InjectionStore.swift
  • brain-bar/Sources/BrainBar/KnowledgeGraph/KGViewModel.swift
  • brain-bar/Sources/BrainBarDaemon/DegradationState.swift
  • brain-bar/Tests/BrainBarTests/DegradationStateTests.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: Cursor Bugbot
  • GitHub Check: test (3.13)
  • GitHub Check: test (3.11)
  • GitHub Check: test (3.12)
🧰 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/DegradationState.swift
  • brain-bar/Sources/BrainBar/InjectionStore.swift
  • brain-bar/Sources/BrainBar/KnowledgeGraph/KGViewModel.swift
  • brain-bar/Sources/BrainBar/BrainBarWindowRootView.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/DegradationState.swift
  • brain-bar/Sources/BrainBar/InjectionStore.swift
  • brain-bar/Sources/BrainBar/BrainBarWindowRootView.swift
🪛 SwiftLint (0.63.2)
brain-bar/Tests/BrainBarTests/DegradationStateTests.swift

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

(required_deinit)


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

(required_deinit)


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

(required_deinit)

🔇 Additional comments (5)
brain-bar/Sources/BrainBar/DegradationState.swift (1)

15-27: LGTM!

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

21-21: LGTM!

Also applies to: 106-115

brain-bar/Sources/BrainBar/KnowledgeGraph/KGViewModel.swift (1)

12-12: LGTM!

Also applies to: 38-68

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

753-763: LGTM!

Also applies to: 775-785, 794-811

brain-bar/Sources/BrainBarDaemon/DegradationState.swift (1)

1-1: ⚡ Quick win

Confirm symlink forwarder is valid (no build-failure risk)
brain-bar/Sources/BrainBarDaemon/DegradationState.swift is committed as a real git symlink (mode 120000) and is a symlink on disk pointing to ../BrainBar/DegradationState.swift, so it won’t be treated as plain text Swift source.

Comment thread brain-bar/Tests/BrainBarTests/DegradationStateTests.swift
Comment thread brain-bar/Tests/BrainBarTests/DegradationStateTests.swift
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: c25dc7602c

ℹ️ 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".

} catch {
lastError = error
if attempt < attemptLimit {
try? await Task.sleep(nanoseconds: 200_000_000)
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 Honor task cancellation before retrying graph load

If the graph view disappears while loadGraph() is running, SwiftUI cancels the .task, but the retry path swallows CancellationError via try? await Task.sleep(...) and still performs the second fetchGraphRows attempt. That means canceled loads keep reading SQLite and can still update degradation state/logs after the UI has gone away, increasing unnecessary DB contention in exactly the lock-sensitive path this change targets. Exit early on cancellation before sleeping/retrying.

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 23, 2026

@coderabbitai review

@EtanHey
Copy link
Copy Markdown
Owner Author

EtanHey commented May 23, 2026

@codex review

@EtanHey
Copy link
Copy Markdown
Owner Author

EtanHey commented May 23, 2026

@cursor @BugBot re-review

@cursor
Copy link
Copy Markdown

cursor Bot commented May 23, 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 23, 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.

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

Codex Review: Didn't find any major issues. 👍

ℹ️ 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

@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/Tests/BrainBarTests/InjectionStoreTests.swift`:
- Around line 152-160: The test helper function listInjectionEvents currently
returns an empty array when eventResults is empty, hiding extra calls; change
the guard in listInjectionEvents to fail fast by throwing a clear test-time
error (e.g., a custom UnexpectedCall/test assertion or call preconditionFailure)
when eventResults is exhausted instead of returning []; update any test
scaffolding that expects this behavior and reference listInjectionEvents and the
eventResults buffer when making the change.
🪄 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: 3ca7001e-a58f-4b95-ac71-84211eb70746

📥 Commits

Reviewing files that changed from the base of the PR and between c25dc76 and 6c885a1.

📒 Files selected for processing (2)
  • brain-bar/Sources/BrainBar/InjectionStore.swift
  • brain-bar/Tests/BrainBarTests/InjectionStoreTests.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). (5)
  • GitHub Check: Cursor Bugbot
  • GitHub Check: test (3.13)
  • GitHub Check: test (3.12)
  • GitHub Check: test (3.11)
  • 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/InjectionStore.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/InjectionStore.swift
🪛 SwiftLint (0.63.2)
brain-bar/Tests/BrainBarTests/InjectionStoreTests.swift

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

(required_deinit)

🔇 Additional comments (2)
brain-bar/Sources/BrainBar/InjectionStore.swift (1)

4-24: LGTM!

Also applies to: 42-73, 109-109, 130-130, 137-173

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

88-129: LGTM!

Also applies to: 132-150, 162-171

Comment thread brain-bar/Tests/BrainBarTests/InjectionStoreTests.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 23, 2026

@cursor @BugBot re-review

@EtanHey
Copy link
Copy Markdown
Owner Author

EtanHey commented May 23, 2026

@coderabbitai review

@EtanHey
Copy link
Copy Markdown
Owner Author

EtanHey commented May 23, 2026

@codex review

@cursor
Copy link
Copy Markdown

cursor Bot commented May 23, 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 23, 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.

Comment on lines 166 to 169
if let id {
guard let database else { return }
if let lookup = try? database.lookupEntity(query: nodeById(id)?.name ?? "") {
selectedEntity = EntityCard(lookupPayload: lookup)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Medium KnowledgeGraph/KGViewModel.swift:166

In selectNode(id:), when database.lookupEntity returns nil (entity not found), selectedEntity retains its previous value from an earlier selection. This leaves the UI in a mismatched state: selectedNodeId points to the newly tapped node, but selectedEntity displays the card of a previously selected, different entity. Consider updating selectedEntity = nil when the lookup fails.

             if let lookup = try? database.lookupEntity(query: nodeById(id)?.name ?? "") {
-                selectedEntity = EntityCard(lookupPayload: lookup)
+                selectedEntity = lookup.map { EntityCard(lookupPayload: $0) }
             }
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file brain-bar/Sources/BrainBar/KnowledgeGraph/KGViewModel.swift around lines 166-169:

In `selectNode(id:)`, when `database.lookupEntity` returns `nil` (entity not found), `selectedEntity` retains its previous value from an earlier selection. This leaves the UI in a mismatched state: `selectedNodeId` points to the newly tapped node, but `selectedEntity` displays the card of a previously selected, different entity. Consider updating `selectedEntity = nil` when the lookup fails.

Evidence trail:
brain-bar/Sources/BrainBar/KnowledgeGraph/KGViewModel.swift lines 164-177 at REVIEWED_COMMIT. Line 165 sets selectedNodeId = id unconditionally. Line 168's `if let lookup = try? database.lookupEntity(...)` only updates selectedEntity on success (line 169). On failure (nil return or throw), selectedEntity is not cleared, while selectedNodeId and selectedEntityChunks (line 171) reflect the new node.

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Chef's kiss.

ℹ️ 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

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

🤖 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/KnowledgeGraph/KGViewModel.swift`:
- Around line 39-42: The initializer init(graphReader:) currently sets database
= nil causing selectNode(_:) and openConversation(_:) to silently no-op; change
these code paths to fail fast and surface errors: in init(graphReader:)
document/mark that database is missing (or make it failable), and inside
selectNode and openConversation add a guard that throws or calls the existing
error/reporting callback (or asserts) when database is nil instead of returning
quietly; reference the database property, init(graphReader:), selectNode(_:),
openConversation(_:), and KnowledgeGraphReading to locate where to add the guard
and error propagation so callers receive an explicit error when the DB
dependency is absent.

In `@brain-bar/Tests/BrainBarTests/DegradationStateTests.swift`:
- Around line 170-205: Add a brief comment above ScriptedKnowledgeGraphReader
explaining why `@unchecked` Sendable is safe: note that
ScriptedKnowledgeGraphReader mutates results and activeRelations and thus is not
intrinsically Sendable, but it is only instantiated and used within `@MainActor`
test contexts (never passed across actors or threads), so the unchecked
conformance is safe; reference the class name ScriptedKnowledgeGraphReader and
the mutable properties results and activeRelations, and mention the methods
fetchKGEntities and fetchKGRelations as the only access points to make the
intended confinement explicit.
- Around line 64-110: The test
testLoadGraphUntilSuccessfulRecoversAfterInitialDegradation asserts the wrong
retry count: loadGraphUntilSuccessful calls the trailing sleep closure after
each failed loadGraph() attempt, so with two failures then a success the retry
counter retrySleeps should be 2; update the assertion
XCTAssertEqual(retrySleeps, 1) to XCTAssertEqual(retrySleeps, 2) (reference
symbols: testLoadGraphUntilSuccessfulRecoversAfterInitialDegradation,
KGViewModel.loadGraphUntilSuccessful, retrySleeps, XCTAssertEqual).
🪄 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: 1d57794c-2a9e-4a16-ba8d-cbad6c2877e5

📥 Commits

Reviewing files that changed from the base of the PR and between 6c885a1 and 2baef0f.

📒 Files selected for processing (5)
  • brain-bar/Sources/BrainBar/DegradationState.swift
  • brain-bar/Sources/BrainBar/KnowledgeGraph/KGCanvasView.swift
  • brain-bar/Sources/BrainBar/KnowledgeGraph/KGViewModel.swift
  • brain-bar/Tests/BrainBarTests/DegradationStateTests.swift
  • brain-bar/Tests/BrainBarTests/InjectionStoreTests.swift
📜 Review details
🧰 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/DegradationState.swift
  • brain-bar/Sources/BrainBar/KnowledgeGraph/KGCanvasView.swift
  • brain-bar/Sources/BrainBar/KnowledgeGraph/KGViewModel.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/DegradationState.swift
🔇 Additional comments (8)
brain-bar/Tests/BrainBarTests/InjectionStoreTests.swift (1)

134-134: LGTM!

Also applies to: 138-138, 156-158

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

1-28: LGTM!

brain-bar/Tests/BrainBarTests/DegradationStateTests.swift (4)

6-6: LGTM!

Also applies to: 115-115, 145-145


25-62: LGTM!


113-141: LGTM!


143-168: LGTM!

brain-bar/Sources/BrainBar/KnowledgeGraph/KGViewModel.swift (1)

3-8: LGTM!

Also applies to: 46-125

brain-bar/Sources/BrainBar/KnowledgeGraph/KGCanvasView.swift (1)

59-63: LGTM!

Comment thread brain-bar/Sources/BrainBar/KnowledgeGraph/KGViewModel.swift
Comment on lines +64 to +110
func testLoadGraphUntilSuccessfulRecoversAfterInitialDegradation() async {
// Regression guard: Cursor Bugbot PR #315 flagged that KGCanvasView
// only performed one load. A first failed read must be retried so the
// graph badge can clear after a later successful graph query.
let reader = ScriptedKnowledgeGraphReader(results: [
.failure(ScriptedKnowledgeGraphError.fetchFailed),
.failure(ScriptedKnowledgeGraphError.fetchFailed),
.success((
entities: [
BrainDatabase.KGEntityRow(
id: "a",
name: "Alice",
entityType: "person",
description: nil,
importance: 7
),
BrainDatabase.KGEntityRow(
id: "b",
name: "BrainLayer",
entityType: "project",
description: nil,
importance: 8
),
],
relations: [
BrainDatabase.KGRelationRow(
id: "r1",
sourceId: "a",
targetId: "b",
relationType: "builds"
),
]
)),
])
let vm = KGViewModel(graphReader: reader)
var retrySleeps = 0

let success = await vm.loadGraphUntilSuccessful(retryDelay: .milliseconds(1)) { _ in
retrySleeps += 1
}

XCTAssertTrue(success)
XCTAssertEqual(vm.degradationState, .healthy)
XCTAssertEqual(vm.nodes.count, 2)
XCTAssertEqual(vm.edges.count, 1)
XCTAssertEqual(retrySleeps, 1)
}
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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Verify loadGraphUntilSuccessful retry and callback semantics

ast-grep --pattern $'func loadGraphUntilSuccessful($$$) async -> Bool {
  $$$
}'

Repository: EtanHey/brainlayer

Length of output: 44


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Find all references/definitions of loadGraphUntilSuccessful
rg -n --hidden --no-ignore-vcs "loadGraphUntilSuccessful" .

# 2) Show nearby implementation blocks for any hits (limit output per file)
#    We'll just cat the relevant file sections after we identify them.

Repository: EtanHey/brainlayer

Length of output: 449


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE_VM="brain-bar/Sources/BrainBar/KnowledgeGraph/KGViewModel.swift"
FILE_VIEW="brain-bar/Sources/BrainBar/KnowledgeGraph/KGCanvasView.swift"

echo "=== KGViewModel.swift (around loadGraphUntilSuccessful) ==="
nl -ba "$FILE_VM" | sed -n '70,170p'

echo
echo "=== KGCanvasView.swift (call site) ==="
nl -ba "$FILE_VIEW" | sed -n '40,110p'

Repository: EtanHey/brainlayer

Length of output: 183


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE_VM="brain-bar/Sources/BrainBar/KnowledgeGraph/KGViewModel.swift"
FILE_VIEW="brain-bar/Sources/BrainBar/KnowledgeGraph/KGCanvasView.swift"

echo "=== KGViewModel.swift (around loadGraphUntilSuccessful) ==="
cat -n "$FILE_VM" | sed -n '70,170p'

echo
echo "=== KGCanvasView.swift (call site) ==="
cat -n "$FILE_VIEW" | sed -n '40,110p'

Repository: EtanHey/brainlayer

Length of output: 7517


Fix retry count assertion in DegradationStateTests
loadGraphUntilSuccessful invokes the trailing closure (the sleep parameter) after every failed loadGraph() attempt. With 2 initial failures followed by success, retrySleeps should be 2, not 1 (update the XCTAssertEqual(retrySleeps, 1) accordingly).

🤖 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/Tests/BrainBarTests/DegradationStateTests.swift` around lines 64 -
110, The test testLoadGraphUntilSuccessfulRecoversAfterInitialDegradation
asserts the wrong retry count: loadGraphUntilSuccessful calls the trailing sleep
closure after each failed loadGraph() attempt, so with two failures then a
success the retry counter retrySleeps should be 2; update the assertion
XCTAssertEqual(retrySleeps, 1) to XCTAssertEqual(retrySleeps, 2) (reference
symbols: testLoadGraphUntilSuccessfulRecoversAfterInitialDegradation,
KGViewModel.loadGraphUntilSuccessful, retrySleeps, XCTAssertEqual).

Comment thread brain-bar/Tests/BrainBarTests/DegradationStateTests.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 23, 2026

@cursor @BugBot re-review

@EtanHey
Copy link
Copy Markdown
Owner Author

EtanHey commented May 23, 2026

@coderabbitai review

@EtanHey
Copy link
Copy Markdown
Owner Author

EtanHey commented May 23, 2026

@codex review

@cursor
Copy link
Copy Markdown

cursor Bot commented May 23, 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 23, 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.

Comment thread brain-bar/Sources/BrainBar/KnowledgeGraph/KGCanvasView.swift Outdated
Comment thread brain-bar/Sources/BrainBar/KnowledgeGraph/KGCanvasView.swift
Comment thread brain-bar/Sources/BrainBar/KnowledgeGraph/KGViewModel.swift
@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Nice work!

ℹ️ 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".

@EtanHey
Copy link
Copy Markdown
Owner Author

EtanHey commented May 23, 2026

@coderabbitai review

@EtanHey
Copy link
Copy Markdown
Owner Author

EtanHey commented May 23, 2026

@cursor @BugBot re-review

@EtanHey
Copy link
Copy Markdown
Owner Author

EtanHey commented May 23, 2026

@codex review

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 23, 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 23, 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 2 potential issues.

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 581bd2c. Configure here.

Comment thread brain-bar/Sources/BrainBar/KnowledgeGraph/KGViewModel.swift
Comment thread brain-bar/Sources/BrainBar/KnowledgeGraph/KGCanvasView.swift
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: 581bd2c233

ℹ️ 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 +80 to 84
if await viewModel.loadGraphRepeatedly(onSuccessfulLoad: {
hasLoadedGraph = true
}) {
hasLoadedGraph = true
}
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 Stop graph polling when tab is not visible

KGCanvasView now starts an infinite loadGraphRepeatedly loop in .task, but BrainBarWindowRootView keeps previously activated tabs mounted and only hides them via opacity (brainBarTabVisibility). That means once a user visits Graph once, background graph reads continue every 30s even after switching away, creating ongoing SQLite read pressure and avoidable lock contention against writes. Gate this loop on active tab visibility or cancel it when the graph tab is hidden.

Useful? React with 👍 / 👎.

@EtanHey
Copy link
Copy Markdown
Owner Author

EtanHey commented May 23, 2026

@cursor @BugBot re-review

@EtanHey
Copy link
Copy Markdown
Owner Author

EtanHey commented May 23, 2026

@coderabbitai review

@EtanHey
Copy link
Copy Markdown
Owner Author

EtanHey commented May 23, 2026

@codex review

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 23, 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 23, 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: b06b74d907

ℹ️ 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 76 to +80
.task {
guard !hasLoadedGraph else { return }
if await viewModel.loadGraph() {
guard !hasStartedGraphPolling else { return }
hasStartedGraphPolling = true
defer { hasStartedGraphPolling = false }
if await viewModel.loadGraphRepeatedly(onSuccessfulLoad: {
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 Stop graph polling when graph view is not active

loadGraphRepeatedly is started from .task and loops until cancellation, but this view is kept mounted after first visit (the tab system hides inactive tabs with opacity rather than removing them). In that state, opening Graph once and switching away still leaves this loop running, so it continues periodic DB reads/retries in the background; with known SQLite lock contention, this can add avoidable read pressure and stale/degraded state churn even when the graph tab is not visible. Tie the polling lifecycle to actual tab visibility (or cancel on inactivity) instead of running unconditionally for the lifetime of the mounted view.

Useful? React with 👍 / 👎.

@EtanHey EtanHey merged commit 5e8c315 into main May 23, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant