Skip to content

feat: link fix suggestions to summary via [S:id] markers#269

Merged
damianloch merged 11 commits into
mainfrom
feat/suggestion-markers
Jun 3, 2026
Merged

feat: link fix suggestions to summary via [S:id] markers#269
damianloch merged 11 commits into
mainfrom
feat/suggestion-markers

Conversation

@beng360
Copy link
Copy Markdown
Contributor

@beng360 beng360 commented Apr 15, 2026

Summary

  • Fix suggestions created by github_fix during RCA were invisible in the UI because the word-matching heuristic between summary text and suggestion titles often failed when wording diverged
  • The summarizer now passes pre-existing type='fix' suggestions into the LLM prompt, which emits [S:id] markers inline next to the relevant "Suggested Next Steps" item
  • The frontend citation parser recognizes [S:id] markers and renders them as green GitBranch buttons that open the FixSuggestionModal directly — no word-matching needed

How it works

Backend (server/chat/background/summarization.py):

  1. New _fetch_fix_suggestions() queries incident_suggestions WHERE type = 'fix' before summary generation
  2. Fix suggestions are injected into the LLM prompt as a CODE FIX SUGGESTIONS block with [S:id] markers
  3. The LLM is instructed to place the marker at the end of the relevant next step bullet

Frontend (client/src/app/incidents/components/IncidentCard.tsx):

  1. renderTextWithCitations regex extended to also split on [S:\d+] patterns
  2. [S:id] markers render as clickable GitBranch buttons that open FixSuggestionModal
  3. <li> handler skips word-matching fallback when a [S:id] marker is present (prevents duplicate buttons)

Test plan

  • Trigger an RCA that produces a github_fix suggestion
  • Verify the regenerated summary contains [S:id] marker in the next steps
  • Verify the green GitBranch button appears inline in the UI
  • Verify clicking the button opens FixSuggestionModal with correct suggestion
  • Verify incidents without fix suggestions are unaffected (no [S:] markers, word-matching still works)
  • Verify the fallback word-matching still works for non-fix suggestions (diagnostic, mitigation)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Incident cards now display code fix suggestions as interactive green buttons, enabling users to quickly select and apply fixes inline.
    • Auto-generated incident summaries have been enhanced to include code fix suggestions within the "Suggested Next Steps" section, providing actionable remediation guidance.

Previously, fix suggestions created by github_fix during RCA were
matched to summary "Suggested Next Steps" items using word overlap
heuristics. This frequently failed when the summary wording diverged
from the suggestion title, leaving the fix button invisible to users.

Backend: the summarizer now fetches pre-existing type='fix' suggestions
and includes them in the LLM prompt with [S:id] markers. The LLM emits
these markers inline next to the relevant next step.

Frontend: the citation parser now recognizes [S:id] markers and renders
them as green GitBranch buttons that open the FixSuggestionModal
directly. Word-matching is skipped for list items that already have a
marker, preventing duplicates.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 15, 2026

Review Change Stack

Warning

Review limit reached

@beng360, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 20 minutes and 26 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 6cbcedb5-0327-4e55-9aa8-b10244f58b0f

📥 Commits

Reviewing files that changed from the base of the PR and between 6f8e4b3 and 69d7ef2.

📒 Files selected for processing (1)
  • server/chat/background/summarization.py

Walkthrough

The PR adds end-to-end support for inline fix-suggestion markers ([S:id]). The frontend recognizes these markers in rendered text and displays interactive GitBranch buttons for fix actions when write permission is granted. The backend fetches pre-existing fix suggestions from the database and embeds them into summarization prompts, allowing the LLM to reference them in suggested next steps.

Changes

Fix-suggestion markers end-to-end

Layer / File(s) Summary
Frontend marker parsing and UI actions
client/src/app/incidents/components/IncidentCard.tsx
Adds findFixSuggestionById helper and extends renderTextWithCitations to split on [S:id] markers in addition to citations. Markers render a green GitBranch button that sets selectedFixSuggestion when canWrite is enabled (otherwise render null). List-item action resolution detects markers and disables the fallback findMatchingSuggestion logic. Hook dependencies updated to include canWrite and the new helper.
Backend fix suggestion fetching
server/chat/background/summarization.py
Adds _fetch_fix_suggestions(incident_id, _retries=2) to query incident_suggestions for fix-type entries, map them to structured dicts, retry on transient failures, and log on error. Integration in generate_incident_summary_from_chat fetches suggestions early and logs the count when any are found.
Prompt integration and embedding
server/chat/background/summarization.py
Updates _build_summary_prompt_with_chat to accept optional fix_suggestions parameter. When provided, constructs a "CODE FIX SUGGESTIONS" reference block with [S:id] markers and explicit instructions to use those markers inline in "Suggested Next Steps". Block is appended to both citation-based and transcript-fallback prompt variants before passing to the LLM.

Sequence Diagrams

sequenceDiagram
  participant IncidentCard
  participant TextRenderer
  participant FixAction
  IncidentCard->>TextRenderer: renderTextWithCitations(text)
  TextRenderer->>TextRenderer: split on [S:id] markers
  alt marker found
    TextRenderer->>TextRenderer: findFixSuggestionById(id)
    alt canWrite enabled
      TextRenderer->>FixAction: render GitBranch button
      FixAction->>IncidentCard: setSelectedFixSuggestion(id)
    else canWrite disabled
      TextRenderer-->>TextRenderer: render null
    end
  else citation found
    TextRenderer->>FixAction: render citation link
  end
Loading
sequenceDiagram
  participant SummarizationTask
  participant FetchHelper
  participant Database
  SummarizationTask->>FetchHelper: _fetch_fix_suggestions(incident_id)
  FetchHelper->>Database: query incident_suggestions where type='fix'
  alt success
    Database-->>FetchHelper: rows
    FetchHelper-->>SummarizationTask: [{ id, title, description, file_path, repository }, ...]
  else transient error with retries
    FetchHelper->>Database: retry with backoff
  else exhausted retries
    FetchHelper-->>SummarizationTask: []
  end
  SummarizationTask->>SummarizationTask: log count if non-empty
Loading
sequenceDiagram
  participant SummarizationService
  participant PromptBuilder
  participant LLM
  SummarizationService->>PromptBuilder: _build_summary_prompt_with_chat(fix_suggestions=[...])
  alt fix_suggestions provided
    PromptBuilder->>PromptBuilder: build CODE FIX SUGGESTIONS block with [S:id] markers
    PromptBuilder->>PromptBuilder: append to prompt with usage instructions
  end
  PromptBuilder->>LLM: send complete prompt (both citation and transcript paths)
  LLM-->>SummarizationService: incident summary (may include [S:id] refs)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • Arvo-AI/aurora#414: Both PRs modify server/chat/background/summarization.py's chat incident summary prompt construction in the same code paths—the main PR adds inline [S:id] code-fix markers, while the retrieved PR injects an "AGENT REASONING" block—so changes may require coordination at shared prompt-building stages.

Suggested reviewers

  • OlivierTrudeau

Poem

🐰 A rabbit hops through markers bright,
[S:id] suggestions, shining green in sight,
From database fetch to prompt so grand,
Inline fixes at the UI's hand,
Code-fix dreams now flow end-to-end! 🌿

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding a feature to link fix suggestions to summaries using [S:id] markers.
Docstring Coverage ✅ Passed Docstring coverage is 85.71% which is sufficient. The required threshold is 80.00%.
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.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/suggestion-markers

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

❤️ Share

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

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@client/src/app/incidents/components/IncidentCard.tsx`:
- Around line 186-207: The code currently only handles suggestion markers when
canWrite is true, leaving raw "[S:id]" tokens visible to read-only users; modify
the logic in the suggestion-handling block (the suggestionMatch check that uses
findFixSuggestionById and setSelectedFixSuggestion) to first check for
suggestionMatch unconditionally and, if present, always consume the marker by
returning null for non-writers, and only render the GitBranch button when
canWrite and a suggestion exists; in other words: change the condition from if
(suggestionMatch && canWrite) { ... } to if (suggestionMatch) { const suggestion
= findFixSuggestionById(...); if (canWrite && suggestion) return (<button ...
/>); return null; }.

In `@server/chat/background/summarization.py`:
- Around line 338-365: The _fetch_fix_suggestions() block currently catches all
exceptions, logs a minimal warning, and returns [], hiding transient DB errors;
instead log the full exception (use logger.exception or include traceback) and
re-raise the exception so the outer generate_incident_summary_from_chat() /
Celery retry path can handle it; specifically update the try/except around
db_pool.get_admin_connection() / cursor.execute() to replace logger.warning(...)
with a full exception log and then raise the caught exception (or omit
swallowing) rather than returning an empty list.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 935383a3-bfb0-4deb-8f8f-ed4008117433

📥 Commits

Reviewing files that changed from the base of the PR and between e3a1471 and 1b39852.

📒 Files selected for processing (2)
  • client/src/app/incidents/components/IncidentCard.tsx
  • server/chat/background/summarization.py

Comment thread client/src/app/incidents/components/IncidentCard.tsx Outdated
Comment thread server/chat/background/summarization.py Outdated
…gestion fetch

- IncidentCard: always match [S:id] markers so read-only users don't see raw tokens
- summarization: retry _fetch_fix_suggestions up to 2 times on transient DB failures

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Comment thread server/chat/background/summarization.py Fixed
@beng360 beng360 added the bug Something isn't working label Apr 21, 2026
…gestion-markers

# Conflicts:
#	server/chat/background/summarization.py
@OlivierTrudeau OlivierTrudeau requested a review from a team as a code owner April 25, 2026 22:41
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

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

⚠️ Outside diff range comments (1)
server/chat/backend/agent/tools/mcp_tools.py (1)

307-311: 🧹 Nitpick | 🔵 Trivial

Stale comment — token is no longer passed via Docker -e.

This branch is now reachable for server_type == "github" but the comment claims the token was set via Docker args; actually the new GitHub-specific env block at Lines 249–258 is what writes GITHUB_PERSONAL_ACCESS_TOKEN. The pass is functionally fine, but the comment will mislead the next reader.

♻️ Update the stale Docker comment
-                # GitHub credentials are passed via Docker -e flag in the command itself
-                # (handled above when building the docker command)
                 elif server_type == "github":
-                    # Token already passed in Docker command args, nothing to add to env
+                    # GITHUB_PERSONAL_ACCESS_TOKEN / GITHUB_TOOLSETS are already
+                    # set in the GitHub-specific env block above; nothing to add here.
                     pass
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/chat/backend/agent/tools/mcp_tools.py` around lines 307 - 311, Update
the stale inline comment in the server_type == "github" branch in mcp_tools.py:
replace the misleading text that says "Token already passed in Docker command
args" with a concise note that the GitHub token is set by the GitHub-specific
env block (writes GITHUB_PERSONAL_ACCESS_TOKEN), or remove the comment entirely;
keep the pass statement as-is (the branch is intentionally no-op) and reference
the GitHub env block handling around the earlier GITHUB_PERSONAL_ACCESS_TOKEN
assignment to clarify where the token is actually injected.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@client/src/app/incidents/components/IncidentCard.tsx`:
- Around line 296-298: The code currently sets hasSuggestionMarker and, if true,
suppresses findMatchingSuggestion for the whole bullet which hides non-fix
actions whenever any [S:id] marker appears; instead remove the markers from the
input passed to the matcher so findMatchingSuggestion can still detect other
actions. Concretely, keep findMatchingSuggestion but call it with a cleaned
string (e.g., const cleanedText = textContent.replace(/\[S:\d+\]/g, '').trim())
and compute matchingSuggestion from cleanedText; you can keep
hasSuggestionMarker for any other logic but do not use it to short-circuit
calling findMatchingSuggestion. Ensure you update the matchingSuggestion
assignment (and any downstream code that depends on matchingSuggestion) to use
the cleanedText result.

In `@server/chat/backend/agent/tools/github_mcp_utils.py`:
- Around line 141-146: The current fallback in parse_file_content decodes base64
raw bytes with latin-1 which will silently accept binary blobs; change the logic
in github_mcp_utils.py (the base64.b64decode -> raw decoding block where
variables raw and decoded are used) to first detect obvious binary (e.g., if
b'\x00' in raw or a high non-text byte ratio) and treat those as
non-text/binary, otherwise decode using utf-8 with a safe fallback such as
raw.decode("utf-8", errors="replace") (or if you keep a fallback branch, avoid
using plain latin-1 and instead mark replacements) so binary artifacts are not
passed through as garbled source for the suggestion pipeline.

In `@server/chat/backend/agent/tools/mcp_tools.py`:
- Line 237: Remove the unnecessary f-string prefix from the logging call in
mcp_tools.py (the logging.info line that currently reads f" GitHub MCP server
command prepared (npx with all toolsets)"); change it to a plain string and also
adjust the message to not claim "all toolsets" since GITHUB_TOOLSETS is not used
by the archived `@modelcontextprotocol/server-github` package—e.g., use a neutral
phrase like "with npx and configured toolsets" to avoid misleading wording while
keeping the log location tied to the existing logging.info call.
- Around line 120-121: The code currently invokes the deprecated npm package
"@modelcontextprotocol/server-github" via npx in the MCP server command (see the
command entry using npx) and force-restarts it on every RCA call (the restart
logic referenced as the RCA restart), which causes security, performance, and
toolset-recognition issues (GITHUB_TOOLSETS and allowed_github_mcp_tools
filtering). Replace the npx invocation with the official GitHub MCP server
deployment (use the ghcr.io/github/github-mcp-server Docker image or build/run
the Go binary from github.com/github/github-mcp-server), preserve and use the
existing environment-based token injection (keep the env-var token logic)
instead of Docker-arg comments, remove the per-RCA forced restart so the server
is reused between calls, and update the stale comment about Docker token passage
to reflect current env-based token injection and ensure GITHUB_TOOLSETS is
passed/recognized by the chosen server implementation.

---

Outside diff comments:
In `@server/chat/backend/agent/tools/mcp_tools.py`:
- Around line 307-311: Update the stale inline comment in the server_type ==
"github" branch in mcp_tools.py: replace the misleading text that says "Token
already passed in Docker command args" with a concise note that the GitHub token
is set by the GitHub-specific env block (writes GITHUB_PERSONAL_ACCESS_TOKEN),
or remove the comment entirely; keep the pass statement as-is (the branch is
intentionally no-op) and reference the GitHub env block handling around the
earlier GITHUB_PERSONAL_ACCESS_TOKEN assignment to clarify where the token is
actually injected.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7ef69909-62c9-42bc-b525-8253bda39f6c

📥 Commits

Reviewing files that changed from the base of the PR and between 1b39852 and a578dc5.

📒 Files selected for processing (3)
  • client/src/app/incidents/components/IncidentCard.tsx
  • server/chat/backend/agent/tools/github_mcp_utils.py
  • server/chat/backend/agent/tools/mcp_tools.py

Comment thread client/src/app/incidents/components/IncidentCard.tsx
Comment thread server/chat/backend/agent/tools/github_mcp_utils.py Outdated
Comment thread server/chat/backend/agent/tools/mcp_tools.py Outdated
Comment thread server/chat/backend/agent/tools/mcp_tools.py Outdated
@sonarqubecloud
Copy link
Copy Markdown

Resolve conflicts:
- mcp_tools.py: keep native github-mcp-server binary (from main)
- summarization.py: combine fix_suggestions (branch) with agent_reasoning (main)
Comment thread client/src/app/incidents/components/IncidentCard.tsx Outdated
Comment thread server/chat/backend/agent/tools/github_mcp_utils.py
Comment thread scripts/validate-env-vars.sh
Comment thread server/chat/backend/agent/tools/mcp_tools.py
Comment thread server/chat/background/summarization.py
- IncidentCard: viewers can see suggestion markers (disabled, not hidden)
- Revert github_mcp_utils.py (fixed upstream)
- Revert validate-env-vars.sh (not needed)
- Revert mcp_tools.py (fixed upstream)
- Remove dead code in summarization.py line 384

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Comment thread server/chat/background/summarization.py Fixed
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (1)
client/src/app/incidents/components/IncidentCard.tsx (1)

338-390: 🧹 Nitpick | 🔵 Trivial | 💤 Low value

Inconsistent permission handling: marker buttons vs. word-matching buttons.

Marker-based GitBranch buttons (lines 238–245) are visible-but-disabled for viewers, while word-matching GitBranch and Play buttons (lines 345–390) are completely hidden when !canWrite. This creates two different UX patterns:

  • [S:id] marker buttons: viewers see a disabled button (can't interact)
  • Word-matched suggestions: viewers see nothing

If this distinction is intentional (e.g., marker buttons are high-fidelity LLM pointers that should be visible to indicate "a fix was suggested here," while heuristic matches are less reliable and shouldn't clutter the viewer's UI), consider adding a code comment to document the rationale. Otherwise, unify the pattern—either hide both or show both as disabled for viewers.

🤖 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 `@client/src/app/incidents/components/IncidentCard.tsx` around lines 338 - 390,
The code shows marker-based buttons for viewers as visible-but-disabled but
hides word-matching suggestion buttons entirely when !canWrite; update the
behavior to be consistent by either (A) always render the suggestion button when
matchingSuggestion exists but mark it disabled/uninteractive for viewers, or (B)
explicitly document the intent with a comment. To implement (A) change the
canShowAction logic (used to decide rendering) so it doesn't gate visibility on
canWrite alone (e.g., allow rendering when matchingSuggestion exists) and
instead apply disabled behavior and UI tweaks when !canWrite: add the disabled
attribute and a non-interactive CSS state (e.g., cursor-not-allowed/opacity) to
the button render path that uses isFixType,
setSelectedFixSuggestion/setSelectedSuggestion, matchingSuggestion,
canShowAction and wasExecuted/execStatus so viewers still see the indicator but
cannot interact; alternatively, if choosing (B) add a clear comment near
canShowAction explaining why marker buttons are visible for viewers but
heuristic matches are hidden.
🤖 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.

Outside diff comments:
In `@client/src/app/incidents/components/IncidentCard.tsx`:
- Around line 338-390: The code shows marker-based buttons for viewers as
visible-but-disabled but hides word-matching suggestion buttons entirely when
!canWrite; update the behavior to be consistent by either (A) always render the
suggestion button when matchingSuggestion exists but mark it
disabled/uninteractive for viewers, or (B) explicitly document the intent with a
comment. To implement (A) change the canShowAction logic (used to decide
rendering) so it doesn't gate visibility on canWrite alone (e.g., allow
rendering when matchingSuggestion exists) and instead apply disabled behavior
and UI tweaks when !canWrite: add the disabled attribute and a non-interactive
CSS state (e.g., cursor-not-allowed/opacity) to the button render path that uses
isFixType, setSelectedFixSuggestion/setSelectedSuggestion, matchingSuggestion,
canShowAction and wasExecuted/execStatus so viewers still see the indicator but
cannot interact; alternatively, if choosing (B) add a clear comment near
canShowAction explaining why marker buttons are visible for viewers but
heuristic matches are hidden.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 26756e83-c598-4ff3-b2c8-c16c634a1496

📥 Commits

Reviewing files that changed from the base of the PR and between 8514b22 and 6f8e4b3.

📒 Files selected for processing (2)
  • client/src/app/incidents/components/IncidentCard.tsx
  • server/chat/background/summarization.py
💤 Files with no reviewable changes (1)
  • server/chat/background/summarization.py

…mplicit (fall through) returns'

Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented Jun 2, 2026

@damianloch damianloch merged commit 32b8f06 into main Jun 3, 2026
15 checks passed
@damianloch damianloch deleted the feat/suggestion-markers branch June 3, 2026 14:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants