Skip to content

feat: add Python cell worksheet blocks - #753

Merged
ilyabo merged 37 commits into
mainfrom
codex/python-cell-blocks
Jun 22, 2026
Merged

ilyabo merged 37 commits into
mainfrom
codex/python-cell-blocks

Conversation

@ilyabo

@ilyabo ilyabo commented Jun 21, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

  • Add a new @sqlrooms/python-cell package with persisted Python cell schemas, runtime adapter/host contracts, a Zustand slice, a worksheet block renderer, and command-backed operations.
  • Wire Python cells into the CLI worksheet block registry without creating a top-level Python/Pyodide artifact.
  • Register worksheet commands for adding, updating, running, and clearing Python cell blocks; AI/code-update commands now run the updated cell by default, with run: false as an opt-out.
  • Add a browser-worker Pyodide runtime adapter for CLI Python cells, including bounded stdout/stderr capture, bounded persisted rich/JSON outputs, queued execution, Pyodide/micropip package loading, and JSON-safe non-finite number output handling.
  • Enable Python CodeMirror syntax highlighting, autocomplete, Cmd/Ctrl+Enter execution parity with SQL blocks, and in-editor Tab / Shift-Tab indentation without moving focus out of the editor.
  • Add the sqlrooms Python bridge for read-only DuckDB queries, including dataframe/raw/records helpers, declared input binding, schema input support in the CLI host, and qualified table-reference handling.
  • Render rich Python outputs, including HTML and Vega-Lite/Altair outputs, in the worksheet result area.

Validation

  • Live CLI app at http://localhost:4175/: verified the app mounted with no browser console errors after starting the local dev server.
  • pnpm --filter @sqlrooms/python-cell typecheck
  • pnpm --filter sqlrooms-cli-app typecheck
  • pnpm --filter @sqlrooms/python-cell build
  • pnpm --filter sqlrooms-cli-app build
  • pre-push hook: knip, turbo typecheck, turbo test

Notes

The CLI Pyodide runtime supports read-only SQL/schema bridge operations. Declared scalar/rich outputs are collected by name; declared table/image output materialization is still explicit future work because the CLI host does not yet provide a concrete write-table/write-asset path.

Summary by CodeRabbit

  • New Features
    • Added support for python-cell blocks in the worksheet UI, including run/clear controls, read-only mode, and rich result rendering (text, tables, images, HTML, JSON, Vega-Lite).
    • Added worksheet commands to create/update/run/clear Python cells with persisted configuration.
    • Enabled Python-cell execution in the CLI UI with a Pyodide runtime adapter using read-only DuckDB query results.
  • Documentation
    • Expanded the Python-cell README with adapter/bridge behavior details and updated Altair + sqlrooms.query* usage guidance.

@coderabbitai

coderabbitai Bot commented Jun 21, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Introduces a new @sqlrooms/python-cell workspace package with Zod-typed schemas, a Zustand slice for persisted cell state, a Pyodide Web Worker runtime adapter (using SharedArrayBuffer+Atomics for host SQL bridging), a command factory, and a PythonCellBlock React component. The sqlrooms-cli-ui app is updated to mount the slice, wire a DuckDB-backed PythonRuntimeHost, register worksheet commands, and render python-cell stateful blocks in the worksheet UI.

Changes

@sqlrooms/python-cell — new package

Layer / File(s) Summary
Zod schemas and TypeScript contracts
packages/python-cell/src/types.ts
Defines all Zod-backed schemas and inferred types for adapter kinds, execution statuses, discriminated cell inputs/outputs (literal, SQL query, table reference, schema), execution policy, durable cell state, slice config, and PythonRuntimeHost/PythonRuntimeAdapter interfaces with bridging request/result types.
Zustand slice and store hook
packages/python-cell/src/PythonCellSlice.ts, packages/python-cell/src/useStoreWithPythonCells.ts
createPythonCellSlice exposes CRUD methods (ensureCell, removeCell, renameCell, updateCellCode, patchCell, clearCellResult) and runCell orchestration (executes via adapter, infers capabilities, bounds/truncates outputs to size limits, persists lastResult). useStoreWithPythonCells is a typed selector hook for store access.
Pyodide Web Worker and host bridging
packages/python-cell/src/pyodideWorker.ts
Lazily loads Pyodide (configurable indexURL), installs the _sqlrooms_bridge Python/JS bridge module, executes Python code sequentially with bounded stdout/stderr capture, detects and optionally installs micropip requirements and altair/pandas packages, classifies result objects into typed outputs (text, markdown, table, image, JSON, HTML, Vega-Lite), and bridges host SQL/schema queries back to main thread via SharedArrayBuffer+Atomics.wait.
Pyodide runtime adapter
packages/python-cell/src/PyodidePythonRuntimeAdapter.ts
Manages the Web Worker lifecycle (lazy creation, pending execution map), intercepts host-bridge hostRequest messages from worker, resolves query/table/schema requests via the provided PythonRuntimeHost, encodes responses into SharedArrayBuffer with 32-bit length header and JSON body, signals completion via Atomics.notify, and provides the createPyodidePythonRuntimeAdapter factory with optional CDN indexURL.
Python cell command factory
packages/python-cell/src/PythonCellCommands.ts
createPythonCellCommands returns RoomCommand handlers for add (persist block + cell state), update code (with optional auto-run), run (execute via adapter), and clear result operations on python-cell stateful blocks. Each command validates Zod-parsed inputs, resolves target artifact/block with type checks, and returns structured {success, commandId, ...} payloads.
PythonCellBlock UI component
packages/python-cell/src/PythonCellBlock.tsx
Renders a CodeMirror Python editor with Python language mode (Mod-Enter or button to run), status badge header (showing running/idle/error states), CellDeclarations showing input/output metadata, and PythonCellResultPanel (handles running/no-result/error/output states). ExecutionOutputView dispatches each output variant (text, markdown, table, image, JSON, HTML, Vega-Lite) via dedicated sub-components. HtmlOutputFrame and createVegaLiteOutputHtml embed results in sandboxed iframes with CSP. createPythonCellBlockDefinition wires lifecycle (ensureState, rename, deleteState) into a StatefulBlockDefinition.
Package configuration and barrel exports
packages/python-cell/package.json, packages/python-cell/src/index.ts, packages/python-cell/tsconfig.json, packages/python-cell/eslint.config.js, packages/python-cell/typedoc.js, packages/python-cell/README.md
Adds package.json (v0.29.0-rc.8, ESM, dependencies: pyodide, CodeMirror Python packages, zod, immer, workspace packages; peer: react/react-dom >= 18). Build/lint config, TypeDoc setup. README documents store setup, runtime adapter contract, Pyodide behavior, and SQLRooms bridge functions. index.ts barrel re-exports all public API (commands, slice, adapter, component, types).

CLI UI app integration

Layer / File(s) Summary
Store types, CLI runtime host, and slice mounting
apps/sqlrooms-cli-ui/package.json, apps/sqlrooms-cli-ui/src/store-types.ts, apps/sqlrooms-cli-ui/src/store.ts
Adds @sqlrooms/python-cell workspace dependency. Extends RoomState type with PythonCellSliceState. Implements createCliPythonRuntimeHost (DuckDB-backed runReadonlySql wrapping user SQL in a subquery, optionally applying LIMIT, and converting Arrow results to JSON via arrowTableToJson; readTable quotes identifiers; readSchema queries information_schema for per-table columns). Extends sliceConfigSchemas with pythonCells config. Registers/unregisters WORKSHEET_PYTHON_CELL_COMMAND_OWNER and its commands during room init/destroy. Mounts createPythonCellSlice with createPyodidePythonRuntimeAdapter() and CLI host in composed store.
Artifact configuration and worksheet renderer
apps/sqlrooms-cli-ui/src/statefulBlockArtifactConfigs.ts, apps/sqlrooms-cli-ui/src/workspace/WorksheetArtifact.tsx
Adds python-cell entry to STATEFUL_BLOCK_ARTIFACT_CONFIGS with labels, defaultTitle/embeddedTitle/embeddedDescription, and lifecycle handlers (ensureState/deleteState/renameState) wired to state.pythonCells.*. Adds WorksheetPythonCellBlockRenderer component mapping BlockDocumentStatefulBlockRendererProps to PythonCellBlock (with title, read-only, compact flags), registered in WORKSHEET_STATEFUL_BLOCK_RENDERERS under the 'python-cell' key.

Sequence Diagram(s)

sequenceDiagram
  participant WorksheetUI
  participant PythonCellBlock
  participant PythonCellSlice
  participant PyodidePythonRuntimeAdapter
  participant pyodideWorker
  participant createCliPythonRuntimeHost
  participant DuckDB

  WorksheetUI->>PythonCellBlock: render (blockId, code, readOnly)
  PythonCellBlock->>PythonCellSlice: runCell(cellId)
  PythonCellSlice->>PyodidePythonRuntimeAdapter: execute(PythonExecutionRequest, host)
  PyodidePythonRuntimeAdapter->>pyodideWorker: postMessage execute(executionId, code)
  pyodideWorker->>pyodideWorker: loadPyodide, install _sqlrooms_bridge
  pyodideWorker->>pyodideWorker: runPythonAsync(code)
  pyodideWorker->>PyodidePythonRuntimeAdapter: postMessage hostRequest(sql, SharedArrayBuffer)
  PyodidePythonRuntimeAdapter->>createCliPythonRuntimeHost: runReadonlySql(wrappedQuery)
  createCliPythonRuntimeHost->>DuckDB: execute wrapped SQL
  DuckDB-->>createCliPythonRuntimeHost: Arrow table
  createCliPythonRuntimeHost-->>PyodidePythonRuntimeAdapter: {columns, rows, rowCount}
  PyodidePythonRuntimeAdapter->>pyodideWorker: writeHostResponse(SharedArrayBuffer) + Atomics.notify
  pyodideWorker->>PyodidePythonRuntimeAdapter: postMessage result(outputs, stdout, durationMs)
  PyodidePythonRuntimeAdapter-->>PythonCellSlice: PythonExecutionResult
  PythonCellSlice->>PythonCellSlice: boundOutput, persist lastResult
  PythonCellSlice-->>PythonCellBlock: updated state (result, isRunning=false)
  PythonCellBlock-->>WorksheetUI: render outputs (text/table/Vega-Lite/HTML)
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

  • sqlrooms/sqlrooms#382: The main PR's python-cell integration wires createPythonCellCommands into the room-store command ownership lifecycle (register/unregister under WORKSHEET_PYTHON_CELL_COMMAND_OWNER), which depends on the command registry/palette command system introduced in #382.
  • sqlrooms/sqlrooms#666: Main PR extends the stateful-block artifact configuration system introduced in PR #666 (same apps/sqlrooms-cli-ui/src/statefulBlockArtifactConfigs.ts), by adding a new 'python-cell' artifact key wired to state.pythonCells.*.

Suggested reviewers

  • dmitriy-kostianetskiy
  • lixun910

🐇 A brand new package hops into the room,
Pyodide spinning, Zustand in bloom!
SharedArrayBuffer whispers SQL to DuckDB,
Vega-Lite charts appear — oh how lucky!
The worksheet now runs Python with glee,
Mod-Enter pressed, results flow free. 🐍✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add Python cell worksheet blocks' accurately summarizes the main change: introducing Python cell support as new worksheet blocks in the SQLRooms CLI app.
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.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/python-cell-blocks

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.

@ilyabo
ilyabo marked this pull request as ready for review June 21, 2026 17:13

@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: 35d71fd489

ℹ️ 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".

private ensureWorker() {
if (this.worker) return this.worker;

const worker = new Worker(new URL('./pyodideWorker.ts', import.meta.url), {

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 Point the worker URL at the emitted JS file

When @sqlrooms/python-cell is built/published, tsc emits dist/pyodideWorker.js but does not rewrite this string, and package.json publishes only dist. Consumers that import the built package instead of the CLI app's source aliases will therefore resolve dist/pyodideWorker.ts, which is not present, so the Pyodide adapter fails when the first cell runs. Use the emitted .js worker URL or otherwise include/rewrite the worker asset for the package output.

Useful? React with 👍 / 👎.

markdown: truncate(output.markdown, DEFAULT_MAX_STDIO_BYTES),
};
}
return output;

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 Bound JSON results before persisting them

For cells that assign a large JSON-serializable result (for example a big list or table preview), this path returns the JSON output unchanged, and runCell then stores it in pythonCells.config.cells[cellId].lastResult. Unlike stdout, text, and markdown, JSON results have no size cap here, so one run can write megabytes into the persisted room state and make saves/reloads or rendering very slow. Please persist a bounded preview/summary for JSON outputs as well.

Useful? React with 👍 / 👎.

@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: c2ed3c33e3

ℹ️ 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".

Comment on lines +163 to +166
activeExecutionId = request.executionId;
try {
const lastExpressionResult = await pyodide.runPythonAsync(request.code, {
filename: `<sqlrooms-python-cell:${request.blockId}>`,

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 Bind declared inputs before running user code

When a cell is created with inputs through worksheet.add-python-cell-block or patchCell, the worker proceeds directly to runPythonAsync(request.code) without ever reading request.inputs; named literal, sql, tableRef, or schema inputs therefore are not bound in Python (and table/sql inputs are not fetched), so code that relies on the declared input API fails with NameError or has to bypass the API with manual queries. Please resolve and bind the declared inputs before executing the cell.

Useful? React with 👍 / 👎.

Comment on lines +144 to +153
const packageNames = new Set(
(request.requirements ?? [])
.filter((requirement) => requirement.source !== 'host')
.map((requirement) => requirement.name),
);
if (usesSqlroomsBridge(request.code)) {
packageNames.add('pandas');
}
if (packageNames.size) {
await pyodide.loadPackage([...packageNames]);

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 Install micropip requirements with micropip

When a requirement is declared with source: 'micropip' (which the new PythonRequirementSpec and command input allow), this path groups it with Pyodide packages and calls pyodide.loadPackage, so PyPI-only packages fail during setup before user code runs. Split requirements by source and invoke micropip.install for micropip entries instead of passing them to loadPackage.

Useful? React with 👍 / 👎.

Comment on lines +86 to +88
const handleRun = useCallback(() => {
if (!blockId || runtime?.status === 'running') return;
void runCell(blockId, {artifactId});

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 Block execution when the cell is read-only

In read-only worksheet/document renders, the editor prevents code edits but handleRun still calls runCell, and the run button is enabled whenever the cell has code. Because runCell writes lastResult back into persisted room state, a viewer of a read-only artifact can mutate it by clicking Run or pressing Mod-Enter; include readOnly in this guard and the run button disabled state.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (5)
packages/python-cell/src/PythonCellSlice.ts (1)

150-156: 💤 Low value

patchCell triggers two state updates.

Calling ensureCell twice (line 151 and 152-155) causes two separate Zustand state updates. This can be consolidated into a single update.

♻️ Proposed consolidation
 patchCell(cellId, patch) {
-  const existing = get().pythonCells.ensureCell(cellId);
-  get().pythonCells.ensureCell(cellId, {
-    ...existing,
-    ...patch,
-  });
+  const existing = get().pythonCells.config.cells[cellId];
+  get().pythonCells.ensureCell(cellId, {
+    ...(existing ?? {}),
+    ...patch,
+  });
 },
🤖 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/python-cell/src/PythonCellSlice.ts` around lines 150 - 156, The
patchCell method is calling ensureCell twice (once to retrieve the existing cell
and once to update it), which triggers two separate Zustand state updates.
Consolidate this into a single call by retrieving the existing cell, merging it
with the patch object, and updating the state in one operation. This will reduce
unnecessary re-renders and improve performance.
packages/python-cell/src/pyodideWorker.ts (1)

256-262: ⚖️ Poor tradeoff

Import detection regex may have false positives.

The regex patterns for usesSqlroomsBridge and usesAltair can match inside string literals or comments (e.g., # import sqlrooms). Consider using Python AST parsing for more accurate detection, or accept this as a known limitation.

🤖 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/python-cell/src/pyodideWorker.ts` around lines 256 - 262, The regex
patterns in the `usesSqlroomsBridge` and `usesAltair` functions can produce
false positives by matching import statements inside string literals or
comments. Either implement Python AST parsing to accurately detect actual import
statements, or add a clear code comment documenting this regex-based approach as
a known limitation with acceptable false positives for performance reasons.
packages/python-cell/src/PythonCellBlock.tsx (1)

38-48: ⚡ Quick win

Add TSDoc for exported props/types and component declarations.

These exported declarations are part of the package API and should have TSDoc to satisfy the package documentation contract.

As per coding guidelines, "**/*.{ts,tsx}: Add tsdoc to exported types and functions`."

Also applies to: 213-219

🤖 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/python-cell/src/PythonCellBlock.tsx` around lines 38 - 48, Add TSDoc
comments to all exported type declarations and component declarations in the
file to satisfy the package documentation contract. Specifically, add TSDoc
documentation above the PythonCellBlockRenderProps type definition, the
PythonCellBlockProps type definition, and the PythonCellBlock component
declaration to provide clear documentation for consumers of these exported API
elements. The TSDoc comments should describe the purpose and usage of each
exported declaration, including any relevant parameters or properties. Also
apply the same TSDoc documentation to the other exported declarations mentioned
at lines 213-219.

Source: Coding guidelines

packages/python-cell/src/PythonCellCommands.ts (1)

15-40: ⚡ Quick win

Add TSDoc to exported command constants and types.

The exported API declarations in this range should be documented for consumer-facing discoverability and generated docs quality.

As per coding guidelines, "**/*.{ts,tsx}: Add tsdoc to exported types and functions`."

🤖 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/python-cell/src/PythonCellCommands.ts` around lines 15 - 40, Add
TSDoc comments to all exported declarations in the range. Document the
PYTHON_CELL_BLOCK_TYPE constant, PYTHON_CELL_COMMAND_SUFFIXES array,
PythonCellCommandSuffix type alias, and CreatePythonCellCommandsOptions type
with clear, descriptive JSDoc comments that explain the purpose and usage of
each exported symbol. These comments should appear immediately before each
declaration to ensure proper API documentation generation for consumers.

Source: Coding guidelines

apps/sqlrooms-cli-ui/src/store-types.ts (1)

44-57: ⚡ Quick win

Add TSDoc on exported RoomState.

This export was modified, but it still has no TSDoc summary.

Proposed change
+/**
+ * Aggregated room store state for the CLI UI application.
+ */
 export type RoomState = RoomShellSliceState &
   ArtifactsSliceState &
   ArtifactAiSliceState &

As per coding guidelines, **/*.{ts,tsx}: Add tsdoc to exported types and functions.

🤖 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/sqlrooms-cli-ui/src/store-types.ts` around lines 44 - 57, Add TSDoc
documentation to the exported RoomState type in the store-types.ts file. This
type currently has no documentation comment, but according to coding guidelines,
all exported types must include TSDoc. Place a TSDoc comment block above the
RoomState type definition that provides a clear summary of what this type
represents - it is an intersection type that combines multiple slice states for
the application state management. Use the standard TSDoc format with a brief
description of the type's purpose.

Source: Coding guidelines

🤖 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/sqlrooms-cli-ui/src/store.ts`:
- Around line 270-293: The readTable method directly interpolates the tableName
parameter into a SQL query without escaping or validation, creating a SQL
injection vulnerability. Modify the readTable method to escape or validate the
tableName before passing it to runReadonlyPythonSql. Ensure the table identifier
is properly quoted or sanitized following your database connector's SQL escaping
conventions so that malicious table names cannot alter the query structure.

In `@packages/python-cell/src/PyodidePythonRuntimeAdapter.ts`:
- Around line 122-128: The dispose() method clears the pending map without
rejecting the stored promises, causing any code awaiting execute() calls to hang
indefinitely. Before calling this.pending.clear() in the dispose() method,
iterate through all entries in this.pending and reject each promise with an
appropriate error message indicating that the runtime has been disposed. After
rejecting all pending promises, then proceed to clear the map as currently done.

In `@packages/python-cell/src/PythonCellCommands.ts`:
- Around line 54-58: The index field in the PythonCellCommands schema currently
accepts negative integers which causes unintended behavior when passed to
insertBlocks. Add a constraint to the index field's Zod validation schema to
restrict it to non-negative values only. Modify the number validator chain for
the index field by adding a non-negativity constraint (such as .min(0) or
.nonnegative()) after the .int() call and before .optional() to ensure only zero
or positive integers are accepted.

In `@packages/python-cell/src/PythonCellSlice.ts`:
- Around line 358-362: The truncate function checks byte size using Blob but
then truncates by character index with slice, which is incompatible for
multi-byte UTF-8 characters and can exceed the maxBytes limit or slice
mid-character. Replace the character-based slicing approach with TextEncoder to
encode the string to bytes, truncate at the proper byte boundary, and then
decode the result back to a string. This ensures the returned value never
exceeds maxBytes and maintains valid UTF-8 encoding.

---

Nitpick comments:
In `@apps/sqlrooms-cli-ui/src/store-types.ts`:
- Around line 44-57: Add TSDoc documentation to the exported RoomState type in
the store-types.ts file. This type currently has no documentation comment, but
according to coding guidelines, all exported types must include TSDoc. Place a
TSDoc comment block above the RoomState type definition that provides a clear
summary of what this type represents - it is an intersection type that combines
multiple slice states for the application state management. Use the standard
TSDoc format with a brief description of the type's purpose.

In `@packages/python-cell/src/pyodideWorker.ts`:
- Around line 256-262: The regex patterns in the `usesSqlroomsBridge` and
`usesAltair` functions can produce false positives by matching import statements
inside string literals or comments. Either implement Python AST parsing to
accurately detect actual import statements, or add a clear code comment
documenting this regex-based approach as a known limitation with acceptable
false positives for performance reasons.

In `@packages/python-cell/src/PythonCellBlock.tsx`:
- Around line 38-48: Add TSDoc comments to all exported type declarations and
component declarations in the file to satisfy the package documentation
contract. Specifically, add TSDoc documentation above the
PythonCellBlockRenderProps type definition, the PythonCellBlockProps type
definition, and the PythonCellBlock component declaration to provide clear
documentation for consumers of these exported API elements. The TSDoc comments
should describe the purpose and usage of each exported declaration, including
any relevant parameters or properties. Also apply the same TSDoc documentation
to the other exported declarations mentioned at lines 213-219.

In `@packages/python-cell/src/PythonCellCommands.ts`:
- Around line 15-40: Add TSDoc comments to all exported declarations in the
range. Document the PYTHON_CELL_BLOCK_TYPE constant,
PYTHON_CELL_COMMAND_SUFFIXES array, PythonCellCommandSuffix type alias, and
CreatePythonCellCommandsOptions type with clear, descriptive JSDoc comments that
explain the purpose and usage of each exported symbol. These comments should
appear immediately before each declaration to ensure proper API documentation
generation for consumers.

In `@packages/python-cell/src/PythonCellSlice.ts`:
- Around line 150-156: The patchCell method is calling ensureCell twice (once to
retrieve the existing cell and once to update it), which triggers two separate
Zustand state updates. Consolidate this into a single call by retrieving the
existing cell, merging it with the patch object, and updating the state in one
operation. This will reduce unnecessary re-renders and improve performance.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3c0749c8-3f00-458d-81ef-9ed25aa3d42e

📥 Commits

Reviewing files that changed from the base of the PR and between 8eaacaf and c2ed3c3.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (18)
  • apps/sqlrooms-cli-ui/package.json
  • apps/sqlrooms-cli-ui/src/statefulBlockArtifactConfigs.ts
  • apps/sqlrooms-cli-ui/src/store-types.ts
  • apps/sqlrooms-cli-ui/src/store.ts
  • apps/sqlrooms-cli-ui/src/workspace/WorksheetArtifact.tsx
  • packages/python-cell/README.md
  • packages/python-cell/eslint.config.js
  • packages/python-cell/package.json
  • packages/python-cell/src/PyodidePythonRuntimeAdapter.ts
  • packages/python-cell/src/PythonCellBlock.tsx
  • packages/python-cell/src/PythonCellCommands.ts
  • packages/python-cell/src/PythonCellSlice.ts
  • packages/python-cell/src/index.ts
  • packages/python-cell/src/pyodideWorker.ts
  • packages/python-cell/src/types.ts
  • packages/python-cell/src/useStoreWithPythonCells.ts
  • packages/python-cell/tsconfig.json
  • packages/python-cell/typedoc.js

Comment thread apps/sqlrooms-cli-ui/src/store.ts
Comment thread packages/python/src/PyodidePythonRuntimeAdapter.ts
Comment thread packages/python/src/PythonBlockCommands.ts
Comment thread packages/python-cell/src/PythonCellSlice.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
packages/python-cell/src/pyodideWorker.ts (2)

44-71: ⚡ Quick win

Consider extracting shared host-bridge types to avoid drift.

PyodideHostRequest and PyodideHostResponse are duplicated between this worker file and PyodidePythonRuntimeAdapter.ts. While duplication across worker/main-thread boundaries is sometimes necessary, these wire-format types must stay exactly in sync. Consider extracting them into a shared module (e.g., pyodideWorkerTypes.ts) that both files import, reducing the risk of silent contract mismatches.

🤖 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/python-cell/src/pyodideWorker.ts` around lines 44 - 71, Extract the
duplicate type definitions `PyodideHostRequest` and `PyodideHostResponse` from
the pyodideWorker.ts file into a new shared module called pyodideWorkerTypes.ts.
Move both type definitions to this new shared module, then update
pyodideWorker.ts to import these types from the new shared module. Additionally,
locate the `PyodidePythonRuntimeAdapter.ts` file and update it to import the
same types from the shared module instead of having its own duplicate
definitions. This ensures both files maintain a single source of truth for these
wire-format types and prevents silent contract mismatches.

349-382: 💤 Low value

quoteSqlIdentifier is duplicated in store.ts.

This helper also exists in apps/sqlrooms-cli-ui/src/store.ts (line 379-381). Consider extracting it to a shared utility module to avoid divergence.

🤖 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/python-cell/src/pyodideWorker.ts` around lines 349 - 382, The
quoteSqlIdentifier function is duplicated in both pyodideWorker.ts and store.ts
(line 379-381), which can lead to maintenance issues if the logic needs to be
updated in one place but not the other. Extract the quoteSqlIdentifier function
to a shared utility module (consider creating a common utilities file or adding
it to an existing shared utilities module), then import and use it in both
pyodideWorker.ts and store.ts to ensure consistency and reduce code duplication.
🤖 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.

Nitpick comments:
In `@packages/python-cell/src/pyodideWorker.ts`:
- Around line 44-71: Extract the duplicate type definitions `PyodideHostRequest`
and `PyodideHostResponse` from the pyodideWorker.ts file into a new shared
module called pyodideWorkerTypes.ts. Move both type definitions to this new
shared module, then update pyodideWorker.ts to import these types from the new
shared module. Additionally, locate the `PyodidePythonRuntimeAdapter.ts` file
and update it to import the same types from the shared module instead of having
its own duplicate definitions. This ensures both files maintain a single source
of truth for these wire-format types and prevents silent contract mismatches.
- Around line 349-382: The quoteSqlIdentifier function is duplicated in both
pyodideWorker.ts and store.ts (line 379-381), which can lead to maintenance
issues if the logic needs to be updated in one place but not the other. Extract
the quoteSqlIdentifier function to a shared utility module (consider creating a
common utilities file or adding it to an existing shared utilities module), then
import and use it in both pyodideWorker.ts and store.ts to ensure consistency
and reduce code duplication.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 15ce099f-d99f-4ebd-9624-510a571b14a7

📥 Commits

Reviewing files that changed from the base of the PR and between c2ed3c3 and 30e60c4.

📒 Files selected for processing (6)
  • apps/sqlrooms-cli-ui/src/store.ts
  • packages/python-cell/src/PyodidePythonRuntimeAdapter.ts
  • packages/python-cell/src/PythonCellBlock.tsx
  • packages/python-cell/src/PythonCellCommands.ts
  • packages/python-cell/src/PythonCellSlice.ts
  • packages/python-cell/src/pyodideWorker.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • packages/python-cell/src/PythonCellBlock.tsx
  • packages/python-cell/src/PyodidePythonRuntimeAdapter.ts
  • apps/sqlrooms-cli-ui/src/store.ts
  • packages/python-cell/src/PythonCellCommands.ts
  • packages/python-cell/src/PythonCellSlice.ts

@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: 30e60c4aaa

ℹ️ 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".

activeExecutionId = undefined;
}

const outputs = readResultOutput(pyodide);

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 Honor declared output names during execution

When a cell is created with an outputs declaration through worksheet.add-python-cell-block, the request carries those declarations but the worker ignores request.outputDeclarations here and only reads the special global result. For example, code that assigns foo = 42 with an output declaration {type: 'json', name: 'foo'} reports no outputs, and declared table/image outputs are never materialized. Please collect the declared output names from globals and route table/asset outputs through the host where applicable instead of relying solely on result.

Useful? React with 👍 / 👎.

Comment on lines +271 to +275
runReadonlyPythonSql(
`SELECT * FROM ${quoteSqlIdentifier(tableName)}`,
maxRows,
),
runReadonlySql: ({query, maxRows}) => runReadonlyPythonSql(query, maxRows),

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 Provide schema bridge for schema inputs

Cells that declare a schema input fail in the CLI because bindInputs sends a schema host request and PyodidePythonRuntimeAdapter requires host.readSchema, but this host only wires table/query access. In that scenario the run errors with SQLRooms Python schema bridge is not configured before user code executes, even though schema inputs are accepted by the command/schema API. Please implement readSchema here or reject schema inputs for the CLI runtime.

Useful? React with 👍 / 👎.

@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: ba77a3141e

ℹ️ 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".

Comment on lines +387 to +390
pyodide.globals.set(
input.name,
requestHostQuery(input.query, input.maxRows),
);

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 Convert declared input payloads before binding

When a declared sql input is used, this writes the raw JS host response into the Python global. Pyodide exposes JS objects as JsProxy unless converted, while the sqlrooms.query() helper explicitly calls .to_py(); cells that pass the input to pandas or return it as result therefore receive/prolong a proxy instead of a native {columns, rows, rowCount} dict. Convert the response with pyodide.toPy(...) (and do the same for table/schema inputs) before binding.

Useful? React with 👍 / 👎.

Comment on lines +207 to +211
if (nextCell) {
nextCell.lastResult = result;
nextCell.updatedAt = Date.now();
}
delete draft.pythonCells.runtimeByCellId[cellId];

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 Preserve active runs when an older execution finishes

When runCell is invoked twice for the same cell through the command/direct API, the second call overwrites runtimeByCellId[cellId] with its execution id. When the first completion reaches this block, it still writes lastResult and deletes the runtime entry, making the UI/API report idle while the second run is queued or running; with non-serial adapters it can also persist a stale result. Check the current execution id before clearing or writing completion state.

Useful? React with 👍 / 👎.


return {"type": "json", "name": name, "value": value}

__sqlrooms_json.dumps([__sqlrooms_output_for("result", result)], default=str)

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 Emit valid JSON for non-finite Python numbers

For outputs containing non-finite floats (result = float('nan'), pandas/NumPy data, or Vega specs with NaN), Python's json.dumps emits NaN/Infinity by default, which is not valid JSON for the subsequent JSON.parse. A successful cell is then reported as an execution error while reading outputs. Normalize or replace non-finite values with a JSON-compatible representation before parsing.

Useful? React with 👍 / 👎.

@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: c7aebc2938

ℹ️ 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".

pyodide.globals.set(
input.name,
requestHostQuery(
`SELECT * FROM ${quoteSqlIdentifier(input.tableName)}`,

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 Handle qualified table references

When a declared tableRef uses the canonical SQLRooms table id produced elsewhere in the app, such as main.events or "main"."events", this quotes the whole string as one identifier ("main.events" / """main"".""events""") instead of preserving the qualified parts. That makes Python cells fail to bind schema-qualified or attached-database tables even though those are the table references agents and table tools hand around; use the existing qualified table-reference quoting/parsing behavior rather than single-identifier quoting here.

Useful? React with 👍 / 👎.

Comment on lines +162 to +163
pyodide.setStdout({batched: (output) => stdout.push(output)});
pyodide.setStderr({batched: (output) => stderr.push(output)});

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 Cap stdout while collecting it

For cells that print a lot of data, this buffers every stdout/stderr chunk in memory and only applies the persisted-size truncation later in PythonCellSlice, while request.limits?.maxStdoutBytes is never enforced in the worker. A single print loop can therefore allocate very large strings/arrays and freeze or crash the browser before the bounded result summary is created; drop or truncate output as it is appended.

Useful? React with 👍 / 👎.

@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: 03434da5df

ℹ️ 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".

activeExecutionId = request.executionId;
try {
bindInputs(pyodide, request.inputs);
const lastExpressionResult = await pyodide.runPythonAsync(request.code, {

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 Enforce timeoutMs during Python execution

When a host supplies limits.timeoutMs or a user runs CPU-bound code such as while True: pass, this await has no timeout or interrupt path. Because the worker is serial, that promise never settles, runtimeByCellId stays running, and all later Python-cell executions queue behind the hung run until the worker/page is reset.

Useful? React with 👍 / 👎.

Comment thread packages/python/src/pyodideWorker.ts Outdated
__sqlrooms_seen_output_names = set()
for __sqlrooms_declaration in __sqlrooms_output_declarations:
__sqlrooms_name = __sqlrooms_declaration.get("name")
if __sqlrooms_name in globals():

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 Clear declared outputs before reading globals

When a cell has a declared output and a previous run created that global, a later successful run that no longer assigns it still passes this globals() check because the Pyodide interpreter is reused and only result is cleared before execution. In that scenario the persisted lastResult shows stale output from an earlier run, which can corrupt worksheet results after code edits.

Useful? React with 👍 / 👎.

Comment on lines +385 to +387
requestHostQuery(
`SELECT * FROM ${quoteSqlTableReference(input.tableName)}`,
input.maxRows,

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 Route tableRef inputs through readTable

PythonRuntimeHost.readTable is the required table-input hook, while runReadonlySql is optional; however a tableRef input is converted into a SQL host request here, and the adapter rejects that request when runReadonlySql is not implemented. Package consumers that implement the documented readTable contract therefore cannot run table-ref inputs unless they also provide the optional SQL bridge.

Useful? React with 👍 / 👎.

@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: 0187576597

ℹ️ 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".

Comment thread apps/sqlrooms-cli-ui/src/store.ts Outdated
const arrowTable = await connector.query(
wrapPythonReadonlyQuery(query, maxRows),
);
const rows = arrowTableToJson(arrowTable);

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 Preserve SQL value types for Python rows

When a Python cell uses sqlrooms.query()/query_df() or a tableRef input over data containing NULL or booleans, this bridge serializes rows with arrowTableToJson; that helper stringifies every non-number/non-BigInt value, so NULL becomes "null" and booleans become "true"/"false". Pandas and downstream Python analysis then operate on corrupted strings rather than the database values, so this path should use a conversion that preserves JSON primitives for Python bridge rows.

Useful? React with 👍 / 👎.

Comment on lines +402 to +404
if (input.kind === 'literal') {
pyodide.globals.set(input.name, input.value);
continue;

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 Convert literal inputs before binding

When a command declares a literal input whose value is an object or array, this branch stores the raw JS value directly in Pyodide. Pyodide exposes that as a JsProxy, unlike the SQL/table/schema paths below that call bindPythonGlobal and .to_py(), so Python code expecting a normal dict/list or returning that value as result gets a proxy/stringified object instead; route literal values through the same conversion path.

Useful? React with 👍 / 👎.

Comment on lines +133 to +135
this.pending.delete(request.executionId);
this.worker?.terminate();
this.worker = undefined;

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 Reject pending runs when terminating the worker

With per-request limits.timeoutMs, a timeout terminates the shared worker but only deletes and rejects the timed-out entry. Any other execution already posted to that worker, such as a queued run for another cell with no timeout or a longer timeout, can no longer receive a result and remains in pending/running state until an external reset; clear and reject all pending requests whenever the worker is terminated.

Useful? React with 👍 / 👎.

@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: 4b8f1c82d2

ℹ️ 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".

Comment on lines +129 to +130
const timeoutId = request.limits?.timeoutMs
? setTimeout(() => {

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 Start execution timeouts when work starts

When timeoutMs is configured and multiple cells are run, this timer starts as soon as execute() posts the request, but pyodideWorker serializes requests behind executionQueue. A queued cell spends part of its execution budget waiting for earlier cells; for example, if the first run takes 55s with a 60s limit, the second cell is killed after only about 5s of actual Python and the shared worker is terminated. Start the timeout when the worker begins that request, or track queue wait separately.

Useful? React with 👍 / 👎.

Comment thread apps/sqlrooms-cli-ui/src/store.ts Outdated
Comment on lines +385 to +388
const qualifiedTableName =
schemaName === 'main'
? currentTableName
: `${schemaName}.${currentTableName}`;

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 Preserve catalog names in schema summaries

For schema inputs without a tableName, this key uses only schema/table and even drops the main schema. In workspaces with attached DuckDB databases, information_schema.columns can return the same table_schema/table_name from different table_catalogs, so their columns are merged under one table and Python cells or agents receive an invalid schema. Include table_catalog in the query/key, or use the existing qualified table-name helpers before grouping.

Useful? React with 👍 / 👎.

@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: 3aee0b8924

ℹ️ 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".


const signal = new SharedArrayBuffer(4);
const response = new SharedArrayBuffer(4 + HOST_RESPONSE_BUFFER_BYTES);
const normalizedMaxRows = normalizeMaxRows(maxRows);

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 Apply the row preview limit to bridge queries

When a caller configures limits.maxRowsPreview, cells that omit an explicit maxRows—for example sqlrooms.query("select * from big_table") or a tableRef input without maxRows—still send an unbounded host request because this path only normalizes the explicit argument and maxRowsPreview is otherwise never read. The CLI host then applies no LIMIT and tries to JSON-copy the full result through the 16 MiB bridge buffer, so large tables can fail with SQLRoomsBridgeResponseTooLarge or consume excessive memory before user code runs; fall back to the request preview limit when maxRows is absent, and apply the same behavior to table requests.

Useful? React with 👍 / 👎.

@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: 9e73d3c2dd

ℹ️ 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".

Comment thread packages/python/src/pyodideWorker.ts Outdated

def __sqlrooms_output_for(name, value, declared_type=None):
if declared_type == "text":
return {"type": "text", "name": name, "text": str(value)}

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 Bound declared text outputs before serialization

When a block declares a text/markdown/html output, this branch converts the full Python value to a string and the worker later JSON-serializes/posts it before PythonSlice truncates persisted summaries. A cell with a declared summary output and summary = 'x' * 10_000_000 can still allocate and send megabytes through the worker/main-thread boundary despite the persisted-size caps; apply the same bounded-string budget here and in the adjacent markdown/html branches before returning the output.

Useful? React with 👍 / 👎.

@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: 03d02a3987

ℹ️ 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".

Comment thread apps/sqlrooms-cli-ui/src/store.ts Outdated
const trimmedQuery = query.trim().replace(/;+$/, '');
const limit =
maxRows === undefined ? '' : ` LIMIT ${Math.max(0, Math.floor(maxRows))}`;
return `SELECT * FROM (${trimmedQuery}) AS sqlrooms_python_query${limit}`;

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 stacked statements in readonly Python SQL

When a Python block calls sqlrooms.query() with SQL that closes the derived table and appends another statement, for example SELECT 1); DROP TABLE t; --, this wrapper produces SELECT * FROM (SELECT 1); DROP TABLE t; --) .... In the CLI path that string is forwarded to DuckDB via con.query(sql) in python/sqlrooms-server/sqlrooms/server/query.py:72, which can execute the stacked statement, so the supposedly readonly bridge can mutate or drop workspace tables. Parse/enforce a single SELECT before forwarding, or route through the existing select-only policy.

Useful? React with 👍 / 👎.

@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: fd3c48bae0

ℹ️ 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".

Comment thread packages/python/src/pyodideWorker.ts Outdated
Comment on lines +414 to +417
function bindInputs(pyodide: PyodideAPI, inputs: PythonInput[]) {
for (const input of inputs) {
if (input.kind === 'literal') {
bindPythonGlobal(pyodide, input.name, input.value);

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 Clear stale input globals before binding

When a block's inputs are removed or renamed after a successful run, this loop only writes the current declarations and never deletes names bound on earlier executions. Because the Pyodide interpreter is reused, Python code can keep reading the old global (for example customers after that input was removed) and persist results from stale data instead of failing or using the new declarations; track and clear previously bound input names before binding the current set.

Useful? React with 👍 / 👎.

Comment thread apps/sqlrooms-cli-ui/src/store.ts Outdated
Comment on lines +339 to +340
if (value instanceof Date) {
return value.getTime();

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 Preserve temporal SQL values for Python rows

When a DuckDB/Arrow DATE or TIMESTAMP reaches this converter as a JS Date, this turns it into an epoch-millisecond number before the rows are handed to Pyodide. sqlrooms.query_df() then builds pandas columns of integers instead of dates/timestamps, so time-based filters, grouping, and charts operate on corrupted types; preserve temporal values with type-aware conversion rather than dropping them to raw milliseconds.

Useful? React with 👍 / 👎.

@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: 5b8dd03faf

ℹ️ 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".

const arrowTable = await connector.query(`
SELECT table_catalog, table_schema, table_name, column_name, data_type
FROM information_schema.columns
WHERE table_schema NOT IN ('pg_catalog', 'information_schema')

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 internal tables from schema inputs

When a schema input omits tableName, this query enumerates every non-pg_catalog/information_schema column, but the CLI's DuckDB schema UI later applies defaultLoadSchemaCatalogFilter and an explicit mosaic-schema filter. In workspaces with SQLRooms persistence tables, the temp catalog, or the mosaic preaggregation cache, Python cells/agents receive hidden internal tables in their schema context and can treat them as user data; apply the same catalog/table filters before grouping the schema summary.

Useful? React with 👍 / 👎.

@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: 90feeee4b6

ℹ️ 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".

Comment thread apps/sqlrooms-cli-ui/src/store.ts Outdated
}
const limit =
maxRows === undefined ? '' : ` LIMIT ${Math.max(0, Math.floor(maxRows))}`;
return `SELECT * FROM (${trimmedQuery}) AS sqlrooms_python_query${limit}`;

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 Preserve trailing line comments when wrapping queries

When a Python bridge query is a valid SELECT that ends with a -- line comment, e.g. sqlrooms.query("SELECT 1 -- note"), this appends the closing ) AS sqlrooms_python_query on the same line, so DuckDB treats the wrapper as part of the comment and sees an unterminated subquery. Add a newline or otherwise terminate trailing line comments before appending the wrapper so commented SELECTs still run.

Useful? React with 👍 / 👎.

@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: 11fdb1a38c

ℹ️ 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".

Comment thread packages/python/src/PythonBlock.tsx Outdated
Comment on lines +396 to +399
sandbox="allow-scripts"
referrerPolicy="no-referrer"
className="bg-background h-96 w-full rounded-sm border"
srcDoc={html}

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 Block scripts in arbitrary HTML outputs

When a persisted html output is rendered, including in a read-only or shared worksheet, this iframe executes whatever scripts are embedded in srcDoc because the sandbox explicitly includes allow-scripts and arbitrary HTML is not wrapped with a restrictive CSP. A stored result such as <script>fetch('https://attacker.example')</script> runs as soon as the viewer opens the worksheet, without pressing Run; either omit script execution for user HTML or apply an allowlisted CSP like the generated Vega-Lite renderer does.

Useful? React with 👍 / 👎.

Comment on lines +228 to +230
const lastExpressionResult = await pyodide.runPythonAsync(request.code, {
filename: `<sqlrooms-python-block:${request.blockId}>`,
});

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 Isolate each Python execution from prior globals

If a user reruns a block after removing an assignment, or runs another Python block in the same worker, globals left by previous executions are still visible here because this executes in Pyodide's shared global namespace while only result, declared outputs, and input names are cleared. For example, after one run defines threshold = 10, a later block containing only result = threshold succeeds with stale state instead of reflecting its declared inputs and code, making results order-dependent; run code with a fresh/per-block globals dict or clear user globals before execution.

Useful? React with 👍 / 👎.

@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: 4a31701ce0

ℹ️ 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".

Comment on lines +125 to +128
if (typeof SharedArrayBuffer === 'undefined') {
throw new Error(
'Pyodide Python execution requires SharedArrayBuffer for SQLRooms host bridge calls.',
);

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 Allow pure Python without SharedArrayBuffer

In non-cross-origin-isolated hosts where SharedArrayBuffer is undefined, this guard rejects every Pyodide execution before looking at whether the request will use the SQLRooms bridge. Blocks that only run local Python, such as result = 1, do not need the synchronous query/table/schema bridge, so package consumers or embedded/static renderers without COOP/COEP cannot run even pure Python. Defer this requirement until a host bridge call is actually needed, or provide a non-bridge execution path.

Useful? React with 👍 / 👎.

@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: 32df68606b

ℹ️ 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".

document: WorksheetMarkdownDocumentBlockRenderer,
'sql-query': WorksheetSqlQueryBlockRenderer,
'html-app': WorksheetHtmlAppBlockRenderer,
python: WorksheetPythonBlockRenderer,

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 Gate Python blocks behind experimental mode

When a worksheet already contains a python stateful block and the CLI is opened without --experimental, this renderer remains active because createWorksheetStatefulBlockRenderers(false) spreads this map and only replaces pivot/document/sql/html-app with the experimental placeholder. That bypasses the stability: 'experimental' setting for Python and still lets non-experimental users view/edit/run the new Python surface; add python: ExperimentalStatefulBlockPlaceholder to the non-experimental branch as well.

Useful? React with 👍 / 👎.

Comment thread packages/python/src/pyodideWorker.ts Outdated
Comment on lines +831 to +833
message:
error instanceof Error ? error.message : 'Python execution failed.',
traceback: error instanceof Error ? error.stack : undefined,

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 Bound worker-side error summaries

If Python raises an exception with a very large message, this worker still copies the raw error.message and stack into the result that is posted back to the main thread; although the slice now truncates before persisting, the fresh evidence is that this worker-side summary remains unbounded before that truncation. A cell like raise Exception('x' * 10_000_000) can still allocate and transfer megabytes across the worker boundary, so apply the same byte cap here before returning the summary.

Useful? React with 👍 / 👎.

@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: ab3e6ba14c

ℹ️ 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".

Comment thread packages/python/src/pyodideWorker.ts Outdated
return value
if isinstance(value, str):
return __sqlrooms_bounded_string(value, budget) if budget is not None else value
if isinstance(value, dict):

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 Protect output serialization from shadowed builtins

Because this serializer runs in the same globals dict as the user's cell, unqualified builtin names here resolve to user variables first. If a valid cell defines a common name such as dict = {} or list = [] and then returns a JSON/dict/list result, checks like isinstance(value, dict) throw during output collection, so the run is reported as an execution error after the user code succeeded; use builtins aliases or a separate helper namespace for these type checks.

Useful? React with 👍 / 👎.

@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: a2cb127faa

ℹ️ 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".

bindInputs(pyodide, globals, request.inputs);
// The adapter starts timeoutMs when it receives this worker's "started"
// message and terminates the worker if execution exceeds that budget.
const lastExpressionResult = await pyodide.runPythonAsync(request.code, {

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 Destroy returned PyProxy values after capturing expressions

When a cell's final expression produces a Python object, such as a DataFrame or list, runPythonAsync returns a JS-owned PyProxy. This path stores that object into the execution globals when result was not assigned, but never destroys the returned proxy; destroying the globals dict later does not release the JS proxy itself, so repeated runs that end in large Python objects can leak worker memory. Destroy the proxy after transferring/reading it, similar to the toPy binding path.

Useful? React with 👍 / 👎.

@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: f28fbf68cc

ℹ️ 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".

Comment on lines +1000 to +1003
...createPythonSlice({
runtimeAdapter: createPyodidePythonRuntimeAdapter(),
host: createCliPythonRuntimeHost(),
})(set, get, store),

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 Pass a timeout to the CLI Python slice

Even though the worker now supports execution timeouts, this CLI wiring leaves request.limits?.timeoutMs undefined for every worksheet Python run. In the shipped UI a cell such as while True: pass therefore never trips the adapter timeout and the shared Pyodide worker keeps all later Python runs queued behind it until the page/runtime is reset; provide a sane CLI limits.timeoutMs when creating the slice.

Useful? React with 👍 / 👎.

@ilyabo
ilyabo merged commit de90cf1 into main Jun 22, 2026
4 checks passed
@ilyabo
ilyabo deleted the codex/python-cell-blocks branch June 22, 2026 16:03
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