Skip to content

fix: resolve Windows runtime failures and desktop context menus - #725

Merged
pedronauck merged 6 commits into
mainfrom
fix/windows-desktop-issues
Oct 11, 2026
Merged

pedronauck merged 6 commits into
mainfrom
fix/windows-desktop-issues

Conversation

@pedronauck

@pedronauck pedronauck commented Oct 11, 2026 •

Copy link
Copy Markdown
Member

Summary

This PR fixes the seven reports shown in the issue batch: Windows daemon startup, rooted directory creation, session removal, session-list latency, CRLF source-build icons, desktop context menus, and desktop spelling controls.

Closes #717
Closes #718
Closes #719
Closes #720
Closes #721
Closes #698
Closes #699

Root causes and changes

Windows daemon startup (#717)

The runtime mutation lock uses a mandatory Windows byte-range lock. Reading, truncating, or writing its owner metadata through a second file handle conflicts with the lock that the process already owns. Windows now stores the process identity in update.lock.owner, following the existing daemon lock's .pid sidecar convention. The exclusive lock remains on update.lock; this does not bypass mutual exclusion. Unix keeps its existing in-file metadata. Release clears the corresponding owner payload, and acquisition continues to distinguish stale process identities.

Windows rooted directory creation (#718)

The no-follow directory walk requested generic write access to every component, including the drive root and Users. A standard user can write under their home without having write access to these ancestors. Existing ancestors now open with read access; only the final mutation target requests write access. Missing components are still created by the handle-relative native operation, and every component retains reparse-point rejection. The regression uses a real ACL that denies mutation on an ancestor while its existing child remains writable.

Session inventory latency (#719)

Persisted session metadata projected the workspace root by calling full runtime Resolve, which validates resource snapshots and scans skills/agents. That work was repeated for historical sessions even though the caller only needed the registration's root directory. Session queries now use the existing inexpensive Get registration operation. The session package's consumed resolver interface explicitly requires that operation, and all consumers/fakes are updated together. Session creation and resume still perform complete runtime resolution. No TTL cache, stale-root fallback, timeout increase, or resource-validation suppression is introduced.

Windows session deletion (#720)

Deletion already quiesces stored readers and holds a database-family lease. However, the deletion capability then pins events.db with an open file handle. Windows refuses a directory rename while a descendant file remains open, including this protective handle. The family owner now closes that descendant handle for the Windows rename while retaining its lease and the bound directory capability. It then reopens the database, verifies the exact file identity and owner, and advances the retained path. This covers staging, commit, rollback, and restart recovery. Final tombstone cleanup closes the database before the bound directory removal transaction. Native recovery testing also exposed a separate durability bug: move capabilities held read/delete access, while removing their contents calls FlushFileBuffers, which requires write access. Bound removal now acquires a mutation handle for the private candidate and verifies the same directory identity before deleting contents; recursive deletion opens nested directories for mutation as well. Rename-only handles retain their narrower rights, and durability errors remain fatal. Unix keeps the existing pinned-handle rename behavior.

CRLF source-build icons (#721)

The embedded Lucide catalog was split on LF without removing CR, so a CRLF checkout produced different catalog keys. Catalog parsing now normalizes line terminators, and .gitattributes pins the embedded generated catalog to LF. Coverage compares the entire LF and CRLF catalogs and checks palette, without modifying generated catalog contents.

Desktop context menus (#698)

The product BrowserWindow now handles Electron's native context-menu event. Editable fields receive capability-aware Undo, Redo, Cut, Copy, Paste, and Select All roles. Text selections receive Copy, and links receive Copy link. The renderer continues to own its custom context menus.

Desktop spellchecking (#699)

Misspelled words expose dictionary suggestions and Add to dictionary. Editable fields include a Spelling submenu with an enable/disable checkbox. Windows and Linux offer independently selectable dictionaries, supporting simultaneous English and Portuguese. At least one dictionary stays selected; disabling is an explicit separate setting. macOS displays the OS-language ownership explanation and retains native language selection.

Optional spelling preferences are stored with existing window geometry/zoom in desktop-window.json and restored at startup. Existing window-state files remain accepted. Languages are restored before the enabled flag: Electron 44.5.1 SetSpellCheckerLanguages enables the checker on Windows/Linux when the language list is nonempty, so applying the enabled flag first incorrectly reactivated a saved disabled preference. The packaged Linux relaunch assertion caught this and remains unchanged. This is a desktop-shell preference, so no daemon config, HTTP route, IPC bridge, or web settings surface is added.

Compatibility and impact

  • No SQLite schema changes, data deletion, public CLI/HTTP/UDS shape changes, native-tool ID changes, hooks, or extension SDK changes.
  • Existing session workspace/profile ownership and query permissions remain intact.
  • Windows owner metadata is operational lock state; exclusivity still uses the original lock file.
  • Existing desktop window state upgrades through the tolerant boundary decoder without losing geometry or zoom.
  • Browser context menus and browser spellcheck remain browser-owned.
  • The owning cross-surface audit is in docs/_memory/change-impact.md; operator desktop documentation and the affected QA scenarios are updated.

Verification

Current head: 8b9efc5eaefa1835b18601e67d18f8cfcdf4e63a. CI run 38114169207 has passed all Go race shards, Go lint/build, native Windows, Darwin, SDK, preflight, desktop verification, runtime E2E, packaged desktop E2E, all four web E2E shards, and the frontend lane. Both aggregate verification gates passed; the complete workflow concluded successfully. React Doctor and Vercel checks also passed for this head.

Issue Owning verification Observed result
#717 Native Windows TestUpdateLock A second runtime mutation is rejected, stale ownership is replaced, and release permits reacquisition.
#718 Native Windows restricted-ancestor directory case An ancestor denies mutation while its protected child permits it; nested creation and an actual file write succeed.
#719 TestManagerQueriesUseWorkspaceRegistration on Windows and the Linux session suite Stopped-session list/status retain registered workspace roots with zero runtime resource resolutions, even when runtime resolution would fail.
#720 Native Windows deletion/rollback/recovery cases and bound-tree removal; Linux session/fileutil suites Stopped sessions are removed, active readers are quiesced, catalog failure restores staged history, restart recovery respects catalog ownership, and files plus nested directories leave no removal transaction behind.
#721 Native Windows identity-symbol regression and Linux profile suite The entire embedded catalog produces identical LF/CRLF keys, including palette.
#698 Packaged Linux Electron shell E2E and desktop unit suite Native editing roles appear, Copy reaches the real clipboard, and dock, tab, and loop-editor custom menus remain usable.
#699 Packaged Linux Electron shell E2E and desktop unit suite English and Portuguese can be selected together; disabled spelling and both languages survive relaunch. Suggestion replacement, dictionary addition, last-language protection, and tolerant preference decoding are covered at the Electron boundary.

Native results: Windows job, packaged Linux desktop job, and daemon-containing race shard. The affected QA scenario files record scoped receipts; they do not claim new full manual provider/composer walks.

Before the operator moved all remaining gates to CI, focused local race suites and workspace create/resume integration passed. A packaged macOS context-menu walk verified the real clipboard, disabled spelling across relaunch, and custom-menu coexistence. Local full-gate attempts are not represented as a complete pass: one storage migration hit its deadline under concurrent load and passed unchanged in isolation; the next full attempt was stopped at the operator's request. No subsequent local gates or test suites were run.

Test preconditions and diagnostic retention

The Windows ACL fixture gives the writable child an explicit protected ACL before restricting its ancestor, because Windows propagates changes to inherited child ACLs. It verifies both the denied ancestor and writable child before checking the nested write. The interrupted-deletion fixture releases both session and attachment capabilities before constructing its replacement manager, matching process death. Existing deletion, ownership, content, and tombstone assertions remain intact.

The Windows workflow previously ran filtered terminal/private-file/Gateway suites rather than the complete Go inventory. It now includes the owning startup, ACL, metadata-query, icon, bound-removal, and deletion/rollback/recovery cases, exercising Windows semantics directly.

A previous Linux daemon run reported a race, but its summarized log omitted the stacks and failed jobs did not retain their raw JSON. The existing artifact upload now runs on failure too. The current daemon execution was fresh (222.142 seconds, no cached result), with 2,135 test/subtest pass events and no race diagnostic in its retained JSON. This does not establish a causal fix for the earlier race, whose source remains unknown. No race suppression, assertion weakening, timeout increase, or automatic retry was added.

Summary by CodeRabbit

  • New Features
    • Context menus now offer text and link copying, misspelling suggestions, and dictionary actions.
    • Spellcheck controls include an enable/disable option and language selection on non-macOS platforms.
  • Bug Fixes
    • Spellcheck preferences and selected languages are retained when reopening the desktop app.
    • Improved reliability when removing sessions and handling files in restricted-access directories.

@vercel

vercel Bot commented Oct 11, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
compozy-site Ready Ready Preview Oct 11, 2026 5:16am UTC

Request Review

@coderabbitai

coderabbitai Bot commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository: compozy/compozy/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: bd793c88-1e09-4b61-b419-973e735fda16

📥 Commits

Reviewing files that changed from the base of the PR and between 8d6c3fd and 8b9efc5.


⛔ Files ignored due to path filters (7)
  • .github/workflows/ci.yml is excluded by !**/*.yml
  • docs/_memory/change-impact.md is excluded by !**/*.md
  • docs/qa/scenarios/APP-desktop-native-edit-shortcuts.md is excluded by !**/*.md
  • docs/qa/scenarios/ET-profile-identity-appearance-catalogs.md is excluded by !**/*.md
  • docs/qa/scenarios/RT-session-delete-owned-history.md is excluded by !**/*.md
  • docs/qa/scenarios/TA-daemon-lifecycle-command-guard.md is excluded by !**/*.md
  • packages/site/content/docs/operations/desktop-app.mdx is excluded by !**/*.mdx

📒 Files selected for processing (31)
  • .gitattributes
  • desktop/e2e/_electron/__tests__/shell.spec.ts
  • desktop/src/window/__tests__/context-menu.test.ts
  • desktop/src/window/context-menu.ts
  • desktop/src/window/product-window.ts
  • desktop/src/window/window-state-store.ts
  • internal/daemon/daemon_test.go
  • internal/daemon/harness_context_integration_test.go
  • internal/daemon/lock_pid_other.go
  • internal/daemon/lock_pid_windows.go
  • internal/daemon/session_manager_deps.go
  • internal/daemon/update_lock.go
  • internal/daemon/workspace_access_integration_test.go
  • internal/extension/host_api_test.go
  • internal/fileutil/directory_remove_bound.go
  • internal/fileutil/rooted_write.go
  • internal/fileutil/rooted_write_windows.go
  • internal/fileutil/rooted_write_windows_test.go
  • internal/profile/icons.go
  • internal/profile/validation_test.go
  • internal/session/manager_delete_capabilities.go
  • internal/session/manager_delete_commit.go
  • internal/session/manager_delete_staging.go
  • internal/session/manager_delete_test.go
  • internal/session/manager_delete_tombstone.go
  • internal/session/manager_options.go
  • internal/session/manager_test_support_test.go
  • internal/session/manager_types.go
  • internal/session/query_test.go
  • internal/session/query_workspace.go
  • internal/store/sessiondb/family_directory.go

Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 6 remain after this review.



Walkthrough

The pull request adds desktop context menus and persisted spellcheck settings. It also changes update-lock owner storage, rooted directory access, session deletion moves, workspace lookup, and embedded Lucide catalog parsing.

Changes

Desktop context menus and spellcheck

Layer / File(s) Summary
Context-menu actions
desktop/src/window/context-menu.ts
The product context menu adds editing and selection actions, link copying, spelling suggestions, dictionary insertion, and spelling controls.
Window integration and persisted settings
desktop/src/window/product-window.ts, desktop/src/window/window-state-store.ts
The product window displays the context menu, applies saved spellcheck settings, and saves changes. Window state validates stored settings and deduplicates languages.
Menu and persistence tests
desktop/src/window/__tests__/context-menu.test.ts, desktop/e2e/_electron/__tests__/shell.spec.ts
Tests cover menu actions, spelling settings, persistence after relaunch, and dock, tab, and loop-editor menus.

Update-lock owner metadata

Layer / File(s) Summary
Platform-specific owner storage
internal/daemon/lock_pid_other.go, internal/daemon/lock_pid_windows.go, internal/daemon/update_lock.go, internal/daemon/daemon_test.go
Owner metadata uses a derived path. Windows uses an .owner sidecar; other platforms use the lock path. Tests check owner cleanup and reacquisition.

Rooted directory access and removal

Layer / File(s) Summary
Rooted directory access
internal/fileutil/rooted_write_windows.go, internal/fileutil/rooted_write.go, internal/fileutil/rooted_write_windows_test.go
Windows directory opens request write access for the target component rather than every ancestor. Tests cover nested removal and ACL-restricted ancestors.
Bound-tree removal identity check
internal/fileutil/directory_remove_bound.go
Bound-tree removal verifies that the writable handle and bound directory refer to the same file before removing contents.

Embedded icon catalog parsing

Layer / File(s) Summary
Catalog line-ending handling
.gitattributes, internal/profile/icons.go, internal/profile/validation_test.go
The catalog file is assigned LF line endings. Parsing strips trailing carriage returns, and tests compare results from LF and CRLF input.

Workspace lookup for sessions

Layer / File(s) Summary
Workspace resolver contract and query behavior
internal/session/query_workspace.go, internal/session/manager_options.go, internal/session/manager_types.go, internal/daemon/session_manager_deps.go
The session resolver interface adds Get. Workspace-root lookup uses it, and manager dependencies use the session resolver type.
Resolver adapters and query coverage
internal/daemon/harness_context_integration_test.go, internal/daemon/workspace_access_integration_test.go, internal/extension/host_api_test.go, internal/session/manager_test_support_test.go, internal/session/query_test.go
Resolver test implementations add Get. A query test checks that stopped-session listing and status use registered workspace data without runtime resolver calls.

Session deletion and database moves

Layer / File(s) Summary
Database-aware parent-directory moves
internal/store/sessiondb/family_directory.go, internal/session/manager_delete_capabilities.go, internal/session/manager_delete_commit.go, internal/session/manager_delete_staging.go
FamilyFile adds MoveParentDirectory. Session deletion staging, commit, and rollback use that method.
Tombstone recovery and cleanup
internal/session/manager_delete_tombstone.go, internal/session/manager_delete_test.go
Recovery moves directories through the database capability and closes it before tombstone removal. The recovery test releases both leases and configures a text logger.

Priority: ➖ Normal

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

Change: Bug fix · Severity of issue fixed: Medium

Sequence Diagram(s)

sequenceDiagram
  actor User
  participant Electron as Electron webContents
  participant ProductWindow
  participant ContextMenu as productContextMenu
  participant WindowState as window state store
  User->>Electron: Right-click editable text or selection
  Electron->>ProductWindow: Emit context-menu event and parameters
  ProductWindow->>ContextMenu: Build menu from context and spellcheck session
  ContextMenu-->>ProductWindow: Return menu template and settings callback
  ProductWindow->>Electron: Display native menu
  User->>Electron: Select a menu action
  Electron->>ProductWindow: Invoke menu action callback
  ProductWindow->>WindowState: Save changed spellcheck settings
Loading

Merge Risk: ⚪ Minimal · up to 8b9ef

This change fixes several Windows runtime failures and adds desktop context menus and spellcheck settings. The review found no concrete merge-blocking risk, so it is reasonable to merge after the normal checks.

Pre-merge checks | Passed 4 | Failed 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 28.57% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 28 functions across 30 files. (1 skipped:… 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 accurately identifies the main change areas: Windows runtime fixes and desktop context menus. It is concise and specific enough for the changeset.
Linked Issues check Passed The reviewed head addresses the coding requirements of all seven active linked issues. [#717] keeps the exclusive lock on update.lock and moves Windows owner metadata to update.lock.owner, with st…
Out of Scope Changes check Passed The changes remain within the linked issue scope. The added tests cover the affected Windows file, lock, session, icon, and desktop behaviors. Resolver interface updates support the [#719] query chang…

Full details: Docstring Coverage

Explanation

Docstring coverage is 28.57% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 28 functions across 30 files. (1 skipped: 1 unsupported.)


  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR


🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

@pedronauck
pedronauck marked this pull request as ready for review October 11, 2026 05:42
@greptile-apps

greptile-apps Bot commented Oct 11, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 4/5

[High impact] Windows daemon startup, file operations, and session cleanup.

Fix the Windows rollback path before merging; a canceled deletion can strand session history until restart.

Findings

  1. P1 Canceled deletion strands history ▶

Summary

This PR fixes Windows runtime operations and adds desktop text menus and saved spelling preferences.

  • Windows keeps update-lock ownership details beside the lock.
  • Windows creates writable folders without write access to their ancestors.
  • Session lists and status use the registered workspace root directly.
  • Windows can rename and remove session folders while retaining their database identity.
  • Profile icons keep their catalog names in CRLF source checkouts.
  • The desktop window offers native edit, selection, and link menus.
  • Desktop spelling menus offer dictionary choices and keep preferences across restarts.

Acknowledgments from pedronauck: the earlier daemon race has no known cause and is not claimed fixed. Remaining full manual spelling/provider walks are explicitly not claimed complete.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Stage session deletion] --> B{Windows?}
  B -->|No| C[Move directory with database file held open]
  B -->|Yes| D[Close held database file]
  D --> E[Move directory]
  E --> F[Reopen database and check owner]
  F -->|Success| G[Continue catalog deletion]
  F -->|Request canceled| H[Return with database file closed]
  H --> I[Attempt rollback]
  I --> J[Move rejected because file is closed]
  J --> K[History remains under staged name]
  C --> G
Loading

Reviews (1) · Last reviewed commit: "ci: retain Go test diagnostics after fai..." · Reviewed by Greptile

Comment on lines +40 to +42
pinned, err := f.lease.pinOwnedFile(ctx, owner, path)
if err != nil {
return result, errors.Join(moveErr, fmt.Errorf("store: rebind database after directory move: %w", err))

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 Canceled deletion strands history

On Windows, canceling a deletion request after the directory rename can prevent rollback. MoveParentDirectory closes the held database file, then uses the request's ctx to reopen it and check its owner. If that check fails, f.pinned.file stays nil. The rollback in stageBoundSessionDelete calls the same move method, but validateActive rejects the closed file before restoring the directory.

The catalog row remains, while its history stays under the staged name until restart recovery. Keep the held database file usable after a committed move, and use a separate cleanup context for reopening and rollback.

@pedronauck
pedronauck merged commit f6376fe into main Oct 11, 2026
52 checks passed
@pedronauck
pedronauck deleted the fix/windows-desktop-issues branch October 11, 2026 15:40
This was referenced Oct 11, 2026

This branch was successfully deployed

1 active deployment
Preview — 8b9efc5e Deployed Oct 11, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment