Name the closure's own context where the framework now demands one
Stage 4 of decision 0023, finished. The dependency swap itself compiled the whole closure on the first attempt; what did not compile was `apps/lumbridge`, and the entire API delta across a ten-month jump turned out to be three signatures over 19 call sites. `Window::focus` is now `focus(&mut self, handle: &FocusHandle, cx: &mut App)` -- 16 sites. `flex_shrink()` takes the factor it used to assume, so it is `flex_shrink(1.0)` -- 2 sites. `Application::new()` is gone with the rest of the entry point, and is `gpui_platform::application()` -- 1 site. That is all of it. Nothing else in the framework's surface moved under this product in ten months. The interesting failure was in the fix, not the framework. Adding `, cx` to every `window.focus` site is right in a method that owns a `cx` parameter and wrong inside `cx.listener(...)`, whose closure is handed its own context as a fourth argument that this code discarded as `_`. One such site capturing the outer `cx` produced 35 errors -- E0596, two E0521s, an E0500, thirty E0501s and two E0524s -- none of which named the actual mistake, and all of which vanished when the discarded parameter was given the name it should have had. Worth recording: a borrow-checker avalanche after a dependency bump is far more likely to be one wrong capture than a framework that changed its ownership model. No crate outside `apps/lumbridge` needed a single change, because none of them imports a gpui type. That boundary was not designed for this and paid for itself anyway. `scripts/workspace-guard.sh` now asserts what it exists to assert. It compared `gpui` against version 0.2.2 from the crates.io registry, which after the swap was a guard that would have failed on the correct state of the tree. It now checks that `gpui` and `gpui_platform` both carry git+https://github.com/zed-industries/zed.git at rev ce48461e -- the same rev, because the pair are one framework snapshot and a disagreement between them would compile against two. Their versions (0.2.2 and 0.1.0) are deliberately not asserted; neither has been bumped upstream in ten months and neither means anything. Both failure modes were induced before being trusted: a wrong expected rev fails on both packages, and a lockfile where only gpui_platform's rev is altered fails on that package alone. One thing to fix next, not here: `cargo deny check sources` now fails. Decision 0023 predicted the move would bring two Git sources; the resolved graph brings four. wasm_thread (via gpui_web and scheduler) and xim-rs, which supplies zed-xim, xim-ctext and xim-parser to gpui_linux, are both unallowed, and zed-industries/scap is in the lockfile too. deny.toml is named in AGENTS.md as a file a decision record governs, so it is left alone and reported rather than edited underneath this commit. The UI gate is green: workspace-guard, fmt, clippy with -D warnings over --all-targets --all-features, and 79 tests passing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SPYebLiN2w4TqnHUYGdECq
This commit is contained in:
co-authored by
Claude Opus 5
parent
378e3eb0eb
commit
3cb5e1002c
Generated
+1622
-1638
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,13 @@ license.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[dependencies]
|
||||
gpui = "0.2.2"
|
||||
# Pinned to a full revision by decision 0023. crates.io publishing of gpui
|
||||
# stopped in October 2025, the platform backends moved into crates Zed does
|
||||
# not publish, and gpui's own x11/wayland features became empty markers
|
||||
# there -- so the published crate can no longer build a Linux shell at all.
|
||||
# Both crates must carry the SAME rev; workspace-guard.sh asserts it.
|
||||
gpui = { git = "https://github.com/zed-industries/zed.git", rev = "ce48461eaadd16c65c31f835511ab96bd3b6e746", default-features = false, features = ["wayland", "x11"] }
|
||||
gpui_platform = { git = "https://github.com/zed-industries/zed.git", rev = "ce48461eaadd16c65c31f835511ab96bd3b6e746", default-features = false, features = ["font-kit", "wayland", "x11"] }
|
||||
lumbridge-core = { path = "../../crates/lumbridge-core" }
|
||||
lumbridge-harness = { path = "../../crates/lumbridge-harness" }
|
||||
lumbridge-runtime = { path = "../../crates/lumbridge-runtime" }
|
||||
|
||||
+24
-20
@@ -16,9 +16,13 @@ use std::collections::BTreeMap;
|
||||
use std::time::Duration;
|
||||
|
||||
use gpui::{
|
||||
App, Application, Bounds, Context, FocusHandle, FontWeight, KeyDownEvent, Pixels, Rgba, Size,
|
||||
Window, WindowBounds, WindowOptions, actions, div, prelude::*, px, relative, rgb, size,
|
||||
App, Bounds, Context, FocusHandle, FontWeight, KeyDownEvent, Pixels, Rgba, Size, Window,
|
||||
WindowBounds, WindowOptions, actions, div, prelude::*, px, relative, rgb, size,
|
||||
};
|
||||
// The application entry point lives in the platform crate at this revision, not
|
||||
// in gpui itself: the backends moved out of the publishable crate, and the
|
||||
// constructor moved with them. See decision 0023.
|
||||
use gpui_platform::application;
|
||||
use lumbridge_core::UsageProvenance;
|
||||
use lumbridge_runtime::{
|
||||
CommandConfig, PtyOptions, RuntimeActorError, RuntimeActorOptions, RuntimeCommand,
|
||||
@@ -466,7 +470,7 @@ impl LumbridgeShell {
|
||||
impl LumbridgeShell {
|
||||
fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
|
||||
let root_focus = cx.focus_handle();
|
||||
window.focus(&root_focus);
|
||||
window.focus(&root_focus, cx);
|
||||
|
||||
Self::drive_synthetic_surfaces(cx);
|
||||
Self::poll_runtime_events(cx);
|
||||
@@ -765,7 +769,7 @@ impl LumbridgeShell {
|
||||
if self.panels.select(pane) {
|
||||
self.persist_panels();
|
||||
}
|
||||
window.focus(&self.root_focus);
|
||||
window.focus(&self.root_focus, cx);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
@@ -785,7 +789,7 @@ impl LumbridgeShell {
|
||||
if self.panels.select(pane) {
|
||||
self.persist_panels();
|
||||
}
|
||||
window.focus(&self.root_focus);
|
||||
window.focus(&self.root_focus, cx);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
@@ -806,7 +810,7 @@ impl LumbridgeShell {
|
||||
if self.panels.select(pane) {
|
||||
self.persist_panels();
|
||||
}
|
||||
window.focus(&self.root_focus);
|
||||
window.focus(&self.root_focus, cx);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
@@ -815,7 +819,7 @@ impl LumbridgeShell {
|
||||
if self.panels.select_attached_at(index) {
|
||||
self.persist_panels();
|
||||
}
|
||||
window.focus(&self.root_focus);
|
||||
window.focus(&self.root_focus, cx);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
@@ -835,7 +839,7 @@ impl LumbridgeShell {
|
||||
self.persist_panels();
|
||||
self.resize_terminal_for_workspace(window, cx);
|
||||
}
|
||||
window.focus(&self.root_focus);
|
||||
window.focus(&self.root_focus, cx);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
@@ -858,7 +862,7 @@ impl LumbridgeShell {
|
||||
self.add_panel_chooser_open = false;
|
||||
self.persist_panels();
|
||||
self.resize_terminal_for_workspace(window, cx);
|
||||
window.focus(&self.root_focus);
|
||||
window.focus(&self.root_focus, cx);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
@@ -868,7 +872,7 @@ impl LumbridgeShell {
|
||||
self.persist_panels();
|
||||
self.resize_terminal_for_workspace(window, cx);
|
||||
}
|
||||
window.focus(&self.root_focus);
|
||||
window.focus(&self.root_focus, cx);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
@@ -906,7 +910,7 @@ impl LumbridgeShell {
|
||||
self.live_terminals.insert(pane, terminal);
|
||||
self.attention.clear(pane);
|
||||
self.pending_terminate = None;
|
||||
window.focus(&self.root_focus);
|
||||
window.focus(&self.root_focus, cx);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
@@ -935,7 +939,7 @@ impl LumbridgeShell {
|
||||
Err(error) => LiveRuntimeStatus::Fault(error.to_string()),
|
||||
};
|
||||
}
|
||||
window.focus(&self.root_focus);
|
||||
window.focus(&self.root_focus, cx);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
@@ -977,7 +981,7 @@ impl LumbridgeShell {
|
||||
Some(_) => None,
|
||||
None => Some(Page::UsageAndQuota),
|
||||
};
|
||||
window.focus(&self.root_focus);
|
||||
window.focus(&self.root_focus, cx);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
@@ -1096,7 +1100,7 @@ impl LumbridgeShell {
|
||||
let rows = sidebar::model::flatten(&self.sidebar_input(), &self.sidebar);
|
||||
self.sidebar.cursor = sidebar::model::move_cursor(&rows, None, 1);
|
||||
}
|
||||
window.focus(&self.root_focus);
|
||||
window.focus(&self.root_focus, cx);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
@@ -1174,7 +1178,7 @@ impl LumbridgeShell {
|
||||
fn add_panel_action(&mut self, _: &AddPanel, window: &mut Window, cx: &mut Context<Self>) {
|
||||
self.dispatch(ShellAction::CloseCommandPalette);
|
||||
self.add_panel_chooser_open = !self.add_panel_chooser_open;
|
||||
window.focus(&self.root_focus);
|
||||
window.focus(&self.root_focus, cx);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
@@ -1192,7 +1196,7 @@ impl LumbridgeShell {
|
||||
if self.panels.move_horizontal(delta) {
|
||||
self.persist_panels();
|
||||
}
|
||||
window.focus(&self.root_focus);
|
||||
window.focus(&self.root_focus, cx);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
@@ -2591,7 +2595,7 @@ impl LumbridgeShell {
|
||||
.on_click(cx.listener(|shell, _, window, cx| {
|
||||
shell.dispatch(ShellAction::CloseCommandPalette);
|
||||
shell.add_panel_chooser_open = !shell.add_panel_chooser_open;
|
||||
window.focus(&shell.root_focus);
|
||||
window.focus(&shell.root_focus, cx);
|
||||
cx.notify();
|
||||
})),
|
||||
)
|
||||
@@ -2622,8 +2626,8 @@ impl LumbridgeShell {
|
||||
.relative()
|
||||
.track_focus(&self.root_focus)
|
||||
.key_context("LumbridgeShell")
|
||||
.on_click(cx.listener(|shell, _, window, _| {
|
||||
window.focus(&shell.root_focus);
|
||||
.on_click(cx.listener(|shell, _, window, cx| {
|
||||
window.focus(&shell.root_focus, cx);
|
||||
}))
|
||||
.on_action(cx.listener(Self::focus_left))
|
||||
.on_action(cx.listener(Self::focus_right))
|
||||
@@ -3429,7 +3433,7 @@ fn spawn_live_runtime(
|
||||
}
|
||||
|
||||
fn main() {
|
||||
Application::new().run(|cx: &mut App| {
|
||||
application().run(|cx: &mut App| {
|
||||
cx.bind_keys(keymap::bindings());
|
||||
|
||||
let bounds = Bounds::centered(None, size(px(1500.0), px(960.0)), cx);
|
||||
|
||||
@@ -198,7 +198,7 @@ pub(crate) fn row(row: &SidebarRow, style: &RowStyle) -> gpui::AnyElement {
|
||||
// that says which pane to look at.
|
||||
.child(
|
||||
div()
|
||||
.flex_shrink()
|
||||
.flex_shrink(1.0)
|
||||
.min_w(px(0.0))
|
||||
.truncate()
|
||||
.text_xs()
|
||||
@@ -261,7 +261,7 @@ pub(crate) fn row(row: &SidebarRow, style: &RowStyle) -> gpui::AnyElement {
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.flex_shrink()
|
||||
.flex_shrink(1.0)
|
||||
.min_w(px(0.0))
|
||||
.truncate()
|
||||
.text_xs()
|
||||
|
||||
+27
-15
@@ -49,30 +49,42 @@ done < <(find apps crates tools -mindepth 2 -maxdepth 2 -name Cargo.toml 2>/dev/
|
||||
#
|
||||
# Checked against Cargo.lock rather than the manifest, because the manifest
|
||||
# states an intent and the lockfile states what would actually be compiled --
|
||||
# and a caret requirement in the manifest silently accepts a version nobody
|
||||
# and a manifest `rev` can drift from the revision that was actually resolved,
|
||||
# just as the caret requirement it replaced silently accepted a version nobody
|
||||
# decided on. Asserted in the fast headless job so it gates every push, and not
|
||||
# in the UI job, which is `continue-on-error` and therefore cannot fail one.
|
||||
#
|
||||
# Changing this line means writing a decision record. See AGENTS.md,
|
||||
# Decision 0023 moved this from published crates.io 0.2.2 to a full-SHA
|
||||
# revision of Zed. Two packages arrive from it, and they must arrive from the
|
||||
# SAME revision: `gpui` is the framework, `gpui_platform` is the entry point and
|
||||
# the Linux backends that the publishable crate no longer contains. A pair that
|
||||
# disagreed would compile against two different framework snapshots. Their
|
||||
# `version` fields differ (0.2.2 and 0.1.0) and mean nothing here -- neither has
|
||||
# been bumped upstream in ten months -- so the revision is what is asserted.
|
||||
#
|
||||
# Changing these lines means writing a decision record. See AGENTS.md,
|
||||
# "Dependencies that are not an agent's decision".
|
||||
expected_gpui_source='registry+https://github.com/rust-lang/crates.io-index'
|
||||
expected_gpui_version='0.2.2'
|
||||
expected_gpui_git='https://github.com/zed-industries/zed.git'
|
||||
expected_gpui_rev='ce48461eaadd16c65c31f835511ab96bd3b6e746'
|
||||
expected_gpui_source="git+$expected_gpui_git?rev=$expected_gpui_rev#$expected_gpui_rev"
|
||||
|
||||
gpui_block="$(awk '/^name = "gpui"$/{found=1} found{print} found&&/^$/{exit}' Cargo.lock)"
|
||||
if [ -z "$gpui_block" ]; then
|
||||
echo "workspace-guard: no gpui package in Cargo.lock; the shell cannot build." >&2
|
||||
for pkg in gpui gpui_platform; do
|
||||
block="$(awk -v pkg="$pkg" '$0 == "name = \"" pkg "\""{found=1} found{print} found&&/^$/{exit}' Cargo.lock)"
|
||||
if [ -z "$block" ]; then
|
||||
echo "workspace-guard: no $pkg package in Cargo.lock; the shell cannot build." >&2
|
||||
status=1
|
||||
else
|
||||
actual_version="$(printf '%s' "$gpui_block" | sed -n 's/^version = "\(.*\)"$/\1/p')"
|
||||
actual_source="$(printf '%s' "$gpui_block" | sed -n 's/^source = "\(.*\)"$/\1/p')"
|
||||
if [ "$actual_version" != "$expected_gpui_version" ] || [ "$actual_source" != "$expected_gpui_source" ]; then
|
||||
echo "workspace-guard: the gpui dependency is not the one the decision record chose." >&2
|
||||
echo " expected $expected_gpui_version from $expected_gpui_source" >&2
|
||||
echo " found $actual_version from $actual_source" >&2
|
||||
continue
|
||||
fi
|
||||
actual_source="$(printf '%s' "$block" | sed -n 's/^source = "\(.*\)"$/\1/p')"
|
||||
if [ "$actual_source" != "$expected_gpui_source" ]; then
|
||||
echo "workspace-guard: the $pkg dependency is not the one the decision record chose." >&2
|
||||
echo " expected $expected_gpui_source" >&2
|
||||
echo " found ${actual_source:-<no source: a path or workspace override>}" >&2
|
||||
echo " Both gpui crates must carry rev $expected_gpui_rev." >&2
|
||||
echo " A bump is a decision record, not a commit. See AGENTS.md." >&2
|
||||
status=1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$status" = 0 ]; then
|
||||
echo "workspace-guard: crate membership and the pinned UI dependency are as recorded."
|
||||
|
||||
Reference in New Issue
Block a user