Skip to content

[codex] Enable v0.3 Notion and Postgres onboarding#224

Open
youhaowei wants to merge 11 commits into
mainfrom
codex/v0.3-remote-onboarding
Open

[codex] Enable v0.3 Notion and Postgres onboarding#224
youhaowei wants to merge 11 commits into
mainfrom
codex/v0.3-remote-onboarding

Conversation

@youhaowei

@youhaowei youhaowei commented Jul 14, 2026

Copy link
Copy Markdown
Owner

Summary

  • enable Notion and Postgres connection flows in onboarding and the data picker
  • keep connector credentials vault-backed while supporting connector-specific configuration
  • discover and materialize remote tables with rollback on failed onboarding/import
  • harden the Clawpatch wrapper against orphaned locks and completed-review false failures

Verification

  • bun check — 84/84 tasks passed
  • bun run test — 44/44 tasks passed
  • bun run typecheck — 49/49 tasks passed
  • scripts/test-clawpatch-wrapper.sh
  • bun run clawpatch:review — exits 0; diff mapped and no eligible features remain

Tracker

Greptile Summary

This PR enables remote connector onboarding and refresh paths. The main changes are:

  • Notion and Postgres connection flows in onboarding and the data picker.
  • Vault-backed credential handling with connector-specific config.
  • Remote table discovery, import, sensitivity review, and rollback logic.
  • Assistant and import error messages that avoid raw runtime text.
  • Clawpatch wrapper handling for stale locks and mapped review completion.

Confidence Score: 4/5

This is close, but the replacement-frame cleanup should be fixed before merging.

  • The remote import path now gates durable row storage before creating a browser frame.
  • The Notion and Postgres import UI now uses stable failure messages.
  • A failed cleanup of the previous frame can make a successful replacement look failed and leave the table pointing at deleted frame data.

packages/app/src/lib/remote-table-materialization.ts

Important Files Changed

Filename Overview
packages/app/src/lib/remote-table-materialization.ts Adds pre-storage sensitivity checks and rollback, but old-frame cleanup can invalidate a committed replacement frame.
packages/app/src/components/data-sources/DataPickerContent.tsx Adds remote connector import flow with reviewed-field handling and sanitized import failures.
scripts/clawpatch.sh Adds stale-lock retry and mapped-diff handling for completed reviews.

Fix All in Claude Code Fix All in Codex Fix All in Cursor

Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
packages/app/src/lib/remote-table-materialization.ts:91
**Cleanup Deletes Replacement**

When a table already has a frame, this cleanup runs after the new frame entry is created and the table has been updated to point at it. If removing the old frame fails, the error escapes to callers even though the replacement is already committed. The data picker then treats the import as failed and removes the new `dataFrameId`, leaving the table pointing at a frame that no longer exists.

Reviews (9): Last reviewed commit: "fix: close remote onboarding review gaps" | Re-trigger Greptile

Greptile also left 1 inline comment on this PR.

Context used (4)

  • Context used - CLAUDE.md (source)
  • Context used - AGENTS.md (source)
  • Context used - NO OFF-TOKEN COLOR (invariant). No raw hex/rgb/okl... (source)
  • Context used - NO RAW RUNTIME ERRORS IN USER-FACING UI (invariant... (source)

@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The change adds Notion and Postgres remote onboarding, typed Postgres mutations, shared remote-table materialization, and stricter data-source configuration validation. It also improves clawpatch stale-lock recovery and adds wrapper tests for review-state handling.

Changes

Remote data onboarding

Layer / File(s) Summary
Data-source configuration contract
packages/types/src/data-sources.ts, apps/server/src/functions/app-artifacts.ts, apps/server/src/functions/app-artifacts.test.ts
Data-source creation accepts connector configuration, rejects credential fields embedded in it, and verifies secret-reference persistence and rejection behavior.
Postgres mutation API
packages/app-data/src/postgres.ts, packages/app-data/src/index.ts
Adds typed table-listing and table-query mutations for Postgres resources and re-exports them from the app-data package.
Remote source onboarding
packages/app/src/lib/remote-connector-onboarding.ts, packages/app/src/lib/remote-connector-onboarding.test.ts
Creates remote sources, probes Notion or Postgres resources, and removes newly created sources when probing fails.
Remote table materialization
packages/app/src/lib/remote-table-materialization.ts, packages/app/src/components/data-sources/DataSourceDisplay.tsx
Centralizes Arrow-result materialization into DataFrames and DataTables, and updates Notion synchronization to use the shared helper.
Remote resource picker integration
packages/app/src/components/data-sources/DataPickerContent.tsx, packages/app/src/components/data-sources/AddConnectionPanel.tsx, packages/app/src/components/data-sources/renderers/ConnectorCardWithForm.tsx, packages/app/src/app/_components/OnboardingView.tsx, packages/app/src/lib/connectors/registry.ts
Enables Notion and Postgres connection options, supports asynchronous connection callbacks, displays discovered resources, materializes selections, and rolls back failed table creation.

Clawpatch review-state handling

Layer / File(s) Summary
Review recovery and mapping checks
scripts/clawpatch.sh
Adds one-time orphaned-lock recovery, original-argument replay, reviewable-file filtering, and explicit mapped/unmapped diff handling.
Wrapper behavior tests
scripts/test-clawpatch-wrapper.sh
Tests stale-lock recovery, converged reviews, and unmapped-file failures using temporary Git state.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant DataPickerContent
  participant connectRemoteSource
  participant DataSourceAPI
  participant RemoteQueryAPI
  participant materializeRemoteTable
  User->>DataPickerContent: submit connector credentials
  DataPickerContent->>connectRemoteSource: create and probe remote source
  connectRemoteSource->>DataSourceAPI: addSource
  DataSourceAPI-->>connectRemoteSource: sourceId
  connectRemoteSource->>RemoteQueryAPI: list remote resources
  RemoteQueryAPI-->>DataPickerContent: discovered resources
  User->>DataPickerContent: select resource
  DataPickerContent->>RemoteQueryAPI: query selected resource
  RemoteQueryAPI-->>DataPickerContent: Arrow query result
  DataPickerContent->>materializeRemoteTable: persist remote result
  materializeRemoteTable-->>DataPickerContent: materialized table
Loading

Possibly related PRs

  • youhaowei/DashFrame#205: Covers the credential-reference rollback behavior exercised by the updated data-source tests.
  • youhaowei/DashFrame#200: Introduced the clawpatch wrapper that this change extends with recovery and validation behavior.
  • youhaowei/DashFrame#124: Also changes server-side data-source credential handling and SecretRef persistence.
🚥 Pre-merge checks | ✅ 3 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description covers summary and verification, but it misses the required Changes section and the UI screenshots section. Add a ## Changes section and a ## Screenshots section with before/after UI images, and keep the verification notes in the required template.
✅ Passed checks (3 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title concisely captures the main change: enabling Notion and Postgres onboarding.

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.

Comment thread packages/app/src/lib/remote-table-materialization.ts
Comment thread packages/app/src/components/data-sources/DataPickerContent.tsx Outdated
Comment thread packages/app/src/components/data-sources/DataPickerContent.tsx Outdated
Comment thread scripts/clawpatch.sh

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c79618f5ec

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

"Data source credentials must use typed credential fields, not config",
);
}
const config: DataSourceConfig = { ...(rawConfig ?? {}) };

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Reject nested credentials in connector config

When a client sends connector config with a nested or alternate credential value, such as { nested: { connectionString: "postgres://..." } } or { dsn: "postgres://..." }, this spread persists it directly in data_sources.config; rowToDataSource then forwards non-credential config keys back to the renderer. That bypasses the new top-level smuggling guard and breaks the plaintext-never-at-rest/no-secret-in-DTO invariant for any buggy or untrusted caller of addDataSource; whitelist expected non-secret keys like defaultSchema or recursively reject credential-shaped data before copying config.

Useful? React with 👍 / 👎.

Comment on lines +297 to +301
setError(
cause instanceof Error
? cause.message
: "Failed to import remote table",
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Surface remote import failures in the resource picker

When importing a selected Notion/Postgres resource fails here, this sets error, but the only place that renders error is AddConnectionPanel, which is hidden while remoteResourceState is active. In that state, query/materialization failures just re-enable the buttons with no visible message, so users cannot tell why selecting a database/table did nothing; render this error in the Choose data to import branch as well.

Useful? React with 👍 / 👎.

Comment on lines +51 to +55
await addDataFrameEntry(dataFrame, {
name,
rowCount: result.rowCount,
columnCount,
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Compensate DataFrame creation on remote import failure

In the new remote import flow, materializeRemoteTable commits a DataFrame entry before the later updateDataTable call links it to the table. If that final update throws after this point, DataPickerContent only removes the newly created DataTable, leaving the DataFrame entry and IndexedDB Arrow blob orphaned; return enough state to delete the frame on failure or make the materialization/linking step atomic.

Useful? React with 👍 / 👎.

Comment on lines +391 to +398
remoteResourceState.resources.map((resource) => (
<Button
key={resource.id}
label={resource.title}
variant="outline"
className="w-full justify-start"
disabled={materializingResourceId !== null}
onClick={() => handleRemoteResourceSelect(resource)}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Filter excluded tables from remote resources

When this picker is used by JoinFlowModal, the current base table is excluded only by its existing DataTable id, but this remote-resource list is keyed by the source database/table id and handleRemoteResourceSelect creates a brand-new DataTable id. Reconnecting the same Notion database or Postgres table from the join modal therefore bypasses the self-table exclusion and lets users join the table with a duplicate copy of itself; filter resources whose id matches an excluded table's table value or pass explicit excluded resource ids.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 5

Caution

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

⚠️ Outside diff range comments (2)
packages/types/src/data-sources.ts (1)

1-1: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

config is validated by blacklist, not allowlist — same root cause across the type contract and the server handler. CreateDataSourceInput.config is untyped (Record<string, unknown>), and the server only rejects two hardcoded credential key names; any other/future key passes straight through into stored config.

  • packages/types/src/data-sources.ts#L56-57: narrow config's type to the actual known non-credential shape (e.g. { defaultSchema?: string }, or a per-connector-type discriminated shape) instead of Record<string, unknown>.
  • apps/server/src/functions/app-artifacts.ts#L493-501: replace/augment the "apiKey" in rawConfig || "connectionString" in rawConfig" blacklist with an allowlist of recognized non-credential keys (or derive the blocked-key set from DataSourceConfig's credential fields) so a new credential field can't be smuggled through config without this check being updated in lockstep.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/types/src/data-sources.ts` at line 1, Restrict
CreateDataSourceInput.config in the data-source type contract to the recognized
non-credential configuration shape instead of an open Record, and update the
validation in the app-artifacts handler around rawConfig to use an allowlist
derived from that contract (or equivalent shared credential metadata). Ensure
unknown and credential fields are rejected so newly added credential keys cannot
pass without the validation being updated.
packages/app/src/components/data-sources/DataSourceDisplay.tsx (1)

601-623: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Postgres data sources still hit the unsupported fallback.

DataSourceDisplay only branches on sourceType === "file" and connector.id === "notion", so Postgres data sources fall through to “Unsupported data source type.” Add a Postgres/remote-api branch here.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/app/src/components/data-sources/DataSourceDisplay.tsx` around lines
601 - 623, Update DataSourceDisplay’s connector branching to handle
Postgres/remote-api data sources before the unsupported fallback. Route the
matching connector to the appropriate remote/Postgres data-source view, while
preserving the existing file and Notion branches and fallback behavior for
unsupported connectors.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/server/src/functions/app-artifacts.ts`:
- Around line 493-501: The credential check before constructing DataSourceConfig
must derive forbidden keys from the canonical typed credential fields instead of
hardcoding only apiKey and connectionString. Update the rawConfig validation in
the surrounding data-source creation flow to automatically reject any credential
field, including future additions, while preserving the existing error behavior
and config construction.

In `@packages/app/src/lib/remote-connector-onboarding.ts`:
- Around line 60-63: Update the rollback handling in the catch block around
removeSource so failures from the cleanup attempt are surfaced through the
established logging mechanism before rethrowing the original cause. Preserve the
existing conditional cleanup and ensure the original probe error remains the
thrown error.

In `@packages/app/src/lib/remote-table-materialization.ts`:
- Around line 30-66: Update materializeRemoteTable to track whether
addDataFrameEntry created a new DataFrame, and ensure that if the subsequent
updateDataTable call fails, the newly created DataFrame and its Arrow bytes are
removed using the existing DataFrame cleanup operation. Do not remove an
existing DataFrame when replacing it, and preserve the original failure
propagation.

In `@packages/types/src/data-sources.ts`:
- Around line 56-57: The config field in the data-source type is too permissive
and can diverge from server validation. Replace Record<string, unknown> with the
recognized non-credential configuration shape, at minimum an optional
defaultSchema string, while preserving config’s optional nature and avoiding
credential fields.

In `@scripts/clawpatch.sh`:
- Around line 132-141: Update the changed-file loop in scripts/clawpatch.sh to
check whether each reviewable changed_file still exists before querying the
feature-map JSON files. Skip deleted files, while preserving the existing
unmapped_reviewable_files behavior for files that remain present.

---

Outside diff comments:
In `@packages/app/src/components/data-sources/DataSourceDisplay.tsx`:
- Around line 601-623: Update DataSourceDisplay’s connector branching to handle
Postgres/remote-api data sources before the unsupported fallback. Route the
matching connector to the appropriate remote/Postgres data-source view, while
preserving the existing file and Notion branches and fallback behavior for
unsupported connectors.

In `@packages/types/src/data-sources.ts`:
- Line 1: Restrict CreateDataSourceInput.config in the data-source type contract
to the recognized non-credential configuration shape instead of an open Record,
and update the validation in the app-artifacts handler around rawConfig to use
an allowlist derived from that contract (or equivalent shared credential
metadata). Ensure unknown and credential fields are rejected so newly added
credential keys cannot pass without the validation being updated.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0e409783-b917-4bb0-878f-a4dedccca7f5

📥 Commits

Reviewing files that changed from the base of the PR and between 9c3da88 and c79618f.

📒 Files selected for processing (16)
  • apps/server/src/functions/app-artifacts.test.ts
  • apps/server/src/functions/app-artifacts.ts
  • packages/app-data/src/index.ts
  • packages/app-data/src/postgres.ts
  • packages/app/src/app/_components/OnboardingView.tsx
  • packages/app/src/components/data-sources/AddConnectionPanel.tsx
  • packages/app/src/components/data-sources/DataPickerContent.tsx
  • packages/app/src/components/data-sources/DataSourceDisplay.tsx
  • packages/app/src/components/data-sources/renderers/ConnectorCardWithForm.tsx
  • packages/app/src/lib/connectors/registry.ts
  • packages/app/src/lib/remote-connector-onboarding.test.ts
  • packages/app/src/lib/remote-connector-onboarding.ts
  • packages/app/src/lib/remote-table-materialization.ts
  • packages/types/src/data-sources.ts
  • scripts/clawpatch.sh
  • scripts/test-clawpatch-wrapper.sh

Comment on lines +493 to +501
if (
rawConfig &&
("apiKey" in rawConfig || "connectionString" in rawConfig)
) {
throw new Error(
"Data source credentials must use typed credential fields, not config",
);
}
const config: DataSourceConfig = { ...(rawConfig ?? {}) };

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Blacklist-based credential-smuggling check doesn't scale.

The check hardcodes "apiKey" and "connectionString" as the only forbidden keys. If a future connector introduces a new credential field (e.g. an OAuth token), it must be added here manually or it can be smuggled through config and persisted in plaintext. Prefer deriving the blocked-key set from the canonical credential fields (or switch to an allowlist of known non-credential keys per connector type) so this stays in sync automatically.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/server/src/functions/app-artifacts.ts` around lines 493 - 501, The
credential check before constructing DataSourceConfig must derive forbidden keys
from the canonical typed credential fields instead of hardcoding only apiKey and
connectionString. Update the rawConfig validation in the surrounding data-source
creation flow to automatically reject any credential field, including future
additions, while preserving the existing error behavior and config construction.

Comment on lines +60 to +63
} catch (cause) {
if (sourceId) await removeSource(sourceId).catch(() => {});
throw cause;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Rollback failures are silently swallowed.

If removeSource fails after a probe error, the failure is discarded with no logging, so a data source can be left orphaned with no trace to diagnose it later.

🩹 Optional: surface rollback failures
   } catch (cause) {
-    if (sourceId) await removeSource(sourceId).catch(() => {});
+    if (sourceId) {
+      await removeSource(sourceId).catch((rollbackErr) => {
+        console.error("connectRemoteSource: rollback failed", sourceId, rollbackErr);
+      });
+    }
     throw cause;
   }
📝 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
} catch (cause) {
if (sourceId) await removeSource(sourceId).catch(() => {});
throw cause;
}
} catch (cause) {
if (sourceId) {
await removeSource(sourceId).catch((rollbackErr) => {
console.error("connectRemoteSource: rollback failed", sourceId, rollbackErr);
});
}
throw cause;
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/app/src/lib/remote-connector-onboarding.ts` around lines 60 - 63,
Update the rollback handling in the catch block around removeSource so failures
from the cleanup attempt are surfaced through the established logging mechanism
before rethrowing the original cause. Preserve the existing conditional cleanup
and ensure the original probe error remains the thrown error.

Comment on lines +30 to +66
/** Persist a server-returned Arrow result and link it to its DataTable. */
export async function materializeRemoteTable(
table: { id: string },
result: RemoteQueryResult,
name: string,
): Promise<MaterializedRemoteTable> {
const columnCount = result.fieldIds.length;
const dataFrame = await DataFrame.create(
decodeBase64ToBytes(result.arrowBuffer),
result.fieldIds as UUID[],
);
const existing = await getDataTable(table.id as UUID);
let dataFrameId: UUID;

if (existing?.dataFrameId) {
await replaceDataFrame(existing.dataFrameId, dataFrame, {
rowCount: result.rowCount,
columnCount,
});
dataFrameId = existing.dataFrameId;
} else {
await addDataFrameEntry(dataFrame, {
name,
rowCount: result.rowCount,
columnCount,
});
dataFrameId = dataFrame.id as UUID;
}

await updateDataTable(table.id as UUID, {
fields: result.fields,
dataFrameId,
lastFetchedAt: Date.now(),
});

return { dataFrameId, rowCount: result.rowCount, columnCount };
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check whether removing a DataTable cascades deletion of its linked/orphaned DataFrame entries.
rg -nP -C6 '\bremoveDataTable\b|\bdeleteDataFrameEntry\b' packages/app-data/src

Repository: youhaowei/DashFrame

Length of output: 1120


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== relevant files ==\n'
git ls-files 'packages/app/src/lib/remote-table-materialization.ts' 'packages/app/src/lib/*' 'packages/app-data/src/*' | sed -n '1,200p'

printf '\n== remote-table-materialization.ts ==\n'
cat -n packages/app/src/lib/remote-table-materialization.ts

printf '\n== data-tables.ts ==\n'
cat -n packages/app-data/src/data-tables.ts

printf '\n== search for DataFrame cleanup / removeDataTable usage ==\n'
rg -n -C4 '\b(removeDataTable|deleteDataFrameEntry|deleteDataFrame|removeDataFrame|replaceDataFrame|addDataFrameEntry|updateDataTable)\b' packages/app-data/src packages/app/src/lib

Repository: youhaowei/DashFrame

Length of output: 31277


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== remote-table-materialization.ts =='
sed -n '1,220p' packages/app/src/lib/remote-table-materialization.ts

echo
echo '== data-tables.ts =='
sed -n '1,220p' packages/app-data/src/data-tables.ts

echo
echo '== package app-data files mentioning DataFrame/DataTable cleanup =='
rg -n -C3 'DataFrame|DataTable|deleteDataFrameEntry|removeDataTable|refreshDataTable|addDataFrameEntry|replaceDataFrame|updateDataTable' packages/app-data/src

Repository: youhaowei/DashFrame

Length of output: 32461


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== find DataPickerContent.handleRemoteResourceSelect =='
rg -n -C6 'handleRemoteResourceSelect|materializeRemoteTable|removeDataTable|rollback' packages/app/src packages/app-data/src

echo
echo '== list files with DataPickerContent =='
fd -i 'DataPickerContent' packages/app/src packages/app-data/src

echo
echo '== list files with handleRemoteResourceSelect references =='
rg -n 'handleRemoteResourceSelect' packages/app/src packages/app-data/src

Repository: youhaowei/DashFrame

Length of output: 13486


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== locate removeDataTable implementation =='
rg -n -C6 'removeDataTable' packages -g '!**/*.test.*'

echo
echo '== locate DataTable/DataFrame schema fields =='
rg -n -C4 'dataFrameId|removeDataFrameEntry|removeDataTableEntry|putDataFrameEntry|updateDataTable' packages/app-data packages/app-core packages

Repository: youhaowei/DashFrame

Length of output: 50378


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '250,305p' packages/app/src/components/data-sources/DataPickerContent.tsx

Repository: youhaowei/DashFrame

Length of output: 2011


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== removeDataTable / removeDataFrame command definitions =='
rg -n -C4 'removeDataTableEntry|removeDataFrameEntry|removeDataTable\b|removeDataFrame\b' packages --glob '!**/*.test.*'

echo
echo '== data-table schema and commands around dataFrameId =='
rg -n -C4 'dataFrameId|refreshDataTable|updateDataTable|createDataTable|addDataTable' packages --glob '!**/*.test.*'

Repository: youhaowei/DashFrame

Length of output: 50378


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== removeDataTableEntry / removeDataFrameEntry references (limited) =='
rg -n -C3 --max-count 20 'removeDataTableEntry|removeDataFrameEntry|removeDataTable\b|removeDataFrame\b' .

echo
echo '== data-table / data-frame command definitions in app-data src =='
rg -n -C3 --max-count 40 'addDataTable|createDataTable|updateDataTable|refreshDataTable|removeDataTable|putDataFrameEntry|updateDataFrameEntry|removeDataFrameEntry' packages/app-data/src

Repository: youhaowei/DashFrame

Length of output: 19036


Clean up the DataFrame when remote materialization fails. If addDataFrameEntry succeeds and updateDataTable throws, the rollback only removes the DataTable row; removeDataTable does not delete the newly created DataFrame or its Arrow bytes, so this can leave an orphaned frame behind.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/app/src/lib/remote-table-materialization.ts` around lines 30 - 66,
Update materializeRemoteTable to track whether addDataFrameEntry created a new
DataFrame, and ensure that if the subsequent updateDataTable call fails, the
newly created DataFrame and its Arrow bytes are removed using the existing
DataFrame cleanup operation. Do not remove an existing DataFrame when replacing
it, and preserve the original failure propagation.

Comment on lines +56 to +57
/** Connector-specific, non-credential settings. */
config?: Record<string, unknown>;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Loosely-typed config weakens the connector-config contract.

config?: Record<string, unknown> accepts any shape, while the server-side DataSourceConfig only recognizes defaultSchema as a non-credential field. Nothing here ties the two together, so a typo'd or future field can silently pass through server validation (which only blacklists apiKey/connectionString). Consider a narrower type (e.g. { defaultSchema?: string } or a per-connector discriminated shape) to keep the client/server contract in sync.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/types/src/data-sources.ts` around lines 56 - 57, The config field in
the data-source type is too permissive and can diverge from server validation.
Replace Record<string, unknown> with the recognized non-credential configuration
shape, at minimum an optional defaultSchema string, while preserving config’s
optional nature and avoiding credential fields.

Comment thread scripts/clawpatch.sh
Comment on lines +132 to +141
unmapped_reviewable_files=""
while IFS= read -r changed_file; do
case "$changed_file" in
*.ts|*.tsx|*.js|*.jsx|*.mjs|*.cjs|*.go|*.py|*.rb|*.ex|*.exs|*.rs|*.cs|*.cpp|*.cc|*.c|*.h|*.hpp|*.swift|*.java|*.kt|*.kts|*.php|*.vue|*.svelte)
if ! grep -Fq -- "\"path\": \"$changed_file\"" "$state_dir"/features/*.json 2>/dev/null; then
unmapped_reviewable_files+="${unmapped_reviewable_files:+$'\n'}$changed_file"
fi
;;
esac
done <<<"$changed_files"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Exclude deleted files from the unmapped files check.

If a reviewable file is deleted, git diff still includes it in changed_files. Since a deleted file will no longer be mapped in the features JSON after updating the map, this loop will incorrectly flag it as an unmapped file and cause a false failure. Check for file existence before querying the feature map to safely ignore deletions.

🐛 Proposed fix
           *.ts|*.tsx|*.js|*.jsx|*.mjs|*.cjs|*.go|*.py|*.rb|*.ex|*.exs|*.rs|*.cs|*.cpp|*.cc|*.c|*.h|*.hpp|*.swift|*.java|*.kt|*.kts|*.php|*.vue|*.svelte)
-            if ! grep -Fq -- "\"path\": \"$changed_file\"" "$state_dir"/features/*.json 2>/dev/null; then
+            if [[ -e "$changed_file" ]] && ! grep -Fq -- "\"path\": \"$changed_file\"" "$state_dir"/features/*.json 2>/dev/null; then
               unmapped_reviewable_files+="${unmapped_reviewable_files:+$'\n'}$changed_file"
             fi
📝 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
unmapped_reviewable_files=""
while IFS= read -r changed_file; do
case "$changed_file" in
*.ts|*.tsx|*.js|*.jsx|*.mjs|*.cjs|*.go|*.py|*.rb|*.ex|*.exs|*.rs|*.cs|*.cpp|*.cc|*.c|*.h|*.hpp|*.swift|*.java|*.kt|*.kts|*.php|*.vue|*.svelte)
if ! grep -Fq -- "\"path\": \"$changed_file\"" "$state_dir"/features/*.json 2>/dev/null; then
unmapped_reviewable_files+="${unmapped_reviewable_files:+$'\n'}$changed_file"
fi
;;
esac
done <<<"$changed_files"
unmapped_reviewable_files=""
while IFS= read -r changed_file; do
case "$changed_file" in
*.ts|*.tsx|*.js|*.jsx|*.mjs|*.cjs|*.go|*.py|*.rb|*.ex|*.exs|*.rs|*.cs|*.cpp|*.cc|*.c|*.h|*.hpp|*.swift|*.java|*.kt|*.kts|*.php|*.vue|*.svelte)
if [[ -e "$changed_file" ]] && ! grep -Fq -- "\"path\": \"$changed_file\"" "$state_dir"/features/*.json 2>/dev/null; then
unmapped_reviewable_files+="${unmapped_reviewable_files:+$'\n'}$changed_file"
fi
;;
esac
done <<<"$changed_files"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/clawpatch.sh` around lines 132 - 141, Update the changed-file loop in
scripts/clawpatch.sh to check whether each reviewable changed_file still exists
before querying the feature-map JSON files. Skip deleted files, while preserving
the existing unmapped_reviewable_files behavior for files that remain present.

Comment thread packages/app/src/lib/remote-table-materialization.ts
Comment thread packages/app/src/components/data-sources/DataPickerContent.tsx Outdated
Comment thread packages/app/src/components/data-sources/DataPickerContent.tsx
Comment thread packages/app/src/lib/remote-table-materialization.ts
Comment thread packages/app/src/components/data-sources/DataPickerContent.tsx Outdated
Comment thread packages/app/src/components/data-sources/DataPickerContent.tsx
Comment thread packages/app/src/lib/remote-table-materialization.ts
Comment thread packages/app/src/components/data-sources/DataPickerContent.tsx Outdated
Comment on lines +297 to +301
setError(
cause instanceof Error
? cause.message
: "Failed to import remote table",
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Raw Errors Reach UI

This still copies arbitrary runtime Error.message text into the picker error state. Failures from Notion or Postgres mutations, vault-backed connector calls, Arrow decoding, or DataFrame persistence can include connector details or internal runtime text, and the picker renders that string directly. A failed remote import should show a fixed recovery message instead of exposing the raw error.

Context Used: NO RAW RUNTIME ERRORS IN USER-FACING UI (invariant... (source)

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/app/src/components/data-sources/DataPickerContent.tsx
Line: 297-301

Comment:
**Raw Errors Reach UI**

This still copies arbitrary runtime `Error.message` text into the picker error state. Failures from Notion or Postgres mutations, vault-backed connector calls, Arrow decoding, or DataFrame persistence can include connector details or internal runtime text, and the picker renders that string directly. A failed remote import should show a fixed recovery message instead of exposing the raw error.

**Context Used:** NO RAW RUNTIME ERRORS IN USER-FACING UI (invariant... ([source](greptile.json))

How can I resolve this? If you propose a fix, please make it concise.

Fix in Claude Code Fix in Codex Fix in Cursor

Comment on lines +37 to +40
const dataFrame = await DataFrame.create(
decodeBase64ToBytes(result.arrowBuffer),
result.fieldIds as UUID[],
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 security Rows Persist Before Gate

This still creates the browser DataFrame from the full remote Arrow payload before any field-sensitivity decision is applied. DataFrame.create(...) writes the decoded bytes to persistent browser storage, and result.fields is only stored later through updateDataTable(...). When a newly imported Notion or Postgres table contains absent, unclassified, or sensitive fields, raw row values can already be durable at rest before the cleared-only cache policy can reject them.

Context Used: NO OFF-TOKEN COLOR (invariant). No raw hex/rgb/okl... (source)

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/app/src/lib/remote-table-materialization.ts
Line: 37-40

Comment:
**Rows Persist Before Gate**

This still creates the browser `DataFrame` from the full remote Arrow payload before any field-sensitivity decision is applied. `DataFrame.create(...)` writes the decoded bytes to persistent browser storage, and `result.fields` is only stored later through `updateDataTable(...)`. When a newly imported Notion or Postgres table contains absent, `unclassified`, or `sensitive` fields, raw row values can already be durable at rest before the cleared-only cache policy can reject them.

**Context Used:** NO OFF-TOKEN COLOR (invariant). No raw hex/rgb/okl... ([source](greptile.json))

How can I resolve this? If you propose a fix, please make it concise.

Fix in Claude Code Fix in Codex Fix in Cursor

Comment thread packages/app/src/components/data-sources/DataPickerContent.tsx Outdated
Comment thread packages/app/src/components/data-sources/DataPickerContent.tsx
Comment thread scripts/clawpatch.sh
}

if (existing?.dataFrameId && existing.dataFrameId !== dataFrame.id) {
await removeDataFrame(existing.dataFrameId);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Cleanup Deletes Replacement

When a table already has a frame, this cleanup runs after the new frame entry is created and the table has been updated to point at it. If removing the old frame fails, the error escapes to callers even though the replacement is already committed. The data picker then treats the import as failed and removes the new dataFrameId, leaving the table pointing at a frame that no longer exists.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/app/src/lib/remote-table-materialization.ts
Line: 91

Comment:
**Cleanup Deletes Replacement**

When a table already has a frame, this cleanup runs after the new frame entry is created and the table has been updated to point at it. If removing the old frame fails, the error escapes to callers even though the replacement is already committed. The data picker then treats the import as failed and removes the new `dataFrameId`, leaving the table pointing at a frame that no longer exists.

How can I resolve this? If you propose a fix, please make it concise.

Fix in Claude Code Fix in Codex Fix in Cursor

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.

1 participant