Skip to content

ENG-1225: Discourse node migration simple settings#696

Open
sid597 wants to merge 4 commits intographite-base/696from
eng-1225-discourse-node-migrate-settings-prod
Open

ENG-1225: Discourse node migration simple settings#696
sid597 wants to merge 4 commits intographite-base/696from
eng-1225-discourse-node-migrate-settings-prod

Conversation

@sid597
Copy link
Collaborator

@sid597 sid597 commented Jan 14, 2026

https://www.loom.com/share/4d58cd87bf7e40c9b6e1b644d53742d0

ENG-1225: Discourse node migration

Migrate personal settings text inputs

remove unnecessary checks zod takes care

migrate attributes overlay and suggestive rules


Open with Devin

Summary by CodeRabbit

  • Refactor
    • Migrated all settings storage from global to personal user-scoped configuration
    • Added real-time validation and error message feedback for node configuration settings, including tags and format specifications
    • Refactored component interfaces to reduce dependencies and improve maintainability
    • Standardized keyboard shortcut configuration across menu trigger features

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

@linear
Copy link

linear bot commented Jan 14, 2026

@supabase
Copy link

supabase bot commented Jan 14, 2026

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 ↗︎.

@sid597 sid597 changed the title ENG-1225: Discourse node migration ENG-1225: Discourse node migration simple settings Jan 14, 2026
@sid597 sid597 marked this pull request as ready for review January 14, 2026 09:02
@sid597 sid597 force-pushed the eng-1225-discourse-node-migrate-settings-prod branch from 3ac2996 to d549f8d Compare January 17, 2026 18:16
@sid597 sid597 force-pushed the eng-1273-migrate-all-small-personal-settings branch from a82cfaf to bc0de10 Compare January 17, 2026 18:16
@sid597 sid597 force-pushed the eng-1225-discourse-node-migrate-settings-prod branch from d549f8d to 78b7dbb Compare January 18, 2026 05:25
@sid597 sid597 force-pushed the eng-1273-migrate-all-small-personal-settings branch from bc0de10 to 57abc42 Compare January 18, 2026 05:25
@sid597 sid597 force-pushed the eng-1225-discourse-node-migrate-settings-prod branch from 78b7dbb to d66fa03 Compare January 19, 2026 04:51
@sid597 sid597 force-pushed the eng-1273-migrate-all-small-personal-settings branch from 57abc42 to 7ead54e Compare January 19, 2026 04:51
@sid597 sid597 force-pushed the eng-1225-discourse-node-migrate-settings-prod branch from d66fa03 to cb22590 Compare January 19, 2026 05:01
@sid597 sid597 force-pushed the eng-1273-migrate-all-small-personal-settings branch from 490810f to 8dc5276 Compare January 19, 2026 06:03
@sid597 sid597 force-pushed the eng-1225-discourse-node-migrate-settings-prod branch from cb22590 to 78abef0 Compare January 19, 2026 06:03
@sid597 sid597 force-pushed the eng-1273-migrate-all-small-personal-settings branch from 8dc5276 to 8286d6b Compare January 19, 2026 06:34
@sid597 sid597 force-pushed the eng-1225-discourse-node-migrate-settings-prod branch from 78abef0 to 3966245 Compare January 19, 2026 06:34
This was referenced Jan 19, 2026
@sid597 sid597 force-pushed the eng-1273-migrate-all-small-personal-settings branch from 8286d6b to 78e8311 Compare January 20, 2026 16:06
@sid597 sid597 force-pushed the eng-1225-discourse-node-migrate-settings-prod branch from 3966245 to 7015148 Compare January 20, 2026 16:06
@sid597 sid597 changed the base branch from eng-1273-migrate-all-small-personal-settings to graphite-base/696 January 28, 2026 21:16
@sid597 sid597 force-pushed the eng-1225-discourse-node-migrate-settings-prod branch from 7015148 to ecdee21 Compare January 29, 2026 11:34
@sid597 sid597 force-pushed the graphite-base/696 branch from 78e8311 to 938f6aa Compare January 29, 2026 11:34
@sid597 sid597 changed the base branch from graphite-base/696 to eng-1273-migrate-all-small-personal-settings January 29, 2026 11:34
Copy link

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 1 potential issue.

View issue and 6 additional flags in Devin Review.

Open in Devin Review

uid={node.isFirstChild?.uid || ""}
parentUid={parentUid}
value={node.isFirstChild?.value || false}
settingKeys={["suggestiveRules", "isFirstChild", "value"]}

Choose a reason for hiding this comment

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

🔴 isFirstChild setting path creates invalid schema structure missing required uid field

The DiscourseNodeFlagPanel for "First Child" uses settingKeys={["suggestiveRules", "isFirstChild", "value"]} to set only the boolean value.

Click to expand

Problem

The schema in zodSchema.ts:20-26 defines isFirstChild as:

isFirstChild: z.object({
  uid: z.string(),
  value: z.boolean(),
}).optional()

When the setter saves to path ["suggestiveRules", "isFirstChild", "value"], it creates:

{ "suggestiveRules": { "isFirstChild": { "value": true } } }

This object is missing the required uid field.

Why validation passes on write but fails on read

The setDiscourseNodeSetting validation at accessors.ts:437-446 validates only the leaf value (true) against the leaf schema (z.boolean()), which passes.

However, when the full node settings are later read via getDiscourseNodeSettings, the entire object is parsed through DiscourseNodeSchema.safeParse() at accessors.ts:399. The isFirstChild: { value: true } object fails validation because uid: z.string() is required.

Impact

  • User changes to the "First Child" toggle are saved in an invalid format
  • Subsequent reads of the discourse node settings will fail schema validation
  • This breaks suggestive mode functionality for affected nodes

Recommendation: Either change the setting path to set the entire isFirstChild object (e.g., settingKeys={["suggestiveRules", "isFirstChild"]} with value { uid: generateUID(), value: checked }), or update the schema to make uid optional or restructure isFirstChild to just be a boolean.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@sid597 sid597 force-pushed the eng-1225-discourse-node-migrate-settings-prod branch from ecdee21 to 96f4575 Compare January 30, 2026 06:10
Comment on lines 156 to 179
const validateTag = (tag: string): string | undefined => {
const cleanTag = getCleanTagText(tag);
if (!cleanTag) return undefined;
const format = getDiscourseNodeSetting<string>(node.type, ["format"])!;
const roamTagRegex = /#?\[\[(.*?)\]\]|#(\S+)/g;
const formatTags: string[] = [];
for (const match of format.matchAll(roamTagRegex)) {
const tagName = match[1] || match[2];
if (tagName) formatTags.push(tagName.toUpperCase());
}
if (formatTags.includes(cleanTag)) {
return `The tag "${tag}" is referenced in the format. Please use a different tag or format.`;
}
return undefined;
};
Copy link

Choose a reason for hiding this comment

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

Race condition in validation logic. The validateTag function reads format from settings via getDiscourseNodeSetting, but due to the 500ms debounce in BaseTextPanel (BlockPropSettingPanels.tsx:126-128), the format in settings may be stale. If a user types in the format field and immediately switches to edit the tag field, validateTag will validate against the old format value still in settings, not the current input value.

// Fix: Pass the current format value as a parameter
const validateTag = (tag: string, currentFormat: string): string | undefined => {
  const cleanTag = getCleanTagText(tag);
  if (!cleanTag) return undefined;
  const roamTagRegex = /#?\[\[(.*?)\]\]|#(\S+)/g;
  const formatTags: string[] = [];
  for (const match of currentFormat.matchAll(roamTagRegex)) {
    const tagName = match[1] || match[2];
    if (tagName) formatTags.push(tagName.toUpperCase());
  }
  // ...
};

Similarly, validateFormat needs to accept the current tag value as a parameter to avoid reading stale data from settings.

Suggested change
const validateTag = (tag: string): string | undefined => {
const cleanTag = getCleanTagText(tag);
if (!cleanTag) return undefined;
const format = getDiscourseNodeSetting<string>(node.type, ["format"])!;
const roamTagRegex = /#?\[\[(.*?)\]\]|#(\S+)/g;
const formatTags: string[] = [];
for (const match of format.matchAll(roamTagRegex)) {
const tagName = match[1] || match[2];
if (tagName) formatTags.push(tagName.toUpperCase());
}
if (formatTags.includes(cleanTag)) {
return `The tag "${tag}" is referenced in the format. Please use a different tag or format.`;
}
return undefined;
};
const validateTag = (tag: string, currentFormat?: string): string | undefined => {
const cleanTag = getCleanTagText(tag);
if (!cleanTag) return undefined;
const format = currentFormat || getDiscourseNodeSetting<string>(node.type, ["format"])!;
const roamTagRegex = /#?\[\[(.*?)\]\]|#(\S+)/g;
const formatTags: string[] = [];
for (const match of format.matchAll(roamTagRegex)) {
const tagName = match[1] || match[2];
if (tagName) formatTags.push(tagName.toUpperCase());
}
if (formatTags.includes(cleanTag)) {
return `The tag "${tag}" is referenced in the format. Please use a different tag or format.`;
}
return undefined;
};

Spotted by Graphite Agent

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.

const nodeId = node.type;
tools[nodeId] = {
id: nodeId,
// TODO: port this when all node settings are done
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

remove already addressed or noted in the linear ticket

ControlGroup,
Checkbox,
Radio,
RadioGroup,
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

changed the format of import why?

@sid597 sid597 requested a review from mdroidian January 31, 2026 17:33
@sid597 sid597 force-pushed the eng-1225-discourse-node-migrate-settings-prod branch from c09b869 to 67ecd97 Compare January 31, 2026 18:25
Copy link
Contributor

@mdroidian mdroidian left a comment

Choose a reason for hiding this comment

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

Could you please record a loom video showing the working changed UI. Show the settings being updating in both the legacy as well as the new prop storage.

Please also format all files with prettier.

Also the CI is failing.

@sid597 sid597 force-pushed the eng-1225-discourse-node-migrate-settings-prod branch from 67ecd97 to 610365f Compare February 3, 2026 18:56
@sid597 sid597 force-pushed the eng-1273-migrate-all-small-personal-settings branch from 938f6aa to 0dee3d3 Compare February 3, 2026 18:56
Comment on lines 1 to 5
<<<<<<< HEAD
import React, { useState, useCallback, useRef } from "react";
=======
import React, { useState, useRef, useEffect } from "react";
>>>>>>> 67ecd97e (fix order)
Copy link

Choose a reason for hiding this comment

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

Unresolved merge conflict markers will cause syntax error

The file contains Git merge conflict markers (<<<<<<< HEAD, =======, >>>>>>> 67ecd97e) that will prevent the code from compiling. This needs immediate resolution.

// Remove conflict markers and merge the changes properly:
import React, { useState, useRef, useEffect } from "react";
Suggested change
<<<<<<< HEAD
import React, { useState, useCallback, useRef } from "react";
=======
import React, { useState, useRef, useEffect } from "react";
>>>>>>> 67ecd97e (fix order)
import React, { useState, useRef, useEffect } from "react";

Spotted by Graphite Agent

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.

Copy link

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 1 new potential issue.

View issue and 14 additional flags in Devin Review.

Open in Devin Review

@sid597 sid597 force-pushed the eng-1273-migrate-all-small-personal-settings branch from 0dee3d3 to d4ed8a1 Compare February 3, 2026 19:08
@sid597 sid597 force-pushed the eng-1225-discourse-node-migrate-settings-prod branch from 610365f to 80bb0fb Compare February 3, 2026 19:08
@sid597 sid597 changed the base branch from eng-1273-migrate-all-small-personal-settings to graphite-base/696 February 3, 2026 20:35
@sid597 sid597 force-pushed the eng-1225-discourse-node-migrate-settings-prod branch from 80bb0fb to dfe4388 Compare February 3, 2026 21:41
@sid597 sid597 force-pushed the graphite-base/696 branch from d4ed8a1 to 88d4ff5 Compare February 3, 2026 21:41
@sid597 sid597 changed the base branch from graphite-base/696 to eng-1273-migrate-all-small-personal-settings February 3, 2026 21:41
@sid597 sid597 force-pushed the eng-1225-discourse-node-migrate-settings-prod branch from dfe4388 to a449a73 Compare February 3, 2026 21:49
Copy link

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 1 new potential issue.

View issue and 20 additional flags in Devin Review.

Open in Devin Review

Comment on lines +281 to 292
<DiscourseNodeTextPanel
nodeType={node.type}
title="Tag"
description={`Designate a hashtag for marking potential ${node.text}.`}
value={tagValue}
onChange={handleTagChange}
onBlur={handleTagBlur}
error={tagError}
settingKeys={["tag"]}
defaultValue={node.tag}
placeholder={generateTagPlaceholder(node)}
validate={validateTag}
onChange={setTagValue}
parentUid={node.type}
uid={tagUid}
/>

Choose a reason for hiding this comment

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

🟡 Tag validation error state becomes stale when format field changes

The tag validation error state is now managed internally within BaseTextPanel and only updates when the tag input changes. However, the validateTag function depends on formatValue to check for conflicts between tag and format.

Click to expand

Root Cause

In the previous implementation, validation ran via useEffect whenever either tagValue or formatValue changed:

useEffect(() => {
  validate({ tag: tagValue, format: formatValue });
}, [tagValue, formatValue, validate]);

In the new implementation (NodeConfig.tsx:241-243), only formatError is updated:

useEffect(() => {
  validateFormat({ format: formatValue });
}, [tagValue, formatValue, validateFormat]);

The tag error is now managed inside BaseTextPanel state (BlockPropSettingPanels.tsx:118-119) and is only set via handleChange (BlockPropSettingPanels.tsx:142-144).

Impact

  1. User enters a tag that conflicts with the format → tag error is shown
  2. User modifies the format to remove the conflict
  3. The tag error message persists until the user manually edits the tag field again

This is a regression in UX where stale error messages can confuse users.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@sid597 sid597 force-pushed the eng-1225-discourse-node-migrate-settings-prod branch from a449a73 to 84aa5ac Compare February 3, 2026 21:53
Comment on lines +262 to +269
<DiscourseNodeTextPanel
nodeType={node.type}
title="Description"
description={`Describing what the ${node.text} node represents in your graph.`}
value={descriptionValue}
onChange={handleDescriptionChange}
onBlur={handleDescriptionBlur}
settingKeys={["description"]}
defaultValue={node.description}
parentUid={node.type}
uid={descriptionUid}
Copy link

Choose a reason for hiding this comment

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

The DiscourseNodeTextPanel for Description is missing the required order prop. Without this prop, hasBlockSync will be false in BaseTextPanel (requires both parentUid and order to be defined), preventing changes from being synced to Roam blocks.

<DiscourseNodeTextPanel
  nodeType={node.type}
  title="Description"
  description={`Describing what the ${node.text} node represents in your graph.`}
  settingKeys={["description"]}
  defaultValue={node.description}
  order={1}  // Add this line
  parentUid={node.type}
  uid={descriptionUid}
/>
Suggested change
<DiscourseNodeTextPanel
nodeType={node.type}
title="Description"
description={`Describing what the ${node.text} node represents in your graph.`}
value={descriptionValue}
onChange={handleDescriptionChange}
onBlur={handleDescriptionBlur}
settingKeys={["description"]}
defaultValue={node.description}
parentUid={node.type}
uid={descriptionUid}
<DiscourseNodeTextPanel
nodeType={node.type}
title="Description"
description={`Describing what the ${node.text} node represents in your graph.`}
settingKeys={["description"]}
defaultValue={node.description}
order={1}
parentUid={node.type}
uid={descriptionUid}

Spotted by Graphite Agent

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.

@sid597 sid597 force-pushed the eng-1273-migrate-all-small-personal-settings branch from 88d4ff5 to f5d0a0a Compare February 4, 2026 17:56
@sid597 sid597 force-pushed the eng-1225-discourse-node-migrate-settings-prod branch from 84aa5ac to 8ad6b12 Compare February 4, 2026 17:56
Copy link

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 1 new potential issue.

View issue and 24 additional flags in Devin Review.

Open in Devin Review

Comment on lines +18 to +22
<<<<<<< HEAD
=======
settingKeys={["Export", "Remove Special Characters"]}
defaultValue={exportSettings.removeSpecialCharacters.value || false}
>>>>>>> 84aa5ac2 (restack and fix unnecessary changes)

Choose a reason for hiding this comment

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

🔴 Unresolved git merge conflicts in ExportSettings.tsx

The file contains unresolved git merge conflict markers (<<<<<<< HEAD, =======, >>>>>>> 84aa5ac2) at multiple locations throughout the file. This will cause syntax errors and prevent the application from compiling.

Click to expand

Impact

The file has 7 unresolved merge conflicts at lines 18-22, 32-36, 45-49, 59-63, 74-79, 92-96, and 105-109. Each conflict contains both HEAD and the other branch's changes, making the file invalid JavaScript/TypeScript.

Example of the conflict pattern:

<<<<<<< HEAD
=======
          settingKeys={["Export", "Remove Special Characters"]}
          defaultValue={exportSettings.removeSpecialCharacters.value || false}
>>>>>>> 84aa5ac2 (restack and fix unnecessary changes)

Actual vs Expected

  • Actual: File contains merge conflict markers that are invalid syntax
  • Expected: File should have resolved conflicts with valid TypeScript code

Recommendation: Resolve the git merge conflicts by choosing the appropriate code from either HEAD or the other branch, then remove all conflict markers.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@sid597 sid597 changed the base branch from eng-1273-migrate-all-small-personal-settings to graphite-base/696 February 7, 2026 18:29
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