feat(actions): store Global Actions in gxserver - #79
Conversation
Global Actions apply to every project instead of one, so they cannot live in projects.customCommandsJson the way Project Actions do. Add a daemon-owned global_sidebar_commands table (migration 0018) so mobile, web, and every desktop build read one list rather than mirroring a per-project column. The four default actions (dev/build/test/setup) stay project-scoped, so every global action is user-created. That removes the whole default-resurrection and tombstone branch: the stored rows are the entire list. readSidebarHud attaches globalCommands after the fact, the same way it already handles commandsByProject, so read_sidebar_hud stays a pure projection of project rows. mutateSidebarHudSettings gains a globalCommand target; SidebarHudSettingsMutation was project-only, so it grows one optional field and a global write schedules no projectUpdated delta because it touches no project row. Project and Global saves validate through one shared path — the payloads are identical and only ownership differs, so splitting the validation would let the two lists drift into accepting different action shapes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (6)
🚧 Files skipped from review as they are similar to previous changes (6)
📝 WalkthroughWalkthroughThis change adds daemon-owned global sidebar commands. It adds SQLite persistence, CRUD and ordering operations, HUD mutation handling, protocol support, global command projection, and tests for empty, filtered, and ordered command lists. ChangesGlobal sidebar commands
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant Client
participant readSidebarHud
participant DomainRepository
participant SQLite
Client->>readSidebarHud: Submit globalCommand mutation
readSidebarHud->>DomainRepository: Save, delete, or order command
DomainRepository->>SQLite: Persist global command state
SQLite-->>DomainRepository: Return stored state
DomainRepository-->>readSidebarHud: Return refreshed command data
readSidebarHud-->>Client: Return globalCommands and persisted order
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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 `@gxserver-rs/src/domain.rs`:
- Around line 521-540: Update list_global_sidebar_commands so serde_json parsing
failures for definitionJson are propagated as DomainResult errors, matching
list_stashed_prompts and similar row-based list methods. Replace the silent
filter_map/.ok() behavior with error-aware collection, preserving the existing
ordering and successful deserialization behavior.
- Around line 594-642: Wrap the per-command UPDATE loop in
order_global_sidebar_commands with a BEGIN IMMEDIATE TRANSACTION and commit only
after every write succeeds; on any failure, roll back and return the error.
Follow the existing transaction pattern from update_session_order, preserving
the current ordering logic and error propagation.
🪄 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 Plus
Run ID: a5235d30-a636-4c63-8d18-5fe5a78c1975
📒 Files selected for processing (8)
gxserver-rs/src/constants.rsgxserver-rs/src/domain.rsgxserver-rs/src/server.rsgxserver-rs/src/sidebar_hud.rsgxserver-rs/src/storage.rsshared/gxserver-protocol.tsshared/sidebar-commands.test.tsshared/sidebar-commands.ts
Reorder issued one UPDATE per row with no surrounding transaction, so a failure partway left some rows on the new sortOrder and others on the old — an interleaved list that the server would then echo back as the confirmed order. Wrap it in BEGIN IMMEDIATE / COMMIT / ROLLBACK, mirroring update_session_order, which guards the identical case. Taking the writer reservation before the read also closes the read-then-write race against a concurrent save or delete, whose row would otherwise keep a stale sortOrder. Deduplicate the requested ids as well: a repeated id consumed two index positions, because the append guard skipped the duplicate while the write loop still assigned it twice. Reject the reserved built-in ids (dev/build/test/setup) for global actions. The read projection recomputes isDefault from the id, so a stored global action called "dev" came back marked as a default however it was written. Rejecting at save is what makes the invariant hold on both paths, and it keeps the global and project id spaces from colliding on the reserved names. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
maddada#75 landed the showOnProjectRow surface, which touches the same HUD contract. Both sides are additive, so the resolution keeps each: - readSidebarHud and mutateSidebarHudSettings call maddada#75's new apply_commands_by_project_if_requested helper and still attach globalCommands. - The shared save validator carries show_on_project_row, so global and project saves keep taking the identical action definition. - The mutation target union widens to "command" | "globalCommand" alongside maddada#75's showOnProjectRow field. Also updates two normalizeStoredSidebarCommands expectations that maddada#75 left asserting the pre-showOnProjectRow shape; they fail on main as merged, independently of this branch. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
First of two PRs for the Global Actions feature you specced. This one is the daemon and contract layer only — nothing is user-visible until the follow-up adds the tab strip. Splitting it so the storage shape gets reviewed before the GPUI work depends on it.
Why a new table
Global Actions apply to every project, so they can't live in
projects.customCommandsJsonthe way Project Actions do. This adds a daemon-ownedglobal_sidebar_commandstable (migration0018), matching yourapp_user_data/automations/stashed_promptsprecedent — so mobile, web and every desktop build read one list instead of mirroring a per-project column.Per your note: "global actions should be stored in gxserver so they're available for mobile/web/macos/windows/etc."
Notable design points
deletedDefaultCommandIdsbranch — the stored rows are the entire list.read_sidebar_hudstays pure.globalCommandsis attached by the server afterwards, the same pattern already used forcommandsByProject, so the projection over project rows is untouched.sidebar_command_scopeassumed exactly one owner project. That's the assumption that had to give:SidebarHudSettingsMutationgrows one optional field, and a global write schedules noprojectUpdateddelta because it touches no project row.Verification
cargo checkclean;cargo test --lib655 passedbun run typecheckclean;bun test1279 passedHOME: create with zero projects open, reorder (full and partial), edit-keeps-position, delete, and persistence across restartThis commit is verified to build and test green on its own, so it's mergeable independently of the follow-up.
🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Tests