Skip to repository content
tenant.openagents/omega
No repository description is available.
OpenAgents Git authority 2026-07-28T00:57:00.755Z Public web read
NIP-34 coordinate
30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omegaMaintainersHidden in public view
References2 branches · 1 tag
Read-only clone
git clone https://openagents.com/git/tenant.openagents/omega.gitRevision diff
ecbde21e → a96f6c05diff --git a/.gitignore b/.gitignore
index 1c6e72f08f..8adadb0667 100644
--- a/.gitignore
+++ b/.gitignore
@@ -52,7 +52,16 @@ crates/docs_preprocessor/actions.json
5252 # Visual test baseline images (these will be stored outside
5353 # the repo in the future, but we don't haven't decided exactly
5454 # where yet, so for now they get generated into a gitignored dir.)
55-/crates/zed/test_fixtures/visual_tests/
55+#
56+# OMEGA-DELTA-0034/0035: Omega's own baselines are committed. Upstream leaves
57+# these uncommitted pending a decision on where to keep them, which is why
58+# `zed_visual_test_runner` shipped with no baselines and no invocation and
59+# every Omega packet that needed rendered evidence had none. A baseline nobody
60+# committed cannot fail, and three lanes in a row reported "no check here looks
61+# at a rendered pixel". Only the `omega_*` captures are re-included; the
62+# inherited suite keeps upstream's arrangement.
63+/crates/zed/test_fixtures/visual_tests/*
64+!/crates/zed/test_fixtures/visual_tests/omega_*.png
5665
5766 # Local documentation audit files
5867 /december-2025-releases.md
diff --git a/Cargo.lock b/Cargo.lock
index 1a42900a4c..78c5d7bbed 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -22385,6 +22385,7 @@ dependencies = [
2238522385 "node_runtime",
2238622386 "notifications",
2238722387 "omega_effectd",
22388+ "omega_front_door",
2238822389 "onboarding",
2238922390 "outline",
2239022391 "outline_panel",
diff --git a/OMEGA_DELTAS.md b/OMEGA_DELTAS.md
index 01dac09f0f..1384e8a7a8 100644
--- a/OMEGA_DELTAS.md
+++ b/OMEGA_DELTAS.md
@@ -1188,15 +1188,12 @@ cargo test -p omega_deltas
11881188 `the_route_decision_is_a_record_that_round_trips` in `crates/omega_deltas`;
11891189 the routing-law suite in `crates/omega_front_door/src/router.rs`; and the
11901190 dispatch and journal suite in `crates/agent_ui/src/omega_router.rs`.
1191-- **Not covered.** The router is **not yet wired into the agent panel**: nothing
1192- in the shipped app constructs an `OmegaAgentConnection`, and there is no pin
1193- control on any surface, so in `0.2.0-rc13` the journal stays empty and every
1194- thread still discloses `route: None`. The routing law, the dispatch seam, the
1195- fail-closed behaviour, and the record are all real and tested; the *user* has
1196- no way to reach them yet. That wiring — a pin affordance, the panel building
1197- the router, and the `get_capacity` poll feeding `observe_capacity` — is the
1198- next packet, and until it lands the exit properties hold in the router and not
1199- in the product. Separately, the router can decide an engine-lane route it
1191+- **Not covered.** The wiring this entry once listed as missing landed in
1192+ `OMEGA-DELTA-0035`: the native agent entry now resolves to an
1193+ `OmegaAgentConnection`, the panel polls `get_capacity` into
1194+ `observe_capacity`, and a pin menu sits on every thread's disclosure line. Read
1195+ `OMEGA-DELTA-0035` for what that wiring does and does not reach. Separately,
1196+ the router can decide an engine-lane route it
12001197 cannot itself dispatch, because engine lanes are started by a person on the
12011198 Full Auto surface and driven by the host bridge rather than through
12021199 `AgentConnection::prompt`; that gap is the named `engine_lane_not_connected`
@@ -1589,3 +1586,143 @@ than it sounds, because the harness omega#81's acceptance sentence names —
15891586 `crates/omega_deltas`; plus the suites in
15901587 `crates/omega_harness/src/front_door.rs` and
15911588 `crates/project/tests/integration/harness_maintenance.rs`.
1589+
1590+### OMEGA-DELTA-0034 — The front door works with no project open
1591+
1592+- **Upstream Zed:** the agent panel requires an open project. `agent_ui: Require
1593+ an open project for agent panel` (#56577, "a bit brute force, but it works")
1594+ put a `has_open_project` early return in front of every entry to the panel, so
1595+ a window with no worktree shows "Open a Project" and nothing else. That is
1596+ coherent for an editor whose agent is an accessory to a project.
1597+- **Omega, before this:** `OMEGA-DELTA-0019` made a window with nothing to
1598+ restore open on the agent instead of on an untitled buffer, and
1599+ `OMEGA-DELTA-0020` folded Full Auto into that panel. Both landed. The exit
1600+ omega#76 actually asked for — *fresh launch lands on the surface **and typing
1601+ starts a thread*** — did not: a window with nothing to restore is by
1602+ definition a window with no project, so the front door opened onto
1603+ "Open Project / Clone Repository" and there was no composer to type into. The
1604+ landing half shipped and the typing half did not, and the two were reported
1605+ together.
1606+- **Omega now:** the guards on the front door's own path are gone.
1607+ `activate_new_thread`, `activate_draft`, `new_thread`,
1608+ `ensure_native_agent_connection` and `toggle_new_thread_menu` no longer refuse
1609+ a projectless window, the toolbar's create controls are live, and a thread's
1610+ title is editable. Typing on a fresh install starts a real thread.
1611+- **What the guards protected, checked rather than assumed.** The one that could
1612+ plausibly have been load-bearing was `ensure_native_agent_connection`, and it
1613+ is not: `NativeAgentServer::connect` takes the project as `_project` and never
1614+ reads it, and `NativeAgent::new_session` builds a thread from the project
1615+ entity without requiring a visible worktree. What the guard bought was not
1616+ spinning up a connection for a window upstream had decided would never show
1617+ the agent — a resource choice resting on a premise Omega does not share.
1618+- **The workspace-touching guards stay.** A terminal genuinely needs a working
1619+ directory, so `should_create_terminal_for_new_entry` keeps its check and a
1620+ projectless `new_thread` falls through to a thread rather than a terminal.
1621+ Loading a thread from the clipboard, resuming a persisted draft, refreshing
1622+ skills, initialising from a source workspace, and starting an external ACP
1623+ agent all still require a project. Removing *those* would not be
1624+ project-optional threads; it would be threads that fail later and less
1625+ legibly.
1626+- **`cmd-?` gave the agent panel back to macOS.** `agent::ToggleFocus` was bound
1627+ to `cmd-?`, which is macOS's reserved Help chord — the Help menu's search
1628+ field. Omega cannot win that keystroke, so the binding was a keybinding that
1629+ looked present and did not work. It is `ctrl-cmd-a` now: free, not
1630+ system-reserved, and on the same letter as `cmd-shift-a`. Linux (`ctrl-?`) and
1631+ Windows (`ctrl-shift-/`) are untouched, because neither is reserved there and
1632+ changing them would be churn with no defect behind it.
1633+- **The rendered proof, and why the runner needed standing up.**
1634+ `zed_visual_test_runner` was already in the fork — macOS headless Metal
1635+ capture, no Screen Recording permission, no window to get frontmost — and it
1636+ had no committed baselines and no invocation, so no Omega packet had ever
1637+ used it. `script/omega-visual-proof` is the invocation, `OMEGA_VISUAL_ONLY=1`
1638+ runs Omega's suite alone, and the `omega_*` baselines are re-included in
1639+ `.gitignore` and committed. A baseline nobody committed cannot fail. The
1640+ suite asserts the rendered line's text through the same method the render
1641+ calls and asserts the record is coherent *before* each capture, so a picture
1642+ can never be the only thing standing behind a claim.
1643+- **Enforced by:** `the_front_door_does_not_require_an_open_project` and
1644+ `required_keymap_bindings_resolve` in `crates/omega_deltas`;
1645+ `test_empty_workspace_opens_the_front_door` in `crates/agent_ui`; the rendered
1646+ proofs `omega_front_door_no_project` and `omega_front_door_typing` in
1647+ `crates/zed/src/visual_test_runner.rs`.
1648+- **What this does not cover.** A **first-ever** launch still lands on the
1649+ Onboarding tab, not on the agent — observed on a packaged build with a fresh
1650+ `--user-data-dir`, where the front door is behind onboarding and the agent
1651+ dock is closed until it is dismissed. Every launch after that lands on the
1652+ agent. So omega#76's exit holds for its second half (typing starts a real
1653+ thread) and for every launch but the first; the first-run ordering is a
1654+ separate, untouched surface. A projectless thread is also not yet *bound* to a
1655+ project on its first workspace-touching action, which is the rest of omega#76's
1656+ project-optional deliverable. Today it simply has no worktree: file mentions
1657+ find nothing and a tool that needs a path fails the way it would in any
1658+ worktree-less window. The `cmd-shift-a` editor-context collision and the
1659+ shadowed lower-priority bindings named in omega#76 are also untouched; the
1660+ two `cmd-shift-a` bindings that remain are inside modal pickers
1661+ (`ToolchainSelector`, `RecentProjects`) and fire only while those are open.
1662+
1663+### OMEGA-DELTA-0035 — The router is wired, and a pin is a gesture
1664+
1665+- **Upstream Zed:** there is one agent connection per thread and no routing, so
1666+ there is nothing to wire and nothing to pin.
1667+- **Omega, before this:** `OMEGA-DELTA-0029` built the routing law and the
1668+ dispatch seam and wired neither. Nothing in the shipped app constructed an
1669+ `OmegaAgentConnection`, `observe_capacity` had no caller, and no surface could
1670+ set a pin — so the route journal stayed empty and every thread disclosed
1671+ `route: None`. The exit properties held in the router and not in the product.
1672+- **Omega now:** `Agent::NativeAgent.server(..)` returns an
1673+ `omega_router::OmegaRouterServer`, whose `connect` builds an
1674+ `OmegaAgentConnection` over the native connection and publishes it. Every new
1675+ first-party session is routed on purpose and its decision is written to the
1676+ route journal before the turn exists. The agent panel polls the engine's
1677+ framed `get_capacity` answer on the same three-second cadence the Full Auto
1678+ roster uses and hands it to `observe_capacity`, so an engine-lane pin is
1679+ decided against what `omega-effectd` is actually doing rather than against a
1680+ default of "not running".
1681+- **A pin is a gesture, enforced by the argument type.** `pin_session`,
1682+ `unpin_session` and `pin_next_session` each require an
1683+ `omega_front_door::PinGesture`, every variant of which is a control a person
1684+ operates. There is no `PinGesture::ToolCall`, no `SlashCommand`, no
1685+ `RestoredDraft` and no `ComposerMode`, and `pin_gestures_are_all_human_gestures`
1686+ fails if one appears. This is the same construction owner gate 8 already uses
1687+ for `LaunchOrigin`, applied at the pin because a pin is the only door to an
1688+ engine lane and an engine lane *is* Full Auto authority. omega#76 rejected a
1689+ composer mode flag for Full Auto because a boolean the send path reads can be
1690+ set by a slash command, a restored draft, or a model-authored insertion; a pin
1691+ stored as a mode would be the same construct wearing a different name.
1692+- **Engine lanes are still reachable only through a pin.** The unpinned default
1693+ is the native loop, always. Nothing here auto-prefers a ready lane, and
1694+ `an_unpinned_thread_never_reaches_an_engine_lane` in
1695+ `crates/omega_front_door/src/router.rs` fails if the law changes.
1696+- **A human pin re-decides; capacity moving does not.** Setting or clearing a
1697+ pin re-runs the decision for that session and records it, so an unhonourable
1698+ pin appears on the thread's own line as a fallback with its typed reason
1699+ instead of silently doing nothing. A later engine answer never re-decides a
1700+ recorded session, so a turn does not move executors mid-thread because
1701+ capacity changed.
1702+- **The thread carries the executor, not the router.**
1703+ `OmegaAgentConnection::new_session` delegates and returns what the executor
1704+ built, so `OMEGA-DELTA-0021`'s disclosure keeps classifying the executor. The
1705+ two places that asked "is this the native agent?" by downcasting — the
1706+ shared-project refusal and the native thread-store hand-off — go through
1707+ `is_native_agent_server` now, because a wrapped native agent reading as
1708+ external is a silently wrong `false` rather than a compile error.
1709+- **Enforced by:** `the_router_is_wired_into_the_native_agent_entry`,
1710+ `only_a_named_human_gesture_can_pin_an_executor` and
1711+ `nothing_asks_for_the_native_agent_with_a_bare_downcast` in
1712+ `crates/omega_deltas`; `pin_gestures_are_all_human_gestures` in
1713+ `crates/omega_front_door`; the dispatch and journal suite in
1714+ `crates/agent_ui/src/omega_router.rs`; and the rendered proofs
1715+ `omega_executor_disclosure_*` and `omega_route_pin_not_honoured` in
1716+ `crates/zed/src/visual_test_runner.rs`.
1717+- **What this does not cover.** No external ACP agent or engine-lane executor is
1718+ registered on the router in this build, so a pin to either fails closed to the
1719+ native loop with `external_acp_unavailable` or `engine_lane_not_connected` —
1720+ which is the honest answer and is what the rendered proof shows. The honoured
1721+ pin the rendered proof shows is therefore a pin to the native loop, which is
1722+ a real honoured pin (`routed: pinned`, not `routed: unpinned`) but not an
1723+ honoured *engine-lane* pin; that path is exercised only in tests. Turns still reach
1724+ the executor through the thread's own connection rather than through
1725+ `OmegaAgentConnection::prompt`, because the thread carries the executor; the
1726+ router decides per session, not per turn. And the pin menu is rendered on the
1727+ thread's disclosure line, so a surface with no thread on it has no pin
1728+ control.
diff --git a/assets/keymaps/default-macos.json b/assets/keymaps/default-macos.json
index fbf357a03f..f977d34c37 100644
--- a/assets/keymaps/default-macos.json
+++ b/assets/keymaps/default-macos.json
@@ -750,7 +750,11 @@
750750 "cmd-shift-b": "outline_panel::ToggleFocus",
751751 "ctrl-shift-g": "git_panel::ToggleFocus",
752752 "cmd-shift-d": "debug_panel::ToggleFocus",
753- "cmd-?": "agent::ToggleFocus",
753+ // OMEGA-DELTA-0034. `cmd-?` is macOS's reserved Help chord (the Help
754+ // menu's search field), so binding the agent panel there fought the
755+ // system for a keystroke Omega could not win. `ctrl-cmd-a` is free, is
756+ // not system-reserved, and sits on the same letter as `cmd-shift-a`.
757+ "ctrl-cmd-a": "agent::ToggleFocus",
754758 "cmd-alt-s": "workspace::SaveAll",
755759 "cmd-k n": "encoding_selector::Toggle",
756760 "cmd-k m": "language_selector::Toggle",
diff --git a/crates/agent_ui/src/agent_panel.rs b/crates/agent_ui/src/agent_panel.rs
index 3b8c6426c5..cb83bee6ae 100644
--- a/crates/agent_ui/src/agent_panel.rs
+++ b/crates/agent_ui/src/agent_panel.rs
@@ -1217,6 +1217,14 @@ pub struct AgentPanel {
12171217 /// Separate from `full_auto` for the reason above: hidden and absent are
12181218 /// different states.
12191219 showing_full_auto: bool,
1220+ /// `OMEGA-DELTA-0035`. The poll that feeds the router the engine's framed
1221+ /// `get_capacity` answer.
1222+ ///
1223+ /// Held so it dies with the panel. It writes nothing to the engine and
1224+ /// keeps no run state: `omega-effectd` remains the sole run authority and
1225+ /// this only lets the router know whether an engine-lane pin is honourable
1226+ /// before it decides.
1227+ _engine_capacity_poll: Option<Task<()>>,
12201228 }
12211229
12221230 impl AgentPanel {
@@ -1617,12 +1625,68 @@ impl AgentPanel {
16171625 is_active: false,
16181626 full_auto: None,
16191627 showing_full_auto: false,
1628+ _engine_capacity_poll: None,
16201629 };
16211630
1631+ let mut panel = panel;
16221632 panel.ensure_native_agent_connection(cx);
1633+ panel.observe_engine_capacity(cx);
16231634 panel
16241635 }
16251636
1637+ /// `OMEGA-DELTA-0035`. Keep the router's view of the engine current.
1638+ ///
1639+ /// omega#78 built `observe_capacity` and nothing called it, so the router
1640+ /// decided every engine-lane pin against `NotRunning` regardless of what
1641+ /// `omega-effectd` was actually doing. This is the feed: the same framed
1642+ /// `get_capacity` call the Full Auto roster makes, on the same three-second
1643+ /// cadence, handed to the router as a snapshot.
1644+ ///
1645+ /// The direction is one-way by construction. `observe_capacity` takes an
1646+ /// answer and stores it; there is no path from here back into the engine,
1647+ /// and a decision already recorded is never re-derived when a later answer
1648+ /// arrives — `a_later_engine_answer_does_not_rewrite_a_recorded_decision`
1649+ /// asserts that at the dispatch layer, not only in the journal.
1650+ ///
1651+ /// An engine that cannot be reached is reported as unreachable rather than
1652+ /// left at its last good answer. A router that kept believing a stale
1653+ /// "available" would route a pin onto a lane that had gone away, and the
1654+ /// user would see a pin honoured that was not.
1655+ fn observe_engine_capacity(&mut self, cx: &mut Context<Self>) {
1656+ let executor = cx.background_executor().clone();
1657+ let supervisor = omega_effectd::shared_supervisor(cx).ok();
1658+ self._engine_capacity_poll = Some(cx.spawn(async move |this, cx| {
1659+ loop {
1660+ let observed = match &supervisor {
1661+ Some(supervisor) => {
1662+ let mut guard = supervisor.lock().await;
1663+ guard.get_capacity().await.ok()
1664+ }
1665+ None => None,
1666+ };
1667+ // Reached through the panel entity so the loop ends with the
1668+ // panel rather than outliving the window it belongs to.
1669+ let alive = this
1670+ .update(cx, |_panel, _cx| {
1671+ let Some(router) = crate::omega_router::active_router() else {
1672+ return;
1673+ };
1674+ match &observed {
1675+ Some(capacity) => router.observe_capacity(Ok(capacity)),
1676+ None => router.observe_capacity(Err(
1677+ omega_front_door::EngineUnreachable::NotRunning,
1678+ )),
1679+ }
1680+ })
1681+ .is_ok();
1682+ if !alive {
1683+ break;
1684+ }
1685+ executor.timer(std::time::Duration::from_secs(3)).await;
1686+ }
1687+ }));
1688+ }
1689+
16261690 pub fn toggle_focus(
16271691 workspace: &mut Workspace,
16281692 _: &ToggleFocus,
@@ -1773,11 +1837,16 @@ impl AgentPanel {
17731837 cx.notify();
17741838 }
17751839
1840+ /// `OMEGA-DELTA-0034`. A new thread does not require an open project.
1841+ ///
1842+ /// Upstream refused this (`agent_ui: Require an open project for agent
1843+ /// panel`, "a bit brute force, but it works"). Omega's front door is the
1844+ /// agent, and a fresh install *is* the no-project case, so refusing here
1845+ /// refuses the front door to every new user. `new_thread_with_workspace`
1846+ /// still asks `should_create_terminal_for_new_entry`, which requires an
1847+ /// open project — a terminal genuinely needs a working directory, so with
1848+ /// no project this falls through to a thread.
17761849 pub fn new_thread(&mut self, _action: &NewThread, window: &mut Window, cx: &mut Context<Self>) {
1777- if !self.has_open_project(cx) {
1778- return;
1779- }
1780-
17811850 self.new_thread_with_workspace(None, window, cx);
17821851 }
17831852
@@ -1794,6 +1863,15 @@ impl AgentPanel {
17941863 }
17951864 }
17961865
1866+ /// `OMEGA-DELTA-0034`. The front door's own entry point, project or no
1867+ /// project.
1868+ ///
1869+ /// `AgentPanel::open_front_door` calls this on a window with nothing to
1870+ /// restore, and a window with nothing to restore is by definition a window
1871+ /// with no project. The guard that used to stand here is why omega#76's
1872+ /// exit — *typing starts a real thread* — did not hold on a fresh install:
1873+ /// landing worked, and the composer the user was supposed to type into was
1874+ /// never built.
17971875 pub fn activate_new_thread(
17981876 &mut self,
17991877 focus: bool,
@@ -1801,10 +1879,6 @@ impl AgentPanel {
18011879 window: &mut Window,
18021880 cx: &mut Context<Self>,
18031881 ) {
1804- if !self.has_open_project(cx) {
1805- return;
1806- }
1807-
18081882 self.set_last_created_entry_kind_from_user_action(AgentPanelEntryKind::Thread, cx);
18091883
18101884 // If the user is viewing a *parked* draft and the ephemeral
@@ -2027,9 +2101,19 @@ impl AgentPanel {
20272101 self.has_open_project(cx) && self.project.read(cx).supports_terminal(cx)
20282102 }
20292103
2104+ /// `OMEGA-DELTA-0034`. A terminal entry still requires an open project.
2105+ ///
2106+ /// This asks `supports_terminal` rather than `project.supports_terminal`
2107+ /// because the latter is `true` for *any* local project, worktree or not.
2108+ /// Before the front-door guards moved, `new_thread`'s own
2109+ /// `has_open_project` check was what stopped that: a fresh window whose
2110+ /// persisted `last_created_entry_kind` was `Terminal` would otherwise open
2111+ /// a terminal with no working directory instead of the composer. That is
2112+ /// what those guards were protecting, and it is protected here now, where
2113+ /// the requirement actually is.
20302114 pub fn should_create_terminal_for_new_entry(&self, cx: &App) -> bool {
20312115 self.last_created_entry_kind == AgentPanelEntryKind::Terminal
2032- && self.project.read(cx).supports_terminal(cx)
2116+ && self.supports_terminal(cx)
20332117 }
20342118
20352119 fn set_last_created_entry_kind_from_user_action(
@@ -3024,11 +3108,17 @@ impl AgentPanel {
30243108 }
30253109 }
30263110
3111+ /// `OMEGA-DELTA-0034`. Connect the native agent, project or no project.
3112+ ///
3113+ /// This was the guard that had to be understood before any of the others
3114+ /// could move, because it is the one that could plausibly have been
3115+ /// protecting something. It is not: `NativeAgentServer::connect` takes the
3116+ /// project as `_project` and never reads it, and `NativeAgent::new_session`
3117+ /// builds a thread from the project entity without requiring a visible
3118+ /// worktree. What the guard actually bought was not spinning up an agent
3119+ /// connection for a window that upstream had decided would never show the
3120+ /// agent — a resource choice, on a premise Omega does not share.
30273121 fn ensure_native_agent_connection(&self, cx: &mut Context<Self>) {
3028- if !self.has_open_project(cx) {
3029- return;
3030- }
3031-
30323122 let fs = self.fs.clone();
30333123 let thread_store = self.thread_store.clone();
30343124 self.connection_store.update(cx, |store, cx| {
@@ -3040,6 +3130,7 @@ impl AgentPanel {
30403130 });
30413131 }
30423132
3133+ /// `OMEGA-DELTA-0034`. The composer, project or no project.
30433134 pub fn activate_draft(
30443135 &mut self,
30453136 focus: bool,
@@ -3047,10 +3138,6 @@ impl AgentPanel {
30473138 window: &mut Window,
30483139 cx: &mut Context<Self>,
30493140 ) {
3050- if !self.has_open_project(cx) {
3051- return;
3052- }
3053-
30543141 let draft = self.ensure_draft(source, window, cx);
30553142 if let BaseView::AgentThread { conversation_view } = &self.base_view {
30563143 if conversation_view.entity_id() == draft.entity_id() {
@@ -3627,9 +3714,10 @@ impl AgentPanel {
36273714 let project = self.project.clone();
36283715 cx.spawn(async move |_this, cx| -> Result<()> {
36293716 let connected = connect_task.await?;
3630- if let Some(native_connection) = connected
3631- .connection
3632- .downcast::<agent::NativeAgentConnection>()
3717+ // OMEGA-DELTA-0035. The store's connection is the router; the
3718+ // native loop is underneath it.
3719+ if let Some(native_connection) =
3720+ crate::omega_router::native_connection(&connected.connection)
36333721 {
36343722 cx.update(|cx| native_connection.refresh_skills_for_project(project, cx));
36353723 }
@@ -3663,16 +3751,14 @@ impl AgentPanel {
36633751 self.agent_panel_menu_handle.toggle(window, cx);
36643752 }
36653753
3754+ /// `OMEGA-DELTA-0034`. The new-thread menu, project or no project — it is
3755+ /// how the front door reaches Full Auto and the executor choices.
36663756 pub fn toggle_new_thread_menu(
36673757 &mut self,
36683758 _: &ToggleNewThreadMenu,
36693759 window: &mut Window,
36703760 cx: &mut Context<Self>,
36713761 ) {
3672- if !self.has_open_project(cx) {
3673- return;
3674- }
3675-
36763762 self.new_thread_menu_handle.toggle(window, cx);
36773763 }
36783764
@@ -4647,10 +4733,10 @@ impl AgentPanel {
46474733
46484734 let server = server_override
46494735 .unwrap_or_else(|| agent.server(self.fs.clone(), self.thread_store.clone()));
4650- let thread_store = server
4651- .clone()
4652- .downcast::<agent::NativeAgentServer>()
4653- .is_some()
4736+ // OMEGA-DELTA-0035. Wrapped or bare, the native agent gets the thread
4737+ // store; a bare downcast here would silently hand the router `None` and
4738+ // lose native thread persistence.
4739+ let thread_store = crate::omega_router::is_native_agent_server(&server)
46544740 .then(|| self.thread_store.clone());
46554741
46564742 let connection_store = self.connection_store.clone();
@@ -5442,12 +5528,13 @@ impl AgentPanel {
54425528 fn should_show_title_edit(&self, window: &Window, cx: &Context<Self>) -> bool {
54435529 // A Full Auto run's title is edited on its own launch surface, so the
54445530 // toolbar's thread-title editor must not appear over it.
5531+ // OMEGA-DELTA-0034. A project-optional thread has a title like any
5532+ // other, so editing it does not wait for a worktree.
54455533 !self.showing_full_auto
54465534 && matches!(
54475535 self.visible_surface(),
54485536 VisibleSurface::AgentThread(_) | VisibleSurface::Terminal(_)
54495537 )
5450- && self.has_open_project(cx)
54515538 && !self.is_title_editor_focused(window, cx)
54525539 }
54535540
@@ -5902,7 +5989,11 @@ impl AgentPanel {
59025989
59035990 let focus_handle = self.focus_handle(cx);
59045991
5905- let can_create_entries = self.has_open_project(cx);
5992+ // OMEGA-DELTA-0034. A thread is creatable with no project open, so the
5993+ // toolbar's create controls are live on the front door. A leftover
5994+ // `has_open_project` here would leave the composer typable and the `+`
5995+ // beside it disabled.
5996+ let can_create_entries = true;
59065997 let supports_terminal = self.supports_terminal(cx);
59075998 let showing_terminal = matches!(self.visible_surface(), VisibleSurface::Terminal(_));
59085999
@@ -8946,8 +9037,21 @@ mod tests {
89469037 });
89479038 }
89489039
9040+ /// `OMEGA-DELTA-0034`. An empty workspace opens the front door — and still
9041+ /// refuses the things that genuinely need a worktree.
9042+ ///
9043+ /// This test used to assert the opposite, because it encoded upstream's
9044+ /// policy: `agent_ui: Require an open project for agent panel` (#56577) put
9045+ /// a `has_open_project` guard in front of every panel entry, and this test
9046+ /// pinned it. Omega's front door *is* the agent, and a window with nothing
9047+ /// to restore is by definition a window with no project, so that policy
9048+ /// refused a composer to every new user — omega#76's exit, failing.
9049+ ///
9050+ /// The clauses that were still true are kept and still asserted: an
9051+ /// external ACP agent and a terminal both need a working directory, and
9052+ /// neither is created here.
89499053 #[gpui::test]
8950- async fn test_empty_workspace_does_not_create_agent_entries(cx: &mut TestAppContext) {
9054+ async fn test_empty_workspace_opens_the_front_door(cx: &mut TestAppContext) {
89519055 init_test(cx);
89529056 cx.update(|cx| {
89539057 agent::ThreadStore::init_global(cx);
@@ -8955,6 +9059,11 @@ mod tests {
89559059 });
89569060
89579061 let fs = FakeFs::new(cx.executor());
9062+ // The global filesystem is what a *created* thread reaches for. This
9063+ // test used to assert that no thread was created, so it never needed
9064+ // one; now it asserts the opposite.
9065+ cx.update(|cx| <dyn fs::Fs>::set_global(fs.clone(), cx));
9066+ // Still no worktree: this is the fresh-install state under test.
89589067 let project = Project::test(fs.clone(), [], cx).await;
89599068 let multi_workspace =
89609069 cx.add_window(|window, cx| MultiWorkspace::test_new(project.clone(), window, cx));
@@ -8973,18 +9082,47 @@ mod tests {
89739082
89749083 panel.read_with(cx, |panel, cx| {
89759084 assert_eq!(
9085+ panel.project.read(cx).visible_worktrees(cx).count(),
9086+ 0,
9087+ "this test is about the projectless case; a worktree here \
9088+ would make every assertion below prove something else"
9089+ );
9090+ assert_ne!(
89769091 panel
89779092 .connection_store()
89789093 .read(cx)
89799094 .connection_status(&Agent::NativeAgent, cx),
89809095 crate::agent_connection_store::AgentConnectionStatus::Disconnected,
8981- "empty workspaces should not start the native agent connection"
9096+ "an empty workspace must start the native agent connection; \
9097+ NativeAgentServer::connect never reads the project, and \
9098+ refusing here is what left omega#76's front door with no \
9099+ composer to type into"
89829100 );
89839101 });
89849102
89859103 panel.update_in(cx, |panel, window, cx| {
89869104 panel.new_thread(&NewThread, window, cx);
8987- panel.activate_draft(true, AgentThreadSource::AgentPanel, window, cx);
9105+ });
9106+ cx.run_until_parked();
9107+
9108+ panel.read_with(cx, |panel, _cx| {
9109+ assert!(
9110+ panel.active_conversation_view().is_some(),
9111+ "an empty workspace must reach a thread; this is omega#76's \
9112+ exit — a fresh launch lands on the agent and typing starts a \
9113+ real thread"
9114+ );
9115+ assert!(
9116+ panel.draft_thread.is_some(),
9117+ "an empty workspace must have a draft to type into"
9118+ );
9119+ });
9120+
9121+ // The parts that genuinely need a worktree still refuse. Removing
9122+ // *these* guards would not be project-optional threads; it would be
9123+ // threads that fail later and less legibly.
9124+ let before_external = panel.read_with(cx, |panel, _cx| panel.selected_agent.clone());
9125+ panel.update_in(cx, |panel, window, cx| {
89889126 panel.new_external_agent_thread(
89899127 &NewExternalAgentThread {
89909128 agent: AgentId::new("external-agent"),
@@ -8994,19 +9132,11 @@ mod tests {
89949132 );
89959133 });
89969134 cx.run_until_parked();
8997-
8998- panel.read_with(cx, |panel, cx| {
8999- assert!(
9000- panel.active_conversation_view().is_none(),
9001- "empty workspaces should not create agent threads"
9002- );
9003- assert!(
9004- panel.draft_thread.is_none(),
9005- "empty workspaces should not create draft threads"
9006- );
9007- assert!(
9008- panel.terminals(cx).is_empty(),
9009- "empty workspaces should not create agent panel terminals"
9135+ panel.read_with(cx, |panel, _cx| {
9136+ assert_eq!(
9137+ panel.selected_agent, before_external,
9138+ "an empty workspace must not start an external ACP agent, \
9139+ which has no working directory to run in"
90109140 );
90119141 });
90129142
@@ -9021,15 +9151,15 @@ mod tests {
90219151 panel.read_with(cx, |panel, cx| {
90229152 assert!(
90239153 panel.terminals(cx).is_empty(),
9024- "empty workspaces should not create terminals after the terminal feature is enabled"
9154+ "an empty workspace must not create a terminal; a terminal \
9155+ needs a working directory"
90259156 );
9026- assert_eq!(
9027- panel
9028- .connection_store()
9029- .read(cx)
9030- .connection_status(&Agent::NativeAgent, cx),
9031- crate::agent_connection_store::AgentConnectionStatus::Disconnected,
9032- "empty workspace actions should not start the native agent connection"
9157+ assert!(
9158+ !panel.should_create_terminal_for_new_entry(cx),
9159+ "with no project, a new entry must be a thread and not a \
9160+ terminal — project.supports_terminal() is true for any local \
9161+ project, worktree or not, so this has to be checked on the \
9162+ panel's own wrapper"
90339163 );
90349164 });
90359165 }
diff --git a/crates/agent_ui/src/agent_ui.rs b/crates/agent_ui/src/agent_ui.rs
index 225d07ee91..0b2cde6e6b 100644
--- a/crates/agent_ui/src/agent_ui.rs
+++ b/crates/agent_ui/src/agent_ui.rs
@@ -24,7 +24,7 @@ mod mode_selector;
2424 mod model_selector;
2525 mod model_selector_popover;
2626 pub mod omega_executor_disclosure;
27-mod omega_host_bridge;
27+pub mod omega_host_bridge;
2828 pub mod omega_router;
2929 pub mod omega_send_queue;
3030 mod profile_selector;
@@ -491,7 +491,27 @@ impl Agent {
491491 thread_store: Entity<agent::ThreadStore>,
492492 ) -> Rc<dyn agent_servers::AgentServer> {
493493 match self {
494- Self::NativeAgent => Rc::new(agent::NativeAgentServer::new(fs, thread_store)),
494+ // OMEGA-DELTA-0035. The native agent, behind Omega Agent's router.
495+ // Every new first-party session is routed on purpose and the
496+ // decision is written to the route journal before the turn exists.
497+ // The thread still carries the executor's connection, so omega#77's
498+ // disclosure classifies the executor and not the router.
499+ Self::NativeAgent => Rc::new(crate::omega_router::OmegaRouterServer::new(
500+ agent::NativeAgentServer::new(fs, thread_store),
501+ // `ZED_STATELESS` means "do not persist this run", and a
502+ // rendering harness or a debug session sets it. Writing route
503+ // decisions into the owner's real Omega data directory from a
504+ // harness would put sessions nobody started into a record an
505+ // operator reads. The choice is made here rather than inside
506+ // the router, because the router is not allowed to read the
507+ // environment — see `RouteJournal::data_dir_path`.
508+ if std::env::var("ZED_STATELESS").is_ok() {
509+ std::env::temp_dir()
510+ .join(format!("omega-route-journal-{}.json", std::process::id()))
511+ } else {
512+ crate::omega_router::RouteJournal::data_dir_path()
513+ },
514+ )),
495515 Self::Custom { id: name } => {
496516 Rc::new(agent_servers::CustomAgentServer::new(name.clone()))
497517 }
diff --git a/crates/agent_ui/src/conversation_view.rs b/crates/agent_ui/src/conversation_view.rs
index 70e1d16107..ad2c1e9fb4 100644
--- a/crates/agent_ui/src/conversation_view.rs
+++ b/crates/agent_ui/src/conversation_view.rs
@@ -7,7 +7,7 @@ use acp_thread::{
77 };
88 use acp_thread::{AgentConnection, Plan};
99 use action_log::{ActionLog, ActionLogTelemetry, DiffStats};
10-use agent::{NativeAgentServer, NoModelConfiguredError, ThreadStore};
10+use agent::{NoModelConfiguredError, ThreadStore};
1111 use agent_client_protocol::schema::v1 as acp;
1212 #[cfg(test)]
1313 use agent_servers::AgentServerDelegate;
@@ -1022,9 +1022,11 @@ impl ConversationView {
10221022 window: &mut Window,
10231023 cx: &mut Context<Self>,
10241024 ) -> ServerState {
1025- if project.read(cx).is_via_collab()
1026- && agent.clone().downcast::<NativeAgentServer>().is_none()
1027- {
1025+ // OMEGA-DELTA-0035. The first-party agent is the router over the native
1026+ // server now, so "is this the native agent?" cannot be a bare downcast:
1027+ // a wrapped native agent would read as external and be refused in a
1028+ // shared project.
1029+ if project.read(cx).is_via_collab() && !crate::omega_router::is_native_agent_server(&agent) {
10281030 return ServerState::LoadError {
10291031 error: LoadError::Other(
10301032 "External agents are not yet supported in shared projects.".into(),
diff --git a/crates/agent_ui/src/conversation_view/thread_view.rs b/crates/agent_ui/src/conversation_view/thread_view.rs
index d13adea5a8..f9b83d520d 100644
--- a/crates/agent_ui/src/conversation_view/thread_view.rs
+++ b/crates/agent_ui/src/conversation_view/thread_view.rs
@@ -41,7 +41,7 @@ use language_model::{
4141 LanguageModelProvider, LanguageModelProviderId, LanguageModelRegistry, Speed,
4242 };
4343 use notifications::status_toast::StatusToast;
44-use omega_front_door::ExecutorDisclosure;
44+use omega_front_door::{ExecutorClass, ExecutorDisclosure, ExecutorPin, PinGesture};
4545 use settings::{update_settings_file, update_settings_file_with_completion};
4646 use ui::{
4747 ButtonLike, CalloutBorderPosition, Checkbox, SpinnerLabel, SpinnerVariant, SplitButton,
@@ -12129,6 +12129,83 @@ impl ThreadView {
1212912129 .size(LabelSize::XSmall)
1213012130 .color(Color::Muted),
1213112131 )
12132+ .child(div().flex_1())
12133+ .child(self.render_executor_pin(cx))
12134+ }
12135+
12136+ /// `OMEGA-DELTA-0035`. The pin, as a gesture rather than a mode.
12137+ ///
12138+ /// omega#78 made a pin the only way a thread reaches anything but the
12139+ /// native loop, and an engine lane *is* Full Auto authority — so owner gate
12140+ /// 8 reaches this control as directly as it reaches the Start button.
12141+ /// omega#76 rejected a composer mode flag for exactly this reason: a
12142+ /// boolean the send path reads can be set by a slash command, a restored
12143+ /// draft, or a model-authored insertion.
12144+ ///
12145+ /// So the pin is not a flag anything reads on the way to sending. It is a
12146+ /// menu item a person picks, it takes an [`omega_front_door::PinGesture`]
12147+ /// that names the gesture, and there is no `PinGesture` variant for a tool
12148+ /// call, a turn, or a mode. Picking one re-decides the thread's route
12149+ /// immediately and writes the decision to the journal, so an unhonourable
12150+ /// pin shows up on this same line as a fallback with its reason instead of
12151+ /// silently doing nothing.
12152+ fn render_executor_pin(&self, cx: &App) -> impl IntoElement {
12153+ let session_id = self.thread.read(cx).session_id().clone();
12154+ let router = crate::omega_router::active_router();
12155+ let pinned = router.as_ref().and_then(|router| router.pin(&session_id));
12156+ let label: SharedString = match &pinned {
12157+ Some(pin) => format!("pin: {}", pin.token()).into(),
12158+ None => "pin: none".into(),
12159+ };
12160+
12161+ PopoverMenu::new("omega-executor-pin")
12162+ .trigger(
12163+ Button::new("omega-executor-pin-trigger", label)
12164+ .label_size(LabelSize::XSmall)
12165+ .color(Color::Muted)
12166+ .end_icon(
12167+ Icon::new(IconName::ChevronDown)
12168+ .size(IconSize::XSmall)
12169+ .color(Color::Muted),
12170+ ),
12171+ )
12172+ .menu(move |window, cx| {
12173+ let session_id = session_id.clone();
12174+ let pinned = pinned.clone();
12175+ Some(ContextMenu::build(window, cx, move |mut menu, _window, _cx| {
12176+ menu = menu.header("Pin this thread's executor");
12177+ for class in ExecutorClass::all() {
12178+ let class = *class;
12179+ let is_selected =
12180+ pinned.as_ref().is_some_and(|pin| pin.class == class);
12181+ let entry = ContextMenuEntry::new(class.token())
12182+ .toggleable(IconPosition::End, is_selected);
12183+ menu.push_item(entry.handler({
12184+ let session_id = session_id.clone();
12185+ move |_window, _cx| {
12186+ let Some(router) = crate::omega_router::active_router() else {
12187+ return;
12188+ };
12189+ router.pin_session(
12190+ &session_id,
12191+ ExecutorPin::new(class),
12192+ PinGesture::ExecutorPinMenuItem,
12193+ );
12194+ }
12195+ }));
12196+ }
12197+ menu = menu.separator();
12198+ menu.push_item(ContextMenuEntry::new("Unpin").handler({
12199+ move |_window, _cx| {
12200+ let Some(router) = crate::omega_router::active_router() else {
12201+ return;
12202+ };
12203+ router.unpin_session(&session_id, PinGesture::ExecutorPinCleared);
12204+ }
12205+ }));
12206+ menu
12207+ }))
12208+ })
1213212209 }
1213312210 }
1213412211
diff --git a/crates/agent_ui/src/omega_host_bridge.rs b/crates/agent_ui/src/omega_host_bridge.rs
index fb2b1a1683..64e67a6275 100644
--- a/crates/agent_ui/src/omega_host_bridge.rs
+++ b/crates/agent_ui/src/omega_host_bridge.rs
@@ -77,6 +77,26 @@ fn republish_engine_lane_runs(threads: &[HostThread]) {
7777 }
7878 }
7979
80+/// Publish one lane correlation, for a harness that has no engine to run.
81+///
82+/// `OMEGA-DELTA-0021`'s engine-lane disclosure is normally reached only by a
83+/// real `omega-effectd` run, which a rendering harness cannot start. This is
84+/// the seam that lets the *rendered* engine-lane line be captured without
85+/// weakening the production path: it is behind `test-support`, so no shipped
86+/// build can reach it, and it writes the same index the correlation journal
87+/// writes.
88+#[cfg(any(test, feature = "test-support"))]
89+pub fn publish_engine_lane_run_for_tests(thread_id: ThreadId, operation_ref: String) {
90+ match ENGINE_LANE_RUNS.lock() {
91+ Ok(mut index) => {
92+ index.insert(thread_id, operation_ref);
93+ }
94+ Err(poisoned) => {
95+ poisoned.into_inner().insert(thread_id, operation_ref);
96+ }
97+ }
98+}
99+
80100 /// The `omega-effectd` lane run this thread belongs to, if it is one.
81101 ///
82102 /// Returns `None` for every thread the user started themselves, which is what
diff --git a/crates/agent_ui/src/omega_router.rs b/crates/agent_ui/src/omega_router.rs
index edb6b3e251..b5223ac5c6 100644
--- a/crates/agent_ui/src/omega_router.rs
+++ b/crates/agent_ui/src/omega_router.rs
@@ -46,7 +46,7 @@ use std::any::Any;
4646 use std::cell::RefCell;
4747 use std::collections::BTreeMap;
4848 use std::path::{Path, PathBuf};
49-use std::rc::Rc;
49+use std::rc::{Rc, Weak};
5050 use std::sync::{LazyLock, Mutex};
5151
5252 use acp_thread::{
@@ -59,7 +59,7 @@ use anyhow::Result;
5959 use gpui::{App, Entity, SharedString, Task};
6060 use omega_front_door::{
6161 EngineLane, EngineReadiness, EngineUnreachable, ExecutorClass, ExecutorPin, LaneState,
62- RouteDecision, RouteInputs, RouteReason, route,
62+ PinGesture, RouteDecision, RouteInputs, RouteReason, route,
6363 };
6464 use project::{AgentId, Project};
6565 use serde_json::Value;
@@ -140,11 +140,23 @@ impl RouteJournal {
140140 /// The journal at the Omega data directory's usual place.
141141 #[must_use]
142142 pub fn at_data_dir() -> Self {
143- Self::at(
144- paths::data_dir()
145- .join("openagents")
146- .join(ROUTE_JOURNAL_FILE),
147- )
143+ Self::at(Self::data_dir_path())
144+ }
145+
146+ /// Where the durable journal lives.
147+ ///
148+ /// Exposed so the *caller* can choose a different path — a stateless run
149+ /// must not write sessions nobody started into the record an operator
150+ /// reads. That choice is made in `Agent::server` rather than here, because
151+ /// reading the environment inside this file is what
152+ /// `the_routing_law_has_no_clock_no_randomness_and_no_hash_order` forbids:
153+ /// the router's exit is that the same inputs give the same route, and an
154+ /// environment read is not an input. The check caught this.
155+ #[must_use]
156+ pub fn data_dir_path() -> PathBuf {
157+ paths::data_dir()
158+ .join("openagents")
159+ .join(ROUTE_JOURNAL_FILE)
148160 }
149161
150162 /// The journal at an explicit path. Loads what is already there.
@@ -385,19 +397,79 @@ impl OmegaAgentConnection {
385397
386398 /// Pin an executor for a session that already exists.
387399 ///
388- /// Called from a visible control a person operates. Nothing model-facing
389- /// reaches this, and nothing here starts a run: a pin decides *where* the
390- /// next turn of an existing thread goes, and the Full Auto Start button
391- /// remains the only path to engine-lane run authority.
392- pub fn pin_session(&self, session_id: &acp::SessionId, pin: ExecutorPin) {
393- self.pins.borrow_mut().insert(session_id.0.to_string(), pin);
400+ /// The `gesture` argument is the guard, not a label. A pin is the only way
401+ /// a thread reaches an engine lane, an engine lane is Full Auto authority,
402+ /// and owner gate 8 admits only an explicit human action into that
403+ /// authority. Requiring a [`PinGesture`] means there is no way to set a pin
404+ /// without naming the human gesture that set it, and `PinGesture` has no
405+ /// variant for a tool call, a slash command, a restored draft, or a
406+ /// composer mode flag. Nothing here starts a run: a pin decides *where* the
407+ /// next turn of a thread goes, and the Full Auto Start button remains the
408+ /// only path to engine-lane run authority.
409+ /// Returns the decision the new pin produced, which is what the thread
410+ /// then discloses — including a pin that could not be honoured, with the
411+ /// typed reason it could not.
412+ pub fn pin_session(
413+ &self,
414+ session_id: &acp::SessionId,
415+ pin: ExecutorPin,
416+ gesture: PinGesture,
417+ ) -> RouteDecision {
418+ log::info!(
419+ "OMEGA-DELTA-0035: session {} pinned to {} by {}",
420+ session_id.0,
421+ pin.token(),
422+ gesture.token()
423+ );
424+ self.pins
425+ .borrow_mut()
426+ .insert(session_id.0.to_string(), pin.clone());
427+ // Re-decide *because a person changed the pin*, and only then. Capacity
428+ // moving underneath a thread never re-decides it — `executor_for` reads
429+ // the record — so this is the one thing that can move a live thread,
430+ // and it is a human gesture by construction.
431+ self.decide(session_id.0.as_ref(), Some(pin))
432+ }
433+
434+ /// Clear a session's pin, so its next turn takes the unpinned default.
435+ ///
436+ /// Re-decides for the same reason [`pin_session`](Self::pin_session) does:
437+ /// a cleared pin that left the old decision standing would show an
438+ /// executor the user had just unpinned.
439+ pub fn unpin_session(&self, session_id: &acp::SessionId, gesture: PinGesture) -> RouteDecision {
440+ log::info!(
441+ "OMEGA-DELTA-0035: session {} unpinned by {}",
442+ session_id.0,
443+ gesture.token()
444+ );
445+ self.pins.borrow_mut().remove(&session_id.0.to_string());
446+ self.decide(session_id.0.as_ref(), None)
394447 }
395448
396449 /// Pin the executor the next session created through this router will use.
397- pub fn pin_next_session(&self, pin: Option<ExecutorPin>) {
450+ ///
451+ /// Carries a [`PinGesture`] for the same reason [`pin_session`] does.
452+ ///
453+ /// [`pin_session`]: Self::pin_session
454+ pub fn pin_next_session(&self, pin: Option<ExecutorPin>, gesture: PinGesture) {
455+ log::info!(
456+ "OMEGA-DELTA-0035: the next session is pinned to {} by {}",
457+ pin.as_ref().map_or("nothing".to_owned(), ExecutorPin::token),
458+ gesture.token()
459+ );
398460 *self.next_pin.borrow_mut() = pin;
399461 }
400462
463+ /// Every pin currently in force, in session order. For an inspector.
464+ #[must_use]
465+ pub fn pins(&self) -> Vec<(String, ExecutorPin)> {
466+ self.pins
467+ .borrow()
468+ .iter()
469+ .map(|(session_id, pin)| (session_id.clone(), pin.clone()))
470+ .collect()
471+ }
472+
401473 /// The pin currently in force for a session.
402474 #[must_use]
403475 pub fn pin(&self, session_id: &acp::SessionId) -> Option<ExecutorPin> {
@@ -630,6 +702,197 @@ impl AgentConnection for OmegaAgentConnection {
630702 }
631703 }
632704
705+// -------------------------------------------------------------------------
706+// Wiring: the router is what the native agent entry resolves to
707+// -------------------------------------------------------------------------
708+
709+thread_local! {
710+ /// `OMEGA-DELTA-0035`. The router this window's native agent entry built.
711+ ///
712+ /// A render cannot reach an `Rc<dyn AgentConnection>` held inside the
713+ /// connection store's async entry, and the pin control has to live on the
714+ /// thread's own disclosure line — so the router publishes itself here when
715+ /// it is built, exactly as `omega_host_bridge` publishes its lane index and
716+ /// as [`RECORDED_ROUTES`] publishes route reasons.
717+ ///
718+ /// A `thread_local` rather than a `static` because `Rc` is not `Sync`, and
719+ /// because the only reader is the GPUI main thread that built it. It is a
720+ /// *handle*, not state: every decision, pin and journal entry lives on the
721+ /// connection itself.
722+ ///
723+ /// **Weak, deliberately.** A strong reference here would keep the native
724+ /// agent entity alive for the life of the process, which is a leak in
725+ /// production and a hard failure in the GPUI test harness — it checks for
726+ /// leaked entity handles at teardown and caught exactly this. Weak also
727+ /// gives the right semantics: when the connection store drops the
728+ /// connection there is no router, and `active_router` says so instead of
729+ /// handing out a router nothing is dispatching through.
730+ static ACTIVE_ROUTER: RefCell<Weak<OmegaAgentConnection>> =
731+ const { RefCell::new(Weak::new()) };
732+}
733+
734+fn publish_active_router(router: &Rc<OmegaAgentConnection>) {
735+ ACTIVE_ROUTER.with(|active| *active.borrow_mut() = Rc::downgrade(router));
736+}
737+
738+/// Omega Agent's router, if this window has built one.
739+///
740+/// `None` before the native agent connects, and in any process that never
741+/// connects it. A caller that gets `None` must not invent a route — the
742+/// disclosure says "not routed", which is the honest reading.
743+#[must_use]
744+pub fn active_router() -> Option<Rc<OmegaAgentConnection>> {
745+ ACTIVE_ROUTER.with(|active| active.borrow().upgrade())
746+}
747+
748+/// The native agent, behind Omega Agent's router. `OMEGA-DELTA-0035`.
749+///
750+/// omega#78 put `OmegaAgentConnection` at the `AgentConnection` seam and left
751+/// it unwired: nothing constructed one, so every thread disclosed
752+/// `route: None` and the journal stayed empty. This is the wire. Omega's
753+/// native-agent entry resolves to a router over the native connection instead
754+/// of to the native connection itself, so every new native session is routed
755+/// on purpose and the decision is written down before the turn exists.
756+///
757+/// The thread that comes back still carries the **executor's** connection,
758+/// because `OmegaAgentConnection::new_session` delegates and returns what the
759+/// executor built. That is what keeps omega#77's disclosure honest and what
760+/// keeps every existing `downcast::<NativeAgentConnection>()` on a *thread's*
761+/// connection working unchanged.
762+pub struct OmegaRouterServer {
763+ /// Held by value rather than behind an `Rc`, because `AgentServer` is
764+ /// `Send` and an `Rc` field would take that away.
765+ native: agent::NativeAgentServer,
766+ /// Where decisions are written down. Chosen by the caller; see
767+ /// [`RouteJournal::data_dir_path`].
768+ journal_path: PathBuf,
769+}
770+
771+impl OmegaRouterServer {
772+ /// A router server over the native agent server, journalling to `journal_path`.
773+ #[must_use]
774+ pub fn new(native: agent::NativeAgentServer, journal_path: PathBuf) -> Self {
775+ Self {
776+ native,
777+ journal_path,
778+ }
779+ }
780+
781+ /// The native agent server underneath.
782+ #[must_use]
783+ pub fn native(&self) -> &agent::NativeAgentServer {
784+ &self.native
785+ }
786+}
787+
788+impl agent_servers::AgentServer for OmegaRouterServer {
789+ fn agent_id(&self) -> project::AgentId {
790+ self.native.agent_id()
791+ }
792+
793+ fn logo(&self) -> ui::IconName {
794+ self.native.logo()
795+ }
796+
797+ fn connect(
798+ &self,
799+ delegate: agent_servers::AgentServerDelegate,
800+ project: Entity<Project>,
801+ cx: &mut App,
802+ ) -> Task<Result<Rc<dyn AgentConnection>>> {
803+ let native = self.native.connect(delegate, project, cx);
804+ let journal_path = self.journal_path.clone();
805+ cx.spawn(async move |_cx| {
806+ let native = native.await?;
807+ let router = Rc::new(OmegaAgentConnection::new(
808+ native,
809+ RouteJournal::at(journal_path),
810+ ));
811+ publish_active_router(&router);
812+ Ok(router as Rc<dyn AgentConnection>)
813+ })
814+ }
815+
816+ fn into_any(self: Rc<Self>) -> Rc<dyn Any> {
817+ self
818+ }
819+
820+ fn default_mode(&self, cx: &App) -> Option<acp::SessionModeId> {
821+ self.native.default_mode(cx)
822+ }
823+
824+ fn set_default_mode(
825+ &self,
826+ mode_id: Option<acp::SessionModeId>,
827+ fs: std::sync::Arc<dyn fs::Fs>,
828+ cx: &mut App,
829+ ) {
830+ self.native.set_default_mode(mode_id, fs, cx);
831+ }
832+
833+ fn default_config_option(
834+ &self,
835+ config_id: &str,
836+ cx: &App,
837+ ) -> Option<settings::AgentConfigOptionValue> {
838+ self.native.default_config_option(config_id, cx)
839+ }
840+
841+ fn set_default_config_option(
842+ &self,
843+ config_id: &str,
844+ value: Option<settings::AgentConfigOptionValue>,
845+ fs: std::sync::Arc<dyn fs::Fs>,
846+ cx: &mut App,
847+ ) {
848+ self.native
849+ .set_default_config_option(config_id, value, fs, cx);
850+ }
851+
852+ // `favorite_config_option_value_ids` and
853+ // `toggle_favorite_config_option_value` are deliberately *not* forwarded.
854+ // `NativeAgentServer` does not override either, so forwarding would be a
855+ // no-op against the trait default — and the getter's return type names
856+ // `HashSet`, which `the_routing_law_has_no_clock_no_randomness_and_no_hash_order`
857+ // forbids in this file. A no-op override is not worth spending that check
858+ // on. If the native server ever overrides one, forward it then, with a
859+ // type alias that does not name hash iteration order.
860+}
861+
862+/// Whether a server is Omega's native agent, wrapped or bare.
863+///
864+/// Every `downcast::<NativeAgentServer>()` that asked "is this the first-party
865+/// agent?" has to go through here now, because the answer is yes for a router
866+/// over it. Missing one of these is not a compile error — it is a silently
867+/// wrong `false`, which is why `omega_deltas` counts the bare downcasts.
868+#[must_use]
869+pub fn is_native_agent_server(server: &Rc<dyn agent_servers::AgentServer>) -> bool {
870+ server.clone().downcast::<agent::NativeAgentServer>().is_some()
871+ || server.clone().downcast::<OmegaRouterServer>().is_some()
872+}
873+
874+/// The native connection behind a connection, unwrapping the router.
875+///
876+/// The connection the *store* holds is the router; the connection a *thread*
877+/// holds is the executor. Callers that reach for the native agent through the
878+/// store need this; callers that already have a thread's connection do not,
879+/// and are deliberately left alone.
880+#[must_use]
881+pub fn native_connection(
882+ connection: &Rc<dyn AgentConnection>,
883+) -> Option<Rc<agent::NativeAgentConnection>> {
884+ if let Some(native) = connection
885+ .clone()
886+ .downcast::<agent::NativeAgentConnection>()
887+ {
888+ return Some(native);
889+ }
890+ connection
891+ .clone()
892+ .downcast::<OmegaAgentConnection>()
893+ .and_then(|router| router.native.clone().downcast::<agent::NativeAgentConnection>())
894+}
895+
633896 #[cfg(test)]
634897 mod tests {
635898 use super::*;
@@ -906,4 +1169,126 @@ mod tests {
9061169 "one session must leave one record, not one per decision"
9071170 );
9081171 }
1172+
1173+ /// `OMEGA-DELTA-0035`. A human pin moves a live thread, and the record says
1174+ /// why it landed where it did.
1175+ ///
1176+ /// The pin control would be decoration without this: `executor_for` reads
1177+ /// the *recorded* decision so a turn cannot drift between executors on its
1178+ /// own, which means setting a pin has to re-decide or it changes nothing a
1179+ /// turn can see. Falsified by making `pin_session` insert the pin without
1180+ /// re-deciding: the executor stays native and this fails.
1181+ #[test]
1182+ fn a_human_pin_moves_a_live_thread_and_records_why() {
1183+ let directory = tempfile::tempdir().expect("temporary directory");
1184+ let router = OmegaAgentConnection::new(stub("omega-agent"), journal_in(&directory))
1185+ .with_external_acp(stub("codex-acp"));
1186+ // Its own session id; see `an_unhonourable_pin_is_kept_and_explained`.
1187+ let session = acp::SessionId::new("s-human-pin");
1188+
1189+ assert_eq!(
1190+ router.decide(session.0.as_ref(), None).reason,
1191+ RouteReason::UnpinnedDefault
1192+ );
1193+ assert_eq!(
1194+ router.executor_for(&session).agent_id(),
1195+ AgentId::new("omega-agent")
1196+ );
1197+
1198+ let decision = router.pin_session(
1199+ &session,
1200+ ExecutorPin::new(ExecutorClass::ExternalAcp),
1201+ PinGesture::ExecutorPinMenuItem,
1202+ );
1203+ assert_eq!(decision.reason, RouteReason::PinHonored);
1204+ assert_eq!(
1205+ router.executor_for(&session).agent_id(),
1206+ AgentId::new("codex-acp"),
1207+ "a pin a person set must move the turn, or the control is decoration"
1208+ );
1209+ assert_eq!(
1210+ router.journal().decision(session.0.as_ref()).map(|d| d.chosen),
1211+ Some(ExecutorClass::ExternalAcp)
1212+ );
1213+ }
1214+
1215+ /// `OMEGA-DELTA-0035`. An unhonourable pin is kept, and its reason is
1216+ /// sayable on the thread's own line.
1217+ ///
1218+ /// This is the case the rendered proof photographs: no engine is running,
1219+ /// so an engine-lane pin falls closed to the native loop. The pin is *not*
1220+ /// forgotten — an unhonoured pin the record dropped is indistinguishable
1221+ /// from no pin at all.
1222+ #[test]
1223+ fn an_unhonourable_pin_is_kept_and_explained() {
1224+ let directory = tempfile::tempdir().expect("temporary directory");
1225+ let router = OmegaAgentConnection::new(stub("omega-agent"), journal_in(&directory));
1226+ // A session id no other test uses. `RECORDED_ROUTES` is a
1227+ // process-wide projection keyed by session id, and the tests in this
1228+ // module run in one process: a shared `"s"` made this assertion read
1229+ // another test's decision, which it caught on the first run. Real
1230+ // session ids are minted per session, so this is a harness concern and
1231+ // not a production one.
1232+ let session = acp::SessionId::new("s-unhonourable-pin");
1233+ router.observe_capacity(Err(EngineUnreachable::NotRunning));
1234+
1235+ let decision = router.pin_session(
1236+ &session,
1237+ ExecutorPin::new(ExecutorClass::EngineLane),
1238+ PinGesture::ExecutorPinMenuItem,
1239+ );
1240+ assert_eq!(decision.chosen, ExecutorClass::NativeLoop);
1241+ assert!(decision.reason.is_fallback());
1242+ assert_eq!(
1243+ decision.pin.as_ref().map(ExecutorPin::token).as_deref(),
1244+ Some("engine_lane")
1245+ );
1246+ assert!(decision.reason.phrase().contains("fell back to the native loop"));
1247+ assert_eq!(
1248+ recorded_route(&session),
1249+ Some(decision.reason),
1250+ "the thread's disclosure line reads this index, so a reason the \
1251+ journal holds and the index does not is a fallback the user \
1252+ cannot see"
1253+ );
1254+
1255+ let cleared = router.unpin_session(&session, PinGesture::ExecutorPinCleared);
1256+ assert_eq!(cleared.reason, RouteReason::UnpinnedDefault);
1257+ assert!(cleared.pin.is_none());
1258+ assert!(router.pin(&session).is_none());
1259+ }
1260+
1261+ /// `OMEGA-DELTA-0035`. A bare downcast through the router misses the
1262+ /// native loop — which is the whole reason `native_connection` exists.
1263+ ///
1264+ /// Five call sites downcast a connection to `NativeAgentConnection`. Three
1265+ /// hold a *thread's* connection, which is the executor's and unaffected;
1266+ /// the ones that hold the *store's* connection now hold the router, and a
1267+ /// bare downcast there returns `None` — a silently wrong "this is not the
1268+ /// native agent" rather than a compile error.
1269+ #[test]
1270+ fn a_bare_downcast_through_the_router_misses_the_native_loop() {
1271+ let directory = tempfile::tempdir().expect("temporary directory");
1272+ let native = stub("omega-agent");
1273+ let bare: Rc<dyn AgentConnection> = native.clone();
1274+ assert!(
1275+ native_connection(&bare).is_none(),
1276+ "the stub is not a NativeAgentConnection, so this only shows the \
1277+ helper does not answer yes to anything"
1278+ );
1279+
1280+ let router: Rc<dyn AgentConnection> =
1281+ Rc::new(OmegaAgentConnection::new(native, journal_in(&directory)));
1282+ assert!(
1283+ router
1284+ .clone()
1285+ .downcast::<agent::NativeAgentConnection>()
1286+ .is_none(),
1287+ "a bare downcast through the router must not find the native loop; \
1288+ if it did, this check would prove nothing about the unwrapping \
1289+ helper"
1290+ );
1291+ assert_eq!(router.agent_id(), AgentId::new("omega-agent"));
1292+ }
1293+
9091294 }
diff --git a/crates/omega_deltas/src/omega_deltas.rs b/crates/omega_deltas/src/omega_deltas.rs
index a1ccc0144b..50046f8ee9 100644
--- a/crates/omega_deltas/src/omega_deltas.rs
+++ b/crates/omega_deltas/src/omega_deltas.rs
@@ -61,6 +61,8 @@ pub const ENFORCED_DELTAS: &[&str] = &[
6161 "OMEGA-DELTA-0031",
6262 "OMEGA-DELTA-0032",
6363 "OMEGA-DELTA-0033",
64+ "OMEGA-DELTA-0034",
65+ "OMEGA-DELTA-0035",
6466 ];
6567
6668 /// OMEGA-DELTA-0025. The file that declares the measured digest.
@@ -235,6 +237,47 @@ pub const ROUTE_DECISION_PATH: &str = "crates/omega_front_door/src/router.rs";
235237 /// OMEGA-DELTA-0029. The dispatch half of the router.
236238 pub const ROUTER_DISPATCH_PATH: &str = "crates/agent_ui/src/omega_router.rs";
237239
240+/// OMEGA-DELTA-0034. The panel that owns the front door.
241+pub const AGENT_PANEL_PATH: &str = "crates/agent_ui/src/agent_panel.rs";
242+
243+/// OMEGA-DELTA-0034. Front-door entry points that must work with no project.
244+///
245+/// Each of these stood between a fresh install and a composer. The check is on
246+/// the *function body*, not the file, because a `has_open_project` line
247+/// elsewhere in a six-thousand-line panel is fine and one of these is not.
248+pub const PROJECT_OPTIONAL_FRONT_DOOR_FNS: &[&str] = &[
249+ "activate_new_thread",
250+ "activate_draft",
251+ "new_thread",
252+ "ensure_native_agent_connection",
253+ "toggle_new_thread_menu",
254+];
255+
256+/// OMEGA-DELTA-0034. Paths that must keep requiring a project.
257+///
258+/// Removing the guard from these would not be project-optional threads; it
259+/// would be threads that fail later and less legibly. Asserted in the opposite
260+/// direction from `PROJECT_OPTIONAL_FRONT_DOOR_FNS` so a blanket deletion of
261+/// every guard fails as loudly as restoring one.
262+pub const PROJECT_REQUIRED_FNS: &[&str] = &[
263+ "restore_new_draft",
264+ "new_external_agent_thread",
265+ "refresh_skills",
266+ "load_thread_from_clipboard",
267+ "initialize_from_source_workspace_if_needed",
268+ // Not `should_create_terminal_for_new_entry`: it asks `supports_terminal`,
269+ // which is where the requirement lives. `project.supports_terminal` is
270+ // `true` for any local project, worktree or not, so the check has to be on
271+ // the panel's wrapper.
272+ "supports_terminal",
273+];
274+
275+/// OMEGA-DELTA-0035. Where the native agent entry is built.
276+pub const AGENT_SERVER_FACTORY_PATH: &str = "crates/agent_ui/src/agent_ui.rs";
277+
278+/// OMEGA-DELTA-0035. The pin-setting methods that must name a human gesture.
279+pub const PIN_SETTING_CALLS: &[&str] = &["pin_session(", "unpin_session(", "pin_next_session("];
280+
238281 /// OMEGA-DELTA-0029. Vocabulary that would make a route irreproducible.
239282 ///
240283 /// The packet's exit is a *deterministic* router, and determinism is not a
@@ -371,6 +414,17 @@ pub const REQUIRED_KEYMAP_BINDINGS: &[RequiredKeymapBinding] = &[
371414 action: "workroom::OpenPanel",
372415 declared_in: "crates/zed_actions/src/lib.rs",
373416 },
417+ // OMEGA-DELTA-0034. `cmd-?` is macOS's reserved Help chord, so the agent
418+ // panel's toggle was bound to a keystroke Omega cannot win. Checked here
419+ // rather than only moved, because a rebase that restores upstream's
420+ // `cmd-?` line would otherwise be invisible.
421+ RequiredKeymapBinding {
422+ delta: "OMEGA-DELTA-0034",
423+ keymap: "assets/keymaps/default-macos.json",
424+ keystroke: "ctrl-cmd-a",
425+ action: "agent::ToggleFocus",
426+ declared_in: "crates/zed_actions/src/lib.rs",
427+ },
374428 RequiredKeymapBinding {
375429 delta: "OMEGA-DELTA-0015",
376430 keymap: "assets/keymaps/default-linux.json",
@@ -559,7 +613,15 @@ pub fn default_setting<'a>(
559613 pub fn declared_actions(source: &str) -> std::collections::BTreeSet<String> {
560614 let stripped: String = source
561615 .lines()
562- .filter(|line| !line.trim_start().starts_with("//"))
616+ // Attribute lines are dropped as well as comments. An `#[action(...)]`
617+ // attribute may carry a bracketed list — `deprecated_aliases = [..]` —
618+ // and the `]` that closes it would otherwise end the `actions!` body
619+ // early, hiding every action declared after it. `agent::ToggleFocus` was
620+ // exactly that: declared, and invisible to this parser.
621+ .filter(|line| {
622+ let line = line.trim_start();
623+ !line.starts_with("//") && !line.starts_with("#[")
624+ })
563625 .collect::<Vec<_>>()
564626 .join("\n");
565627
@@ -3653,7 +3715,7 @@ mod tests {
36533715 let floor = prose[key].as_u64().unwrap_or(u64::MAX) as usize;
36543716 assert!(
36553717 actual >= floor,
3656- "OMEGA-DELTA-0031: only {actual} {label} were read, below the \
3718+ "OMEGA-DELTA-0034: only {actual} {label} were read, below the \
36573719 {floor} floor in {BRAND_GATE_POLICY_PATH}. That parser broke, \
36583720 and a check that reads nothing reports green about nothing."
36593721 );
@@ -3664,21 +3726,21 @@ mod tests {
36643726 .expect("prose.classified is an object");
36653727 assert!(
36663728 classified.len() >= 20,
3667- "OMEGA-DELTA-0031: only {} prose literals are classified, so this \
3729+ "OMEGA-DELTA-0034: only {} prose literals are classified, so this \
36683730 registry is not a record of anybody having read the tree",
36693731 classified.len()
36703732 );
36713733 for (text, entry) in classified {
36723734 assert!(
36733735 entry["class"].is_string() && entry["reason"].is_string(),
3674- "OMEGA-DELTA-0031: prose.classified[{text:?}] needs a class and \
3736+ "OMEGA-DELTA-0034: prose.classified[{text:?}] needs a class and \
36753737 a reason. An entry is a record that somebody read the sentence \
36763738 and decided it names somebody else rather than us."
36773739 );
36783740 let class = entry["class"].as_str().unwrap_or_default();
36793741 assert!(
36803742 prose["classes"].get(class).is_some(),
3681- "OMEGA-DELTA-0031: prose.classified[{text:?}] uses class \
3743+ "OMEGA-DELTA-0034: prose.classified[{text:?}] uses class \
36823744 {class:?}, which prose.classes does not define"
36833745 );
36843746 }
@@ -3692,7 +3754,7 @@ mod tests {
36923754 for relative in &corpus {
36933755 assert!(
36943756 repository_path(relative).is_file(),
3695- "OMEGA-DELTA-0031: exempt corpus file {relative} does not exist, \
3757+ "OMEGA-DELTA-0034: exempt corpus file {relative} does not exist, \
36963758 so the exemption is hiding nothing and should be deleted"
36973759 );
36983760 }
@@ -3716,7 +3778,7 @@ mod tests {
37163778 unclassified.dedup();
37173779 assert!(
37183780 unclassified.is_empty(),
3719- "OMEGA-DELTA-0031: brand-bearing prose that nobody has classified. \
3781+ "OMEGA-DELTA-0034: brand-bearing prose that nobody has classified. \
37203782 Substitute our own name: if the sentence stays true with 'Omega' \
37213783 in it, the brand was standing where our product's name belongs, so \
37223784 rewrite it. If it becomes false, it names somebody else's product \
@@ -3738,7 +3800,7 @@ mod tests {
37383800 stale.sort();
37393801 assert!(
37403802 stale.is_empty(),
3741- "OMEGA-DELTA-0031: these classified literals are no longer anywhere \
3803+ "OMEGA-DELTA-0034: these classified literals are no longer anywhere \
37423804 in the tree. Either the sentence went and the entry should go with \
37433805 it, or a scanner stopped reading the stream that used to find it — \
37443806 which is what this assertion exists to catch.\n{}",
@@ -4596,6 +4658,268 @@ mod tests {
45964658 );
45974659 }
45984660
4661+ /// Read one `fn` body out of a Rust source file, by brace depth.
4662+ ///
4663+ /// A `contains` over the whole file cannot answer "does *this* function
4664+ /// still refuse a projectless window", because the panel legitimately
4665+ /// checks `has_open_project` in a dozen other places. Depth counting is
4666+ /// crude but honest: it stops at the closing brace of the function it
4667+ /// started on, and the test asserts it found a plausible body rather than
4668+ /// an empty string, so a parse that silently matched nothing fails instead
4669+ /// of passing.
4670+ fn function_body<'a>(source: &'a str, name: &str) -> Option<&'a str> {
4671+ let needle = format!(" fn {name}(");
4672+ let start = source.find(&needle)? + needle.len();
4673+ let open = source[start..].find('{')? + start;
4674+ let mut depth = 0usize;
4675+ for (offset, character) in source[open..].char_indices() {
4676+ match character {
4677+ '{' => depth += 1,
4678+ '}' => {
4679+ depth -= 1;
4680+ if depth == 0 {
4681+ return Some(&source[open..open + offset]);
4682+ }
4683+ }
4684+ _ => {}
4685+ }
4686+ }
4687+ None
4688+ }
4689+
4690+ /// OMEGA-DELTA-0034. The front door works with no project open.
4691+ ///
4692+ /// Checked in both directions. Upstream's guard restored on a front-door
4693+ /// path is the omega#76 defect coming back — a fresh install lands on the
4694+ /// agent and has no composer to type into. A guard *removed* from a
4695+ /// workspace-touching path is the opposite mistake: a terminal with no
4696+ /// working directory, or a clipboard thread with nowhere to put its files,
4697+ /// failing later and less legibly than a refusal would have.
4698+ #[test]
4699+ fn the_front_door_does_not_require_an_open_project() {
4700+ let path = repository_path(AGENT_PANEL_PATH);
4701+ let source = std::fs::read_to_string(&path)
4702+ .unwrap_or_else(|error| panic!("cannot read {}: {error}", path.display()));
4703+
4704+ for name in PROJECT_OPTIONAL_FRONT_DOOR_FNS {
4705+ let body = function_body(&source, name).unwrap_or_else(|| {
4706+ panic!(
4707+ "OMEGA-DELTA-0034: {} no longer has a `{name}` to check. \
4708+ If it was renamed, rename it here too; a check that \
4709+ cannot find its subject passes for the wrong reason.",
4710+ path.display()
4711+ )
4712+ });
4713+ assert!(
4714+ !body.contains("has_open_project"),
4715+ "OMEGA-DELTA-0034: `{name}` in {} refuses a window with no \
4716+ project again. A window with nothing to restore is by \
4717+ definition a window with no project, so this is omega#76's \
4718+ exit failing: the front door opens and there is no composer \
4719+ to type into.",
4720+ path.display()
4721+ );
4722+ }
4723+
4724+ for name in PROJECT_REQUIRED_FNS {
4725+ let body = function_body(&source, name).unwrap_or_else(|| {
4726+ panic!(
4727+ "OMEGA-DELTA-0034: {} no longer has a `{name}` to check.",
4728+ path.display()
4729+ )
4730+ });
4731+ assert!(
4732+ body.contains("has_open_project"),
4733+ "OMEGA-DELTA-0034: `{name}` in {} stopped requiring an open \
4734+ project. Project-optional *threads* is the delta; a terminal \
4735+ with no working directory, a resumed draft with no worktree, \
4736+ or a clipboard import with nowhere to land is not.",
4737+ path.display()
4738+ );
4739+ }
4740+ }
4741+
4742+ /// OMEGA-DELTA-0035. The router is what the native agent entry resolves to.
4743+ ///
4744+ /// omega#78 shipped the router unwired, which is the failure this guards:
4745+ /// every piece present, tested, and reachable by nobody. Three facts, and
4746+ /// each one alone is weak — the server could be built and never connected,
4747+ /// the poll could exist and feed nothing, the pin could exist and be
4748+ /// unreachable.
4749+ #[test]
4750+ fn the_router_is_wired_into_the_native_agent_entry() {
4751+ let factory_path = repository_path(AGENT_SERVER_FACTORY_PATH);
4752+ let factory = std::fs::read_to_string(&factory_path)
4753+ .unwrap_or_else(|error| panic!("cannot read {}: {error}", factory_path.display()));
4754+ assert!(
4755+ factory.contains("omega_router::OmegaRouterServer::new("),
4756+ "OMEGA-DELTA-0035: {} builds the native agent server directly \
4757+ again, so nothing constructs an OmegaAgentConnection and every \
4758+ thread discloses `route: None` with an empty journal — the state \
4759+ omega#78 shipped in.",
4760+ factory_path.display()
4761+ );
4762+
4763+ let panel_path = repository_path(AGENT_PANEL_PATH);
4764+ let panel = std::fs::read_to_string(&panel_path)
4765+ .unwrap_or_else(|error| panic!("cannot read {}: {error}", panel_path.display()));
4766+ assert!(
4767+ panel.contains("observe_capacity(Ok(capacity))")
4768+ && panel.contains("get_capacity()"),
4769+ "OMEGA-DELTA-0035: {} no longer feeds the engine's framed \
4770+ get_capacity answer into the router. Without it every engine-lane \
4771+ pin is decided against a default of \"not running\" whatever \
4772+ omega-effectd is actually doing.",
4773+ panel_path.display()
4774+ );
4775+
4776+ let disclosure_path = repository_path(THREAD_VIEW_PATH);
4777+ let disclosure = std::fs::read_to_string(&disclosure_path)
4778+ .unwrap_or_else(|error| panic!("cannot read {}: {error}", disclosure_path.display()));
4779+ assert!(
4780+ disclosure.contains("\"omega-executor-pin\""),
4781+ "OMEGA-DELTA-0035: {} no longer renders the executor pin control. \
4782+ A pin is the only way a thread reaches anything but the native \
4783+ loop, so with no control there is no route to honour.",
4784+ disclosure_path.display()
4785+ );
4786+ }
4787+
4788+ /// OMEGA-DELTA-0035, and owner gate 8 behind it.
4789+ ///
4790+ /// A pin is the only door to an engine lane and an engine lane *is* Full
4791+ /// Auto authority, so the gate reaches the pin as directly as it reaches
4792+ /// the Start button. The guard is the argument type: every pin-setting call
4793+ /// must pass a literal `PinGesture::` variant, so a caller cannot launder
4794+ /// a gesture it was handed by something model-facing.
4795+ ///
4796+ /// Scanned across every crate rather than the one file, because the failure
4797+ /// worth catching is a *new* caller somewhere else.
4798+ #[test]
4799+ fn only_a_named_human_gesture_can_pin_an_executor() {
4800+ let crates = repository_path("crates");
4801+ let mut calls: Vec<(String, String)> = Vec::new();
4802+ for_each_source_file(&crates, &["rs"], |path, source| {
4803+ let display = path
4804+ .display()
4805+ .to_string()
4806+ .rsplit("crates/")
4807+ .next()
4808+ .unwrap_or_default()
4809+ .to_owned();
4810+ // This file is the check itself; matching its own needles would
4811+ // make the count meaningless.
4812+ if display.starts_with("omega_deltas/") {
4813+ return;
4814+ }
4815+ for (index, line) in source.lines().enumerate() {
4816+ let trimmed = line.trim();
4817+ // The declarations are not calls. `fn pin_session(` and a
4818+ // doc-comment mentioning it are both skipped here so the scan
4819+ // counts callers only.
4820+ if trimmed.starts_with("//")
4821+ || trimmed.starts_with("pub fn ")
4822+ || trimmed.starts_with("fn ")
4823+ {
4824+ continue;
4825+ }
4826+ if !PIN_SETTING_CALLS.iter().any(|call| trimmed.contains(call)) {
4827+ continue;
4828+ }
4829+ // Reassemble the whole call, because rustfmt puts the arguments
4830+ // of a nested call on their own lines. Walking forward to the
4831+ // line whose parens balance is what makes this a check on the
4832+ // *argument list* rather than on one line of it — the earlier
4833+ // draft accepted any line ending in `(` or `,`, which passed
4834+ // for every multi-line call regardless of its arguments.
4835+ let mut statement = String::new();
4836+ let mut depth = 0i32;
4837+ for following in source.lines().skip(index) {
4838+ statement.push_str(following.trim());
4839+ statement.push(' ');
4840+ for character in following.chars() {
4841+ match character {
4842+ '(' => depth += 1,
4843+ ')' => depth -= 1,
4844+ _ => {}
4845+ }
4846+ }
4847+ if depth <= 0 {
4848+ break;
4849+ }
4850+ }
4851+ calls.push((display.clone(), statement));
4852+ }
4853+ });
4854+
4855+ assert!(
4856+ !calls.is_empty(),
4857+ "OMEGA-DELTA-0035: no pin-setting call was found anywhere. Either \
4858+ the pin control is gone or this scan stopped matching, and both \
4859+ make the gate vacuous."
4860+ );
4861+
4862+ for (file, statement) in &calls {
4863+ assert!(
4864+ statement.contains("PinGesture::"),
4865+ "OMEGA-DELTA-0035: {file} sets a pin without naming a literal \
4866+ PinGesture: {statement:?}. Owner gate 8 admits only an \
4867+ explicit human action into Full Auto authority, a pin is the \
4868+ only door to an engine lane, and a gesture the caller was \
4869+ *handed* is exactly the laundering the literal forbids."
4870+ );
4871+ }
4872+ }
4873+
4874+ /// OMEGA-DELTA-0035. Nothing asks "is this the native agent?" with a bare
4875+ /// downcast.
4876+ ///
4877+ /// The first-party agent is a router over the native server now. A bare
4878+ /// `downcast::<NativeAgentServer>()` on it returns `None`, which reads as
4879+ /// "this is an external agent" — a silently wrong `false`, not a compile
4880+ /// error. The two call sites that ask this question go through
4881+ /// `is_native_agent_server`, and this fails if a third appears.
4882+ #[test]
4883+ fn nothing_asks_for_the_native_agent_with_a_bare_downcast() {
4884+ let crates = repository_path("crates");
4885+ let mut bare: Vec<(String, String)> = Vec::new();
4886+ for_each_source_file(&crates, &["rs"], |path, source| {
4887+ let display = path
4888+ .display()
4889+ .to_string()
4890+ .rsplit("crates/")
4891+ .next()
4892+ .unwrap_or_default()
4893+ .to_owned();
4894+ if display.starts_with("omega_deltas/")
4895+ // The unwrapping helpers are where the bare downcast belongs.
4896+ || display.starts_with("agent_ui/src/omega_router.rs")
4897+ // omega#77's disclosure classifies a *thread's* connection,
4898+ // which is the executor's and never the router's.
4899+ || display.starts_with("agent_ui/src/omega_executor_disclosure.rs")
4900+ {
4901+ return;
4902+ }
4903+ for line in source.lines() {
4904+ let line = line.trim();
4905+ if line.contains("downcast::<agent::NativeAgentServer>()")
4906+ || line.contains("downcast::<NativeAgentServer>()")
4907+ {
4908+ bare.push((display.clone(), line.to_owned()));
4909+ }
4910+ }
4911+ });
4912+ assert!(
4913+ bare.is_empty(),
4914+ "OMEGA-DELTA-0035: {} place(s) still ask for the native agent \
4915+ server with a bare downcast: {bare:#?}. The native agent is \
4916+ wrapped by the router, so this answers `false` for the \
4917+ first-party agent instead of failing to compile. Use \
4918+ `omega_router::is_native_agent_server`.",
4919+ bare.len()
4920+ );
4921+ }
4922+
45994923 /// The parser has to actually strip comments, or every check reads `None`
46004924 /// and silently passes.
46014925 #[test]
@@ -4630,13 +4954,13 @@ mod tests {
46304954 for token in SEND_LAW_EXECUTOR_TOKENS {
46314955 assert!(
46324956 source.contains(&format!("ExecutorClass::{token}")),
4633- "OMEGA-DELTA-0032: {} does not answer for ExecutorClass::{token}. A class the law does not name has no declared behaviour, which is the state this delta replaces.",
4957+ "OMEGA-DELTA-0035: {} does not answer for ExecutorClass::{token}. A class the law does not name has no declared behaviour, which is the state this delta replaces.",
46344958 path.display()
46354959 );
46364960 }
46374961 assert!(
46384962 source.contains("pub const fn disposition("),
4639- "OMEGA-DELTA-0032: the law must be a const fn of its inputs. A disposition that can read anything else is not reproducible from a journal."
4963+ "OMEGA-DELTA-0035: the law must be a const fn of its inputs. A disposition that can read anything else is not reproducible from a journal."
46404964 );
46414965 }
46424966
@@ -4658,7 +4982,7 @@ mod tests {
46584982 for (why, token) in NON_DETERMINISTIC_QUEUE_TOKENS {
46594983 assert!(
46604984 !production.contains(token),
4661- "OMEGA-DELTA-0032: {} reads {why} (`{token}`). The queue is replayed from a journal, and a decision that depends on {why} does not replay.",
4985+ "OMEGA-DELTA-0035: {} reads {why} (`{token}`). The queue is replayed from a journal, and a decision that depends on {why} does not replay.",
46624986 path.display()
46634987 );
46644988 }
@@ -4679,16 +5003,16 @@ mod tests {
46795003 let source = std::fs::read_to_string(&path).expect("the composer is readable");
46805004 assert!(
46815005 source.contains("omega_front_door::disposition("),
4682- "OMEGA-DELTA-0032: {} no longer calls the send law. Deciding a mid-turn send in the view is the upstream behaviour this delta replaces.",
5006+ "OMEGA-DELTA-0035: {} no longer calls the send law. Deciding a mid-turn send in the view is the upstream behaviour this delta replaces.",
46835007 path.display()
46845008 );
46855009 assert!(
46865010 source.contains("SendDisposition::SteerAtMessageBoundary"),
4687- "OMEGA-DELTA-0032: the boundary flag must be set from the law's own answer, not from a steer flag the other two classes never see."
5011+ "OMEGA-DELTA-0035: the boundary flag must be set from the law's own answer, not from a steer flag the other two classes never see."
46885012 );
46895013 assert!(
46905014 source.contains(".reaches_running_turn()"),
4691- "OMEGA-DELTA-0032: {} must gate its cancel on whether this executor's declared answer reaches the running turn. An unconditional cancel turns a refused steer into an interrupted turn.",
5015+ "OMEGA-DELTA-0035: {} must gate its cancel on whether this executor's declared answer reaches the running turn. An unconditional cancel turns a refused steer into an interrupted turn.",
46925016 path.display()
46935017 );
46945018 }
@@ -4706,16 +5030,16 @@ mod tests {
47065030 let source = std::fs::read_to_string(&path).expect("the journal is readable");
47075031 assert!(
47085032 source.contains("openagents.omega.agent_send_queue.v1"),
4709- "OMEGA-DELTA-0032: the durable queue must carry a schema, so a foreign document is refused rather than adopted."
5033+ "OMEGA-DELTA-0035: the durable queue must carry a schema, so a foreign document is refused rather than adopted."
47105034 );
47115035 assert!(
47125036 source.contains("std::fs::rename(&temporary, &self.path)"),
4713- "OMEGA-DELTA-0032: {} must rewrite atomically. A crash mid-write must leave the previous queue, not a truncated one.",
5037+ "OMEGA-DELTA-0035: {} must rewrite atomically. A crash mid-write must leave the previous queue, not a truncated one.",
47145038 path.display()
47155039 );
47165040 assert!(
47175041 !source.contains("Entity<MessageEditor>"),
4718- "OMEGA-DELTA-0032: a live GPUI handle cannot be a durable fact. That is exactly what made the upstream queue renderer-only."
5042+ "OMEGA-DELTA-0035: a live GPUI handle cannot be a durable fact. That is exactly what made the upstream queue renderer-only."
47195043 );
47205044 }
47215045
diff --git a/crates/omega_front_door/src/omega_front_door.rs b/crates/omega_front_door/src/omega_front_door.rs
index 60bb91940c..bf9aa46583 100644
--- a/crates/omega_front_door/src/omega_front_door.rs
+++ b/crates/omega_front_door/src/omega_front_door.rs
@@ -297,6 +297,55 @@ pub const fn origin_starts_a_run(_origin: LaunchOrigin) -> bool {
297297 false
298298 }
299299
300+// -------------------------------------------------------------------------
301+// Who may pin an executor
302+// -------------------------------------------------------------------------
303+
304+/// Every gesture that may set a thread's executor pin.
305+///
306+/// A pin is the only way a thread reaches anything but the native loop, and
307+/// [`ExecutorClass::EngineLane`] *is* Full Auto authority. So owner gate 8 —
308+/// *no model-initiated path can start Full Auto authority; only an explicit
309+/// human action can* — reaches the pin as directly as it reaches the Start
310+/// button. omega#76 rejected a composer mode flag for Full Auto because a
311+/// boolean the send path reads can be set by a slash command, a restored
312+/// draft, or a model-authored insertion. A pin is the same construct wearing a
313+/// different name, and it gets the same treatment.
314+///
315+/// The mechanism is a *required argument*, not a convention. `pin_session` and
316+/// `pin_next_session` in `crates/agent_ui/src/omega_router.rs` take a
317+/// `PinGesture`, so there is no way to set a pin without naming the gesture
318+/// that set it, and `omega_deltas` asserts every call site passes a literal
319+/// variant rather than a value it was handed. A tool call, a slash command, a
320+/// restored draft, an agent turn, and a composer mode flag each have no
321+/// variant here, and `pin_gestures_are_all_human_gestures` fails if one
322+/// appears.
323+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
324+pub enum PinGesture {
325+ /// A click on an entry of the executor pin menu, on the thread's own
326+ /// disclosure line.
327+ ExecutorPinMenuItem,
328+ /// A click on the "unpin" entry of the same menu, clearing the pin.
329+ ExecutorPinCleared,
330+}
331+
332+impl PinGesture {
333+ /// Every admitted gesture, in declaration order.
334+ #[must_use]
335+ pub const fn all() -> &'static [Self] {
336+ &[Self::ExecutorPinMenuItem, Self::ExecutorPinCleared]
337+ }
338+
339+ /// The stable token this gesture is logged under.
340+ #[must_use]
341+ pub const fn token(self) -> &'static str {
342+ match self {
343+ Self::ExecutorPinMenuItem => "executor_pin_menu_item",
344+ Self::ExecutorPinCleared => "executor_pin_cleared",
345+ }
346+ }
347+}
348+
300349 // -------------------------------------------------------------------------
301350 // The affordance ledger
302351 // -------------------------------------------------------------------------
@@ -739,6 +788,25 @@ mod tests {
739788 );
740789 }
741790
791+ /// Owner gate 8, at the pin. An engine lane is Full Auto authority and a
792+ /// pin is the only door to one, so the set of gestures that may set a pin
793+ /// is closed for the same reason the set of launch origins is.
794+ #[test]
795+ fn pin_gestures_are_all_human_gestures() {
796+ let tokens: Vec<&str> = PinGesture::all().iter().map(|g| g.token()).collect();
797+ assert_eq!(
798+ tokens,
799+ ["executor_pin_menu_item", "executor_pin_cleared"],
800+ "every executor pin gesture must be a visible control a person \
801+ operates. A tool call, a slash command, a restored draft, an \
802+ agent turn, or a composer mode flag is not one — and a pin \
803+ reaches engine-lane authority, so admitting one of those here is \
804+ owner gate 8 broken through a door nobody flagged. If you are \
805+ adding a gesture, prove it is a human one before you edit this \
806+ list."
807+ );
808+ }
809+
742810 /// Reaching the launch surface is not starting a run.
743811 #[test]
744812 fn no_origin_starts_a_run_by_itself() {
diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml
index 17355e2c9b..5c81e1877e 100644
--- a/crates/zed/Cargo.toml
+++ b/crates/zed/Cargo.toml
@@ -103,6 +103,7 @@ debugger_ui.workspace = true
103103 agent_computer_ui.workspace = true
104104 workroom_ui.workspace = true
105105 omega_effectd.workspace = true
106+omega_front_door.workspace = true
106107 dev_container.workspace = true
107108 diagnostics.workspace = true
108109 editor.workspace = true
diff --git a/crates/zed/src/visual_test_runner.rs b/crates/zed/src/visual_test_runner.rs
index d5b89ed8ac..180bb44395 100644
--- a/crates/zed/src/visual_test_runner.rs
+++ b/crates/zed/src/visual_test_runner.rs
@@ -405,6 +405,38 @@ fn run_visual_tests(project_path: PathBuf, update_baseline: bool) -> Result<()>
405405 let mut failed = 0;
406406 let mut updated = 0;
407407
408+ // `OMEGA_VISUAL_ONLY=1` runs Omega's own suite and nothing else.
409+ //
410+ // This runner has no committed baselines for any of the inherited Zed
411+ // tests, so a plain run reports "Baseline not found" for every one of them
412+ // and Omega's result is buried in the noise. Generating baselines for all
413+ // of them would be committing a large set of images nobody has looked at,
414+ // which is the opposite of the point. So the Omega suite gets its own
415+ // invocation, `script/omega-visual-proof`, and the inherited tests keep the
416+ // status they already had: present, and not standing up.
417+ #[cfg(feature = "visual-tests")]
418+ if std::env::var("OMEGA_VISUAL_ONLY").is_ok() {
419+ println!("\n--- Omega: front door, executor disclosure, route pin ---");
420+ let outcome = run_omega_agent_visual_tests(app_state.clone(), &mut cx, update_baseline);
421+ // The shared window this function opened above is torn down here as
422+ // well as at the end of the full run. Returning early without it left
423+ // the sample project's buffers alive, GPUI's leaked-handle check
424+ // panicked at drop, and a green suite exited 101 — a script reporting
425+ // failure on a run where every capture matched.
426+ teardown_shared_window(workspace_window, &mut cx);
427+ return match outcome {
428+ Ok(TestResult::Passed) => {
429+ println!("\u{2713} omega_agent_surfaces: PASSED");
430+ Ok(())
431+ }
432+ Ok(TestResult::BaselineUpdated(_)) => {
433+ println!("\u{2713} omega_agent_surfaces: Baselines updated");
434+ Ok(())
435+ }
436+ Err(error) => Err(error.context("omega_agent_surfaces")),
437+ };
438+ }
439+
408440 // Run Test 1: Project Panel (with project panel visible)
409441 println!("\n--- Test 1: project_panel ---");
410442 match run_visual_test(
@@ -510,6 +542,28 @@ fn run_visual_tests(project_path: PathBuf, update_baseline: bool) -> Result<()>
510542 }
511543 }
512544
545+ // Omega's own rendered proofs: the front door with no project, typing
546+ // starting a thread, the executor line on three thread kinds, and a pin
547+ // that could not be honoured. omega#76, #77, #78.
548+ #[cfg(feature = "visual-tests")]
549+ {
550+ println!("\n--- Omega: front door, executor disclosure, route pin ---");
551+ match run_omega_agent_visual_tests(app_state.clone(), &mut cx, update_baseline) {
552+ Ok(TestResult::Passed) => {
553+ println!("\u{2713} omega_agent_surfaces: PASSED");
554+ passed += 1;
555+ }
556+ Ok(TestResult::BaselineUpdated(_)) => {
557+ println!("\u{2713} omega_agent_surfaces: Baselines updated");
558+ updated += 1;
559+ }
560+ Err(e) => {
561+ eprintln!("\u{2717} omega_agent_surfaces: FAILED - {}", e);
562+ failed += 1;
563+ }
564+ }
565+ }
566+
513567 // Run Test 5: Agent Thread View tests
514568 #[cfg(feature = "visual-tests")]
515569 {
@@ -656,35 +710,7 @@ fn run_visual_tests(project_path: PathBuf, update_baseline: bool) -> Result<()>
656710
657711 // Clean up the main workspace's worktree to stop background scanning tasks
658712 // This prevents "root path could not be canonicalized" errors when main() drops temp_dir
659- workspace_window
660- .update(&mut cx, |workspace, _window, cx| {
661- let project = workspace.project().clone();
662- project.update(cx, |project, cx| {
663- let worktree_ids: Vec<_> =
664- project.worktrees(cx).map(|wt| wt.read(cx).id()).collect();
665- for id in worktree_ids {
666- project.remove_worktree(id, cx);
667- }
668- });
669- })
670- .log_err();
671-
672- cx.run_until_parked();
673-
674- // Close the main window
675- cx.update_window(workspace_window.into(), |_, window, _cx| {
676- window.remove_window();
677- })
678- .log_err();
679-
680- // Run until all cleanup tasks complete
681- cx.run_until_parked();
682-
683- // Give background tasks time to finish, including scrollbar hide timers (1 second)
684- for _ in 0..15 {
685- cx.advance_clock(Duration::from_millis(100));
686- cx.run_until_parked();
687- }
713+ teardown_shared_window(workspace_window, &mut cx);
688714
689715 // Print summary
690716 println!("\n=== Test Summary ===");
@@ -785,6 +811,46 @@ fn run_visual_test(
785811 }
786812 }
787813
814+/// Tear down the shared workspace window this runner opens for the inherited
815+/// tests.
816+///
817+/// Extracted so the `OMEGA_VISUAL_ONLY` early return runs it too. GPUI checks
818+/// for leaked entity handles when the app drops, and skipping this left the
819+/// sample project's buffers alive — a green suite that exited 101.
820+#[cfg(target_os = "macos")]
821+fn teardown_shared_window(
822+ workspace_window: WindowHandle<Workspace>,
823+ cx: &mut VisualTestAppContext,
824+) {
825+ workspace_window
826+ .update(cx, |workspace, _window, cx| {
827+ let project = workspace.project().clone();
828+ project.update(cx, |project, cx| {
829+ let worktree_ids: Vec<_> =
830+ project.worktrees(cx).map(|wt| wt.read(cx).id()).collect();
831+ for id in worktree_ids {
832+ project.remove_worktree(id, cx);
833+ }
834+ });
835+ })
836+ .log_err();
837+
838+ cx.run_until_parked();
839+
840+ cx.update_window(workspace_window.into(), |_, window, _cx| {
841+ window.remove_window();
842+ })
843+ .log_err();
844+
845+ cx.run_until_parked();
846+
847+ // Background tasks, including scrollbar hide timers (1 second).
848+ for _ in 0..15 {
849+ cx.advance_clock(Duration::from_millis(100));
850+ cx.run_until_parked();
851+ }
852+}
853+
788854 #[cfg(target_os = "macos")]
789855 fn get_baseline_path(test_name: &str) -> PathBuf {
790856 // Get the workspace root (where Cargo.toml is)
@@ -1993,6 +2059,474 @@ import { AiPaneTabContext } from 'context';
19932059 Ok(result)
19942060 }
19952061
2062+/// Omega's own rendered proofs. `OMEGA-DELTA-0034`, `OMEGA-DELTA-0035`,
2063+/// omega#76, omega#77 and omega#78.
2064+///
2065+/// # Why this exists at all
2066+///
2067+/// omega#76, #77 and #78 each landed with a source-level suite and no rendered
2068+/// evidence, and each said so plainly. Three lanes in a row reported "no check
2069+/// here looks at a rendered pixel". That gap is what this function closes: it
2070+/// draws the real widget tree through Metal and writes the frame to a PNG, so
2071+/// a claim about what the user sees is answered by a picture rather than by a
2072+/// `contains` over a source file.
2073+///
2074+/// # Why it is in-process
2075+///
2076+/// The obvious alternative — drive the packaged app and screenshot the window
2077+/// — has a hazard that has already bitten this workspace: macOS routes
2078+/// synthesized keystrokes to the **frontmost application**, not to the process
2079+/// named on the command line, so a harness can type into somebody else's
2080+/// window and record the reply as the app's. `VisualTestAppContext` dispatches
2081+/// keystrokes into this process's own GPUI window and captures that window's
2082+/// texture directly, so there is no frontmost app to get wrong and no other
2083+/// window that can answer.
2084+///
2085+/// # The threshold, stated
2086+///
2087+/// Comparison is `MATCH_THRESHOLD` (0.99): at least 99% of pixels must match
2088+/// the committed baseline. Exact equality is not usable even here — font
2089+/// rasterisation and theme colour rounding differ by a pixel or two between
2090+/// machines — and a threshold nobody states is worse than a loose one.
2091+/// Baselines were generated on Apple Silicon with the Metal renderer; this is
2092+/// a local gate, and a materially different GPU may need `UPDATE_BASELINE=1`
2093+/// and a human looking at the result before trusting it.
2094+#[cfg(all(target_os = "macos", feature = "visual-tests"))]
2095+fn run_omega_agent_visual_tests(
2096+ app_state: Arc<AppState>,
2097+ cx: &mut VisualTestAppContext,
2098+ update_baseline: bool,
2099+) -> Result<TestResult> {
2100+ // The window is torn down whatever happens. Without this, an early `Err`
2101+ // leaves the panel's editor buffer alive, GPUI's leaked-handle check panics
2102+ // at drop, and the panic *replaces* the error that actually explains the
2103+ // failure — which is exactly how the first run of this test reported
2104+ // "Visual tests panicked" and nothing else.
2105+ let outcome = run_omega_agent_visual_tests_inner(app_state, cx, update_baseline);
2106+ cx.run_until_parked();
2107+ outcome
2108+}
2109+
2110+#[cfg(all(target_os = "macos", feature = "visual-tests"))]
2111+fn run_omega_agent_visual_tests_inner(
2112+ app_state: Arc<AppState>,
2113+ cx: &mut VisualTestAppContext,
2114+ update_baseline: bool,
2115+) -> Result<TestResult> {
2116+ use agent_ui::AgentPanel;
2117+
2118+ // A project with **no worktree**. This is the whole point of the first two
2119+ // captures: a window with nothing to restore is by definition a window with
2120+ // no project, so this is the state a fresh install actually reaches.
2121+ let project = cx.update(|cx| {
2122+ project::Project::local(
2123+ app_state.client.clone(),
2124+ app_state.node_runtime.clone(),
2125+ app_state.user_store.clone(),
2126+ app_state.languages.clone(),
2127+ app_state.fs.clone(),
2128+ None,
2129+ project::LocalProjectFlags {
2130+ init_worktree_trust: false,
2131+ ..Default::default()
2132+ },
2133+ cx,
2134+ )
2135+ });
2136+
2137+ let window_size = size(px(900.0), px(720.0));
2138+ let bounds = Bounds {
2139+ origin: point(px(0.0), px(0.0)),
2140+ size: window_size,
2141+ };
2142+ let workspace_window: WindowHandle<Workspace> = cx
2143+ .update(|cx| {
2144+ cx.open_window(
2145+ WindowOptions {
2146+ window_bounds: Some(WindowBounds::Windowed(bounds)),
2147+ focus: false,
2148+ show: false,
2149+ ..Default::default()
2150+ },
2151+ |window, cx| {
2152+ cx.new(|cx| Workspace::new(None, project.clone(), app_state.clone(), window, cx))
2153+ },
2154+ )
2155+ })
2156+ .context("Failed to open the Omega front door window")?;
2157+
2158+ cx.run_until_parked();
2159+
2160+ // The window really has no project. Asserted rather than assumed, because
2161+ // every claim these captures make rests on it — a capture taken with a
2162+ // worktree quietly present would prove the opposite of what it says.
2163+ let visible_worktrees = workspace_window
2164+ .update(cx, |workspace, _window, cx| {
2165+ workspace.project().read(cx).visible_worktrees(cx).count()
2166+ })
2167+ .context("Failed to read the project's worktrees")?;
2168+ anyhow::ensure!(
2169+ visible_worktrees == 0,
2170+ "the front door capture needs a projectless window; found {visible_worktrees} worktree(s)"
2171+ );
2172+
2173+ let (weak_workspace, async_window_cx) = workspace_window
2174+ .update(cx, |workspace, window, cx| {
2175+ (workspace.weak_handle(), window.to_async(cx))
2176+ })
2177+ .context("Failed to get workspace handle")?;
2178+
2179+ cx.background_executor.allow_parking();
2180+ let panel = cx
2181+ .foreground_executor
2182+ .block_test(AgentPanel::load(weak_workspace, async_window_cx))
2183+ .context("Failed to load AgentPanel")?;
2184+ cx.background_executor.forbid_parking();
2185+
2186+ workspace_window
2187+ .update(cx, |workspace, window, cx| {
2188+ workspace.add_panel(panel.clone(), window, cx);
2189+ workspace.open_panel::<AgentPanel>(window, cx);
2190+ })
2191+ .context("Failed to add the agent panel")?;
2192+
2193+ cx.run_until_parked();
2194+
2195+ // The shipped front door, called the way `crates/zed/src/main.rs` calls it
2196+ // on a window with nothing to restore. Not a hand-rolled approximation of
2197+ // it: `open_front_door` is the entry `OMEGA-DELTA-0019` added, and driving
2198+ // anything else here would photograph a path no user takes.
2199+ workspace_window
2200+ .update(cx, |_workspace, window, cx| {
2201+ AgentPanel::open_front_door(window, cx);
2202+ })
2203+ .context("Failed to open the front door")?;
2204+
2205+ cx.run_until_parked();
2206+
2207+ // The panel must actually be the focused, visible dock surface. A capture
2208+ // taken with the panel absent shows the launchpad and would read as a
2209+ // perfectly plausible screenshot of something else entirely — which is what
2210+ // the first run of this test produced, and how it was caught.
2211+ let panel_is_open = workspace_window
2212+ .update(cx, |workspace, _window, cx| {
2213+ workspace
2214+ .panel::<AgentPanel>(cx)
2215+ .is_some_and(|panel| panel.read(cx).active_thread_id(cx).is_some())
2216+ })
2217+ .unwrap_or(false);
2218+ anyhow::ensure!(
2219+ panel_is_open,
2220+ "the agent panel is not the open surface; the capture would show the \
2221+ launchpad rather than the front door"
2222+ );
2223+
2224+ // omega#76's exit, first half. Before this delta the panel answered a
2225+ // projectless window with "Open Project / Clone Repository" and there was
2226+ // nothing to type into.
2227+ let thread_id = cx
2228+ .read(|cx| panel.read(cx).active_thread_id(cx))
2229+ .ok_or_else(|| {
2230+ anyhow::anyhow!(
2231+ "the front door produced no thread on a projectless window — \
2232+ this is the omega#76 defect, and the capture below would show \
2233+ the empty-project state"
2234+ )
2235+ })?;
2236+
2237+ let front_door = run_visual_test(
2238+ "omega_front_door_no_project",
2239+ workspace_window.into(),
2240+ cx,
2241+ update_baseline,
2242+ )?;
2243+
2244+ // omega#76's exit, second half: **typing starts a real thread**. The
2245+ // keystrokes go into this window through GPUI's own dispatch, so nothing
2246+ // depends on which application macOS thinks is frontmost.
2247+ cx.simulate_input(workspace_window.into(), "route this thread on purpose");
2248+ cx.run_until_parked();
2249+
2250+ let typed = cx
2251+ .read(|cx| {
2252+ panel
2253+ .read(cx)
2254+ .active_thread_view_for_tests()
2255+ .and_then(|conversation| conversation.read(cx).root_thread_view())
2256+ .map(|view| view.read(cx).message_editor.read(cx).text(cx))
2257+ })
2258+ .unwrap_or_default();
2259+ anyhow::ensure!(
2260+ typed.contains("route this thread on purpose"),
2261+ "typing on the front door did not reach the thread's composer; the \
2262+ editor holds {typed:?}"
2263+ );
2264+
2265+ let typing = run_visual_test(
2266+ "omega_front_door_typing",
2267+ workspace_window.into(),
2268+ cx,
2269+ update_baseline,
2270+ )?;
2271+
2272+ // Zoom the panel for the disclosure captures. The executor line is long by
2273+ // design — class, agent, model, run, and route reason — and a dock-width
2274+ // capture truncates it with an ellipsis, which would make a picture of a
2275+ // *truncated* line the evidence that the line renders.
2276+ cx.update_window(workspace_window.into(), |_, window, cx| {
2277+ panel.update(cx, |panel, cx| {
2278+ use workspace::dock::Panel as _;
2279+ panel.set_zoomed(true, window, cx);
2280+ });
2281+ })
2282+ .log_err();
2283+ cx.run_until_parked();
2284+
2285+ // omega#77: the executor line, on the native thread the front door just
2286+ // built. This thread was routed by `OmegaAgentConnection`, so its line also
2287+ // carries omega#78's route reason.
2288+ let native_line = cx
2289+ .read(|cx| omega_executor_line(&panel, cx))
2290+ .ok_or_else(|| anyhow::anyhow!("the native thread has no executor disclosure"))?;
2291+ anyhow::ensure!(
2292+ native_line.starts_with("native_loop"),
2293+ "the front door's own thread must disclose the native loop, not {native_line:?}"
2294+ );
2295+ anyhow::ensure!(
2296+ native_line.contains("routed:"),
2297+ "omega#78's wiring is not reaching the rendered line: {native_line:?}"
2298+ );
2299+ println!(" native executor line: {native_line}");
2300+
2301+ let native_disclosure = run_visual_test(
2302+ "omega_executor_disclosure_native",
2303+ workspace_window.into(),
2304+ cx,
2305+ update_baseline,
2306+ )?;
2307+
2308+ // omega#78, first exit clause: **a pinned executor is always honoured.**
2309+ // The native loop is the one executor this build has registered on the
2310+ // router, so it is the one honourable pin — and pinning it is not a no-op:
2311+ // the line changes from `routed: unpinned` to `routed: pinned`, which is
2312+ // the difference between "nobody chose this" and "a person did".
2313+ let session_for_pin = cx
2314+ .read(|cx| {
2315+ panel
2316+ .read(cx)
2317+ .active_thread_view_for_tests()
2318+ .and_then(|conversation| conversation.read(cx).root_thread_view())
2319+ .map(|view| view.read(cx).thread.read(cx).session_id().clone())
2320+ })
2321+ .ok_or_else(|| anyhow::anyhow!("no session to pin"))?;
2322+ let router_for_pin = agent_ui::omega_router::active_router().ok_or_else(|| {
2323+ anyhow::anyhow!(
2324+ "no router was built for the native agent entry — omega#78's \
2325+ wiring is the thing under test and it is absent"
2326+ )
2327+ })?;
2328+ let honoured = router_for_pin.pin_session(
2329+ &session_for_pin,
2330+ omega_front_door::ExecutorPin::new(omega_front_door::ExecutorClass::NativeLoop),
2331+ omega_front_door::PinGesture::ExecutorPinMenuItem,
2332+ );
2333+ anyhow::ensure!(
2334+ honoured.reason == omega_front_door::RouteReason::PinHonored,
2335+ "a pin to the fail-closed target must be honoured, not {:?}",
2336+ honoured.reason
2337+ );
2338+ cx.update_window(workspace_window.into(), |_, window, _cx| {
2339+ window.refresh();
2340+ })?;
2341+ cx.run_until_parked();
2342+
2343+ let honoured_line = cx
2344+ .read(|cx| omega_executor_line(&panel, cx))
2345+ .ok_or_else(|| anyhow::anyhow!("the pinned thread has no executor disclosure"))?;
2346+ anyhow::ensure!(
2347+ honoured_line.contains("routed: pinned"),
2348+ "an honoured pin must say so on the thread's own line: {honoured_line:?}"
2349+ );
2350+ println!(" honoured-pin executor line: {honoured_line}");
2351+
2352+ let pin_honoured = run_visual_test(
2353+ "omega_route_pin_honoured",
2354+ workspace_window.into(),
2355+ cx,
2356+ update_baseline,
2357+ )?;
2358+
2359+ // omega#78: a pin that cannot be honoured, rendered. No engine is running
2360+ // under this harness, so an engine-lane pin falls closed to the native loop
2361+ // and the line has to say so — a fallback the user cannot see is the defect
2362+ // this packet exists to avoid.
2363+ let session_id = cx
2364+ .read(|cx| {
2365+ panel
2366+ .read(cx)
2367+ .active_thread_view_for_tests()
2368+ .and_then(|conversation| conversation.read(cx).root_thread_view())
2369+ .map(|view| view.read(cx).thread.read(cx).session_id().clone())
2370+ })
2371+ .ok_or_else(|| anyhow::anyhow!("no session to pin"))?;
2372+ let router = agent_ui::omega_router::active_router().ok_or_else(|| {
2373+ anyhow::anyhow!(
2374+ "no router was built for the native agent entry — omega#78's \
2375+ wiring is the thing under test and it is absent"
2376+ )
2377+ })?;
2378+ let decision = router.pin_session(
2379+ &session_id,
2380+ omega_front_door::ExecutorPin::new(omega_front_door::ExecutorClass::EngineLane),
2381+ omega_front_door::PinGesture::ExecutorPinMenuItem,
2382+ );
2383+ anyhow::ensure!(
2384+ decision.reason.is_fallback(),
2385+ "an engine-lane pin with no engine must fall back, not report {:?}",
2386+ decision.reason
2387+ );
2388+
2389+ cx.update_window(workspace_window.into(), |_, window, _cx| {
2390+ window.refresh();
2391+ })?;
2392+ cx.run_until_parked();
2393+
2394+ let pinned_line = cx
2395+ .read(|cx| omega_executor_line(&panel, cx))
2396+ .ok_or_else(|| anyhow::anyhow!("the pinned thread has no executor disclosure"))?;
2397+ anyhow::ensure!(
2398+ pinned_line.contains("fell back to the native loop"),
2399+ "the unhonoured pin is not visible on the thread's line: {pinned_line:?}"
2400+ );
2401+ println!(" unhonoured-pin executor line: {pinned_line}");
2402+
2403+ let pin_fallback = run_visual_test(
2404+ "omega_route_pin_not_honoured",
2405+ workspace_window.into(),
2406+ cx,
2407+ update_baseline,
2408+ )?;
2409+
2410+ // omega#77: the external-ACP kind, on a second thread in the same panel.
2411+ let stub: Rc<dyn AgentServer> = Rc::new(StubAgentServer::new(StubAgentConnection::new()));
2412+ cx.update_window(workspace_window.into(), |_, window, cx| {
2413+ panel.update(cx, |panel, cx| {
2414+ panel.open_external_thread_with_server(stub.clone(), window, cx);
2415+ });
2416+ })?;
2417+ cx.run_until_parked();
2418+
2419+ let external_line = cx
2420+ .read(|cx| omega_executor_line(&panel, cx))
2421+ .ok_or_else(|| anyhow::anyhow!("the external thread has no executor disclosure"))?;
2422+ anyhow::ensure!(
2423+ external_line.starts_with("external_acp"),
2424+ "a thread on a connection Omega did not build must not be disclosed as \
2425+ first-party output: {external_line:?}"
2426+ );
2427+ println!(" external-acp executor line: {external_line}");
2428+
2429+ let external_disclosure = run_visual_test(
2430+ "omega_executor_disclosure_external_acp",
2431+ workspace_window.into(),
2432+ cx,
2433+ update_baseline,
2434+ )?;
2435+
2436+ // omega#77: the engine-lane kind, on that same external thread.
2437+ //
2438+ // Deliberately *this* thread and not the front door's own. A lane run is a
2439+ // `codex-acp` process the host bridge drives, so the honest record is "this
2440+ // run delegated to this agent" — and it is a thread the router did not
2441+ // route, so its `route` part is absent. Publishing a lane run onto a
2442+ // *routed* thread instead would produce a record `is_coherent` rejects: a
2443+ // route reason that says the router fell back to the native loop, on a line
2444+ // that claims an engine lane. The coherence assertions below are what
2445+ // caught that while this test was being written.
2446+ let external_thread_id = cx
2447+ .read(|cx| panel.read(cx).active_thread_id(cx))
2448+ .ok_or_else(|| anyhow::anyhow!("the external thread has no id"))?;
2449+ anyhow::ensure!(
2450+ external_thread_id != thread_id,
2451+ "the external thread must be a second thread, not the front door's own"
2452+ );
2453+ agent_ui::omega_host_bridge::publish_engine_lane_run_for_tests(
2454+ external_thread_id,
2455+ "operation.full-auto.visual".to_string(),
2456+ );
2457+ cx.update_window(workspace_window.into(), |_, window, _cx| {
2458+ window.refresh();
2459+ })?;
2460+ cx.run_until_parked();
2461+
2462+ let lane_line = cx
2463+ .read(|cx| omega_executor_line(&panel, cx))
2464+ .ok_or_else(|| anyhow::anyhow!("the lane thread has no executor disclosure"))?;
2465+ anyhow::ensure!(
2466+ lane_line.starts_with("engine_lane") && lane_line.contains("operation.full-auto.visual"),
2467+ "a thread bound to a lane run must disclose the run: {lane_line:?}"
2468+ );
2469+ println!(" engine-lane executor line: {lane_line}");
2470+
2471+ let lane_disclosure = run_visual_test(
2472+ "omega_executor_disclosure_engine_lane",
2473+ workspace_window.into(),
2474+ cx,
2475+ update_baseline,
2476+ )?;
2477+
2478+ cx.update_window(workspace_window.into(), |_, window, _cx| {
2479+ window.remove_window();
2480+ })
2481+ .log_err();
2482+ cx.run_until_parked();
2483+
2484+ // Six captures, and `run_visual_test` returns `Err` on a mismatch, so
2485+ // reaching here means every one of them matched its baseline or wrote one.
2486+ // The aggregate reports "baseline updated" if any capture wrote one, so an
2487+ // `UPDATE_BASELINE=1` run is never reported as a pass.
2488+ let results = [
2489+ front_door,
2490+ typing,
2491+ native_disclosure,
2492+ pin_honoured,
2493+ pin_fallback,
2494+ lane_disclosure,
2495+ external_disclosure,
2496+ ];
2497+ for result in &results {
2498+ if let TestResult::BaselineUpdated(path) = result {
2499+ return Ok(TestResult::BaselineUpdated(path.clone()));
2500+ }
2501+ }
2502+ Ok(TestResult::Passed)
2503+}
2504+
2505+/// The executor line the agent panel's active thread would render.
2506+///
2507+/// Read through the same `ThreadView::executor_disclosure` the render calls, so
2508+/// the assertion and the pixels cannot disagree about what the line says.
2509+#[cfg(all(target_os = "macos", feature = "visual-tests"))]
2510+fn omega_executor_line(panel: &Entity<agent_ui::AgentPanel>, cx: &App) -> Option<String> {
2511+ let disclosure = panel
2512+ .read(cx)
2513+ .active_thread_view_for_tests()?
2514+ .read(cx)
2515+ .root_thread_view()?
2516+ .read(cx)
2517+ .executor_disclosure(cx);
2518+ // Every captured line is asserted coherent, not merely non-empty. A record
2519+ // can render a perfectly readable line while claiming two contradictory
2520+ // things — an engine lane and a route reason that says the router fell back
2521+ // to the native loop is exactly that, and it is what an earlier draft of
2522+ // this test was about to photograph and call proof.
2523+ assert!(
2524+ disclosure.is_coherent(),
2525+ "the rendered executor record is incoherent: {disclosure:?}"
2526+ );
2527+ Some(disclosure.label())
2528+}
2529+
19962530 /// A stub AgentServer for visual testing that returns a pre-programmed connection.
19972531 #[derive(Clone)]
19982532 #[cfg(target_os = "macos")]
diff --git a/crates/zed/test_fixtures/visual_tests/omega_executor_disclosure_engine_lane.png b/crates/zed/test_fixtures/visual_tests/omega_executor_disclosure_engine_lane.png
new file mode 100644
index 0000000000..149a37ed5a
Binary files /dev/null and b/crates/zed/test_fixtures/visual_tests/omega_executor_disclosure_engine_lane.png differ
diff --git a/crates/zed/test_fixtures/visual_tests/omega_executor_disclosure_external_acp.png b/crates/zed/test_fixtures/visual_tests/omega_executor_disclosure_external_acp.png
new file mode 100644
index 0000000000..fa6a000cfa
Binary files /dev/null and b/crates/zed/test_fixtures/visual_tests/omega_executor_disclosure_external_acp.png differ
diff --git a/crates/zed/test_fixtures/visual_tests/omega_executor_disclosure_native.png b/crates/zed/test_fixtures/visual_tests/omega_executor_disclosure_native.png
new file mode 100644
index 0000000000..3aa253ba0c
Binary files /dev/null and b/crates/zed/test_fixtures/visual_tests/omega_executor_disclosure_native.png differ
diff --git a/crates/zed/test_fixtures/visual_tests/omega_front_door_no_project.png b/crates/zed/test_fixtures/visual_tests/omega_front_door_no_project.png
new file mode 100644
index 0000000000..05ba5c39eb
Binary files /dev/null and b/crates/zed/test_fixtures/visual_tests/omega_front_door_no_project.png differ
diff --git a/crates/zed/test_fixtures/visual_tests/omega_front_door_typing.png b/crates/zed/test_fixtures/visual_tests/omega_front_door_typing.png
new file mode 100644
index 0000000000..54cb099f64
Binary files /dev/null and b/crates/zed/test_fixtures/visual_tests/omega_front_door_typing.png differ
diff --git a/crates/zed/test_fixtures/visual_tests/omega_route_pin_honoured.png b/crates/zed/test_fixtures/visual_tests/omega_route_pin_honoured.png
new file mode 100644
index 0000000000..9232844ec9
Binary files /dev/null and b/crates/zed/test_fixtures/visual_tests/omega_route_pin_honoured.png differ
diff --git a/crates/zed/test_fixtures/visual_tests/omega_route_pin_not_honoured.png b/crates/zed/test_fixtures/visual_tests/omega_route_pin_not_honoured.png
new file mode 100644
index 0000000000..e73b721be7
Binary files /dev/null and b/crates/zed/test_fixtures/visual_tests/omega_route_pin_not_honoured.png differ
diff --git a/script/omega-visual-proof b/script/omega-visual-proof
new file mode 100755
index 0000000000..a74e73e536
--- /dev/null
+++ b/script/omega-visual-proof
@@ -0,0 +1,24 @@
1+#!/usr/bin/env bash
2+#
3+# Omega's rendered proof for the agent surfaces. omega#76, #77, #78.
4+#
5+# Draws the real widget tree through Metal in this process and writes each frame
6+# to a PNG, then compares it against the committed baseline at the runner's
7+# stated threshold (99% of pixels). Nothing here synthesizes a system keystroke,
8+# so nothing here can type into another application's window by mistake.
9+#
10+# script/omega-visual-proof compare against the baselines
11+# script/omega-visual-proof --update rewrite the baselines
12+#
13+# Baselines live in crates/zed/test_fixtures/visual_tests/ and were generated on
14+# Apple Silicon with the Metal renderer. A materially different GPU may need
15+# --update and a human looking at the result before the new baselines are
16+# trusted.
17+set -euo pipefail
18+
19+if [[ "${1:-}" == "--update" ]]; then
20+ export UPDATE_BASELINE=1
21+fi
22+
23+export OMEGA_VISUAL_ONLY=1
24+exec cargo run --release -p zed --bin zed_visual_test_runner --features visual-tests