Skip to content

Rca prompt v2 comparison - #457

Merged
damianloch merged 11 commits into
mainfrom
rca-prompt-v2-comparison
Jun 1, 2026
Merged

Rca prompt v2 comparison#457
damianloch merged 11 commits into
mainfrom
rca-prompt-v2-comparison

Conversation

@OlivierTrudeau

@OlivierTrudeau OlivierTrudeau commented May 28, 2026

Copy link
Copy Markdown
Contributor

Make one consolidated build rca prompt for the user message which just uses the raw webhook instead of parsing it and potentially losing important info

Summary by CodeRabbit

  • New Features

    • Alert field extraction tool to fetch specific values from stored alert payloads
    • UI shows clearer command label when using the alert-field tool
  • Improvements

    • Unified RCA prompt generation across integrations, now includes raw alert payloads (with safe truncation)
    • Depth-aware truncation/summarization for large payloads
    • Optional eager persistence of the initial RCA user message (configurable)
  • Behavior

    • Netdata now triggers background RCA for every webhook received

@OlivierTrudeau
OlivierTrudeau requested a review from a team as a code owner May 28, 2026 22:00
@coderabbitai

coderabbitai Bot commented May 28, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 81564a94-b39e-41b6-baad-84fc22fcd441

📥 Commits

Reviewing files that changed from the base of the PR and between c0a77bc and 209f2e0.

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

Walkthrough

Rewrites build_rca_prompt to accept (source, title, payload) with embedded serialized/truncated payloads, adds get_alert_field (dot-path payload extractor with DB fallback and descriptive errors), extends JSON truncation with depth limits, migrates provider tasks to the unified builder, and adds an opt-in eager message persist.

Changes

Unified RCA Prompt Builder with Alert Payload Extraction

Layer / File(s) Summary
Core RCA prompt builder refactor
server/chat/background/rca_prompt_builder.py
Redesigns build_rca_prompt(source, title, payload, user_id) to serialize/truncate raw payloads, embed them in an <alert_payload> block, and extract rail_text from attacker-controlled payload fields using a new helper.
Output sanitization
server/chat/backend/agent/tools/output_sanitizer.py
truncate_json_fields gains max_depth recursion limiting and summarization for deeper structures while preserving non-container values.
Alert payload extraction tool
server/chat/backend/agent/tools/alert_payload_tool.py, server/chat/backend/agent/tools/cloud_tools.py, client/src/components/tool-calls/ToolExecutionWidget.tsx, .env.example
Adds GetAlertFieldArgs and get_alert_field() for dot-path extraction from stored webhook payloads (primary lookup and windowed fallback), with JSON parsing, dict/list traversal errors, pretty-serialization of containers, truncated output; conditionally registers the tool for incident RCA background sessions; UI displays get_alert_field: <json_path> and a new env var gates eager UI persistence.
Trigger & chat-task wiring
server/chat/backend/agent/tools/trigger_rca_tool.py, server/chat/background/task.py
trigger_rca now builds a structured payload and calls the unified builder; run_background_chat can eagerly persist the initial RCA user message into chat_sessions.messages when enabled.
Provider integrations migration
server/routes/{bigpanda,datadog,dynatrace,grafana,incidentio,jenkins,netdata,newrelic,opsgenie,pagerduty,sentry,spinnaker,splunk}/tasks.py
Many provider task modules now import and call build_rca_prompt(source, title, payload, user_id); local prompt-builder helpers were removed and imports updated.
Netdata helper changes
server/routes/netdata/helpers.py, server/routes/netdata/tasks.py
Removes in-module prompt builder and changes should_trigger_background_chat to always return True, ensuring background RCA is triggered for all Netdata webhooks.

Sequence Diagram(s)

sequenceDiagram
  participant RawPayload
  participant build_rca_prompt
  participant truncate_json_fields
  participant extract_rail_text
  RawPayload->>build_rca_prompt: (source, title, payload, user_id)
  build_rca_prompt->>truncate_json_fields: serialize & conditional truncation
  build_rca_prompt->>extract_rail_text: collect attacker-controlled strings
  truncate_json_fields-->>build_rca_prompt: truncated payload JSON
  extract_rail_text-->>build_rca_prompt: rail_text
  build_rca_prompt-->>RawPayload: (rca_prompt, rail_text)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • damianloch
  • beng360
  • isiddharthsingh

Poem

🐰 Whiskers twitch, bytes aglow,

I hop through payloads, high and low,
A dot-path leap, a value found,
Pretty-printed, safe and sound,
The rabbit smiles: "Field fetched—hip hooray!"

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'Rca prompt v2 comparison' is vague and does not clearly convey the main objective of the changeset. While it references 'RCA prompt v2', it uses the non-descriptive term 'comparison' which does not indicate what the actual change is (consolidating prompt builders, using raw webhooks, or other specifics). Consider a more descriptive title such as 'Consolidate RCA prompt builders to use raw webhook payload' or 'Unify RCA prompt construction with raw webhook data' to better communicate the primary change.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 93.10% 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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch rca-prompt-v2-comparison

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.

@OlivierTrudeau
OlivierTrudeau marked this pull request as draft May 28, 2026 22:00

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🤖 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 `@server/chat/backend/agent/tools/alert_payload_tool.py`:
- Around line 52-155: The get_alert_field function is doing too many things;
split it into small helper functions to reduce complexity and make testing
easier: create validate_get_alert_field_inputs(json_path, user_id, incident_id)
to perform the initial checks and return early errors;
fetch_payload_for_incident(cursor, conn, incident_id, user_id,
_SOURCE_TABLE_MAP) to encapsulate the DB logic (direct id lookup, org fallback,
use set_rls_context and db_pool) and return the raw payload or None;
parse_json_payload(payload) to handle string vs already-parsed JSON and JSON
errors; traverse_json_path(payload, json_path) to walk dot/index parts and
return either the found value or structured error messages; and
format_tool_output(value, MAX_TOOL_OUTPUT_CHARS) to serialize/truncate results;
keep exception logging via logger.exception in get_alert_field and call these
helpers in sequence (preserving existing symbols get_alert_field,
_SOURCE_TABLE_MAP, set_rls_context, db_pool, MAX_TOOL_OUTPUT_CHARS) so behavior
is unchanged but modularized.
- Around line 94-105: The current fallback in the block that sets payload_row
(uses table, user_id, org_id and ORDER BY received_at DESC) is too broad and can
return a payload from a different incident; replace it with a stricter fallback:
fetch the incident row from incidents to get the incident timestamp (e.g.,
created_at) and any provider-specific id (source_alert_id/source_event_id) and
first attempt a lookup by that source-specific identifier; if that fails, query
the payload table for rows matching the same org_id and user_id but constrain by
a tight time window around the incident timestamp (e.g., received_at BETWEEN
created_at - window AND created_at + window) and, if available, match
provider/source fields; if still no match, return None (fail-closed) instead of
returning the latest row. Ensure you modify the code that references
payload_row, cursor.execute(...) and the SELECT payload FROM {table} ... ORDER
BY received_at DESC LIMIT 1 block to implement these stricter checks.

In `@server/chat/backend/agent/tools/cloud_tools.py`:
- Around line 2374-2376: get_alert_field is being wrapped without injecting the
required incident_id, causing runtime "No incident context" errors; update the
wrapping so the function receives incident_id from the current tool/agent
context before further wrapping. Concretely, change the line building _ctx so
that you inject the incident id into get_alert_field (e.g., build a small
wrapper around get_alert_field that reads incident_id from the runtime
context/args and calls get_alert_field(incident_id=..., json_path=...) or extend
with_user_context usage to include incident_id), then apply
with_completion_notification and wrap_func_with_capture as before (symbols:
get_alert_field, with_user_context, with_completion_notification,
wrap_func_with_capture, args_schema).

In `@server/chat/backend/agent/tools/output_sanitizer.py`:
- Around line 31-35: The current truthy check on max_list_items causes incorrect
behavior for 0 and negative values; change the logic so you explicitly handle
None vs numeric values: in the block that uses max_list_items and data (the code
calling truncate_json_fields on list items), replace the truthy test with an
explicit None check (e.g., if max_list_items is not None:) and then coerce/clamp
numeric inputs (e.g., if max_list_items < 0: max_list_items = 0) so that
max_list_items==0 yields an empty items slice plus the "... [N more items]"
sentinel and None continues to mean "no length limit." Ensure you still call
truncate_json_fields for each item and use data[:max_list_items] when
max_list_items is numeric; leave behavior unchanged when max_list_items is None.

In `@server/chat/background/rca_prompt_builder.py`:
- Around line 310-323: The guardrail field match is case-sensitive because
_RAIL_FIELDS contains mixed-case entries (e.g., "ruleName") while _collect
lowercases keys; update the logic so matching is normalized: either convert all
entries in _RAIL_FIELDS to lowercase (canonicalize the tuple) or compare
key.lower().rstrip('_') against a lowercased set created from _RAIL_FIELDS;
ensure this change is applied where _RAIL_FIELDS is referenced in the _collect
function so fields like "ruleName" are correctly detected and added to parts.

In `@server/chat/background/task.py`:
- Around line 464-468: The eager append path assumes each entry in messages is a
dict and blindly sets message_number=1; fix by first normalizing/validating the
messages list (e.g., when messages is a list, filter or coerce only items where
isinstance(item, dict) and 'sender' in item and 'text' in item), then check for
any item with sender == "user" using that cleaned list, and if none compute the
next message_number dynamically (e.g., max(int(m.get("message_number", 0)) for m
in cleaned_messages) + 1 or fallback to len(cleaned_messages)+1) before
appending the new dict; ensure you use the cleaned list for both the existence
check and the append to avoid exceptions and duplicate numbering in
functions/variables around messages, initial_message, and the cursor write.

In `@server/routes/netdata/tasks.py`:
- Around line 315-317: The RCA prompt is being built from the normalized `data`
which can drop raw webhook fields; update the call to `build_rca_prompt` in
`server/routes/netdata/tasks.py` to pass the original raw `payload` instead of
`data` so Netdata uses the unmodified webhook payload (keep
`build_rca_prompt("netdata", ..., user_id=user_id)` and the returned
`rca_prompt, rail_text` unchanged).
🪄 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: e67406e5-8bea-4611-831a-63dc88faa49d

📥 Commits

Reviewing files that changed from the base of the PR and between cf4a39a and ffa30fc.

📒 Files selected for processing (20)
  • server/chat/backend/agent/tools/alert_payload_tool.py
  • server/chat/backend/agent/tools/cloud_tools.py
  • server/chat/backend/agent/tools/output_sanitizer.py
  • server/chat/backend/agent/tools/trigger_rca_tool.py
  • server/chat/background/rca_prompt_builder.py
  • server/chat/background/task.py
  • server/routes/bigpanda/tasks.py
  • server/routes/datadog/tasks.py
  • server/routes/dynatrace/tasks.py
  • server/routes/grafana/tasks.py
  • server/routes/incidentio/tasks.py
  • server/routes/jenkins/tasks.py
  • server/routes/netdata/helpers.py
  • server/routes/netdata/tasks.py
  • server/routes/newrelic/tasks.py
  • server/routes/opsgenie/tasks.py
  • server/routes/pagerduty/tasks.py
  • server/routes/sentry/tasks.py
  • server/routes/spinnaker/tasks.py
  • server/routes/splunk/tasks.py
💤 Files with no reviewable changes (1)
  • server/routes/netdata/helpers.py

Comment thread server/chat/backend/agent/tools/alert_payload_tool.py Outdated
Comment thread server/chat/backend/agent/tools/alert_payload_tool.py Outdated
Comment thread server/chat/backend/agent/tools/cloud_tools.py Outdated
Comment thread server/chat/backend/agent/tools/output_sanitizer.py Outdated
Comment thread server/chat/background/rca_prompt_builder.py Outdated
Comment thread server/chat/background/task.py Outdated
Comment thread server/routes/netdata/tasks.py
Comment thread server/chat/backend/agent/tools/alert_payload_tool.py Fixed
@OlivierTrudeau
OlivierTrudeau marked this pull request as ready for review May 29, 2026 21:58
@OlivierTrudeau

Copy link
Copy Markdown
Contributor Author

@CodeRabbit ai review

@coderabbitai

coderabbitai Bot commented May 29, 2026

Copy link
Copy Markdown
Contributor

@OlivierTrudeau I’ll review the changes in this PR.

✅ 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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

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

⚠️ Outside diff range comments (1)
server/chat/background/rca_prompt_builder.py (1)

398-416: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Preserve alert_service for Aurora Learn and prediscovery lookups.

alert_service is now always "", so both retrieval paths lose a high-signal discriminator and will return less relevant context for generic titles like CPU, latency, or deploy alerts. Please derive it from common payload fields or pass it in explicitly from callers.

♻️ One low-impact option
-    alert_service = ""
+    alert_service = ""
+    if isinstance(payload, dict):
+        for key in ("service", "host", "app", "application", "component"):
+            value = payload.get(key)
+            if isinstance(value, str) and value.strip():
+                alert_service = value.strip()
+                break
🤖 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 `@server/chat/background/rca_prompt_builder.py` around lines 398 - 416,
alert_service is hard-coded to "" before calling _get_similar_good_rcas_context
and _get_prediscovery_context which removes a high-signal discriminator; change
the code in rca_prompt_builder (the block creating alert_service) to derive the
service name from the incoming alert payload (e.g., check common fields like
payload.get("service"), payload.get("resource"), payload.get("component") or a
nested metadata/service field) and fall back to an explicit parameter if the
caller provides one, then pass that derived alert_service into
_get_similar_good_rcas_context and _get_prediscovery_context; if callers don't
supply service, update the call sites to accept and forward the service when
available.
♻️ Duplicate comments (1)
server/chat/backend/agent/tools/alert_payload_tool.py (1)

92-103: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fail closed instead of guessing from the newest row in the time window.

This fallback still picks whichever payload for the same user happened most recently in the 10-minute window. If two alerts from the same source arrive close together, get_alert_field() can drill into the wrong incident payload and send the RCA down a false path.

🔧 Minimal safe fix
-    # Fallback: time-window constrained lookup around incident creation
-    if not payload_row or not payload_row[0]:
-        if incident_created_at:
-            window_start = incident_created_at - _PAYLOAD_LOOKUP_WINDOW
-            window_end = incident_created_at + _PAYLOAD_LOOKUP_WINDOW
-            cursor.execute(
-                f"SELECT payload FROM {table} "
-                f"WHERE user_id = %s AND received_at BETWEEN %s AND %s "
-                f"ORDER BY received_at DESC LIMIT 1",
-                (user_id, window_start, window_end),
-            )
-            payload_row = cursor.fetchone()
+    # Fail closed until we have a source-specific fallback that can uniquely
+    # correlate the incident to a stored payload row.
+    if not payload_row or not payload_row[0]:
+        return None, f"Error: No payload found in {table} for source_alert_id {source_alert_id}."
🤖 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 `@server/chat/backend/agent/tools/alert_payload_tool.py` around lines 92 - 103,
The fallback lookup currently picks the most recent payload in the time window
which can return the wrong incident; change the logic in the block that queries
payload_row (symbols: payload_row, incident_created_at, _PAYLOAD_LOOKUP_WINDOW,
cursor, table, user_id, get_alert_field) so it does NOT silently choose the
newest row when multiple candidate payloads exist: fetch all matching rows (or
COUNT them) for the given user_id BETWEEN window_start and window_end and if
more than one result is returned, treat it as no unambiguous match (leave
payload_row as None / return None) instead of selecting the latest; only accept
and assign payload_row if exactly one matching payload is found (or else fail
closed).
🤖 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 `@client/src/components/tool-calls/ToolExecutionWidget.tsx`:
- Around line 193-198: The parse-failure branch currently overwrites any
existing command label with "get alert field"; instead, when
JSON.parse(normalizedInput) throws, do not change command so the precomputed
tool.command (or prior default) is preserved—only assign command when you
successfully parse and extract a non-empty json_path from parsed.json_path or
parsed.kwargs?.json_path; keep the existing fallback behavior elsewhere that
collapses raw JSON to the default name if needed. Ensure you reference the
normalizedInput parse block, the parsed variable, and the command variable in
your change.

In `@server/chat/background/rca_prompt_builder.py`:
- Around line 366-378: The code truncates payload fields in build_rca_prompt()
using truncate_json_fields even for chat-origin payloads which cannot be
recovered by get_alert_field; modify the logic so that if payload.get("source")
== "chat" (or payload["source"] == "chat") you do not perform aggressive
truncation—always preserve full chat fields like "issue_description" (or at
minimum include the untruncated "issue_description") and skip the second-stage
deep truncation (max_field_length=80, max_depth=1); keep references to
PAYLOAD_CHAR_THRESHOLD, truncate_json_fields, and get_alert_field to locate
where to change behavior.

---

Outside diff comments:
In `@server/chat/background/rca_prompt_builder.py`:
- Around line 398-416: alert_service is hard-coded to "" before calling
_get_similar_good_rcas_context and _get_prediscovery_context which removes a
high-signal discriminator; change the code in rca_prompt_builder (the block
creating alert_service) to derive the service name from the incoming alert
payload (e.g., check common fields like payload.get("service"),
payload.get("resource"), payload.get("component") or a nested metadata/service
field) and fall back to an explicit parameter if the caller provides one, then
pass that derived alert_service into _get_similar_good_rcas_context and
_get_prediscovery_context; if callers don't supply service, update the call
sites to accept and forward the service when available.

---

Duplicate comments:
In `@server/chat/backend/agent/tools/alert_payload_tool.py`:
- Around line 92-103: The fallback lookup currently picks the most recent
payload in the time window which can return the wrong incident; change the logic
in the block that queries payload_row (symbols: payload_row,
incident_created_at, _PAYLOAD_LOOKUP_WINDOW, cursor, table, user_id,
get_alert_field) so it does NOT silently choose the newest row when multiple
candidate payloads exist: fetch all matching rows (or COUNT them) for the given
user_id BETWEEN window_start and window_end and if more than one result is
returned, treat it as no unambiguous match (leave payload_row as None / return
None) instead of selecting the latest; only accept and assign payload_row if
exactly one matching payload is found (or else fail closed).
🪄 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: 1b0b14dc-8124-4548-8916-f11caa1b8f60

📥 Commits

Reviewing files that changed from the base of the PR and between ffa30fc and 9875d04.

📒 Files selected for processing (8)
  • client/src/components/tool-calls/ToolExecutionWidget.tsx
  • server/chat/backend/agent/tools/alert_payload_tool.py
  • server/chat/backend/agent/tools/cloud_tools.py
  • server/chat/backend/agent/tools/output_sanitizer.py
  • server/chat/backend/agent/tools/trigger_rca_tool.py
  • server/chat/background/rca_prompt_builder.py
  • server/chat/background/task.py
  • server/routes/netdata/tasks.py

Comment thread client/src/components/tool-calls/ToolExecutionWidget.tsx Outdated
Comment thread server/chat/background/rca_prompt_builder.py Outdated
Comment thread server/chat/background/task.py Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

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

⚠️ Outside diff range comments (1)
server/chat/background/task.py (1)

456-491: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don't write the RCA prompt to chat_sessions.messages before the input rail runs.

This bypasses the existing safety boundary: if the workflow later blocks on rail_text, the attacker-controlled payload has already been persisted to chat history. server/chat/backend/agent/workflow.py:1096-1103 intentionally saves user text only after the rail passes to avoid exactly that.

Suggested fix
-    # Eagerly persist the initial user message so it's visible in the UI immediately (opt-in)
-    if os.environ.get("DISPLAY__RCA_USER_MSG", "").lower() in ("1", "true", "yes"):
-        try:
-            with db_pool.get_admin_connection() as conn:
-                with conn.cursor() as cursor:
-                    set_rls_context(cursor, conn, user_id, log_prefix="[BackgroundChat:EagerMsg]")
-                    cursor.execute("SELECT messages FROM chat_sessions WHERE id = %s", (session_id,))
-                    row = cursor.fetchone()
-                    messages = row[0] if row and row[0] else []
-                    if isinstance(messages, str):
-                        messages = json.loads(messages)
-                    if not isinstance(messages, list):
-                        messages = []
-                    has_user_message = any(
-                        isinstance(m, dict) and m.get("sender") == "user"
-                        for m in messages
-                    )
-                    if not has_user_message:
-                        existing_numbers = [
-                            int(m.get("message_number", 0))
-                            for m in messages
-                            if isinstance(m, dict)
-                        ]
-                        next_num = max(existing_numbers, default=0) + 1
-                        messages.append({
-                            "sender": "user",
-                            "text": initial_message,
-                            "message_number": next_num,
-                        })
-                        cursor.execute(
-                            "UPDATE chat_sessions SET messages = %s::jsonb WHERE id = %s",
-                            (json.dumps(messages), session_id),
-                        )
-                conn.commit()
-        except Exception as e:
-            logger.warning(f"[BackgroundChat] Failed to eagerly persist user message: {e}")
+    # Keep persistence behind the existing input-rail gate inside the workflow.
🤖 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 `@server/chat/background/task.py` around lines 456 - 491, The current
eager-persist block gated by DISPLAY__RCA_USER_MSG writes the user/ RCA prompt
into chat_sessions.messages immediately, which bypasses the input-rail safety
check; instead, stop persisting the prompt here and only persist after the rail
has been validated. Remove or disable the eager write in the block around
db_pool.get_admin_connection()/set_rls_context and either (a) move the
persistence logic to the existing workflow save path (the code in
server/chat/backend/agent/workflow.py that writes user text after rail_text
passes), or (b) add a guard that explicitly verifies the rail has passed for
session_id (e.g., query workflow/rail status) before updating
chat_sessions.messages; reference DISPLAY__RCA_USER_MSG, initial_message,
chat_sessions messages, get_admin_connection, and set_rls_context when making
the change.
🤖 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 `@server/chat/background/rca_prompt_builder.py`:
- Around line 405-410: The alert_service assignment uses
(payload.get("metadata", {}) or {}).get("service", "") which fails if
payload["metadata"] is not a dict; change the logic in the alert_service
construction to first read metadata = payload.get("metadata") and coerce it to a
dict only when it's a dict (e.g., metadata_dict = metadata if
isinstance(metadata, dict) else {}), then compute alert_service =
payload.get("service") or payload.get("resource") or payload.get("component") or
metadata_dict.get("service", "") or ""; update the code around the alert_service
variable in rca_prompt_builder.py to use that guarded metadata lookup so
AttributeError cannot occur.
- Around line 367-368: The current truncation slices the serialized JSON blob
(variables serialized, json_content) which can break JSON syntax; instead parse
the payload back to an object, walk string fields (or specific fields used by
get_alert_field) and truncate their values so the total serialized size is <=
CHAT_PAYLOAD_MAX, then re-serialize to json_content; update the logic around
payload_size/CHAT_PAYLOAD_MAX in rca_prompt_builder.py to perform field-level
truncation (preferably only on large text fields) to preserve valid JSON shape
and maintain compatibility with get_alert_field.

---

Outside diff comments:
In `@server/chat/background/task.py`:
- Around line 456-491: The current eager-persist block gated by
DISPLAY__RCA_USER_MSG writes the user/ RCA prompt into chat_sessions.messages
immediately, which bypasses the input-rail safety check; instead, stop
persisting the prompt here and only persist after the rail has been validated.
Remove or disable the eager write in the block around
db_pool.get_admin_connection()/set_rls_context and either (a) move the
persistence logic to the existing workflow save path (the code in
server/chat/backend/agent/workflow.py that writes user text after rail_text
passes), or (b) add a guard that explicitly verifies the rail has passed for
session_id (e.g., query workflow/rail status) before updating
chat_sessions.messages; reference DISPLAY__RCA_USER_MSG, initial_message,
chat_sessions messages, get_admin_connection, and set_rls_context 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 8a229749-e9a9-496c-a343-d31766d2f358

📥 Commits

Reviewing files that changed from the base of the PR and between 9875d04 and c0a77bc.

📒 Files selected for processing (5)
  • .env.example
  • client/src/components/tool-calls/ToolExecutionWidget.tsx
  • server/chat/backend/agent/tools/alert_payload_tool.py
  • server/chat/background/rca_prompt_builder.py
  • server/chat/background/task.py

Comment thread server/chat/background/rca_prompt_builder.py
Comment thread server/chat/background/rca_prompt_builder.py
@sonarqubecloud

sonarqubecloud Bot commented Jun 1, 2026

Copy link
Copy Markdown

@damianloch
damianloch merged commit 6417b9f into main Jun 1, 2026
16 checks passed
@damianloch
damianloch deleted the rca-prompt-v2-comparison branch June 1, 2026 18:16
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.

2 participants