Skip to content

Serve the app's tools to Redrob Code through the app bridge - #78

Merged
its-janghoon merged 3 commits into
developfrom
feat/app-bridge
Oct 17, 2026
Merged

its-janghoon merged 3 commits into
developfrom
feat/app-bridge

Conversation

@its-janghoon

Copy link
Copy Markdown
Member

What's done

  • apps/redrob-query/src/app_bridge.rs: the Redrob Code app bridge. redrob-app-bridge is started on its own thread once the egui context exists and registers as app:query; a daemon that cannot be reached is logged and the app runs without it. Handlers run on the bridge's threads and push BridgeRequests onto a channel, ask for a repaint and block on the reply (30 s). QueryApp::serve_bridge drains the queue once per frame (after the panels polled) and runs each request against the real app state through the same ProfilesPanel / Workspace / tab functions the UI uses; engine Pending answers are polled on the following frames and replied to the moment they land, so the UI thread never waits on a database.
  • Tools: list_connections (id, name, driver, connected), connect {id} (id or name; answers once the profiles panel reports it up, so the schema tree and the query tab open as if clicked), list_tables {connection?, schema?}, describe_table {connection?, table} (roots then columns, schema.table accepted, ambiguous names refused with candidates), run_sql {connection?, sql, max_rows?} (TSV header + rows, default 200, max 10 000, summary line with count / ms / "more rows exist" / rows affected; the status line notes the run), open_in_editor {sql, connection?} (new tab bound to the given or active connection, or an unbound tab), export_result {path, format: csv|tsv} (last run_sql page, else the active tab's grid).
  • app.rs: the bridge field, Bridge::start in new, serve_bridge() after poll() in ui(), bridge.stop() in on_exit, and pub(crate) on the fields and helpers (workspace, profiles, tabs, active, tab_counter, notify, active_profile, push_tab, open_tab_for) the bridge module shares. main.rs: mod app_bridge. Cargo.toml: the git dependency (branch feat/channels-app-bridge, TODO pin to the release tag).
  • README.md / README.ko.md: "Control from Redrob Code" / "Redrob Code에서 조작" sections listing the tools.

What's not done

  • No cargo run of any kind (per the stage rules; the one build is the release). rustfmt was run on the touched files only.
  • run_sql does not run writes. The engine's execute_query admits exactly one read-only statement (is_single_read_only_statement) and the Workspace exposes no arbitrary-mutation path (prepare_mutation / apply_mutation are on DataService, which the shell cannot reach); the README's "No free-form write SQL" boundary stands. A write reaches the agent as the engine's own ReadOnlyViolation text. Lifting that needs a core change outside this piece's file scope.
  • Cargo.lock untouched; the release build resolves the git dependency.
  • No history entry for bridged runs (the history list is per tab); the status line reports them instead.

What to review

  • app_bridge.rs bridge_advance: the connect job resolves from ProfilesPanel::is_connected / is_connecting rather than holding its own Pending, so the panel's Connected event path (schema load, tab open) stays the single one.
  • The MetadataStep continuation (describe_table is two loads in a row) and that step.then is a FnOnce with explicit &Workspace parameter types for the higher-ranked bound.
  • page_as_delimited escaping: TSV escapes tabs / newlines inside a field, CSV quotes per RFC 4180.
  • Thread safety of the handler closure: it captures a Sender<BridgeRequest> and an egui::Context only.

Intentions

Let the Redrob Code agent drive Redrob Query from any surface with the same bounds the UI has, without a mock or an unimplemented tool, and without putting the egui state behind a mutex.

The egui shell now starts `redrob-app-bridge` on a thread once the egui context exists
and registers as `app:query` with the shared daemon (optional: a daemon that cannot be
reached is logged and the app runs without it). Tool handlers run on the bridge's threads
and push requests onto a channel; `QueryApp::serve_bridge` drains it once per frame and
runs each request against the real app state (ProfilesPanel, Workspace, tabs), polling the
engine's `Pending` answers on the following frames so the UI thread never blocks.

Tools: list_connections, connect, list_tables, describe_table, run_sql (TSV, 200 rows by
default, summary line with count / time / truncation / rows affected), open_in_editor,
export_result (csv | tsv of the last run_sql page, else the active tab's grid).

app.rs only gains the bridge field, the start / serve / stop hooks and pub(crate) on the
fields and helpers the bridge module shares. READMEs get a "Control from Redrob Code"
section in both languages.
Lock redrob-app-bridge (redrob-code v0.7.0) and its dependencies into
Cargo.lock, expose QueryApp.bridge to the crate so main.rs can stop it,
name the metadata-step continuation type and use map_or_else, as clippy
-D warnings asks.
@its-janghoon
its-janghoon merged commit 2271172 into develop Oct 17, 2026
2 checks passed
@its-janghoon
its-janghoon deleted the feat/app-bridge branch October 17, 2026 07:32
@its-janghoon its-janghoon mentioned this pull request Oct 17, 2026
its-janghoon added a commit that referenced this pull request Oct 17, 2026
Workspace version 0.4.0 -> 0.5.0; CHANGELOG 0.5.0 section for the Redrob Code app bridge (#78); CONTRIBUTING release example bumped.
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