Skip to content

feat: AppShell application platform layer#8

Merged
BumpyClock merged 13 commits into
mainfrom
feat/app-platform
Jul 17, 2026
Merged

feat: AppShell application platform layer#8
BumpyClock merged 13 commits into
mainfrom
feat/app-platform

Conversation

@BumpyClock

Copy link
Copy Markdown
Owner

Adds the app platform layer from docs/learned/app-platform-plan.md: three new crates that let a gpui-component app's main.rs be ~20 lines.

  • gpui-component-storage — platform paths, atomic/durable writes, schema-versioned envelopes, single-writer locking, debounced stores with backup rotation
  • gpui-component-manifest — app identity from [package.metadata.gpui-app] with app-local build.rs codegen (include_identity!), MSIX/CFBundle version derivation, and a gpui-app-doctor packaging verifier
  • gpui-component-app — the AppShell builder: lifecycle event stream (queue-until-ready, reentrancy-safe), hold/release liveness, Send+Sync AppInfo/AppProxy, window manager, typed settings + ShellPreferences, command registry driving native menus, theme service, honest PlatformCapabilities
  • Conformance examples (app_shell, app_shell_tray) wired into a new CI launch-smoke job; COMPATIBILITY.md + versioning discipline

Also fixes pre-existing workspace breakage (app_assets dep, system_monitor timer lint, tiles.rs Menu field). No crates/ui API changes.


Entire change is AI-generated (Claude Fable 5 orchestrating Opus + Codex gpt-5.6-sol agents; five structured review cycles, 24 findings fixed).

Phase 0 of the app platform plan (docs/learned/app-platform-plan.md):

- gpui-component-storage: platform paths, atomic/durable writes,
  schema-versioned envelopes with four-way load outcomes, single-writer
  advisory locking (lock owned by the store worker thread), debounced
  stores with backup rotation and corrupt-primary recovery
- gpui-component-manifest: app identity schema and validation,
  MSIX/CFBundle version derivation, app-local build.rs codegen
  (emit_identity + include_identity!) with a downstream-workspace
  fixture proving identity resolves from the consuming app's manifest
- CI: mandatory 3-OS platform-crate test step; native-launch smoke
  placeholder for phase 1
- workspace: shared package version; docs/COMPATIBILITY.md matrix;
  update-gpui skill owns version/tag/compat bookkeeping
- scaffold gpui-component-app crate for phase 1
- fix app_assets example missing gpui-component-assets dependency

Reviewed: codex gpt-5.6-sol structured review, two rounds, 14 findings
fixed (settings-file loss on failed commit, pending-update loss, writer
lock lifetime, backup recovery outcomes, UTF-8 corruption classing,
path traversal validation, MSIX store rules, CFBundleIdentifier grammar).
Phase 1 of the app platform plan (docs/learned/app-platform-plan.md):
gpui-component-app, the application shell that lets a new app's main.rs
be ~20 lines while supporting multi-window, single-window, and tray-first
zero-window shapes.

Core:
- AppShell builder over a sealed plugin/phase mechanism with a central
  startup sequencer and reverse-order shutdown (partial-init unwind)
- lifecycle as an AppEvent stream: early platform-listener registration,
  queue-until-ready delivery, reentrancy-safe dispatch, single
  request_quit path with accurate ShutdownReason attribution
- explicit thread affinity: AppInfo/AppProxy (Send+Sync; proxy closes at
  the shutdown boundary), main-thread ShellState global via AppShellExt
- liveness as hold/release leases; activation policy (Passive for
  tray-first apps); WhenIdle/Explicit exit policies; startup-idle exit
- PlatformCapabilities with honest per-target judgements for fork stubs

Services (always compiled, builder-activated):
- windows: WindowSpec/WindowKey, auto-Root wrap via RootPolicy, singleton
  state machine, per-title numbering, overlay surfaces, close-driven
  registry reconciliation, standard window command handlers
- settings: typed named stores on gpui-component-storage with stepwise
  migrations, FutureVersionPolicy::RefuseToWrite, debounced saves with
  shutdown flush; platform-owned ShellPreferences store
- commands: registry with stable IDs; native app/edit/window menus, dock
  menu, and theme sections as projections; precedence-preserving keymap
  rebuilds for dynamic registration
- theme: ThemeSource (bundled sync / registry watch / custom palettes),
  selection persistence via ShellPreferences, menu integration

Proof and tooling:
- examples/app_shell and examples/app_shell_tray conformance apps with
  --smoke CI mode (exit 0 iff a window opened and quit was clean)
- gpui-app-doctor: verifies packaging artifacts against the manifest
- CI native-launch-smoke job enabled (macOS launch; Linux/Windows
  documented-off pending display/GPU verification)
- fix pre-existing workspace breakage (system_monitor disallowed timer,
  tiles.rs Menu field)

Reviewed: codex gpt-5.6-sol structured review, five cycles, 24 findings
fixed, 2 rejected with fork-source evidence (fork's global_mut notifies;
no Copy derive existed). Implementation by opus + codex agent team.
@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 38 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: ef7440ad-0331-431e-a1fc-8a19bac10855

📥 Commits

Reviewing files that changed from the base of the PR and between f142f4f and bf2de7d.

📒 Files selected for processing (3)
  • .github/workflows/ci.yml
  • crates/app-manifest/src/doctor.rs
  • crates/app-storage/src/lock.rs

Walkthrough

The change adds manifest, storage, and application-shell crates. It introduces identity generation and verification, persistent storage, lifecycle and service APIs, menus, themes, windows, conformance examples, downstream tests, and CI smoke coverage.

Changes

App Platform Foundation

Layer / File(s) Summary
Workspace and manifest contracts
Cargo.toml, crates/app-manifest/...
Registers new crates and defines validated identity schemas, version derivation, build-time code generation, artifact verification, and the doctor CLI.
Storage persistence
crates/app-storage/...
Adds platform-aware paths, atomic writes, versioned envelopes, writer locks, debounced persistence, backups, recovery, and failure handling.
Application shell runtime
crates/app/...
Adds lifecycle phases, event delivery, cross-thread dispatch, liveness, capabilities, plugins, settings, themes, and shell startup orchestration.
Commands and windows
crates/app/src/commands/*, crates/app/src/windows/*
Adds command registries, native menus, keybindings, window numbering, singleton windows, overlays, and lifecycle reconciliation.
Conformance and CI
.github/workflows/ci.yml, examples/app_shell/*, examples/app_shell_tray/*, crates/app/tests/*
Adds downstream/headless validation, app-shell examples, platform-crate tests, and native launch smoke coverage.
Documentation and supporting updates
docs/*, .claude/skills/update-gpui/SKILL.md, examples/system_monitor/*, crates/story/examples/tiles.rs
Documents compatibility and architecture procedures, updates gpui bookkeeping guidance, changes timer usage, adds an assets dependency, and enables the Quit menu item.

Estimated code review effort: 5 (Critical) | ~120 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description summarizes the work but omits required template sections like Closes, Screenshot, Break Changes, How to Test, and Checklist. Add the missing template sections, including Closes #[issue], Screenshot, Break Changes (or remove it if none), How to Test, and Checklist items.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main change: adding the AppShell application platform layer.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/app-platform

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 27

🤖 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 @.claude/skills/update-gpui/SKILL.md:
- Around line 50-53: Update the version and compatibility bookkeeping procedure
in .claude/skills/update-gpui/SKILL.md to include creating and pushing the
annotated gpui-vYYYY.MM.DD-<shortsha> fork tag alongside the workspace tag, or
reference the automation that performs it. Align the fork-tag requirement in
docs/COMPATIBILITY.md with this executable procedure; update both sites as
needed.

In @.github/workflows/ci.yml:
- Around line 101-102: Harden the CI action usage in the affected job by pinning
both actions/checkout and actions-rust-lang/setup-rust-toolchain to immutable
commit SHAs. Configure checkout with persist-credentials disabled and disable
automatic caching in setup-rust-toolchain, matching the required security
posture and applying the same fix to the duplicate action usage in the test job.

In `@crates/app-manifest/src/doctor.rs`:
- Around line 246-283: Update verify_desktop and its desktop_value lookups so
Name, Exec, StartupWMClass, and Icon are read exclusively from the [Desktop
Entry] section, preventing Desktop Action sections from satisfying launcher
fields. Apply the same restriction to the additional affected block and preserve
the existing comparison and presence-reporting behavior.
- Around line 109-147: Update the entitlement handling in the manifest scan to
read and parse every candidate entitlements.plist file rather than treating its
filename as sufficient. Aggregate the entitlement keys across matching files and
compare them against every entry in identity.macos.entitlements, reporting each
declared entitlement as present only when found; preserve the missing-report
behavior when no candidate satisfies it. Add coverage for empty and unrelated
entitlement files, including equivalent platform-specific branching tests where
applicable.
- Around line 204-208: Update the icon validation in the bundle manifest parsing
flow to accept only nonblank string values. Replace the current array acceptance
in the icon filter with validation that rejects arrays, empty strings, and
whitespace-only strings while preserving valid nonblank string entries.

In `@crates/app-manifest/src/schema.rs`:
- Around line 140-157: Update AppManifest::validate to reject an explicitly
provided empty data_namespace, while preserving the existing handling for
omitted namespaces; return the appropriate manifest validation error and add a
regression test covering data_namespace = "" at the manifest boundary.

In `@crates/app-manifest/src/versions.rs`:
- Around line 70-79: Add boundary-focused tests for the MSIX component
validation loop in the relevant manifest version tests: verify 65535 is accepted
and 65536 returns ManifestError::InvalidMsixComponent for each component,
including the sideload build value. Ensure the cases exercise both the normal
version fields and the build component branch.

In `@crates/app-manifest/tests/downstream.rs`:
- Around line 8-16: Make the downstream fixture dependency resolution
reproducible: in crates/app-manifest/tests/downstream.rs lines 8-16, remove the
fresh-resolution rationale and add --locked to the cargo run arguments; in
crates/app-manifest/tests/fixtures/downstream-app/.gitignore line 1, remove the
/Cargo.lock entry and commit the generated fixture lockfile.

In `@crates/app-storage/src/atomic.rs`:
- Around line 75-92: The write_impl staging flow must preserve the target’s
restrictive permissions during atomic replacement. Before renaming the staged
file, read the existing path’s permissions when available, otherwise default new
files to 0600, then apply those permissions to the staging file before it
replaces the target.

In `@crates/app-storage/src/envelope.rs`:
- Around line 180-208: Make archive_corrupt in
crates/app-storage/src/envelope.rs require collision-safe quarantine: avoid
overwriting an existing archive, and propagate a failure when the rename cannot
succeed so corruption handling prevents subsequent writes. Update the rotation
logic around the affected store path in crates/app-storage/src/store.rs (lines
419-449) to never copy an unvalidated primary into .bak and to account for a
prior failed quarantine before writing or rotating files.

In `@crates/app-storage/src/paths.rs`:
- Around line 255-259: Update Paths::sub to validate name with validate_relative
before joining it to the selected base directory, preventing absolute paths and
parent traversal from escaping the base. Change the return type to
Result<PathBuf, StorageError> and propagate validation errors while returning
the joined path on success.
- Around line 294-299: Update platform_default_creates_nothing to use a unique
test namespace, assert the corresponding config directory is absent before
constructing AppPaths, and assert it remains absent after construction. Remove
the current exists/read_dir condition, while preserving the
PathLayout::PlatformDefault setup and cleanup expectations.

In `@crates/app-storage/src/store.rs`:
- Around line 203-227: Update the backup recovery branch in the store load
method around load_envelope so a corrupt primary archived to archived_to does
not leave subsequent loads unable to find valid backups. Either treat a Missing
result from a backup probe as eligible for recovery where appropriate, or
atomically restore the selected usable backup to the primary path before
returning; preserve migration and future-version outcomes.
- Around line 214-216: Update the backup-probing match in the store recovery
flow so the wildcard arm no longer handles I/O errors. In the surrounding
backup-generation logic, continue only for Missing and Corrupt outcomes, and
propagate genuine I/O errors to the caller instead of initializing defaults over
inaccessible data.

In `@crates/app-storage/tests/process_lock.rs`:
- Around line 110-129: The child-process wait around the locked-state reader
must enforce the timeout and cleanup on every exit path. Replace the blocking
read_line loop with a reader thread and channel consumed via recv_timeout, and
add a cleanup guard for the holder process that signals or kills it and always
reaps it, including assertion and early-break paths.

In `@crates/app-storage/tests/store.rs`:
- Around line 171-207: Replace the permission-based failure setup in
worker_write_error_is_surfaced at crates/app-storage/tests/store.rs:171-207 with
deterministic, portable write-failure injection, preserving the assertions that
flush returns an error and last_error retains it. Apply the same
failure-injection mechanism to the primary-preservation test at
crates/app-storage/tests/store.rs:209-246, removing its chmod-based setup and
keeping its existing preservation assertions unchanged.
- Around line 98-117: The backup rotation test backup_generations_rotate
currently checks only file existence, so it must validate generation ordering
and contents. Deserialize each store file and assert the primary contains
document generation 3, .bak contains 2, .bak.1 contains 1, and .bak.2 contains
0, preserving the existing rotation setup and assertions.

In `@crates/app/src/capabilities.rs`:
- Around line 61-76: Update the capability documentation in the judgments
section and the corresponding capability definitions around the reported range
to include actionable fallback guidance for every unsupported capability. For
each platform limitation, state the practical workaround, such as using regular
windows instead of overlays, manually registering URL schemes, using an
alternative tray or credential flow, and restoring windows without absolute
positioning on Wayland; preserve the existing support classifications.

In `@crates/app/src/commands/plugin.rs`:
- Around line 143-158: Make command binding validation fallible before
load_binding calls KeyBinding::load: update the command builder/registration
flow, including with_binding and register_command, to parse or validate the
keystroke and return a structured AppShellError for malformed bindings.
Propagate this error through initial menu setup and post-launch rebuilds, and
remove the expect-based panic in load_binding while preserving valid binding
registration.

In `@crates/app/src/defaults.rs`:
- Around line 21-41: The new AppShellBuilder entry points lack focused coverage.
Add test_settings_builder, test_theme_builder, and test_menus_builder tests that
verify SettingsPlugin registration, persisted theme selection through
read_theme_preference/write_theme_preference, and ThemeMenuBridge menu-section
projection respectively, using the public builder methods settings, theme, and
menus.

In `@crates/app/src/error.rs`:
- Around line 54-58: Remove the blanket
From<gpui_component_storage::StorageError> for AppShellError implementation in
error.rs. At the AppPaths::new call in the shell initialization flow, explicitly
map only that path-resolution error to AppShellError::Paths while preserving
other StorageError propagation and classifications.

In `@crates/app/src/handles.rs`:
- Around line 329-354: Update spawn_drain_loop to cap the number of commands
processed in each foreground drain pass, then yield or wait for the next poll
interval before continuing even when commands remain queued. Also add explicit
backpressure or command coalescing at the ProxyCommand producer boundary so the
unbounded channel cannot grow without limit, while preserving closed-proxy
checks between callbacks and before sleeping.

In `@crates/app/src/lifecycle.rs`:
- Around line 109-121: Update Lifecycle::push to return the AppEvent when the
shell is ready, preserving ownership for immediate delivery, and return no event
when buffering it in pending. Adjust the method’s return type, ready and
not-ready branches, and documentation to describe the returned event/absence
instead of a boolean.

In `@crates/app/src/settings/runtime.rs`:
- Around line 130-160: Update flush_for_exit so exit_flushed is set only after
the flush result succeeds, never immediately after spawning the worker. Ensure
timeout, worker failure, and flush errors leave the operation retryable while
preventing concurrent retries; use an explicit lifecycle state such as
NotStarted/InFlight/Completed if needed, and preserve the existing
lifecycle_error reporting.

In `@crates/app/src/windows/spec.rs`:
- Around line 18-29: Remove the Raw variant from RootPolicy in
crates/app/src/windows/spec.rs. In crates/app/src/windows.rs, remove
WindowManager::open_raw, ensure overlay content is wrapped in
gpui_component::Root, and adjust the creation method’s return type to expose
both the Root window handle and content entity; every window path must use Root
as its first-level view.
- Around line 59-155: Add test_window_spec_builder and test_overlay_spec_builder
for the two public specification builders. Verify default values, every mutator
including title, sizing, root/background policies, and hooks, plus invalid and
boundary sizing inputs; assert hook registration and resulting builder state
without invoking platform-specific behavior.

In `@docs/learned/app-platform-plan.md`:
- Around line 328-330: Update the conformance-example references in the plan
from examples/app-shell and examples/app-shell-tray to the actual repository
paths examples/app_shell and examples/app_shell_tray, preserving the surrounding
descriptions and documentation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 218e12e4-656e-4eba-9095-406661da1b6d

📥 Commits

Reviewing files that changed from the base of the PR and between e1be776 and 714e8a3.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (62)
  • .claude/skills/update-gpui/SKILL.md
  • .github/workflows/ci.yml
  • Cargo.toml
  • crates/app-manifest/Cargo.toml
  • crates/app-manifest/src/bin/gpui-app-doctor.rs
  • crates/app-manifest/src/build.rs
  • crates/app-manifest/src/doctor.rs
  • crates/app-manifest/src/error.rs
  • crates/app-manifest/src/lib.rs
  • crates/app-manifest/src/parse.rs
  • crates/app-manifest/src/schema.rs
  • crates/app-manifest/src/versions.rs
  • crates/app-manifest/tests/downstream.rs
  • crates/app-manifest/tests/fixtures/downstream-app/.gitignore
  • crates/app-manifest/tests/fixtures/downstream-app/Cargo.toml
  • crates/app-manifest/tests/fixtures/downstream-app/build.rs
  • crates/app-manifest/tests/fixtures/downstream-app/src/main.rs
  • crates/app-storage/Cargo.toml
  • crates/app-storage/src/atomic.rs
  • crates/app-storage/src/envelope.rs
  • crates/app-storage/src/error.rs
  • crates/app-storage/src/lib.rs
  • crates/app-storage/src/lock.rs
  • crates/app-storage/src/paths.rs
  • crates/app-storage/src/store.rs
  • crates/app-storage/tests/process_lock.rs
  • crates/app-storage/tests/store.rs
  • crates/app/Cargo.toml
  • crates/app/src/capabilities.rs
  • crates/app/src/commands/menu.rs
  • crates/app/src/commands/mod.rs
  • crates/app/src/commands/plugin.rs
  • crates/app/src/commands/standard.rs
  • crates/app/src/defaults.rs
  • crates/app/src/error.rs
  • crates/app/src/handles.rs
  • crates/app/src/lib.rs
  • crates/app/src/lifecycle.rs
  • crates/app/src/liveness.rs
  • crates/app/src/phases.rs
  • crates/app/src/plugin.rs
  • crates/app/src/settings.rs
  • crates/app/src/settings/runtime.rs
  • crates/app/src/settings/tests.rs
  • crates/app/src/shell.rs
  • crates/app/src/theme.rs
  • crates/app/src/windows.rs
  • crates/app/src/windows/key.rs
  • crates/app/src/windows/registry.rs
  • crates/app/src/windows/spec.rs
  • crates/app/tests/headless.rs
  • crates/story/examples/tiles.rs
  • docs/COMPATIBILITY.md
  • docs/learned/app-platform-plan.md
  • examples/app_assets/Cargo.toml
  • examples/app_shell/Cargo.toml
  • examples/app_shell/build.rs
  • examples/app_shell/src/main.rs
  • examples/app_shell_tray/Cargo.toml
  • examples/app_shell_tray/build.rs
  • examples/app_shell_tray/src/main.rs
  • examples/system_monitor/src/main.rs

Comment on lines +50 to +53
8. **Version + compatibility bookkeeping** (this skill is the ONLY place a gpui rev changes — see docs/learned/app-platform-plan.md D6):
- Bump `[workspace.package] version` in the root `Cargo.toml` (minor bump for a gpui rev change or breaking API change while pre-1.0; patch otherwise). Keep the `version = "X.Y.Z"` values inside `[workspace.dependencies]` for the path crates (`gpui-component`, `gpui-component-*`) in sync.
- Add a row to `docs/COMPATIBILITY.md` pairing the new workspace version with the new gpui rev.
- After the change lands on `main`, create the annotated tag: `git tag -a v<version> -m "gpui-component v<version> (gpui <shortsha>)"` and push it. Apps pin these tags — never bare revs.

@coderabbitai coderabbitai Bot Jul 17, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== File list ==\n'
git ls-files '.claude/skills/update-gpui/SKILL.md' 'docs/COMPATIBILITY.md' 'docs/learned/app-platform-plan.md'

printf '\n== update-gpui skill (relevant lines) ==\n'
sed -n '40,70p' .claude/skills/update-gpui/SKILL.md | cat -n

printf '\n== compatibility doc (relevant lines) ==\n'
sed -n '1,60p' docs/COMPATIBILITY.md | cat -n

printf '\n== search for gpui tag workflow ==\n'
rg -n --hidden --no-messages 'gpui-vYYYY\.MM\.DD|annotated tag|create the annotated tag|push it|tag creation|release automation|update-gpui' .claude docs Cargo.toml

Repository: BumpyClock/gpui-component

Length of output: 5187


🏁 Script executed:

#!/bin/bash
set -euo pipefail

ast-grep outline .claude/skills/update-gpui/SKILL.md || true
printf '\n== surrounding lines in skill ==\n'
sed -n '1,120p' .claude/skills/update-gpui/SKILL.md | cat -n

printf '\n== related docs/learned reference ==\n'
sed -n '1,220p' docs/learned/app-platform-plan.md | cat -n

Repository: BumpyClock/gpui-component

Length of output: 18664


🏁 Script executed:

#!/bin/bash
set -euo pipefail
sed -n '40,70p' .claude/skills/update-gpui/SKILL.md | cat -n
printf '\n---\n'
sed -n '1,60p' docs/COMPATIBILITY.md | cat -n
printf '\n---\n'
rg -n 'gpui-vYYYY\.MM\.DD|annotated tag|update-gpui|release automation' .claude docs Cargo.toml

Repository: BumpyClock/gpui-component

Length of output: 4968


Complete the gpui fork tag step in the update flow.
The release discipline expects both the fork tag and the workspace tag, but this procedure only documents the workspace tag.

  • .claude/skills/update-gpui/SKILL.md#L50-L53: add the gpui-vYYYY.MM.DD-<shortsha> tag creation/push step, or point to the release automation that creates it.
  • docs/COMPATIBILITY.md#L24-L27: keep the fork-tag requirement aligned with the executable procedure.
🧰 Tools
🪛 LanguageTool

[grammar] ~50-~50: Ensure spelling is correct
Context: ...eping** (this skill is the ONLY place a gpui rev changes — see docs/learned/app-plat...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)


[grammar] ~51-~51: Ensure spelling is correct
Context: ...the root Cargo.toml (minor bump for a gpui rev change or breaking API change while...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)


[grammar] ~52-~52: Ensure spelling is correct
Context: ... the new workspace version with the new gpui rev. - After the change lands on main,...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

📍 Affects 2 files
  • .claude/skills/update-gpui/SKILL.md#L50-L53 (this comment)
  • docs/COMPATIBILITY.md#L24-L27
🤖 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 @.claude/skills/update-gpui/SKILL.md around lines 50 - 53, Update the version
and compatibility bookkeeping procedure in .claude/skills/update-gpui/SKILL.md
to include creating and pushing the annotated gpui-vYYYY.MM.DD-<shortsha> fork
tag alongside the workspace tag, or reference the automation that performs it.
Align the fork-tag requirement in docs/COMPATIBILITY.md with this executable
procedure; update both sites as needed.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Real but out of scope here (release-process docs in the update-gpui skill + COMPATIBILITY.md, not code touched by this PR). Tracking as a follow-up to add the fork-tag step / point at the automation that creates it.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@BumpyClock, acknowledged. This is a valid release-process follow-up, but it does not need to block this App Platform PR. If no follow-up issue exists yet, would you like me to open one for the fork-tag/automation documentation alignment?

Comment thread .github/workflows/ci.yml
Comment on lines +101 to +102
- uses: actions/checkout@v4
- uses: actions-rust-lang/setup-rust-toolchain@v1

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Unpinned actions + no persist-credentials: false + cache-poisoning surface.

zizmor flags actions/checkout@v4 (no persist-credentials: false, unpinned) and actions-rust-lang/setup-rust-toolchain@v1 (unpinned, auto-caching enabled by default → cache-poisoning surface) on these lines. This duplicates the same unpinned pattern already present in the test job above (lines 29-30), so it's a repo-wide gap being extended rather than a new regression, but worth addressing here since it's touched by this PR.

🔒 Suggested hardening
-      - uses: actions/checkout@v4
-      - uses: actions-rust-lang/setup-rust-toolchain@v1
+      - uses: actions/checkout@v4 # pin to a commit SHA
+        with:
+          persist-credentials: false
+      - uses: actions-rust-lang/setup-rust-toolchain@v1 # pin to a commit SHA
+        with:
+          cache: false
🧰 Tools
🪛 zizmor (1.26.1)

[warning] 101-101: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)


[error] 101-101: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)


[error] 102-102: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)


[error] 102-102: runtime artifacts potentially vulnerable to a cache poisoning attack (cache-poisoning): enables caching by default

(cache-poisoning)

🤖 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 @.github/workflows/ci.yml around lines 101 - 102, Harden the CI action usage
in the affected job by pinning both actions/checkout and
actions-rust-lang/setup-rust-toolchain to immutable commit SHAs. Configure
checkout with persist-credentials disabled and disable automatic caching in
setup-rust-toolchain, matching the required security posture and applying the
same fix to the duplicate action usage in the test job.

Source: Linters/SAST tools

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Out of scope for this PR. As noted, this is a repo-wide gap — the same unpinned pattern is in the pre-existing test job. Pinning actions to commit SHAs plus persist-credentials: false should be a dedicated repo-wide CI-hardening change; tracking as a follow-up rather than partially applying it here.

Comment thread crates/app-manifest/src/doctor.rs
Comment thread crates/app-manifest/src/doctor.rs
Comment thread crates/app-manifest/src/doctor.rs
Comment thread crates/app/src/lifecycle.rs
Comment thread crates/app/src/settings/runtime.rs
Comment thread crates/app/src/windows/spec.rs
Comment thread crates/app/src/windows/spec.rs
Comment thread docs/learned/app-platform-plan.md Outdated
…forms

The headless bootstrap test drove a quit through `request_quit()` to end the
run loop after its assertions passed. That only terminates the process on
macOS, where the headless platform's `quit()` routes through `NSApp
terminate`. On Linux and Windows the headless run loop neither terminates the
process nor returns to `main` after `cx.quit()`, so the test printed its
success line and then hung until the 30s watchdog fired the job red.

Terminate with an explicit `std::process::exit(0)` at the point success is
fully known instead of relying on platform loop-return semantics. Quit and
teardown ordering is already covered by the pure unit tests; this test only
asserts the boot lifecycle reaches `on_launch` with a working shell global, so
the launched-flag bookkeeping that verified that after `run` returned is now
redundant and removed.
… joins

Addresses CodeRabbit findings on PR #8, grouped for the storage crate:

- atomic.rs no longer imports `File` unconditionally. It was only used inside
  the `#[cfg(unix)]` directory-fsync path, so the Windows `-D warnings` build
  failed on an unused import. Qualify it at the use site instead. This is the
  Windows CI failure (Lint and native-launch-smoke both tripped on it).

- Atomic replacement now preserves the target's permissions. The staging file
  was created under the process umask (typically 0644), so renaming it over a
  0600 settings/secrets file silently widened it to world-readable. New files
  get a restrictive 0600 default; genuine stat failures are propagated.

- Backup probing during corrupt-primary recovery no longer swallows I/O
  errors. The `_ => continue` arm treated a permission/device failure like an
  absent backup, which could let a caller initialize defaults over
  still-recoverable data. Only Missing/Corrupt are skipped; real errors surface.

- `AppPaths::sub` validates its `name` (rejecting empty, absolute, and `..`)
  and returns a `Result`, so a caller-supplied component can no longer discard
  or escape the selected base directory.

Tests cover permission preservation on overwrite, sub-path rejection, a
unique-namespace check that actually detects on-disk creation, and backup
generation contents/order (not just existence).
Addresses CodeRabbit findings on PR #8, grouped for the manifest crate:

- doctor now verifies each declared macOS entitlement key against the union of
  every entitlements file's contents, rather than reporting success for a
  matching filename alone. An empty or wrong-keyed helper entitlement no longer
  satisfies a required entitlement.

- doctor's `.desktop` reader restricts launcher-field lookups to the
  `[Desktop Entry]` group. Keys in `[Desktop Action ...]` groups could
  otherwise satisfy a missing Name/Exec/Icon and produce a false success.

- doctor's bundle-icon check rejects blank strings and arrays containing empty
  or non-string entries (e.g. `[""]`, `[1]`), which previously passed.

- `AppIdentity::validate` rejects an explicitly empty `data_namespace` at the
  manifest boundary. An omitted namespace still derives from `app_id`; only an
  explicit empty value is rejected, instead of passing here and failing later
  in `AppShell`.

Adds unit tests for the desktop-group gating, entitlement key presence, the
explicit-empty namespace case, and MSIX component boundaries (65535 accepted,
65536 rejected for both patch and sideload build).
Addresses CodeRabbit findings on PR #8, grouped for the app crate:

- Drop the blanket `From<StorageError> for AppShellError`, which mapped every
  storage failure to `AppShellError::Paths`. Only path resolution is a Paths
  error; the one call site (`AppPaths::new`) now maps explicitly, so a future
  write/lock/corruption error reached via `?` cannot be misclassified.

- `EventQueue::push` returns `Some(event)` / `None` instead of a `bool`. The
  bool signalled "deliver now" but consumed the event, so a ready caller could
  not actually perform the documented immediate delivery. Handing the event
  back makes the ready path usable.

- `flush_for_exit` marks the exit flush complete only after a flush actually
  succeeds. It previously set the flag before awaiting the result, so a timeout
  or worker error left the shell believing the flush had completed and
  suppressed the persistence failure on the next call — violating the
  "report failure" half of the shutdown persistence contract.

Adds `test_window_spec_builder` / `test_overlay_spec_builder` covering the two
public window specifications (defaults, mutators, hooks, sizing guards).
The plan referenced `examples/app-shell` and `examples/app-shell-tray`, but the
crates and directories are `app_shell` and `app_shell_tray`. Align the doc so
the copy/paste build and CI commands resolve.
The `typos` CI check flags "unparseable" in two envelope.rs doc comments. Use
"unparsable" so the gate passes. (Note: the macOS Typo-check job also failed at
exit 127 because `cargo install typos-cli` did not put the binary on PATH and
the `|| echo` masked it — that is a separate workflow-infra issue, tracked for
the lead.)
…arge_err

CI's Windows lint (newer clippy than the local toolchain) rejects `DoctorError`
under `-D warnings` for `clippy::result_large_err`: the `Err` variant rode in
every `Result<_, DoctorError>` on the doctor happy path at ~120 bytes, driven by
the inlined `ManifestError` (120) and `toml::de::Error` (88). This surfaced only
now that the earlier unused-import error stopped compilation before the doctor
bin was linted.

Box both heavy payloads so the error is ~40 bytes. `Manifest` drops its
`#[from]` (the boxed field would derive `From<Box<..>>`, not `From<..>`), so the
one call site maps the error explicitly. A `const` size assertion pins the type
small to keep the lint from regressing on toolchains where it fires.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (1)
crates/app-storage/src/atomic.rs (1)

90-112: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Apply permissions before the durable sync_all.

write_atomic_durable syncs the staging file before set_permissions, so the restrictive mode change is outside the durability barrier. A crash can leave the renamed inode with its original umask-derived mode despite a successful durable write. Move preserve_permissions inside the staging closure before file.sync_all().

Proposed fix
         let mut file = OpenOptions::new()
             .write(true)
             .create_new(true)
             .open(&temp)
             .map_err(|e| StorageError::io(&temp, e))?;
+        #[cfg(unix)]
+        preserve_permissions(path, &temp)?;
         file.write_all(bytes)
             .map_err(|e| StorageError::io(&temp, e))?;
         if durable {
             file.sync_all().map_err(|e| StorageError::io(&temp, e))?;
         }
         Ok(())
     })();
 
-    #[cfg(unix)]
-    if let Err(e) = preserve_permissions(path, &temp) {
-        let _ = fs::remove_file(&temp);
-        return Err(e);
-    }
-
🤖 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 `@crates/app-storage/src/atomic.rs` around lines 90 - 112, Move the Unix-only
preserve_permissions call into the staging closure in write_atomic_durable,
after writing and before the durable file.sync_all() barrier. Remove the later
post-closure permission block, while retaining temp-file cleanup and error
propagation for permission failures.
🤖 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.

Outside diff comments:
In `@crates/app-storage/src/atomic.rs`:
- Around line 90-112: Move the Unix-only preserve_permissions call into the
staging closure in write_atomic_durable, after writing and before the durable
file.sync_all() barrier. Remove the later post-closure permission block, while
retaining temp-file cleanup and error propagation for permission failures.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: eda5452c-6a7a-4b1a-ab87-68f8e1c6d733

📥 Commits

Reviewing files that changed from the base of the PR and between 3cb575b and f142f4f.

📒 Files selected for processing (17)
  • crates/app-manifest/src/bin/gpui-app-doctor.rs
  • crates/app-manifest/src/doctor.rs
  • crates/app-manifest/src/error.rs
  • crates/app-manifest/src/schema.rs
  • crates/app-manifest/src/versions.rs
  • crates/app-storage/src/atomic.rs
  • crates/app-storage/src/envelope.rs
  • crates/app-storage/src/paths.rs
  • crates/app-storage/src/store.rs
  • crates/app-storage/tests/store.rs
  • crates/app/src/error.rs
  • crates/app/src/lifecycle.rs
  • crates/app/src/settings/runtime.rs
  • crates/app/src/shell.rs
  • crates/app/src/windows/spec.rs
  • crates/app/tests/headless.rs
  • docs/learned/app-platform-plan.md

@BumpyClock
BumpyClock merged commit 1a16c6a into main Jul 17, 2026
8 checks passed
@BumpyClock
BumpyClock deleted the feat/app-platform branch July 17, 2026 13:12
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