Skip to content

ENG-1455: Dual-read from old-system settings and from blockprops#812

Open
sid597 wants to merge 10 commits intomigration-block-init-staging-branchfrom
eng-1455-add-old-system-read-routing-to-accessor-getters
Open

ENG-1455: Dual-read from old-system settings and from blockprops#812
sid597 wants to merge 10 commits intomigration-block-init-staging-branchfrom
eng-1455-add-old-system-read-routing-to-accessor-getters

Conversation

@sid597
Copy link
Collaborator

@sid597 sid597 commented Feb 22, 2026

https://www.loom.com/share/1894f6a6fef2493e97ad1bf06fed851f

 ### PR chain
  ```
  main
    └── migration-block-init-staging-branch  (staging base)
         └── **eng-1455** (this PR)
              └── eng-1472 (PR #813)
                   └── siblings (1456, 1473, 1467, 1468, ...)

Open with Devin

Summary by CodeRabbit

  • New Features
    • Added "Enable dual read" feature flag to enhance settings compatibility with legacy configurations
    • Updated default query metadata visibility settings to improve performance by hiding metadata by default
    • Modified default query pages configuration for better content organization

@linear
Copy link

linear bot commented Feb 22, 2026

@supabase
Copy link

supabase bot commented Feb 22, 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 ↗︎.

initializeSupabaseSync();
}

const { extensionAPI } = onloadArgs;
Copy link
Collaborator Author

@sid597 sid597 Feb 22, 2026

Choose a reason for hiding this comment

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

For testing add

  const debugWindow = window as unknown as { [key: string]: unknown };
  debugWindow["__DG_DEBUG__"] = {
    runDualReadProbe,
  };

then go to browser console and do

const report = window.__DG_DEBUG__.runDualReadProbe();
  report.summary;  // this provides a summary
  report.mismatches;  // if there are any mismatches we will see them
report.rows; // will print all the values we read from the graph in personal/global/discourse node + blockprops form

@@ -43,6 +43,7 @@
} from "./data/userSettings";
import { initSchema } from "./components/settings/utils/init";
import { setupPullWatchOnSettingsPage } from "./components/settings/utils/pullWatchers";
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

for testing add
import { runDualReadProbe } from "./components/settings/utils/accessors";

}

return nodes;
};
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

For testing add the following here

type DualReadProbeCase = { keys: string[] };

type DualReadProbeNodeCase = {
  nodeType: string;
  keys: string[];
};

type DualReadProbeItem = {
  scope: "personal" | "global" | "discourseNode";
  keys: string[];
  nodeType?: string;
  legacyValue?: unknown;
  blockPropsValue?: unknown;
  legacyError?: string;
  blockPropsError?: string;
  match: boolean;
};

const normalizeProbeError = (error: unknown): string => {
  return error instanceof Error ? error.message : String(error);
};

const safeSerialize = (value: unknown): string => {
  try {
    return JSON.stringify(value);
  } catch {
    return String(value);
  }
};

const normalizeForComparison = (value: unknown): unknown => {
  if (Array.isArray(value)) {
    return value.map(normalizeForComparison);
  }
  if (isRecord(value)) {
    return Object.keys(value)
      .sort()
      .reduce<Record<string, unknown>>((acc, key) => {
        acc[key] = normalizeForComparison(value[key]);
        return acc;
      }, {});
  }
  return value;
};

const runProbeRead = (
  reader: () => unknown,
): {
  value?: unknown;
  error?: string;
} => {
  try {
    return { value: reader() };
  } catch (error) {
    return { error: normalizeProbeError(error) };
  }
};

const hasProbeMatch = ({
  legacyError,
  blockPropsError,
  legacyValue,
  blockPropsValue,
}: {
  legacyError?: string;
  blockPropsError?: string;
  legacyValue?: unknown;
  blockPropsValue?: unknown;
}): boolean => {
  if (legacyError || blockPropsError) return false;
  return (
    safeSerialize(normalizeForComparison(legacyValue)) ===
    safeSerialize(normalizeForComparison(blockPropsValue))
  );
};

const DEFAULT_DISCOURSE_NODE_PROBE_TYPE = "Claim";
const DEFAULT_DISCOURSE_NODE_PROBE_KEYS: string[][] = [
  ["description"],
  ["shortcut"],
  ["tag"],
  ["index"],
  ["format"],
  ["specification", "enabled"],
  ["specification", "query"],
  ["template"],
  ["attributes"],
  ["overlay"],
  ["canvasSettings", "color"],
  ["canvasSettings", "alias"],
  ["canvasSettings", "key-image"],
  ["canvasSettings", "key-image-option"],
  ["canvasSettings", "query-builder-alias"],
  ["graphOverview"],
  ["suggestiveRules", "embeddingRef"],
  ["suggestiveRules", "isFirstChild"],
];

export const runDualReadProbe = ({
  personalCases = [
    { keys: ["Discourse context overlay"] },
    { keys: ["Suggestive mode overlay"] },
    { keys: ["Text selection popup"] },
    { keys: ["Disable sidebar open"] },
    { keys: ["Page preview"] },
    { keys: ["Hide feedback button"] },
    { keys: ["Auto canvas relations"] },
    { keys: ["Overlay in canvas"] },
    { keys: ["Streamline styling"] },
    { keys: ["Disable product diagnostics"] },
    { keys: ["Discourse tool shortcut"] },
    { keys: ["Personal node menu trigger"] },
    { keys: ["Node search menu trigger"] },
    { keys: ["Query"] },
    { keys: ["Query", "Hide query metadata"] },
    { keys: ["Query", "Default page size"] },
    { keys: ["Query", "Query pages"] },
    { keys: ["Query", "Default filters"] },
    { keys: ["Left sidebar"] },
  ],
  globalCases = [
    { keys: ["Trigger"] },
    { keys: ["Canvas page format"] },
    { keys: ["Left sidebar"] },
    { keys: ["Left sidebar", "Children"] },
    { keys: ["Left sidebar", "Settings", "Collapsable"] },
    { keys: ["Left sidebar", "Settings", "Folded"] },
    { keys: ["Export"] },
    { keys: ["Export", "Remove special characters"] },
    { keys: ["Export", "Resolve block references"] },
    { keys: ["Export", "Resolve block embeds"] },
    { keys: ["Export", "Append referenced node"] },
    { keys: ["Export", "Link type"] },
    { keys: ["Export", "Max filename length"] },
    { keys: ["Export", "Frontmatter"] },
    { keys: ["Suggestive mode"] },
    { keys: ["Suggestive mode", "Include current page relations"] },
    { keys: ["Suggestive mode", "Include parent and child blocks"] },
    { keys: ["Suggestive mode", "Page groups"] },
    { keys: ["Relations"] },
  ],
  discourseNodeCases = DEFAULT_DISCOURSE_NODE_PROBE_KEYS.map((keys) => ({
    nodeType: DEFAULT_DISCOURSE_NODE_PROBE_TYPE,
    keys,
  })),
}: {
  personalCases?: DualReadProbeCase[];
  globalCases?: DualReadProbeCase[];
  discourseNodeCases?: DualReadProbeNodeCase[];
} = {}) => {
  const rows: DualReadProbeItem[] = [];

  personalCases.forEach(({ keys }) => {
    const legacy = runProbeRead(() => getLegacyPersonalSetting(keys));
    const blockProps = runProbeRead(() =>
      readPathValue(getPersonalSettings(), keys),
    );
    rows.push({
      scope: "personal",
      keys,
      legacyValue: legacy.value,
      blockPropsValue: blockProps.value,
      legacyError: legacy.error,
      blockPropsError: blockProps.error,
      match: hasProbeMatch({
        legacyError: legacy.error,
        blockPropsError: blockProps.error,
        legacyValue: legacy.value,
        blockPropsValue: blockProps.value,
      }),
    });
  });

  globalCases.forEach(({ keys }) => {
    const legacy = runProbeRead(() => getLegacyGlobalSetting(keys));
    const blockProps = runProbeRead(() =>
      readPathValue(getGlobalSettings(), keys),
    );
    rows.push({
      scope: "global",
      keys,
      legacyValue: legacy.value,
      blockPropsValue: blockProps.value,
      legacyError: legacy.error,
      blockPropsError: blockProps.error,
      match: hasProbeMatch({
        legacyError: legacy.error,
        blockPropsError: blockProps.error,
        legacyValue: legacy.value,
        blockPropsValue: blockProps.value,
      }),
    });
  });

  discourseNodeCases.forEach(({ nodeType, keys }) => {
    const legacy = runProbeRead(() =>
      getLegacyDiscourseNodeSetting(nodeType, keys),
    );
    const blockProps = runProbeRead(() =>
      readPathValue(getDiscourseNodeSettings(nodeType), keys),
    );
    rows.push({
      scope: "discourseNode",
      nodeType,
      keys,
      legacyValue: legacy.value,
      blockPropsValue: blockProps.value,
      legacyError: legacy.error,
      blockPropsError: blockProps.error,
      match: hasProbeMatch({
        legacyError: legacy.error,
        blockPropsError: blockProps.error,
        legacyValue: legacy.value,
        blockPropsValue: blockProps.value,
      }),
    });
  });

  const mismatches = rows.filter((row) => !row.match);

  return {
    dualReadEnabled: isDualReadEnabled(),
    summary: {
      total: rows.length,
      matches: rows.filter((row) => row.match).length,
      mismatches: mismatches.length,
    },
    rows,
    mismatches,
  };
};

devin-ai-integration[bot]

This comment was marked as resolved.

@sid597
Copy link
Collaborator Author

sid597 commented Feb 22, 2026

@coderabbitai full review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 22, 2026

✅ Actions performed

Full review triggered.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 22, 2026

📝 Walkthrough

Walkthrough

Introduces dual-read compatibility for settings by adding extensive legacy reading paths and helper functions. When dual-read is disabled, the system falls back to legacy settings resolution for Personal, Global, and Discourse Node configurations. Updates query-related default values and introduces a new dual-read feature flag.

Changes

Cohort / File(s) Summary
Dual-read Legacy Compatibility
apps/roam/src/components/settings/utils/accessors.ts
Adds comprehensive legacy reading fallback paths with helper functions (readPathValue, pathKey) and resolution logic for Personal, Global, Discourse Node, and Relations settings. Updates getGlobalSetting, getPersonalSetting, and getDiscourseNodeSetting to check dual-read status and fall back to legacy reading when disabled.
Schema Defaults & Feature Flags
apps/roam/src/components/settings/utils/zodSchema.ts, apps/roam/src/components/settings/utils/zodSchema.example.ts
Introduces defaultNodeIndexValue constant and updates DiscourseNodeSchema specification.query and index field transforms. Changes Query defaults: "Hide query metadata" from false to true, "Query pages" from empty array to ["discourse-graph/queries/*"]. Adds "Enable dual read" feature flag with false default.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: implementing dual-read functionality for both old-system settings and blockprops, which aligns with the primary objective of the changeset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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


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.

coderabbitai[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

graphite-app[bot]

This comment was marked as resolved.

@sid597 sid597 changed the base branch from eng-1465-add-enable-dual-read-feature-flag-with-reactive-accessor to graphite-base/812 February 23, 2026 10:26
@sid597 sid597 force-pushed the eng-1455-add-old-system-read-routing-to-accessor-getters branch from 5a02381 to b67fd5f Compare February 23, 2026 10:40
@sid597 sid597 changed the base branch from graphite-base/812 to eng-1465-add-enable-dual-read-feature-flag-with-reactive-accessor February 23, 2026 10:40
devin-ai-integration[bot]

This comment was marked as resolved.

@sid597 sid597 force-pushed the eng-1455-add-old-system-read-routing-to-accessor-getters branch from b67fd5f to adf5fb5 Compare February 23, 2026 13:51
@sid597 sid597 force-pushed the eng-1465-add-enable-dual-read-feature-flag-with-reactive-accessor branch from 23d75a0 to 9dddded Compare February 23, 2026 13:51
@sid597 sid597 force-pushed the eng-1455-add-old-system-read-routing-to-accessor-getters branch from adf5fb5 to 75d22ad Compare February 23, 2026 14:13
@sid597 sid597 force-pushed the eng-1465-add-enable-dual-read-feature-flag-with-reactive-accessor branch from 9dddded to 41691df Compare February 23, 2026 14:13
@sid597 sid597 force-pushed the eng-1455-add-old-system-read-routing-to-accessor-getters branch from 75d22ad to cc8a3f2 Compare February 23, 2026 14:31
@sid597 sid597 force-pushed the eng-1465-add-enable-dual-read-feature-flag-with-reactive-accessor branch from 41691df to 176d0d1 Compare February 23, 2026 14:31
@sid597 sid597 force-pushed the eng-1465-add-enable-dual-read-feature-flag-with-reactive-accessor branch from 0ff501b to 017e4da Compare February 24, 2026 17:15
@sid597 sid597 force-pushed the eng-1455-add-old-system-read-routing-to-accessor-getters branch from 4475ce3 to 7160d5e Compare February 24, 2026 17:16
@sid597 sid597 force-pushed the eng-1465-add-enable-dual-read-feature-flag-with-reactive-accessor branch from 017e4da to 990543f Compare February 24, 2026 19:07

export const QuerySettingsSchema = z.object({
"Hide query metadata": z.boolean().default(false),
"Hide query metadata": z.boolean().default(true),
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

same default as legacy settings

"Hide query metadata": z.boolean().default(true),
"Default page size": z.number().default(10),
"Query pages": z.array(z.string()).default([]),
"Query pages": z.array(z.string()).default(["discourse-graph/queries/*"]),
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

same defaults as legacy settings

// which returns a flat DiscourseRelation[] with a different structure (one entry per
// if-block, triples at top level, no nodePositions). When migrating getDiscourseRelations()
// to read from block props, it will need a conversion from this shape to the flat array.
const getLegacyRelationsSetting = (): Record<string, unknown> => {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

the plan is to eventually merge to main with the blockprop version so not sure about the performance worries, if any. Will do caching if said so

@sid597 sid597 force-pushed the eng-1455-add-old-system-read-routing-to-accessor-getters branch from 7160d5e to b86b56f Compare February 24, 2026 19:38
devin-ai-integration[bot]

This comment was marked as resolved.

@sid597 sid597 requested a review from mdroidian February 24, 2026 20:02
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.

As per our last meeting, I was under the impression that the plan for Wed was to

  1. merge the 5 PR's that were in review (this is complete)
  2. rebase/restack ENG-1454, 1466+
  3. Then create a pulse update for the project

Is this related to #2, rebase/restack? If so, could you explain to in what way it is related?

Image

From this image, it doesn't look like it is part of "rebase/restack ENG-1454, 1466", as the root of that restack should be the base getters PR. Nor is it mentioned in "Land Orphaned ..." PR.

@sid597 sid597 force-pushed the eng-1465-add-enable-dual-read-feature-flag-with-reactive-accessor branch from 990543f to 1491bbf Compare February 25, 2026 07:34
@sid597 sid597 force-pushed the eng-1455-add-old-system-read-routing-to-accessor-getters branch 2 times, most recently from feb69cc to aac3698 Compare February 25, 2026 07:44
@sid597 sid597 changed the base branch from eng-1465-add-enable-dual-read-feature-flag-with-reactive-accessor to migration-block-init-staging-branch February 25, 2026 07:47
@sid597 sid597 force-pushed the eng-1455-add-old-system-read-routing-to-accessor-getters branch from aac3698 to 847359f Compare February 25, 2026 09:47
if (rawValue === undefined) {
const legacyValue = getLegacyGlobalSetting(keys);
if (legacyValue !== undefined) {
throw new Error(getMissingSettingError({ context: "Global", keys }));
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

explicit throw, we want to know when this happens as it should never happen and not pass through dev testing

@mdroidian
Copy link
Contributor

As per our last meeting, I was under the impression that the plan for Wed was to

  1. merge the 5 PR's that were in review (this is complete)
  2. rebase/restack ENG-1454, 1466+
  3. Then create a pulse update for the project

Is this related to #2, rebase/restack? If so, could you explain to in what way it is related?

Image From this image, it doesn't look like it is part of "rebase/restack ENG-1454, 1466", as the root of that restack should be the base getters PR. Nor is it mentioned in "[Land Orphaned ...](https://github.com//pull/826)" PR.

Discussed in meeting: no longer using graphite which means this is a non issue.

@sid597 sid597 requested a review from mdroidian February 25, 2026 18:11
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