From 2c75c7b6212e60cfb5032012afa60f32eebb5b4c Mon Sep 17 00:00:00 2001 From: "lilly.luo" Date: Tue, 13 Oct 2026 23:23:20 +0000 Subject: [PATCH 1/2] Add read-only smart-router status with native ON/OFF rendering --- docs/claude-mod.md | 9 ++- skills/smart-router/SKILL.md | 13 +++- src/ucode/agents/claude_mods/constants.ts | 1 + src/ucode/agents/claude_mods/register.ts | 40 ++++++++-- src/ucode/cli.py | 32 ++++++++ src/ucode/smart_routing/session_env.py | 6 +- tests/README.md | 4 + tests/integration/README.md | 4 + .../native_claude/mod_tests/register.test.ts | 44 +++++++++++ tests/native_claude/test_mod.py | 27 +++++++ tests/test_smart_router.py | 78 ++++++++++++++++++- 11 files changed, 246 insertions(+), 12 deletions(-) diff --git a/docs/claude-mod.md b/docs/claude-mod.md index eff1837f8..8b08ca476 100644 --- a/docs/claude-mod.md +++ b/docs/claude-mod.md @@ -17,13 +17,20 @@ can list what the module reads and writes, and `plugin validate` rejects a varia position: "$.env.get takes a literal name as its first argument". Keep those names inline. The mod merges the launch recipe into the `Databricks-Smart-Router-Recipe` header -via `ANTHROPIC_CUSTOM_HEADERS` and intercepts the existing skill's `/smart-router on|off` command locally, without inference or command +via `ANTHROPIC_CUSTOM_HEADERS` and intercepts the existing skill's `/smart-router on|off|status` command locally, without inference or command registration. Off sets every routing flag to `0` and the recipe to `DISABLED`; on restores the original flags and recipe, including originally absent variables. Unrelated headers are preserved, and `CLAUDE_CODE_EXTRA_BODY` is left untouched. Duplicate recipe headers are replaced case-insensitively. Recipe line breaks are rejected. A failed write attempts to restore prior values and reports failure; the native environment API does not provide an atomic transaction. +`/smart-router status` reads the effective routing flags through UG's read-only +`claude --smart-routing-status` command, including the latest session overrides. +The `CommandOutput` render hook displays a green ON or gray OFF badge using native +`Box` and `Text` elements. Its text remains available in headless output, and +errors retain Claude's normal rendering. Status does not change routing or orchestration. +Codex and Claude sessions without mods use the skill's corresponding CLI command. + If a caller supplies `smart_router_recipe_name` in the request body, gateways with body-recipe parsing enabled give it precedence over the header; toggles preserve that override. diff --git a/skills/smart-router/SKILL.md b/skills/smart-router/SKILL.md index 439f8d4a8..171ab95c5 100644 --- a/skills/smart-router/SKILL.md +++ b/skills/smart-router/SKILL.md @@ -1,9 +1,10 @@ --- name: smart-router -description: Enable or disable Unity Gateway subagent model routing for the current smart-routed Claude or Codex session, along with Smart Router Orchestrator when opted in. -allowed-tools: Bash("$UCODE_SMART_ROUTER_PYTHON" -m ucode.cli claude --enable-smart-routing), Bash("$UCODE_SMART_ROUTER_PYTHON" -m ucode.cli claude --disable-smart-routing), Bash("$UCODE_SMART_ROUTER_PYTHON" -m ucode.cli codex --enable-smart-routing), Bash("$UCODE_SMART_ROUTER_PYTHON" -m ucode.cli codex --disable-smart-routing) +description: Show status, enable, or disable Unity Gateway subagent model routing for the current smart-routed Claude or Codex session, along with Smart Router Orchestrator when opted in. +argument-hint: "on|off|status" +allowed-tools: Bash("$UCODE_SMART_ROUTER_PYTHON" -m ucode.cli claude --enable-smart-routing), Bash("$UCODE_SMART_ROUTER_PYTHON" -m ucode.cli claude --disable-smart-routing), Bash("$UCODE_SMART_ROUTER_PYTHON" -m ucode.cli claude --smart-routing-status), Bash("$UCODE_SMART_ROUTER_PYTHON" -m ucode.cli codex --enable-smart-routing), Bash("$UCODE_SMART_ROUTER_PYTHON" -m ucode.cli codex --disable-smart-routing), Bash("$UCODE_SMART_ROUTER_PYTHON" -m ucode.cli codex --smart-routing-status) metadata: - version: "1.2.0" + version: "1.3.0" --- # Smart Router @@ -15,13 +16,17 @@ Keep its path quoted and do not substitute `ug` or `python` from PATH. `"$UCODE_SMART_ROUTER_PYTHON" -m ucode.cli claude --disable-smart-routing` for `off`. - In Codex, run exactly `"$UCODE_SMART_ROUTER_PYTHON" -m ucode.cli codex --enable-smart-routing` for `on` or `"$UCODE_SMART_ROUTER_PYTHON" -m ucode.cli codex --disable-smart-routing` for `off`. +- For `status`, run exactly `"$UCODE_SMART_ROUTER_PYTHON" -m ucode.cli claude --smart-routing-status` + in Claude Code or `"$UCODE_SMART_ROUTER_PYTHON" -m ucode.cli codex --smart-routing-status` in Codex. + Return the current session's on/off status without changing routing or orchestration. + Claude's native mod handles this locally and renders an ON/OFF badge; the CLI supplies text in other sessions. In PowerShell, use `& "$env:UCODE_SMART_ROUTER_PYTHON"` in place of `"$UCODE_SMART_ROUTER_PYTHON"`; keep the remaining arguments the same. If `UCODE_SMART_ROUTER_PYTHON` or `UCODE_SESSION_ENV_FILE` is unset, ask the user to restart through an updated Unity Gateway with smart routing enabled. -With no argument, explain that only `on` and `off` are accepted. Do not edit the state file. +With no argument, explain that `on`, `off`, and `status` are accepted. Do not edit the state file. This affects subsequent subagent model selection in the current session, not the root model or first prompt. In sessions launched with `ENABLE_SMART_ROUTER_ORCHESTRATOR=1`, it also controls Smart Router Orchestrator. When turned off, earlier Smart Router Orchestrator instructions diff --git a/src/ucode/agents/claude_mods/constants.ts b/src/ucode/agents/claude_mods/constants.ts index 4075ef5a1..8e41d82d7 100644 --- a/src/ucode/agents/claude_mods/constants.ts +++ b/src/ucode/agents/claude_mods/constants.ts @@ -4,3 +4,4 @@ export const CLI_MODULE = 'ucode.cli'; export const CLI_AGENT = 'claude'; export const ENABLE_ROUTING_FLAG = '--enable-smart-routing'; export const DISABLE_ROUTING_FLAG = '--disable-smart-routing'; +export const ROUTING_STATUS_FLAG = '--smart-routing-status'; diff --git a/src/ucode/agents/claude_mods/register.ts b/src/ucode/agents/claude_mods/register.ts index 3bbce7162..51b8badcd 100644 --- a/src/ucode/agents/claude_mods/register.ts +++ b/src/ucode/agents/claude_mods/register.ts @@ -6,6 +6,7 @@ import { DISABLED_RECIPE, ENABLE_ROUTING_FLAG, RECIPE_HEADER, + ROUTING_STATUS_FLAG, } from './constants.ts'; type Environment = Record; @@ -28,19 +29,19 @@ async function writeEnvironment($: EngineInterface, env: Environment): Promise { +// atomically. The mod runs that CLI for toggles and read-only status. +async function runSessionControl($: EngineInterface, flag: string): Promise { // Claude's capability scanner requires literal environment-variable names. const python = await $.env.get('UCODE_SMART_ROUTER_PYTHON'); const sessionFile = await $.env.get('UCODE_SESSION_ENV_FILE'); if (!python || !sessionFile) { throw new Error('Smart Router session controls are unavailable for this session.'); } - const flag = enabled ? ENABLE_ROUTING_FLAG : DISABLE_ROUTING_FLAG; const result = await $.process.run([python, '-m', CLI_MODULE, CLI_AGENT, flag]); if (result.exitCode !== 0) { throw new Error(`Smart Router controls exited ${result.exitCode}: ${result.stderr.trim()}`); } + return result.stdout.trim(); } async function setRouting( @@ -70,7 +71,7 @@ async function setRouting( // An explicit toggle must also replace the CLI/skill's saved session overrides, // which the routing hooks apply on top of the environment and would otherwise win. if (syncSessionControls) { - await writeSessionControls($, enabled); + await runSessionControl($, enabled ? ENABLE_ROUTING_FLAG : DISABLE_ROUTING_FLAG); } } catch (error) { // Restore earlier successful writes if a later environment write is denied. @@ -96,7 +97,15 @@ export function register(on: On, options: PluginOptions): void { // The installed smart-router skill already supplies the slash command. on('command.run', {command: 'smart-router'}, async ($, e) => { const action = e.args.trim(); - if (action !== 'on' && action !== 'off') return {text: 'Usage: /smart-router on|off'}; + if (action === 'status') { + if (!launchedEnabled) return {text: 'Smart Router is off for this session.'}; + try { + return {text: await runSessionControl($, ROUTING_STATUS_FLAG)}; + } catch { + return {text: 'Could not read Smart Router status. Check session controls and process permissions; retry or restart through UG.'}; + } + } + if (action !== 'on' && action !== 'off') return {text: 'Usage: /smart-router on|off|status'}; if (!launchedEnabled) return {text: 'Smart Router was not enabled for this session.'}; try { await setRouting($, options, action === 'on', true); @@ -108,4 +117,25 @@ export function register(on: On, options: PluginOptions): void { return {text: 'Could not update Smart Router. Check the recipe, ANTHROPIC_CUSTOM_HEADERS, and environment permissions; retry or restart through UG.'}; } }); + + on('ui.render', {component: 'CommandOutput'}, ($, e, next) => { + if (e.props.command !== 'smart-router') return next(e); + const status = /^Smart Router is (on|off) for this session\.$/.exec(e.props.text); + if (!status) return next(e); + const { Box, Text } = $.ui.resolve(e); + const enabled = status[1] === 'on'; + return Box({ + flexDirection: 'row', + columnGap: 1, + children: [ + Text({bold: true, children: ['Unity Gateway Smart Router']}), + Text({ + bold: true, + color: 'black', + backgroundColor: enabled ? 'green' : 'gray', + children: [enabled ? ' ON ' : ' OFF '], + }), + ], + }); + }); } diff --git a/src/ucode/cli.py b/src/ucode/cli.py index 5c37a064a..9ad884ac2 100644 --- a/src/ucode/cli.py +++ b/src/ucode/cli.py @@ -2614,6 +2614,20 @@ def _smart_routing_v2_flag(enabled: bool | None, *, agent: str) -> Iterator[None smart_routing_v2.restore_smart_routing_env(previous) +def _print_smart_routing_status(agent: str, enabled: bool | None) -> None: + try: + if enabled is not None: + raise RuntimeError("Use --smart-routing-status separately from routing toggle flags.") + session_env_path() + routing_enabled = smart_routing_v2.smart_routing_enabled( + effective_environment(agent=agent, strict=True), default=False, agent=agent + ) + except RuntimeError as exc: + print_err(str(exc)) + raise typer.Exit(1) from None + console.print(f"Smart Router is {'on' if routing_enabled else 'off'} for this session.") + + def _toggle_current_smart_routing_session(enabled: bool | None) -> bool: if enabled is None: return False @@ -3542,8 +3556,17 @@ def codex_cmd( help="Enable or disable AI Gateway model routing for this Codex launch or session.", ), ] = None, + smart_routing_status: Annotated[ + bool, + typer.Option( + "--smart-routing-status", help="Show whether Smart Router is on or off in this session." + ), + ] = False, ) -> None: """Launch Codex via Databricks.""" + if smart_routing_status: + _print_smart_routing_status(AGENT_CODEX, enable_smart_routing_flag) + return if _toggle_current_smart_routing_session(enable_smart_routing_flag): return try: @@ -3630,8 +3653,17 @@ def claude_cmd( help="Enable or disable AI Gateway model routing for this Claude Code launch or session.", ), ] = None, + smart_routing_status: Annotated[ + bool, + typer.Option( + "--smart-routing-status", help="Show whether Smart Router is on or off in this session." + ), + ] = False, ) -> None: """Launch Claude Code via Databricks.""" + if smart_routing_status: + _print_smart_routing_status(AGENT_CLAUDE, enable_smart_routing_flag) + return if _toggle_current_smart_routing_session(enable_smart_routing_flag): return try: diff --git a/src/ucode/smart_routing/session_env.py b/src/ucode/smart_routing/session_env.py index 1432601ef..9947f5e9f 100644 --- a/src/ucode/smart_routing/session_env.py +++ b/src/ucode/smart_routing/session_env.py @@ -52,7 +52,9 @@ def _read(path: Path) -> dict[str, str]: return _validate(json.loads(path.read_text(encoding="utf-8"))) -def effective_environment(env: Mapping[str, str] | None = None, *, agent: str) -> dict[str, str]: +def effective_environment( + env: Mapping[str, str] | None = None, *, agent: str, strict: bool = False +) -> dict[str, str]: """Overlay the latest session controls on the hook process environment.""" effective = resolve_environment(env, agent=agent) try: @@ -62,6 +64,8 @@ def effective_environment(env: Mapping[str, str] | None = None, *, agent: str) - try: effective.update(_read(path)) except (OSError, UnicodeError, ValueError) as exc: + if strict: + raise RuntimeError(f"Smart Router session controls at {path} are invalid.") from exc print( f"Smart Router could not read session controls at {path} ({exc}); " "using the inherited environment.", diff --git a/tests/README.md b/tests/README.md index 1f60034e0..5f4e2eeb3 100644 --- a/tests/README.md +++ b/tests/README.md @@ -209,6 +209,10 @@ version is not executed here. first in PATH and a launching interpreter path containing spaces. Both agents' toggles must update the session controls through the launching installation. Launch tests check that Claude settings and Codex's shell policy carry the interpreter and session marker. +Status checks cover both agents' presets and session toggles without launching an agent +or writing state, and reject invalid controls and combined status/toggle flags. +Native Claude component tests check status without inference and the ON/OFF badge trees +on terminal and desktop, with ordinary rendering preserved for errors and other commands. These are component checks; they do not establish native skill permission matching or PowerShell execution. diff --git a/tests/integration/README.md b/tests/integration/README.md index 4a28a9f4d..2e4a578e2 100644 --- a/tests/integration/README.md +++ b/tests/integration/README.md @@ -368,6 +368,10 @@ PATH conflicts for the Smart Router skill have subprocess/component coverage in `ug` first in PATH. The live journeys above do not inject a second installation or establish PowerShell command execution. +Read-only `/smart-router status` has CLI component coverage in `../test_smart_router.py` +and native command/render-tree coverage in `../native_claude/test_mod.py`, including session +overrides, ON/OFF badges, and error rendering. Live interactive status rendering is not covered. + The toggle journeys retain the legacy routing-only and orchestration cases and add each of the three subagent-only `SMART_ROUTER_CONFIG_VERSION` presets. They require only `smart-router` for routing-only cases, and both `smart-router-orchestrator` and `smart-router` when orchestration diff --git a/tests/native_claude/mod_tests/register.test.ts b/tests/native_claude/mod_tests/register.test.ts index 242b99d20..3b4ddf1cb 100644 --- a/tests/native_claude/mod_tests/register.test.ts +++ b/tests/native_claude/mod_tests/register.test.ts @@ -1,5 +1,49 @@ import { expect, test } from 'claude-code/testing'; +test('status is off when routing was not enabled at launch', async ($) => { + expect(await $.command.run({command: 'smart-router', args: 'status'})).toEqual({ + text: 'Smart Router is off for this session.', + }); +}); + +test('status output renders ON and OFF badges on both surfaces', async ($) => { + for (const surface of ['terminal', 'desktop'] as const) { + for (const enabled of [true, false]) { + const ui = await $.ui.mount({ + plugin: 'ug-smart-router', + component: 'CommandOutput', + surface, + requestId: 'router-status', + props: { + command: 'smart-router', + text: `Smart Router is ${enabled ? 'on' : 'off'} for this session.`, + }, + }); + expect(await ui.find({type: 'Text', text: 'Unity Gateway Smart Router'})).toBeDefined(); + const badge = await ui.find({type: 'Text', text: enabled ? ' ON ' : ' OFF '}); + expect(badge.props).toMatchObject({ + bold: true, color: 'black', backgroundColor: enabled ? 'green' : 'gray', + }); + await ui.unmount(); + } + } +}); + +test('other command output and status errors retain the original rendering', async ($, on) => { + on('ui.render', () => ({type: 'Text', props: {}, children: ['original output']})); + for (const props of [ + {command: 'other', text: 'Smart Router is on for this session.'}, + {command: 'smart-router', text: 'Could not read Smart Router status.'}, + ]) { + const ui = await $.ui.mount({ + plugin: 'ug-smart-router', + component: 'CommandOutput', surface: 'terminal', requestId: 'other-output', props, + }); + expect(await ui.find({type: 'Text', text: 'original output'})).toBeDefined(); + await ui.unmount(); + } +}); + test('session start forwards the event and result', async ($, on) => { const event = { surface: 'terminal', isInteractive: true, cwd: '/work' } as const; let calls = 0; diff --git a/tests/native_claude/test_mod.py b/tests/native_claude/test_mod.py index b1bc3bb10..8a00ad370 100644 --- a/tests/native_claude/test_mod.py +++ b/tests/native_claude/test_mod.py @@ -2,9 +2,11 @@ from __future__ import annotations +import json import os import shutil import subprocess +import sys from pathlib import Path import pytest @@ -55,3 +57,28 @@ def test_native_options_are_launch_local(native_binary, tmp_path): assert a.observe()["options"] == _claude_mod_launch_options(env) assert b.observe()["options"] == _claude_mod_launch_options({}) assert not (a.plugin / "hooks/launch.json").exists() + + +def test_status_reads_session_overrides_without_inference(native_binary, tmp_path): + session_file = tmp_path / "env.json" + env = { + "SMART_ROUTER_CONFIG_VERSION": config.SUBAGENT_ONLY_V0, + "UCODE_SESSION_ENV_FILE": str(session_file), + "UCODE_SMART_ROUTER_PYTHON": sys.executable, + } + with Session(native_binary, tmp_path / "status", env) as session: + for overrides, expected in [ + ({}, "on"), + ({"ENABLE_SMART_ROUTING_SUBAGENT_ONLY": "0"}, "off"), + ({}, "on"), + ]: + session_file.write_text(json.dumps(overrides)) + before = session_file.read_bytes() + result = session.send("/smart-router status") + assert f"Smart Router is {expected} for this session." in result["result"] + assert result["num_turns"] == 0 and result["usage"]["output_tokens"] == 0 + assert session_file.read_bytes() == before + session_file.write_text("invalid json") + result = session.send("/smart-router status") + assert "Could not read Smart Router status" in result["result"] + assert session_file.read_text() == "invalid json" diff --git a/tests/test_smart_router.py b/tests/test_smart_router.py index 504cf7991..dccaf4152 100644 --- a/tests/test_smart_router.py +++ b/tests/test_smart_router.py @@ -18,6 +18,65 @@ runner = CliRunner() +@pytest.mark.parametrize("agent", ["claude", "codex"]) +@pytest.mark.parametrize( + "preset", + [ + "subagent_only_v0", + "first_prompt_and_subagent_no_orch_v0", + "subagent_orch_v0_claude_only", + None, + ], +) +def test_session_status_tracks_toggles_without_writes(tmp_path, monkeypatch, agent, preset): + session_file = tmp_path / "env.json" + session_file.write_text("{}") + monkeypatch.setenv(session_env.SESSION_ENV_VAR, str(session_file)) + if preset: + monkeypatch.setenv("SMART_ROUTER_CONFIG_VERSION", preset) + monkeypatch.setattr( + cli, "_launch_tool", Mock(side_effect=AssertionError("status launched agent")) + ) + baseline = preset is not None and not ( + preset == "subagent_orch_v0_claude_only" and agent == "codex" + ) + + for toggle, enabled in [(None, baseline), ("disable", False), ("enable", baseline)]: + if toggle: + assert runner.invoke(cli.app, [agent, f"--{toggle}-smart-routing"]).exit_code == 0 + before = session_file.read_bytes() + environment = dict(os.environ) + result = runner.invoke(cli.app, [agent, "--smart-routing-status"]) + assert result.exit_code == 0, result.output + assert f"Smart Router is {'on' if enabled else 'off'} for this session." in result.output + assert session_file.read_bytes() == before + assert dict(os.environ) == environment + + +@pytest.mark.parametrize("agent", ["claude", "codex"]) +@pytest.mark.parametrize("contents", [None, "not json", '{"unknown":"1"}']) +def test_session_status_rejects_unavailable_controls(tmp_path, monkeypatch, agent, contents): + if contents is not None: + session_file = tmp_path / "env.json" + session_file.write_text(contents) + monkeypatch.setenv(session_env.SESSION_ENV_VAR, str(session_file)) + result = runner.invoke(cli.app, [agent, "--smart-routing-status"]) + assert result.exit_code == 1 + assert "Smart Router is" not in result.output + assert "session" in result.output + + +@pytest.mark.parametrize("agent", ["claude", "codex"]) +def test_session_status_rejects_combined_toggle(tmp_path, monkeypatch, agent): + session_file = tmp_path / "env.json" + session_file.write_text("{}") + monkeypatch.setenv(session_env.SESSION_ENV_VAR, str(session_file)) + result = runner.invoke(cli.app, [agent, "--smart-routing-status", "--disable-smart-routing"]) + assert result.exit_code == 1 + assert "separately" in result.output + assert session_file.read_text() == "{}" + + @pytest.fixture(autouse=True) def _checkout_skill_bundle(monkeypatch): # An editable Python install can still contain an older wheel's copied skill resources. @@ -52,10 +111,13 @@ def test_skill_toggles_with_launch_installation_despite_shadowed_path(tmp_path, other_ug.write_text("#!/bin/sh\necho 'wrong ug installation' >&2\nexit 97\n") other_ug.chmod(0o755) monkeypatch.setenv("PATH", str(shadow) + os.pathsep + os.environ.get("PATH", "")) + monkeypatch.setenv(v2.ENABLE_SUBAGENT_ROUTING_ENV_VAR, "1") session_path = v2._prepare_smart_router_session(agent) skill = config_io.APP_DIR.parent / f".{agent}/skills/{SMART_ROUTER_SKILL}/SKILL.md" - commands = re.findall(r"`([^`\n]*--(?:enable|disable)-smart-routing)`", skill.read_text()) + commands = re.findall( + r"`([^`\n]*--(?:(?:enable|disable)-smart-routing|smart-routing-status))`", skill.read_text() + ) for action in ("disable", "enable"): command = next(cmd for cmd in commands if f" {agent} --{action}-" in cmd) @@ -80,6 +142,20 @@ def test_skill_toggles_with_launch_installation_despite_shadowed_path(tmp_path, else {} ) assert json.loads(session_path.read_text()) == expected + status_command = next(cmd for cmd in commands if f" {agent} --smart-routing-status" in cmd) + before = session_path.read_bytes() + status = subprocess.run( + ["/bin/sh", "-c", status_command], + cwd=tmp_path, + env={**os.environ, "NO_COLOR": "1"}, + stdin=subprocess.DEVNULL, + capture_output=True, + text=True, + timeout=20, + ) + assert status.returncode == 0, status.stdout + status.stderr + assert f"Smart Router is {'off' if action == 'disable' else 'on'}" in status.stdout + assert session_path.read_bytes() == before def test_launcher_flags_control_routing_hook(tmp_path, monkeypatch): From 77a3bada70059166ea83b537039910b2d150a9fd Mon Sep 17 00:00:00 2001 From: "lilly.luo" Date: Tue, 13 Oct 2026 23:23:57 +0000 Subject: [PATCH 2/2] Add lightning icon to Smart Router status badge --- src/ucode/agents/claude_mods/register.ts | 2 +- tests/native_claude/mod_tests/register.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ucode/agents/claude_mods/register.ts b/src/ucode/agents/claude_mods/register.ts index 51b8badcd..48d0d8d4c 100644 --- a/src/ucode/agents/claude_mods/register.ts +++ b/src/ucode/agents/claude_mods/register.ts @@ -128,7 +128,7 @@ export function register(on: On, options: PluginOptions): void { flexDirection: 'row', columnGap: 1, children: [ - Text({bold: true, children: ['Unity Gateway Smart Router']}), + Text({bold: true, children: ['⚡ Unity Gateway Smart Router']}), Text({ bold: true, color: 'black', diff --git a/tests/native_claude/mod_tests/register.test.ts b/tests/native_claude/mod_tests/register.test.ts index 3b4ddf1cb..6a2d9b2dc 100644 --- a/tests/native_claude/mod_tests/register.test.ts +++ b/tests/native_claude/mod_tests/register.test.ts @@ -19,7 +19,7 @@ test('status output renders ON and OFF badges on both surfaces', async ($) => { text: `Smart Router is ${enabled ? 'on' : 'off'} for this session.`, }, }); - expect(await ui.find({type: 'Text', text: 'Unity Gateway Smart Router'})).toBeDefined(); + expect(await ui.find({type: 'Text', text: '⚡ Unity Gateway Smart Router'})).toBeDefined(); const badge = await ui.find({type: 'Text', text: enabled ? ' ON ' : ' OFF '}); expect(badge.props).toMatchObject({ bold: true, color: 'black', backgroundColor: enabled ? 'green' : 'gray',