Skip to content

Conversation

@Dharya4242
Copy link
Contributor

@Dharya4242 Dharya4242 commented Oct 5, 2025

Closes #116

📝 Description

Graph-based onboarding flow that branches on GitHub verification and routes exploration into the GitHub toolkit (MCP-backed). Centralizes onboarding copy, persists minimal onboarding state, and aligns Discord DM onboarding with the same messages and stages.

Goals:

  • Welcome users, nudge verification, and clearly advertise DevRel capabilities per the design doc.
  • Avoid hardcoding GitHub answers in onboarding; auto-route to github_toolkit so MCP-powered repo/org queries answer user prompts.
  • Keep channel UX consistent via reusable messages and a simple state machine.

🔧 Changes Made

Onboarding state machine and copy

  • Add shared onboarding copy and capability sections:
    backend/app/agents/devrel/onboarding/messages.py
  • Add workflow controller (stages: intro, awaiting_choice, encourage_verification, verified_capabilities, completed):
    backend/app/agents/devrel/onboarding/workflow.py
  • Update handler to use workflow and emit stage, next_tool, and capability_sections:
    backend/app/agents/devrel/nodes/handlers/onboarding.py

State + routing integration

  • Persist onboarding_state in AgentState:
    backend/app/agents/state.py
  • Tool wrapper stores onboarding state and sets force_next_tool:
    backend/app/agents/devrel/tool_wrappers.py
  • Supervisor honors force_next_tool to jump straight to github_toolkit:
    backend/app/agents/devrel/nodes/react_supervisor.py

Discord UX alignment

  • Reuse shared messages; verified users get capability intro + final handoff:
    backend/integrations/discord/cogs.py

  • Capability embed mirrors shared sections; add “I’ve verified” button to re-check linkage:
    backend/integrations/discord/views.py

  • Onboarding now hands off to github_toolkit, which integrates with the MCP-backed GitHub service via github_support.

🤝 Collaboration

Guidance from: @smokeyScraper


✅ Checklist

  • I have read the contributing guidelines.
  • I have added tests that prove my fix is effective or that my feature works.
  • I have added necessary documentation (if applicable).
  • Any dependent changes have been merged and published in downstream modules.

Summary by CodeRabbit

  • New Features

    • Multi-stage, data-driven onboarding with richer stages, actions and capability overviews.
    • Interactive Discord onboarding UI (verify/skip) with OAuth link, final handoff DM/embed, and optional post-verification DM.
    • Supervisor routing shortcuts to force a specific tool or complete a task.
  • Enhancements

    • New onboarding messaging and capability rendering for clearer, personalized content.
    • Improved Discord profile refresh for better personalization.
  • Chores

    • Per-channel onboarding state storage and a setting to show/hide the OAuth button.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 5, 2025

Walkthrough

Adds a multi-stage onboarding state machine, messaging and Discord UI; changes onboarding handler signatures and node return shapes to return rich task payloads; propagates onboarding_state through tool wrappers and AgentState; adds Discord cog/view for DMs and OAuth; and extends auth callback to optionally send a post‑verification DM.

Changes

Cohort / File(s) Summary
Onboarding workflow & messages
backend/app/agents/devrel/onboarding/workflow.py, backend/app/agents/devrel/onboarding/messages.py
New onboarding state machine (OnboardingStage, OnboardingFlowResult) and run_onboarding_flow; message builders, capability rendering, intent detection, action generation and structured flow results.
Agent onboarding handler
backend/app/agents/devrel/nodes/handlers/onboarding.py
Handler signature changed to return Dict[str, Any]; adds _latest_text, extracts verification/github info, calls run_onboarding_flow, and returns a rich task_result, current_task, and onboarding_state.
Supervisor / context gathering
backend/app/agents/devrel/nodes/react_supervisor.py, backend/app/agents/devrel/nodes/gather_context.py
react_supervisor adds early exits for force_next_tool and force_complete; gather_context refreshes Discord profile via get_or_create_user_by_discord, includes user_profile in returned context, and returns a structured result dict.
Tool wrappers
backend/app/agents/devrel/tool_wrappers.py
onboarding_tool_node and github_toolkit_tool_node now build and return state_update from add_tool_result, propagate onboarding_state, and set/remove force_next_tool / complete_after_forced_tool / force_complete flags in context.
Agent state
backend/app/agents/state.py
Adds onboarding_state: Dict[str, Any] = Field(default_factory=dict) to AgentState for per-channel onboarding progress.
Discord integration (Cog & Views)
backend/integrations/discord/cogs.py, backend/integrations/discord/views.py
Adds OnboardingCog (on_member_join, onboarding dispatch) and OnboardingView (verify/skip handlers), plus build_final_handoff_embed and send_final_handoff_dm for DM‑based onboarding and optional OAuth link.
Auth & config
backend/app/api/v1/auth.py, backend/app/core/config/settings.py
auth_callback gains app_instance dependency to optionally send post‑verification DM via Discord bot; error text updated to use slash command form; adds onboarding_show_oauth_button setting.
Minor formatting
backend/app/agents/devrel/github/tools/general_github_help.py
Small non‑functional formatting adjustment in an exception return block.

Sequence Diagram(s)

sequenceDiagram
    participant User as Discord User
    participant Bot as Discord Bot
    participant Cog as OnboardingCog
    participant Workflow as OnboardingWorkflow
    participant Agent as DevRel Agent Node
    participant Auth as Auth Callback
    participant DM as Discord DM

    User->>Bot: joins server
    Bot->>Cog: on_member_join(member)
    Cog->>Cog: get_or_create_user_by_discord()
    Cog->>Workflow: run_onboarding_flow(state, latest_message, is_verified, github_username)
    Workflow-->>Cog: OnboardingFlowResult + onboarding_state
    Cog->>DM: send DM with OnboardingView (optional OAuth link)

    alt User clicks "Verify" -> OAuth completes
        User->>Auth: /auth/github/callback
        Auth->>Auth: finalize verification
        Auth->>DM: send_final_handoff_dm(user)  %% (optional DM)
        DM->>User: final handoff embed
    else User uses View (verify/skip) or chooses actions
        User->>DM: interact with OnboardingView
        DM->>Workflow: (via Agent) update onboarding_state / next tool
        DM->>User: updated messages / final handoff
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

enhancement

Suggested reviewers

  • smokeyScraper
  • chandansgowda

"I hopped in with a twitchy nose and grin,
Welcome petals open, let the onboarding begin!
Click verify or skip, then off you go —
Capabilities gleam, now watch me show.
Happy trails, new friend — explore and grow!" 🐇

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.50% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "feat: route onboarding flow through MCP-backed GitHub toolkit" is partially related to the changeset. It clearly identifies a real and significant part of the implementation—the architectural decision to route onboarding handoffs through the MCP-backed GitHub toolkit via force_next_tool in the supervisor node—as reflected in react_supervisor.py and tool_wrappers.py. While the title emphasizes routing and handoff rather than capturing the complete onboarding workflow feature from issue #116, it nonetheless describes an authentic, non-trivial aspect of the changes and satisfies the criteria for a partially related title.
Linked Issues Check ✅ Passed All key coding objectives from issue #116 are met by the implementation. The PR implements a structured onboarding workflow with stages (intro, awaiting_choice, encourage_verification, verified_capabilities, completed) via OnboardingNode and run_onboarding_flow [#116]; detects GitHub verification status and prompts users to run /verify with skip options [#116]; advertises core capabilities via CAPABILITY_SECTIONS and build_verified_capabilities_intro [#116]; includes Discord integration with on_member_join for first-time UX [#116]; encourages user autonomy by referencing /verify and /help commands in messages [#116]; and properly hands off completed onboarding to the main DevRel Agent via force_next_tool routing [#116]. All primary coding requirements from the linked issue are satisfied.
Out of Scope Changes Check ✅ Passed All changes are either directly scoped to the onboarding workflow feature or provide necessary supporting infrastructure. Core onboarding modules (messages.py, workflow.py, handlers/onboarding.py) implement the feature; state.py adds onboarding_state persistence; tool_wrappers.py and react_supervisor.py enable handoff routing; discord/cogs.py and discord/views.py integrate onboarding with Discord; auth.py and settings.py provide OAuth and configuration support for the onboarding experience; gather_context.py adds Discord user profile refresh; and general_github_help.py contains only a minor formatting adjustment. No out-of-scope changes or unrelated functionality are apparent.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
backend/app/agents/devrel/github/tools/general_github_help.py (1)

11-12: Update docstring to document the hint parameter.

The function signature correctly adds the optional hint parameter, but the docstring should be updated to explain its purpose and behavior.

Apply this diff to enhance the docstring:

 async def handle_general_github_help(query: str, llm, hint: Optional[str] = None) -> Dict[str, Any]:
-    """Execute general GitHub help with web search and LLM knowledge"""
+    """
+    Execute general GitHub help with web search and LLM knowledge.
+    
+    Args:
+        query: The user's GitHub-related question
+        llm: The language model to use for generating responses
+        hint: Optional assistant hint to augment the prompt context
+    
+    Returns:
+        Dict containing status, response, and metadata
+    """
backend/app/agents/devrel/onboarding/messages.py (1)

65-74: Consider sanitizing the github_username parameter for markdown safety.

The github_username parameter is wrapped in backticks for markdown formatting (lines 69, 92) without validation. If a username contains backticks or other markdown special characters, it could break the formatting or potentially cause injection issues in the rendered message.

Consider adding a helper function to sanitize usernames before embedding them in formatted strings:

def _sanitize_username(username: str) -> str:
    """Escape markdown special characters in username."""
    # Escape backticks and other markdown characters
    return username.replace("`", "\\`").replace("*", "\\*").replace("_", "\\_")

Then use it when building messages:

 def build_verified_welcome(github_username: Optional[str] = None) -> str:
     """Welcome copy for returning verified contributors."""
     greeting = "👋 Welcome back to the Devr.AI community!"
     if github_username:
-        greeting += f" I see `{github_username}` is already linked, which is great."
+        greeting += f" I see `{_sanitize_username(github_username)}` is already linked, which is great."

Also applies to: 88-98

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 452a278 and 707d33e.

📒 Files selected for processing (12)
  • backend/app/agents/devrel/github/tools/general_github_help.py (3 hunks)
  • backend/app/agents/devrel/nodes/handlers/onboarding.py (1 hunks)
  • backend/app/agents/devrel/nodes/react_supervisor.py (1 hunks)
  • backend/app/agents/devrel/onboarding/messages.py (1 hunks)
  • backend/app/agents/devrel/onboarding/workflow.py (1 hunks)
  • backend/app/agents/devrel/tool_wrappers.py (1 hunks)
  • backend/app/agents/state.py (1 hunks)
  • backend/app/api/v1/auth.py (5 hunks)
  • backend/app/core/config/settings.py (1 hunks)
  • backend/app/integrations/mcp/client.py (1 hunks)
  • backend/integrations/discord/cogs.py (2 hunks)
  • backend/integrations/discord/views.py (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (7)
backend/app/agents/devrel/tool_wrappers.py (1)
backend/app/agents/devrel/nodes/react_supervisor.py (1)
  • add_tool_result (98-114)
backend/app/agents/devrel/github/tools/general_github_help.py (1)
backend/app/services/embedding_service/service.py (1)
  • llm (55-68)
backend/integrations/discord/cogs.py (5)
backend/app/agents/devrel/onboarding/messages.py (4)
  • build_encourage_verification_message (77-85)
  • build_new_user_welcome (51-62)
  • build_verified_capabilities_intro (88-98)
  • build_verified_welcome (65-74)
backend/app/services/auth/management.py (1)
  • get_or_create_user_by_discord (10-41)
backend/app/services/auth/supabase.py (1)
  • login_with_github (28-30)
backend/app/services/auth/verification.py (1)
  • create_verification_session (33-61)
backend/integrations/discord/views.py (3)
  • OAuthView (36-49)
  • OnboardingView (52-120)
  • build_final_handoff_embed (11-24)
backend/app/agents/devrel/onboarding/workflow.py (2)
backend/app/agents/state.py (1)
  • AgentState (18-76)
backend/app/agents/devrel/onboarding/messages.py (5)
  • build_verified_capabilities_intro (88-98)
  • render_capabilities_text (39-48)
  • build_new_user_welcome (51-62)
  • build_encourage_verification_message (77-85)
  • build_verified_welcome (65-74)
backend/integrations/discord/views.py (2)
backend/app/services/auth/management.py (1)
  • get_or_create_user_by_discord (10-41)
backend/app/models/database/supabase.py (1)
  • User (7-69)
backend/app/api/v1/auth.py (6)
backend/app/database/supabase/client.py (1)
  • get_supabase_client (9-13)
backend/app/services/auth/verification.py (2)
  • find_user_by_session_and_verify (63-135)
  • get_verification_session_info (156-176)
backend/app/services/github/user/profiling.py (2)
  • profile_user_from_github (301-333)
  • request (77-79)
backend/app/core/dependencies.py (1)
  • get_app_instance (7-12)
backend/integrations/discord/views.py (1)
  • send_final_handoff_dm (27-34)
backend/main.py (1)
  • DevRAIApplication (26-87)
backend/app/agents/devrel/nodes/handlers/onboarding.py (2)
backend/app/agents/devrel/onboarding/workflow.py (2)
  • OnboardingStage (12-19)
  • run_onboarding_flow (87-276)
backend/app/agents/state.py (1)
  • AgentState (18-76)
🪛 Ruff (0.13.3)
backend/integrations/discord/cogs.py

228-228: String contains ambiguous (RIGHT SINGLE QUOTATION MARK). Did you mean ``` (GRAVE ACCENT)?

(RUF001)


264-265: try-except-pass detected, consider logging the exception

(S110)


264-264: Do not catch blind exception: Exception

(BLE001)


280-281: try-except-pass detected, consider logging the exception

(S110)


280-280: Do not catch blind exception: Exception

(BLE001)


293-294: try-except-pass detected, consider logging the exception

(S110)


293-293: Do not catch blind exception: Exception

(BLE001)


300-300: Consider moving this statement to an else block

(TRY300)


303-303: Do not catch blind exception: Exception

(BLE001)


304-304: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


308-309: try-except-pass detected, consider logging the exception

(S110)


308-308: Do not catch blind exception: Exception

(BLE001)

backend/app/integrations/mcp/client.py

8-8: Do not catch blind exception: Exception

(BLE001)


31-31: Avoid specifying long messages outside the exception class

(TRY003)


43-43: Avoid specifying long messages outside the exception class

(TRY003)


47-47: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


47-47: Avoid specifying long messages outside the exception class

(TRY003)


50-50: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


50-50: Avoid specifying long messages outside the exception class

(TRY003)


50-50: Use explicit conversion flag

Replace with conversion flag

(RUF010)

backend/integrations/discord/views.py

32-34: try-except-pass detected, consider logging the exception

(S110)


32-32: Do not catch blind exception: Exception

(BLE001)


73-73: Unused method argument: button

(ARG002)


84-84: Do not catch blind exception: Exception

(BLE001)


102-103: try-except-pass detected, consider logging the exception

(S110)


102-102: Do not catch blind exception: Exception

(BLE001)


111-111: Unused method argument: button

(ARG002)


118-120: try-except-pass detected, consider logging the exception

(S110)


118-118: Do not catch blind exception: Exception

(BLE001)

backend/app/api/v1/auth.py

21-21: Unused function argument: request

(ARG001)


24-24: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)


98-98: Do not catch blind exception: Exception

(BLE001)

backend/app/agents/devrel/nodes/handlers/onboarding.py

31-32: try-except-pass detected, consider logging the exception

(S110)


31-31: Do not catch blind exception: Exception

(BLE001)

🔇 Additional comments (8)
backend/app/agents/devrel/github/tools/general_github_help.py (1)

1-1: LGTM!

The Optional import is correctly added to support the new hint parameter's type annotation.

backend/app/agents/state.py (1)

29-31: Onboarding state persistence looks good.

Defaulting to an empty dict matches the rest of the state model and cleanly unlocks the new workflow.

backend/app/core/config/settings.py (1)

42-48: Configuration surface extension is consistent.

The new fields follow existing conventions, so wiring them through env vars should be seamless.

backend/app/agents/devrel/tool_wrappers.py (1)

33-44: Onboarding wrapper propagation looks solid.

State updates now carry onboarding_state and force the next tool when present—nice alignment with the supervisor shortcut.

backend/app/agents/devrel/nodes/react_supervisor.py (1)

20-37: Force-next-tool shortcut is clean.

The auto-route path removes the flag, records the decision, and bumps the iteration counter—exactly what the flow needs.

backend/app/agents/devrel/onboarding/messages.py (3)

39-48: LGTM!

The capability text rendering logic is clear and produces well-formatted output. The function correctly iterates through sections and formats examples with proper spacing.


12-12: Verify repository name: ensure the hardcoded 'Devr.AI-backend' matches the actual GitHub repository’s name (including exact capitalization, punctuation, and hyphenation) to avoid confusion.


58-60: Referenced Discord commands exist and are registered
All commands (/verify_github, /verification_status, /help) are defined in backend/integrations/discord/cogs.py.

Comment on lines 26 to 35
if hint:
help_prompt = GENERAL_GITHUB_HELP_PROMPT.format(
query=f"{query}\n\nAssistant hint: {hint}",
search_context=search_context
)
else:
help_prompt = GENERAL_GITHUB_HELP_PROMPT.format(
query=query,
search_context=search_context
)
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Eliminate code duplication in prompt construction.

The GENERAL_GITHUB_HELP_PROMPT.format() call is duplicated in both branches, violating the DRY principle. Construct the augmented query first, then format once.

Apply this diff to refactor the conditional logic:

-        if hint:
-            help_prompt = GENERAL_GITHUB_HELP_PROMPT.format(
-                query=f"{query}\n\nAssistant hint: {hint}",
-                search_context=search_context
-            )
-        else:
-            help_prompt = GENERAL_GITHUB_HELP_PROMPT.format(
-                query=query,
-                search_context=search_context
-            )
+        augmented_query = f"{query}\n\nAssistant hint: {hint}" if hint else query
+        help_prompt = GENERAL_GITHUB_HELP_PROMPT.format(
+            query=augmented_query,
+            search_context=search_context
+        )
📝 Committable suggestion

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

Suggested change
if hint:
help_prompt = GENERAL_GITHUB_HELP_PROMPT.format(
query=f"{query}\n\nAssistant hint: {hint}",
search_context=search_context
)
else:
help_prompt = GENERAL_GITHUB_HELP_PROMPT.format(
query=query,
search_context=search_context
)
augmented_query = f"{query}\n\nAssistant hint: {hint}" if hint else query
help_prompt = GENERAL_GITHUB_HELP_PROMPT.format(
query=augmented_query,
search_context=search_context
)
🤖 Prompt for AI Agents
In backend/app/agents/devrel/github/tools/general_github_help.py around lines 26
to 35, the GENERAL_GITHUB_HELP_PROMPT.format() call is duplicated; refactor by
first building an augmented query string (include the hint when present, e.g.,
augmented_query = f"{query}\n\nAssistant hint: {hint}" if hint else query) and
then call GENERAL_GITHUB_HELP_PROMPT.format(query=augmented_query,
search_context=search_context) once to eliminate duplication.

Comment on lines 38 to 50
async with aiohttp.ClientSession() as session:
try:
async with session.post(self.server_url, headers=headers, json=payload, timeout=timeout) as resp:
text = await resp.text()
if resp.status >= 400:
raise MCPClientError(f"MCP HTTP {resp.status}: {text}")
try:
data = json.loads(text)
except json.JSONDecodeError:
raise MCPClientError("MCP returned invalid JSON")
return data
except asyncio.TimeoutError as e:
raise MCPClientError(f"MCP timeout: {str(e)}")
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Wrap aiohttp transport errors in MCPClientError.

Right now any aiohttp.ClientError (DNS failure, refused connection, etc.) bubbles up directly, so callers stop seeing the unified MCPClientError your docstring implies. Please catch those and re-raise as MCPClientError (ideally using raise … from e) to keep error handling consistent.

Apply this diff:

         async with aiohttp.ClientSession() as session:
             try:
                 async with session.post(self.server_url, headers=headers, json=payload, timeout=timeout) as resp:
                     text = await resp.text()
                     if resp.status >= 400:
                         raise MCPClientError(f"MCP HTTP {resp.status}: {text}")
                     try:
                         data = json.loads(text)
                     except json.JSONDecodeError as e:
-                        raise MCPClientError("MCP returned invalid JSON")
+                        raise MCPClientError("MCP returned invalid JSON") from e
                     return data
-            except asyncio.TimeoutError as e:
-                raise MCPClientError(f"MCP timeout: {str(e)}")
+            except asyncio.TimeoutError as e:
+                raise MCPClientError(f"MCP timeout: {e}") from e
+            except aiohttp.ClientError as e:
+                raise MCPClientError(f"MCP transport error: {e}") from e

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 Ruff (0.13.3)

43-43: Avoid specifying long messages outside the exception class

(TRY003)


47-47: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


47-47: Avoid specifying long messages outside the exception class

(TRY003)


50-50: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


50-50: Avoid specifying long messages outside the exception class

(TRY003)


50-50: Use explicit conversion flag

Replace with conversion flag

(RUF010)

🤖 Prompt for AI Agents
In backend/app/integrations/mcp/client.py around lines 38 to 50, transport-level
aiohttp.ClientError exceptions (DNS failures, connection refused, etc.) are not
being caught and thus escape as raw aiohttp errors; catch aiohttp.ClientError in
the existing try/except block (or add an except clause) and re-raise as an
MCPClientError using "raise MCPClientError(... ) from e" so callers always
receive MCPClientError while preserving the original error context.

it was committed by mistake ,i was experimenting with MCPs
@Dharya4242 Dharya4242 marked this pull request as draft October 9, 2025 14:15
@smokeyScraper
Copy link
Contributor

Any updates @Dharya4242 ?

@Dharya4242
Copy link
Contributor Author

Hey @smokeyScraper
I’ll be working on this tonight to resolve the errors and will update you with my progress tomorrow.

@smokeyScraper
Copy link
Contributor

cool man, actually i was preferring to close all the current issues by working on them myself, but you can give it a try and align it as per your way and then can share your branch's access to me as a collaborator so that I can make commits on top of yours if I feel like a few changes over yours. This way you too would learn from your mistakes in a better way (if any) as I'd be correcting them out.

Thanks!

@Dharya4242
Copy link
Contributor Author

got it , sounds great. Really appreciate the opportunity to learn and your guidance

@Dharya4242 Dharya4242 marked this pull request as ready for review October 17, 2025 21:26
Copy link
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: 0

🧹 Nitpick comments (7)
backend/app/agents/devrel/nodes/gather_context.py (2)

28-46: Discord avatar URL construction, PII logging, and exception scope

  • author['avatar'] is a hash, not a URL. Build the CDN URL when only a hash is available.
  • Redact discord_id in logs.
  • Consider narrowing the catch or documenting why a broad catch is acceptable (to address BLE001), and optionally throttle DB calls if profile is already fresh.
-        avatar_url = author.get("avatar") or author.get("avatar_url")
+        avatar_hash = author.get("avatar")
+        avatar_url = author.get("avatar_url")
+        if not avatar_url and avatar_hash and discord_id:
+            # Discord CDN avatar URL format; default to 256px
+            avatar_url = f"https://cdn.discordapp.com/avatars/{discord_id}/{avatar_hash}.png?size=256"
@@
-        if discord_id:
-            try:
+        if discord_id:
+            try:
                 user = await get_or_create_user_by_discord(
@@
                 )
                 profile_data = user.model_dump()
-            except Exception as exc:  # pragma: no cover - graceful degradation
-                logger.warning("Failed to refresh Discord user profile for %s: %s", discord_id, exc)
+            except Exception as exc:  # noqa: BLE001  # Graceful degradation; service raises heterogeneous errors
+                redacted = str(discord_id)
+                if len(redacted) > 6:
+                    redacted = f"{redacted[:2]}***{redacted[-2:]}"
+                logger.warning("Failed to refresh Discord user profile for id=%s: %s", redacted, exc)

Optional (future): cache a last_profile_refresh timestamp in state.context and skip refresh within N minutes to reduce DB load.


55-63: Minor: timezones for last_interaction_time (optional)

If/when the codebase moves to TZ-aware datetimes, switch to datetime.now(tz=timezone.utc) for consistency; current usage matches AgentState defaults.

backend/app/agents/devrel/onboarding/workflow.py (4)

39-46: Broaden intent detection to cover common phrasings

Improve coverage for “verify/verification/gh” and skip phrasings.

-_INTENT_VERIFIED = re.compile(r"\b(i\s*(have)?\s*)?(linked|connected|verified)\b.*github", re.IGNORECASE)
+_INTENT_VERIFIED = re.compile(r"\b(i\s*(have|am)?\s*)?(verify|verified|link(?:ed)?|connect(?:ed)?)\b.*\b(github|gh)\b", re.IGNORECASE)
-_INTENT_SKIP = re.compile(r"\b(skip|later|not\s+now)\b", re.IGNORECASE)
+_INTENT_SKIP = re.compile(r"\b(skip|later|not\s+now|skip\s+for\s+now)\b", re.IGNORECASE)
-_INTENT_HELP = re.compile(r"\b(how|help|can't|cannot|stuck)\b.*verify", re.IGNORECASE)
+_INTENT_HELP = re.compile(r"\b(how|help|can.?t|cannot|stuck)\b.*verif(y|ication|y(ing)?)", re.IGNORECASE)

107-127: DRY up verified-capabilities/completed branches

Four nearly identical blocks construct verified responses. Extract a small helper to build the result to reduce maintenance risk.

+def _verified_response(github_username: Optional[str], capability_sections) -> OnboardingFlowResult:
+    return OnboardingFlowResult(
+        stage=OnboardingStage.VERIFIED_CAPABILITIES,
+        status="completed",
+        welcome_message=messages.build_verified_capabilities_intro(github_username),
+        final_message=messages.render_capabilities_text(),
+        actions=_exploration_suggestions(),
+        is_verified=True,
+        capability_sections=capability_sections,
+        route_hint="onboarding",
+        handoff="github_toolkit",
+        next_tool="github_toolkit",
+    )
@@
-            intro = messages.build_verified_capabilities_intro(github_username)
-            return (
-                OnboardingFlowResult(
-                    stage=OnboardingStage.VERIFIED_CAPABILITIES,
-                    status="completed",
-                    welcome_message=intro,
-                    final_message=messages.render_capabilities_text(),
-                    actions=_exploration_suggestions(),
-                    is_verified=True,
-                    capability_sections=capability_sections,
-                    route_hint="onboarding",
-                    handoff="github_toolkit",
-                    next_tool="github_toolkit",
-                ),
-                onboarding_state,
-            )
+            return (_verified_response(github_username, capability_sections), onboarding_state)

Apply similarly to the other verified transitions.

Also applies to: 145-165, 204-224, 245-261


226-241: Cap reminders to avoid unbounded growth

Consider a max_reminders cap (e.g., 3) and stop incrementing, to prevent ever-growing counters in persisted state.

-    onboarding_state["reminders_sent"] = reminders_sent + 1
+    max_reminders = 3
+    onboarding_state["reminders_sent"] = min(reminders_sent + 1, max_reminders)

22-37: Consider adding to_dict() as defensive serialization, though current handler already converts stage.value

Verification shows the handler at backend/app/agents/devrel/nodes/handlers/onboarding.py:43 already converts flow_result.stage.value to string. The serialization concern is already addressed. Adding to_dict() to the dataclass would be a defensive measure to prevent future bugs if the handler pattern changes or if OnboardingFlowResult is used elsewhere without explicit serialization. Current code is safe as-is.

backend/app/agents/devrel/tool_wrappers.py (1)

33-46: Gate complete_after_forced_tool to supported tool

The stage value is already serialized as a string (.value in handle_onboarding_node), so the type check is safe. However, scope complete_after_forced_tool to the specific tool where it's cleared to avoid leaving stale routing flags. Only set it when next_tool == "github_toolkit".

     next_tool = tool_result.get("next_tool")
     if next_tool:
         context["force_next_tool"] = next_tool
-        if tool_result.get("stage") in {"verified_capabilities", "completed"}:
-            context["complete_after_forced_tool"] = next_tool
+        if next_tool == "github_toolkit" and tool_result.get("stage") in {"verified_capabilities", "completed"}:
+            context["complete_after_forced_tool"] = next_tool
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 707d33e and 7cca3ef.

📒 Files selected for processing (5)
  • backend/app/agents/devrel/github/tools/general_github_help.py (1 hunks)
  • backend/app/agents/devrel/nodes/gather_context.py (2 hunks)
  • backend/app/agents/devrel/nodes/react_supervisor.py (1 hunks)
  • backend/app/agents/devrel/onboarding/workflow.py (1 hunks)
  • backend/app/agents/devrel/tool_wrappers.py (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • backend/app/agents/devrel/nodes/react_supervisor.py
  • backend/app/agents/devrel/github/tools/general_github_help.py
🧰 Additional context used
🧬 Code graph analysis (3)
backend/app/agents/devrel/tool_wrappers.py (1)
backend/app/agents/devrel/nodes/react_supervisor.py (1)
  • add_tool_result (114-130)
backend/app/agents/devrel/nodes/gather_context.py (2)
backend/app/agents/state.py (1)
  • AgentState (18-76)
backend/app/services/auth/management.py (1)
  • get_or_create_user_by_discord (10-41)
backend/app/agents/devrel/onboarding/workflow.py (2)
backend/app/agents/state.py (1)
  • AgentState (18-76)
backend/app/agents/devrel/onboarding/messages.py (5)
  • build_verified_capabilities_intro (88-98)
  • render_capabilities_text (39-48)
  • build_new_user_welcome (51-62)
  • build_encourage_verification_message (77-85)
  • build_verified_welcome (65-74)
🪛 Ruff (0.14.0)
backend/app/agents/devrel/nodes/gather_context.py

44-44: Do not catch blind exception: Exception

(BLE001)

🔇 Additional comments (3)
backend/app/agents/devrel/nodes/gather_context.py (2)

26-27: Good: initialize profile_data from persisted state

Keeps prior profile fields when refresh fails.


48-48: Context default for user_profile is sensible

Fallback ensures downstream code always has minimal profile metadata.

backend/app/agents/devrel/tool_wrappers.py (1)

70-78: Verify whether the additional clearing is necessary given existing supervisor-level logic

The script output shows force_next_tool is already being cleared in react_supervisor.py:31 after the supervisor processes a forced action. The suggested addition in tool_wrappers.py would clear it earlier, but the existing mechanism in the supervisor node already handles removal.

To confirm if the suggested refactor is necessary or if the current flow already prevents re-routing loops, verify:

  • Whether the supervisor clears force_next_tool before re-evaluating on the next iteration (confirming the existing logic is sufficient), or
  • Whether there's a race condition where force_next_tool persists long enough to cause unwanted re-routing despite the completion flag being set

Without tracing the full state machine execution order between tool completion and supervisor re-evaluation, the necessity of the refactor is unclear.

@smokeyScraper
Copy link
Contributor

Could you please resolve the merge conflicts @Dharya4242 ?

@Dharya4242
Copy link
Contributor Author

Sure @smokeyScraper , I will resolve the merge conflicts and update the PR shortly

Copy link
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: 5

🧹 Nitpick comments (1)
backend/integrations/discord/cogs.py (1)

411-428: Consider simplifying embed construction.

The string manipulation to extract and reformat the welcome message is somewhat fragile—it assumes the welcome text starts with "👋 " and splits on "\n\n". If build_new_user_welcome() changes format, this parsing could break.

Consider either:

  1. Refactoring build_new_user_welcome() to return structured data (title, paragraphs) instead of raw text, or
  2. Making this parsing more defensive with fallbacks if the expected format isn't found.

For example:

 def _build_welcome_embed(self, member: discord.abc.User) -> discord.Embed:
     welcome_text = build_new_user_welcome()
-    first_paragraph, _, remainder = welcome_text.partition("\n\n")
-    if first_paragraph.startswith("👋 "):
-        first_paragraph = first_paragraph[2:]
-
-    description_blocks = [first_paragraph]
-    if remainder:
-        description_blocks.append(remainder.strip())
+    # Remove emoji prefix if present
+    text = welcome_text.lstrip("👋 ").strip()
+    # Split into paragraphs, filter empty ones
+    paragraphs = [p.strip() for p in text.split("\n\n") if p.strip()]
 
     embed = discord.Embed(
         title="Welcome to Devr.AI! 👋",
-        description="\n\n".join(description_blocks).strip(),
+        description="\n\n".join(paragraphs) if paragraphs else text,
         color=discord.Color.blue(),
     )
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7cca3ef and 35ae5e6.

📒 Files selected for processing (3)
  • backend/app/agents/state.py (1 hunks)
  • backend/app/core/config/settings.py (1 hunks)
  • backend/integrations/discord/cogs.py (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
backend/integrations/discord/cogs.py (5)
backend/app/agents/devrel/onboarding/messages.py (4)
  • build_encourage_verification_message (77-85)
  • build_new_user_welcome (51-62)
  • build_verified_capabilities_intro (88-98)
  • build_verified_welcome (65-74)
backend/app/services/auth/management.py (1)
  • get_or_create_user_by_discord (10-41)
backend/app/services/auth/supabase.py (1)
  • login_with_github (28-30)
backend/app/services/auth/verification.py (1)
  • create_verification_session (33-61)
backend/integrations/discord/views.py (3)
  • OAuthView (36-49)
  • OnboardingView (52-120)
  • build_final_handoff_embed (11-24)
🪛 Ruff (0.14.1)
backend/integrations/discord/cogs.py

440-440: String contains ambiguous (RIGHT SINGLE QUOTATION MARK). Did you mean ``` (GRAVE ACCENT)?

(RUF001)


476-477: try-except-pass detected, consider logging the exception

(S110)


476-476: Do not catch blind exception: Exception

(BLE001)


492-493: try-except-pass detected, consider logging the exception

(S110)


492-492: Do not catch blind exception: Exception

(BLE001)


505-506: try-except-pass detected, consider logging the exception

(S110)


505-505: Do not catch blind exception: Exception

(BLE001)


512-512: Consider moving this statement to an else block

(TRY300)


515-515: Do not catch blind exception: Exception

(BLE001)


516-516: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


520-521: try-except-pass detected, consider logging the exception

(S110)


520-520: Do not catch blind exception: Exception

(BLE001)

🔇 Additional comments (4)
backend/app/core/config/settings.py (1)

42-43: LGTM: Onboarding toggle added.

The onboarding_show_oauth_button flag provides a clean way to control OAuth button visibility in the onboarding flow, with a sensible default.

backend/app/agents/state.py (1)

29-30: LGTM: Onboarding state field added.

The onboarding_state field is properly typed and uses default_factory=dict to avoid mutable default pitfalls. This provides a clean way to track per-channel onboarding progress.

backend/integrations/discord/cogs.py (2)

1-21: LGTM: Onboarding integration wired properly.

The imports are well-organized and the OnboardingCog is correctly registered in the setup function alongside the existing DevRelCommands cog.

Also applies to: 399-403


445-448: LGTM: Clean event listener delegation.

The on_member_join handler properly delegates to the shared _send_onboarding_flow method, avoiding code duplication.

Comment on lines 513 to 522
except discord.Forbidden:
return "dm_forbidden"
except Exception as e:
logger.error(f"onboarding flow error: {e}")
try:
await user.send("I hit an error. You can still run /verify_github and /help.")
await user.send(build_encourage_verification_message(reminder_count=1))
except Exception:
pass
return "error"
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Improve exception logging and handling.

Two issues with the exception handling:

  1. Line 516 uses logging.error instead of logging.exception, which omits the traceback and makes debugging harder.
  2. Lines 520-521 silently swallow exceptions when attempting to send error DMs.

As per static analysis

Apply this diff:

         except discord.Forbidden:
             return "dm_forbidden"
         except Exception as e:
-            logger.error(f"onboarding flow error: {e}")
+            logger.exception(f"onboarding flow error: {e}")
             try:
                 await user.send("I hit an error. You can still run /verify_github and /help.")
                 await user.send(build_encourage_verification_message(reminder_count=1))
-            except Exception:
-                pass
+            except discord.Forbidden:
+                logger.warning(f"Cannot DM user {user.id} after onboarding error (DMs disabled)")
+            except Exception as send_error:
+                logger.exception(f"Failed to send error fallback DM to user {user.id}: {send_error}")
             return "error"
🧰 Tools
🪛 Ruff (0.14.1)

515-515: Do not catch blind exception: Exception

(BLE001)


516-516: Use logging.exception instead of logging.error

Replace with exception

(TRY400)


520-521: try-except-pass detected, consider logging the exception

(S110)


520-520: Do not catch blind exception: Exception

(BLE001)

🤖 Prompt for AI Agents
In backend/integrations/discord/cogs.py around lines 513 to 522, replace the
logger.error call with logger.exception so the traceback is included when the
onboarding flow hits an unexpected Exception, and update the inner except that
currently does a silent pass to instead catch the exception as e and log it
(e.g., logger.warning or logger.exception) with context about failing to send
the DM before continuing to return "error"; keep behavior of ignoring
discord.Forbidden earlier intact.

Copy link
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: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 35ae5e6 and abb6690.

📒 Files selected for processing (2)
  • backend/app/core/config/settings.py (2 hunks)
  • backend/integrations/discord/cogs.py (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
backend/integrations/discord/cogs.py (5)
backend/app/agents/devrel/onboarding/messages.py (4)
  • build_encourage_verification_message (77-85)
  • build_new_user_welcome (51-62)
  • build_verified_capabilities_intro (88-98)
  • build_verified_welcome (65-74)
backend/app/services/auth/management.py (1)
  • get_or_create_user_by_discord (10-41)
backend/app/services/auth/supabase.py (1)
  • login_with_github (28-30)
backend/app/services/auth/verification.py (1)
  • create_verification_session (33-61)
backend/integrations/discord/views.py (3)
  • OAuthView (36-49)
  • OnboardingView (52-120)
  • build_final_handoff_embed (11-24)
🪛 Ruff (0.14.1)
backend/integrations/discord/cogs.py

440-440: Expected an expression or a '}'

(invalid-syntax)

backend/app/core/config/settings.py

49-49: Avoid specifying long messages outside the exception class

(TRY003)

🔇 Additional comments (16)
backend/app/core/config/settings.py (3)

3-3: LGTM: Import formatting improved.

Adding a space after the comma follows PEP 8 style conventions.


52-55: LGTM: Model config formatting enhanced.

The multi-line formatting of model_config improves readability without changing behavior.


42-44: Field is properly consumed—verification passed.

The script confirms onboarding_show_oauth_button is actively used in backend/integrations/discord/cogs.py (line 483) to control OAuth button visibility during onboarding. The implementation correctly retrieves the setting with a sensible default fallback. Unlike the mcp_api_key concern, this field serves its intended purpose in the Discord integration flow.

backend/integrations/discord/cogs.py (13)

1-21: LGTM: Imports are well-organized.

The new imports for onboarding messages, authentication, and Discord views are appropriately structured and align with the onboarding flow implementation.


399-403: LGTM: Cog registration follows established pattern.

The OnboardingCog is properly registered alongside DevRelCommands in the setup function.


405-409: LGTM: Clean cog initialization.

The OnboardingCog follows the standard pattern for Discord cog initialization.


411-428: LGTM: Welcome embed construction is clear.

The method appropriately extracts and formats the welcome message, removing the emoji prefix to avoid duplication with the embed title.


445-448: LGTM: Event listener properly delegates to shared flow.

The on_member_join listener appropriately delegates to the shared _send_onboarding_flow method.


450-461: LGTM: Comprehensive documentation.

The method signature and docstring clearly document the onboarding flow's purpose and return values.


462-468: LGTM: Defensive attribute access.

The use of getattr with defaults appropriately handles variations in Discord user object attributes.


470-480: LGTM: Verified user handling with proper logging.

The code now includes appropriate logging for DM failures, addressing the previous review concern. The exception handling correctly distinguishes between permission issues and other failures.


482-485: LGTM: Defensive settings access.

The use of getattr with a default value appropriately handles cases where the onboarding_show_oauth_button setting might not be configured.


486-498: LGTM: Session failure handling with proper logging.

The fallback path for session creation failures now includes appropriate logging, addressing the previous review concern.


500-513: LGTM: OAuth URL failure handling with proper logging.

The fallback path for auth URL generation failures now includes appropriate logging, addressing the previous review concern.


515-518: LGTM: Welcome DM sent with appropriate view.

The code correctly sends the welcome embed with the OnboardingView, handling the optional auth_url parameter.


519-530: LGTM: Comprehensive exception handling with proper logging.

The exception handling now uses logger.exception to capture tracebacks and includes logging for fallback DM failures, addressing all previous review concerns.

Copy link
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: 0

🧹 Nitpick comments (2)
backend/integrations/discord/cogs.py (2)

476-479: Remove redundant exception info from logger.exception.

The logger.exception call automatically includes exception details and traceback, so appending : {e} to the message is redundant.

Apply this diff:

                 except Exception as e:
-                    logger.exception(f"Failed to send verified welcome DM to user {user.id}: {e}")
+                    logger.exception(f"Failed to send verified welcome DM to user {user.id}")

As per static analysis


482-530: Optional: Clean up exception logging and control flow.

The exception handling is comprehensive and correct, but there are a few minor style improvements that would align with best practices:

  1. Redundant exception info in logger.exception calls (lines 497, 512, 522, 529): The logger.exception method automatically includes exception details and traceback, so appending : {e} or : {send_error} to the message is redundant.

  2. Return statement placement (line 518): The return statement could be moved to an else block for cleaner control flow structure.

Apply this diff to address these style points:

                 except Exception as e:
-                    logger.exception(f"Failed to send session failure fallback DM to user {user.id}: {e}")
+                    logger.exception(f"Failed to send session failure fallback DM to user {user.id}")
                 return "session_unavailable"
 
             # Generate GitHub OAuth URL via Supabase
             callback_url = f"{settings.backend_url}/v1/auth/callback?session={session_id}"
             auth = await login_with_github(redirect_to=callback_url)
             auth_url = auth.get("url")
             if not auth_url:
                 try:
                     await user.send("Couldn't generate a verification link. Please use /verify_github.")
                     await user.send(build_encourage_verification_message(reminder_count=1))
                     await user.send(embed=build_final_handoff_embed())
                 except discord.Forbidden:
                     logger.warning(f"Cannot DM user {user.id} after auth URL failure (DMs disabled)")
                 except Exception as e:
-                    logger.exception(f"Failed to send auth failure fallback DM to user {user.id}: {e}")
+                    logger.exception(f"Failed to send auth failure fallback DM to user {user.id}")
                 return "auth_unavailable"
 
         # Send welcome DM with actions (auth_url may be None when button disabled)
         embed = self._build_welcome_embed(user)
         await user.send(embed=embed, view=OnboardingView(auth_url))
         return "onboarding_sent"
-        except discord.Forbidden:
+    except discord.Forbidden:
             return "dm_forbidden"
-        except Exception as e:
+    except Exception:
-            logger.exception(f"onboarding flow error: {e}")
+            logger.exception("onboarding flow error")
             try:
                 await user.send("I hit an error. You can still run /verify_github and /help.")
                 await user.send(build_encourage_verification_message(reminder_count=1))
             except discord.Forbidden:
                 logger.warning(f"Cannot DM user {user.id} after onboarding error (DMs disabled)")
             except Exception as send_error:
-                logger.exception(f"Failed to send error fallback DM to user {user.id}: {send_error}")
+                logger.exception(f"Failed to send error fallback DM to user {user.id}")
             return "error"

As per static analysis

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between abb6690 and af72240.

📒 Files selected for processing (1)
  • backend/integrations/discord/cogs.py (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
backend/integrations/discord/cogs.py (5)
backend/app/agents/devrel/onboarding/messages.py (4)
  • build_encourage_verification_message (77-85)
  • build_new_user_welcome (51-62)
  • build_verified_capabilities_intro (88-98)
  • build_verified_welcome (65-74)
backend/app/services/auth/management.py (1)
  • get_or_create_user_by_discord (10-41)
backend/app/services/auth/supabase.py (1)
  • login_with_github (28-30)
backend/app/services/auth/verification.py (1)
  • create_verification_session (33-61)
backend/integrations/discord/views.py (3)
  • OAuthView (36-49)
  • OnboardingView (52-120)
  • build_final_handoff_embed (11-24)
🪛 Ruff (0.14.1)
backend/integrations/discord/cogs.py

479-479: Redundant exception object included in logging.exception call

(TRY401)


497-497: Redundant exception object included in logging.exception call

(TRY401)


512-512: Redundant exception object included in logging.exception call

(TRY401)


518-518: Consider moving this statement to an else block

(TRY300)


522-522: Redundant exception object included in logging.exception call

(TRY401)


529-529: Redundant exception object included in logging.exception call

(TRY401)

🔇 Additional comments (3)
backend/integrations/discord/cogs.py (3)

1-21: LGTM!

The imports are well-organized and the setup function correctly registers the new OnboardingCog alongside the existing DevRelCommands cog.

Also applies to: 399-403


405-428: LGTM!

The class initialization is straightforward, and the _build_welcome_embed helper cleanly formats the welcome message into a Discord embed with appropriate styling and metadata.


430-448: LGTM!

The test command provides useful feedback for each flow outcome, and the member join listener correctly delegates to the shared flow method. Good separation of concerns.

Copy link
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

Caution

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

⚠️ Outside diff range comments (1)
backend/app/agents/devrel/nodes/gather_context.py (1)

71-79: Missing top-level user_profile in early return causes inconsistent state updates

Early-return path doesn’t update AgentState.user_profile; only context.user_profile is set. Add it here for parity with the non-DB path.

         if prev_context:
             logger.info(f"Retrieved previous conversation context from database")
             context_data["previous_conversation"] = prev_context

             # Populate state with previous conversation summary and topics
             return {
                 "messages": [new_message],
                 "context": {**state.context, **context_data},
                 "conversation_summary": prev_context.get("conversation_summary"),
                 "key_topics": prev_context.get("key_topics", []),
                 "current_task": "context_gathered",
-                "last_interaction_time": datetime.now()
+                "last_interaction_time": datetime.now(),
+                "user_profile": profile_data,
             }
🧹 Nitpick comments (5)
backend/app/agents/devrel/nodes/gather_context.py (5)

38-41: Normalize Discord avatar hash to a full CDN URL

author["avatar"] can be a hash; persist a usable URL.

-        avatar_url = author.get("avatar") or author.get("avatar_url")
+        avatar = author.get("avatar")
+        avatar_url = author.get("avatar_url")
+        # Normalize to CDN URL if only a hash is provided
+        if not avatar_url and avatar and discord_id:
+            ext = "gif" if str(avatar).startswith("a_") else "png"
+            avatar_url = f"https://cdn.discordapp.com/avatars/{discord_id}/{avatar}.{ext}"

27-31: Use timezone-aware UTC timestamps

Prefer UTC for consistency across services.

-from datetime import datetime
+from datetime import datetime, timezone
@@
-    "timestamp": datetime.now().isoformat()
+    "timestamp": datetime.now(timezone.utc).isoformat()
@@
-                "last_interaction_time": datetime.now(),
+                "last_interaction_time": datetime.now(timezone.utc),
@@
-        "last_interaction_time": datetime.now(),
+        "last_interaction_time": datetime.now(timezone.utc),

Also applies to: 78-79, 93-94


62-62: Gate DB fetch explicitly on None

Avoid treating empty strings as absent summaries.

-    should_fetch_from_db = not state.conversation_summary and not state.key_topics
+    should_fetch_from_db = state.conversation_summary is None and not state.key_topics

55-55: Name might mislead: conversation_context is a count

If this is a turn count, consider renaming to message_count or turn_index for clarity. No functional change.


35-37: Minor: unify author extraction

You already have author_info earlier; consider reusing it to avoid divergence.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between af72240 and 92bfe30.

📒 Files selected for processing (1)
  • backend/app/agents/devrel/nodes/gather_context.py (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
backend/app/agents/devrel/nodes/gather_context.py (2)
backend/app/agents/state.py (1)
  • AgentState (18-77)
backend/app/services/auth/management.py (1)
  • get_or_create_user_by_discord (10-41)
🪛 Ruff (0.14.1)
backend/app/agents/devrel/nodes/gather_context.py

51-51: Do not catch blind exception: Exception

(BLE001)

🔇 Additional comments (3)
backend/app/agents/devrel/nodes/gather_context.py (3)

3-7: Imports: OK

Typing and service import additions look correct and scoped.


89-99: Overall shape and delta semantics look good

messages is additive; context merges; top-level user_profile returned on non-DB path. With the early-return fix above, this is consistent.

Please run the unit/integration tests covering both branches (DB hit vs. miss) to confirm user_profile updates in state.


19-25: Remove review comment; both calls query the same users table and use identical user_id lookups

The review comment incorrectly assumes the functions operate on different tables. Both ensure_user_exists and get_or_create_user_by_discord query the same users table (confirmed by schema: discord_id TEXT UNIQUE).

More importantly, gather_context_node is the workflow entry point, so state.context is empty when it executes. This means both database calls use the identical state.user_id for lookups—not differing values. The first call ensures the user exists, and the second call retrieves the same record. No duplication risk exists; the UNIQUE constraint on discord_id further prevents any accidental duplicates.

Likely an incorrect or invalid review comment.

Comment on lines +51 to +53
except Exception as exc: # pragma: no cover - graceful degradation
logger.warning("Failed to refresh Discord user profile for %s: %s", discord_id, exc)

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Don’t swallow asyncio.CancelledError; narrow the exception handling

Catching Exception will absorb cancellations in async tasks. Re-raise CancelledError and keep graceful degradation for others.

+import asyncio
@@
-            except Exception as exc:  # pragma: no cover - graceful degradation
-                logger.warning("Failed to refresh Discord user profile for %s: %s", discord_id, exc)
+            except asyncio.CancelledError:
+                raise
+            except Exception as exc:  # pragma: no cover - graceful degradation
+                logger.warning("Failed to refresh Discord user profile", extra={"discord_id": str(discord_id)}, exc_info=exc)
📝 Committable suggestion

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

Suggested change
except Exception as exc: # pragma: no cover - graceful degradation
logger.warning("Failed to refresh Discord user profile for %s: %s", discord_id, exc)
except asyncio.CancelledError:
raise
except Exception as exc: # pragma: no cover - graceful degradation
logger.warning("Failed to refresh Discord user profile", extra={"discord_id": str(discord_id)}, exc_info=exc)
🧰 Tools
🪛 Ruff (0.14.1)

51-51: Do not catch blind exception: Exception

(BLE001)

🤖 Prompt for AI Agents
In backend/app/agents/devrel/nodes/gather_context.py around lines 51 to 53, the
current broad except Exception block will swallow asyncio.CancelledError; modify
the handler to re-raise CancelledError while keeping graceful degradation for
other exceptions — import asyncio if not present, change the exception handling
to either explicitly catch asyncio.CancelledError and re-raise it, or inspect
the caught exception and re-raise if isinstance(exc, asyncio.CancelledError),
and then log other exceptions as before.

)
profile_data = user.model_dump()
except Exception as exc: # pragma: no cover - graceful degradation
logger.warning("Failed to refresh Discord user profile for %s: %s", discord_id, exc)
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

PII in logs: redact or move to structured fields

Avoid emitting raw discord_id in message text; keep it in extras and rely on log sinks for filtering.

-                logger.warning("Failed to refresh Discord user profile for %s: %s", discord_id, exc)
+                logger.warning("Failed to refresh Discord user profile", extra={"discord_id": str(discord_id)}, exc_info=exc)
🤖 Prompt for AI Agents
In backend/app/agents/devrel/nodes/gather_context.py around line 52, the
logger.warning call embeds the raw discord_id into the message text; instead
remove the discord_id from the formatted message and pass it as a structured
field (e.g., via the logger's extra/context parameter) or redact/mask it if you
must include it in the message, and include the exception via exc_info or a
separate field so the log message reads a generic error and the PII is stored
only in structured metadata handled by the log sink.

Copy link
Contributor

@smokeyScraper smokeyScraper left a comment

Choose a reason for hiding this comment

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

Very well aligned! Thanks @Dharya4242

@smokeyScraper smokeyScraper merged commit 0fa57ec into AOSSIE-Org:main Oct 24, 2025
1 check passed
@smokeyScraper
Copy link
Contributor

Merged!
Thanks a lot @Dharya4242

@Dharya4242
Copy link
Contributor Author

Great! This was my first open-source contribution ,really enjoyed it and learned a lot. Thanks for the guidance 😄

@coderabbitai coderabbitai bot mentioned this pull request Oct 24, 2025
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.

FEATURE REQUEST: Focused Onboarding Workflow for New Contributors

2 participants