Skip to content

Conversation

@maparent
Copy link
Collaborator

@maparent maparent commented Dec 8, 2025

https://linear.app/discourse-graphs/issue/ENG-1101/create-reified-relation-via-suggestion-mode

Create a reified relation from the suggestion mode.

Summary by CodeRabbit

  • New Features
    • Added a toggleable setting for block creation workflows. When enabled, an alternative creation flow with validation and relation processing is used; when disabled, the original method is preserved. Enhanced error notifications improve user feedback.

✏️ Tip: You can customize this high-level summary in your review settings.

@linear
Copy link

linear bot commented Dec 8, 2025

@supabase
Copy link

supabase bot commented Dec 8, 2025

This pull request has been ignored for the connected project zytfjzqyijgagqxrzbmz because there are no changes detected in packages/database/supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@maparent
Copy link
Collaborator Author

maparent commented Dec 8, 2025

@CodeRabbit review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 8, 2025

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

coderabbitai bot commented Dec 8, 2025

📝 Walkthrough

Walkthrough

This change modifies block creation in SuggestionsBody.tsx to support a toggleable alternate flow. When the "use-reified-relations" setting is enabled, block creation attempts to identify source/target node types, validate a relevant discourse relation, and create a reified relation instead of a standard Roam block. When disabled, the original block creation flow is preserved. Imports for toast notifications, extension settings, and reified relation utilities are added to support the new functionality.

Changes

Cohort / File(s) Summary
Conditional reified relation block creation
apps/roam/src/components/SuggestionsBody.tsx
Added toggleable path for block creation based on "use-reified-relations" setting. When enabled, resolves source/target discourse nodes, validates discourse relations, and creates reified relations with error handling. When disabled, preserves original createBlock flow. Added imports for toast rendering, extension settings, and reified relation utilities.

Sequence Diagram

sequenceDiagram
    actor User
    participant SuggestionsBody as SuggestionsBody<br/>(Component)
    participant Settings as Extension<br/>Settings
    participant DiscourseUtil as Discourse<br/>Utilities
    participant Roam as Roam<br/>Graph API
    participant Toast as Toast<br/>Notifications

    User->>SuggestionsBody: Trigger block creation
    SuggestionsBody->>Settings: Check "use-reified-relations"
    
    alt Setting enabled
        SuggestionsBody->>DiscourseUtil: findDiscourseNodeByTitleAndUid (source)
        alt Node found
            DiscourseUtil-->>SuggestionsBody: source node
            SuggestionsBody->>DiscourseUtil: findDiscourseNodeByTitleAndUid (target)
            alt Node found
                DiscourseUtil-->>SuggestionsBody: target node
                SuggestionsBody->>DiscourseUtil: Validate relation type
                alt Valid relation exists
                    DiscourseUtil-->>SuggestionsBody: relation validated
                    SuggestionsBody->>Roam: createReifiedRelation(source, target, relation)
                    Roam-->>SuggestionsBody: success
                    SuggestionsBody->>User: Reified relation created
                else No valid relation
                    SuggestionsBody->>Toast: Show error (relation not found)
                    Toast-->>User: Error message
                end
            else Node not found
                SuggestionsBody->>Toast: Show error (target node not found)
                Toast-->>User: Error message
            end
        else Node not found
            SuggestionsBody->>Toast: Show error (source node not found)
            Toast-->>User: Error message
        end
    else Setting disabled
        SuggestionsBody->>Roam: createBlock (original flow)
        Roam-->>SuggestionsBody: success
        SuggestionsBody->>User: Standard block created
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Areas requiring extra attention:
    • Logic flow of the reified relation creation path with multiple error branches
    • Correct integration with findDiscourseNodeByTitleAndUid and relation validation
    • Proper error handling and toast notification messaging
    • Compatibility of the conditional setting flag with existing block creation logic

Possibly related PRs

Pre-merge checks

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: adding the ability to create reified relations through suggestion mode, with a specific issue reference.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

🧹 Nitpick comments (3)
apps/roam/src/components/SuggestionsBody.tsx (3)

311-324: Move discourseNode check before the API call for efficiency.

The check for discourseNode === false at line 316 should be placed before calling findDiscourseNodeByTitleAndUid. Since discourseNode is already computed (it's a memoized value), checking it first avoids an unnecessary function call when it's falsy.

Apply this diff:

   if (getSetting("use-reified-relations")) {
+    if (discourseNode === false) {
+      renderToast({
+        id: "suggestions-create-block-error",
+        content: "Could not identify type of source",
+        intent: "danger",
+        timeout: 5000,
+      });
+      return;
+    }
     const selectedNodeType = findDiscourseNodeByTitleAndUid({
       uid: node.uid,
       title: node.title as string,
     });
-    if (discourseNode === false) {
-      renderToast({
-        id: "suggestions-create-block-error",
-        content: "Could not identify type of source",
-        intent: "danger",
-        timeout: 5000,
-      });
-      return;
-    }
     if (selectedNodeType === false) {

341-367: Add error handling for createReifiedRelation calls.

The createReifiedRelation calls are awaited but not wrapped in a try/catch. If the operation fails, the error will propagate unhandled and the user won't receive feedback. Consider wrapping in try/catch with appropriate error toast.

       if (relevantRelns.length) {
         if (relevantRelns.length > 1) {
           // I don't want to panick the user with this.
           // TODO: Maybe think of adding a relation type picker?
           console.warn("Picking an arbitrary relation");
         }
         const rel = relevantRelns[0];
-        if (rel.destination === selectedNodeType.type)
-          await createReifiedRelation({
-            sourceUid: tagUid,
-            destinationUid: node.uid,
-            relationBlockUid: rel.id,
-          });
-        else
-          await createReifiedRelation({
-            sourceUid: node.uid,
-            destinationUid: tagUid,
-            relationBlockUid: rel.id,
-          });
+        try {
+          if (rel.destination === selectedNodeType.type) {
+            await createReifiedRelation({
+              sourceUid: tagUid,
+              destinationUid: node.uid,
+              relationBlockUid: rel.id,
+            });
+          } else {
+            await createReifiedRelation({
+              sourceUid: node.uid,
+              destinationUid: tagUid,
+              relationBlockUid: rel.id,
+            });
+          }
+        } catch (error) {
+          console.error("Failed to create reified relation:", error);
+          renderToast({
+            id: "suggestions-create-block-error",
+            content: "Failed to create relation",
+            intent: "danger",
+            timeout: 5000,
+          });
+          return;
+        }
       } else {

342-346: Noted: Multiple relation handling.

The TODO notes the need for a relation type picker when multiple relevant relations exist. The current behavior of picking the first one may surprise users.

Would you like me to open an issue to track implementing a relation type picker for this scenario?

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c93a59c and babcea3.

📒 Files selected for processing (1)
  • apps/roam/src/components/SuggestionsBody.tsx (2 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/main.mdc)

**/*.{ts,tsx}: Use Tailwind CSS for styling where possible
When refactoring inline styles, use tailwind classes
Prefer type over interface in TypeScript
Use explicit return types for functions
Avoid any types when possible
Prefer arrow functions over regular function declarations
Use named parameters (object destructuring) when a function has more than 2 parameters
Use PascalCase for components and types
Use camelCase for variables and functions
Use UPPERCASE for constants
Function names should describe their purpose clearly
Prefer early returns over nested conditionals for better readability

Files:

  • apps/roam/src/components/SuggestionsBody.tsx
apps/roam/**/*.{js,ts,tsx,jsx,json}

📄 CodeRabbit inference engine (.cursor/rules/roam.mdc)

Prefer existing dependencies from package.json when working on the Roam Research extension

Files:

  • apps/roam/src/components/SuggestionsBody.tsx
apps/roam/**/*.{ts,tsx,jsx,js,css,scss}

📄 CodeRabbit inference engine (.cursor/rules/roam.mdc)

Use BlueprintJS 3 components and Tailwind CSS for platform-native UI in the Roam Research extension

Files:

  • apps/roam/src/components/SuggestionsBody.tsx
apps/roam/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/roam.mdc)

apps/roam/**/*.{ts,tsx,js,jsx}: Use the roamAlphaApi docs from https://roamresearch.com/#/app/developer-documentation/page/tIaOPdXCj when implementing Roam functionality
Use Roam Depot/Extension API docs from https://roamresearch.com/#/app/developer-documentation/page/y31lhjIqU when implementing extension functionality

Files:

  • apps/roam/src/components/SuggestionsBody.tsx
apps/roam/**

📄 CodeRabbit inference engine (.cursor/rules/roam.mdc)

Implement the Discourse Graph protocol in the Roam Research extension

Files:

  • apps/roam/src/components/SuggestionsBody.tsx
🧠 Learnings (10)
📓 Common learnings
Learnt from: maparent
Repo: DiscourseGraphs/discourse-graph PR: 0
File: :0-0
Timestamp: 2025-11-06T13:48:35.007Z
Learning: In apps/roam/src/utils/createReifiedBlock.ts, the `setBlockProps` function does not need await as it is synchronous or fire-and-forget.
📚 Learning: 2025-11-06T13:48:35.007Z
Learnt from: maparent
Repo: DiscourseGraphs/discourse-graph PR: 0
File: :0-0
Timestamp: 2025-11-06T13:48:35.007Z
Learning: In apps/roam/src/utils/createReifiedBlock.ts, the `setBlockProps` function does not need await as it is synchronous or fire-and-forget.

Applied to files:

  • apps/roam/src/components/SuggestionsBody.tsx
📚 Learning: 2025-11-25T00:52:41.934Z
Learnt from: CR
Repo: DiscourseGraphs/discourse-graph PR: 0
File: .cursor/rules/roam.mdc:0-0
Timestamp: 2025-11-25T00:52:41.934Z
Learning: Applies to apps/roam/**/*.{ts,tsx,js,jsx} : Use the roamAlphaApi docs from https://roamresearch.com/#/app/developer-documentation/page/tIaOPdXCj when implementing Roam functionality

Applied to files:

  • apps/roam/src/components/SuggestionsBody.tsx
📚 Learning: 2025-06-22T10:40:52.752Z
Learnt from: sid597
Repo: DiscourseGraphs/discourse-graph PR: 232
File: apps/roam/src/utils/getAllDiscourseNodesSince.ts:18-31
Timestamp: 2025-06-22T10:40:52.752Z
Learning: In apps/roam/src/utils/getAllDiscourseNodesSince.ts, the user confirmed that querying for `?title` with `:node/title` and mapping it to the `text` field in the DiscourseGraphContent type is the correct implementation for retrieving discourse node content from Roam Research, despite it appearing to query page titles rather than block text content.

Applied to files:

  • apps/roam/src/components/SuggestionsBody.tsx
📚 Learning: 2025-11-25T00:52:41.934Z
Learnt from: CR
Repo: DiscourseGraphs/discourse-graph PR: 0
File: .cursor/rules/roam.mdc:0-0
Timestamp: 2025-11-25T00:52:41.934Z
Learning: Applies to apps/roam/**/*.{ts,tsx,js,jsx} : Use Roam Depot/Extension API docs from https://roamresearch.com/#/app/developer-documentation/page/y31lhjIqU when implementing extension functionality

Applied to files:

  • apps/roam/src/components/SuggestionsBody.tsx
📚 Learning: 2025-12-07T20:54:19.988Z
Learnt from: maparent
Repo: DiscourseGraphs/discourse-graph PR: 559
File: apps/roam/src/utils/findDiscourseNode.ts:37-39
Timestamp: 2025-12-07T20:54:19.988Z
Learning: In apps/roam/src/utils/findDiscourseNode.ts, the function findDiscourseNodeByTitleAndUid accepts both uid and title parameters where uid is primarily used for cache access (as the cache key) while title is used for the actual matching via matchDiscourseNode. This design reflects the pattern where downstream, the uid is mostly used to fetch the title, so the function caches by uid but matches by title.

Applied to files:

  • apps/roam/src/components/SuggestionsBody.tsx
📚 Learning: 2025-11-25T00:52:41.934Z
Learnt from: CR
Repo: DiscourseGraphs/discourse-graph PR: 0
File: .cursor/rules/roam.mdc:0-0
Timestamp: 2025-11-25T00:52:41.934Z
Learning: Applies to apps/roam/** : Implement the Discourse Graph protocol in the Roam Research extension

Applied to files:

  • apps/roam/src/components/SuggestionsBody.tsx
📚 Learning: 2025-11-05T21:57:14.909Z
Learnt from: maparent
Repo: DiscourseGraphs/discourse-graph PR: 534
File: apps/roam/src/utils/createReifiedBlock.ts:40-48
Timestamp: 2025-11-05T21:57:14.909Z
Learning: In the discourse-graph repository, the function `getPageUidByPageTitle` from `roamjs-components/queries/getPageUidByPageTitle` is a synchronous function that returns a string directly (the page UID or an empty string if not found), not a Promise. It should be called without `await`.

Applied to files:

  • apps/roam/src/components/SuggestionsBody.tsx
📚 Learning: 2025-06-17T23:37:45.289Z
Learnt from: maparent
Repo: DiscourseGraphs/discourse-graph PR: 220
File: apps/roam/src/utils/conceptConversion.ts:42-56
Timestamp: 2025-06-17T23:37:45.289Z
Learning: In the DiscourseNode interface from apps/roam/src/utils/getDiscourseNodes.ts, the field `type` serves as the unique identifier field, not a type classification field. The interface has no `uid` or `id` field, making `node.type` the correct field to use for UID-related operations.

Applied to files:

  • apps/roam/src/components/SuggestionsBody.tsx
📚 Learning: 2025-06-17T23:37:45.289Z
Learnt from: maparent
Repo: DiscourseGraphs/discourse-graph PR: 220
File: apps/roam/src/utils/conceptConversion.ts:42-56
Timestamp: 2025-06-17T23:37:45.289Z
Learning: In the DiscourseNode interface from apps/roam/src/utils/getDiscourseNodes.ts, the field `node.type` serves as the UID field rather than having a conventional `node.uid` field. This is an unusual naming convention where the type field actually contains the unique identifier.

Applied to files:

  • apps/roam/src/components/SuggestionsBody.tsx
🧬 Code graph analysis (1)
apps/roam/src/components/SuggestionsBody.tsx (3)
apps/roam/src/utils/extensionSettings.ts (1)
  • getSetting (3-11)
apps/roam/src/utils/findDiscourseNode.ts (1)
  • findDiscourseNodeByTitleAndUid (23-43)
apps/roam/src/utils/createReifiedBlock.ts (1)
  • createReifiedRelation (111-131)
🔇 Additional comments (1)
apps/roam/src/components/SuggestionsBody.tsx (1)

22-32: LGTM!

The new imports are appropriate for the added reified relation functionality and follow the existing import patterns in the file.

Comment on lines 312 to 315
const selectedNodeType = findDiscourseNodeByTitleAndUid({
uid: node.uid,
title: node.title as string,
});
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 | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# Find the SuggestionsBody.tsx file and check the context around lines 312-315
fd "SuggestionsBody.tsx" --type f

Repository: DiscourseGraphs/discourse-graph

Length of output: 119


🏁 Script executed:

# Search for Result type definition from roamjs-components
rg "export.*Result" --type ts -A 5 | head -100

Repository: DiscourseGraphs/discourse-graph

Length of output: 4394


🏁 Script executed:

# Search for SuggestedNode type definition
rg "type SuggestedNode" --type ts -A 10

Repository: DiscourseGraphs/discourse-graph

Length of output: 1472


🏁 Script executed:

# Check the findDiscourseNodeByTitleAndUid function signature
rg "findDiscourseNodeByTitleAndUid" --type ts -B 2 -A 5

Repository: DiscourseGraphs/discourse-graph

Length of output: 4891


🏁 Script executed:

# Get the exact context around lines 312-315 in SuggestionsBody.tsx
sed -n '300,330p' apps/roam/src/components/SuggestionsBody.tsx

Repository: DiscourseGraphs/discourse-graph

Length of output: 1097


🏁 Script executed:

# Check the Result type definition more carefully
cat -n apps/roam/src/utils/types.ts | head -20

Repository: DiscourseGraphs/discourse-graph

Length of output: 598


🏁 Script executed:

# Check other usages of findDiscourseNodeByTitleAndUid to see what property they pass
rg "findDiscourseNodeByTitleAndUid" --type ts -B 2 -A 8

Repository: DiscourseGraphs/discourse-graph

Length of output: 6792


🏁 Script executed:

# Look at the full SuggestedNode definition and context where it's created
rg "hydeFilteredNodes|SuggestedNode" --type ts -B 3 -A 3 | head -80

Repository: DiscourseGraphs/discourse-graph

Length of output: 5818


🏁 Script executed:

# Search for where hydeFilteredNodes is set/updated
rg "setHydeFilteredNodes" --type ts -B 5 -A 5

Repository: DiscourseGraphs/discourse-graph

Length of output: 2764


🏁 Script executed:

# Check the hyde.ts file for SuggestedNode creation
rg "SuggestedNode|Result" apps/roam/src/utils/hyde.ts -B 2 -A 8

Repository: DiscourseGraphs/discourse-graph

Length of output: 6033


🏁 Script executed:

# Get the exact context of handleCreateBlock to see the full usage
sed -n '310,330p' apps/roam/src/components/SuggestionsBody.tsx

Repository: DiscourseGraphs/discourse-graph

Length of output: 787


Fix type assertion for node.title—use node.text instead.

SuggestedNode (which extends Result) has a text property, not title. The type assertion as string masks accessing a non-existent property; pass node.text to findDiscourseNodeByTitleAndUid instead.

🤖 Prompt for AI Agents
In apps/roam/src/components/SuggestionsBody.tsx around lines 312-315, the code
passes node.title as a string to findDiscourseNodeByTitleAndUid but
SuggestedNode/Result has a text property not title; replace the call to pass
node.text (ensuring it's a string or safely cast/guarded) instead of node.title
as string so the function receives the correct value and the bogus type
assertion is removed.

@maparent maparent requested a review from sid597 December 9, 2025 14:23
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