Where R You?

I'm in more Slack workspaces than I can reasonably keep straight in my head — client workspaces, internal ones, community ones — and every single one of them shows whatever status I last typed, usually hours out of date. Walk to a meeting room, plug into a dock, go home for the day: nothing about my Slack status follows along unless I open half a dozen tabs and retype it half a dozen times.

Where R You? is the fix: a Tauri desktop app that watches where I actually am — Wi-Fi, Ethernet, my calendar — and sets each workspace's status on its own, resolved through a priority ladder I can reorder per workspace. It's macOS-first, built at and for Reaktor, and it isn't released yet — this is the story of building it so far.

What it is

The shape is deliberately the same as Switchboard: a Rust core owns everything that matters — resolution, network reads, calendar polling, Slack calls — and the React webview is a thin, event-driven client that renders already-resolved state and never computes priority itself. The core algorithm is a pure function: workspace, current world (network + calendar), priority order in, one resolved status out — override first if active, then a reorderable middle of one tier per connected calendar plus a locked Wi-Fi unit (BSSID (Basic Service Set Identifier) always beats SSID (Service Set Identifier)) and Ethernet, then a fallback at the bottom. No I/O anywhere in that function, which is exactly why it could be exhaustively unit-tested before a single screen existed. The Overview screen below is that function's output made visible: no override is set, so the ladder falls through to the Work calendar tier, highlighted ACTIVE while Manual override sits on NO MATCH above it — and the result line spells out the whole chain: Working remotely · won by Work calendar.

No override set: Work calendar tier ACTIVE, Manual override on NO MATCH — resolving to "Working remotely," won by Work calendar.

The manual override

The override sits pinned above that entire ladder for a reason — sometimes I know better than any rule, and the app needs to let me say so without fighting it. Setting one targets any subset of connected workspaces at once, with an explicit clear condition instead of a vague "I'll remember to undo this later": a specific time, a duration, joining or leaving a network, the next calendar event, or manual. It always wins until that condition fires, and then it falls back through the ladder rather than going blank. The override shot below shows that same Reaktor workspace a moment later: the card now reads Heads-down — async only, tagged OVERRIDE, counting down "clears in 42 min · at 23:37" — while the priority panel shows exactly why nothing else can touch it: Manual override is highlighted ACTIVE, and every tier below it, including Work calendar, has dropped to STANDBY.

Reaktor overridden to "Heads-down — async only," clearing in 42 minutes — Manual override ACTIVE, every ladder tier below it on STANDBY.

The division of labour

Same as Switchboard: the overwhelming majority of the code is Claude's, and my job is planning, review, and pushing on real hardware. Two things had to be green before any application screen was allowed to exist — the crates/core resolver and the Storybook design system — and only once both gates passed did screens get wired. The resolver hit that bar early: twenty unit tests, zero I/O, done before Phase 4 touched a single pixel.

The one piece that can't be TDD (Test-Driven Development)'d in the normal sense is reading the network itself. Location Services authorization on macOS gates SSID (Service Set Identifier)/BSSID (Basic Service Set Identifier) reads entirely — without it CoreWLAN just returns nothing — so the plan was always to unit-test the Rust-side JSON (JavaScript Object Notation) parsing against fixtures and then validate for real. A few weeks in, on actual hardware, the Location prompt fired and the Swift helper handed back a live read: real SSID (Service Set Identifier), real BSSID (Basic Service Set Identifier), real signal strength, the works. That's the project's one load-bearing assumption — that macOS will actually let an app read this — and it held.

By the time this was written the build sat at 159 commits over about six weeks, all seven application screens (Overview, Rules, Calendar, Networks, Presets, Settings, Activity) matching the design's behavior spec, and 27 committed memory files recording exactly why things are built the way they are.

Borrowed scar tissue

Where R You? didn't start from a blank page — the agent memory explicitly names Switchboard as "the primary template": the CI (Continuous Integration) shape, the signing flow, the git-cliff config, the event-bus-plus-Activity-log pattern, and the persistent-memory convention itself all came over wholesale. One bug came over pre-fixed rather than pre-discovered: both a debug and a release build carry the same bundle identifier, so without doing anything about it they'd share one on-disk config folder. Switchboard hit that the hard way — a healthy tauri dev session kept quietly resetting a crash guard that then never tripped in production. Where R You? split debug builds into their own subdirectory of the app-data folder from day one, instead of re-discovering the same failure mode a second time.

Two bugs every automated layer missed

The priority ladder is the whole point of the app, and for a while it silently didn't work. It used native HTML5 drag-and-drop — draggable, dataTransfer, onDrop — which reorders happily in a Chromium browser preview and in jsdom, and does nothing in macOS's WKWebView. No error, no console warning, the row just snaps back. Every automated layer passed: the design-fidelity screenshots were Chromium, the component tests were jsdom, and the Playwright suite mocks the Tauri bridge rather than driving a real window — tauri-driver has no WebDriver support on macOS at all, so there is structurally no Tier 3 here. Only a manual pnpm tauri dev session would have caught it, and for a while nobody ran one. The fix (ReorderList, pointer events via Motion instead of the browser's native drag) shipped with a permanent gate that fails the test suite outright if raw HTML5 drag-and-drop reappears anywhere in the codebase — and, in the interest of the same honesty Switchboard's post led with: as of the last progress note, the keyboard-driven reorder path still hasn't been confirmed by hand in a real window. It's correct by construction and covered by unit tests, which per the project's own rules is not the same thing as verified.

The second bug was quieter: the Activity log's timestamps were hardcoded to 24-hour time via toLocaleString(), which reads the OS (Operating System) locale rather than the app's own Settings → Localization panel. Invisible on a 24-hour dev machine, wrong the moment someone on a 12-hour locale opened the log. Fixed with a <DateTime /> component that actually reads the setting, plus — same pattern as the drag-and-drop fix — a standing test that fails the suite if a raw toLocaleString() sneaks back into a view. The Activity view below shows the same 24-hour convention it was fixed to respect: a newest-first log — override set from the focus preset, the network switching to Office-5G, Dev Community pausing, the ladder resolving Reaktor to "Working remotely" — every row timestamped HH:MM:SS because that's what Settings → Localization actually says, not whatever the OS (Operating System) happens to default to.

Five events, newest first: an override set from the focus preset, a network change, and the ladder resolving Reaktor to "Working remotely" — all timestamped from Settings, not the OS (Operating System).

Every one of those Activity rows comes from the same place: one event bus that every mutation broadcasts into, whether it was triggered by the UI (User Interface), the tray, or eventually the CLI (Command-Line Interface). That's also why the timestamp bug was so easy to fix everywhere at once — there's exactly one row-rendering path for it to live in.

Status

Actively developed, not released — the three-step connect flow below is still what greets a fresh install today: connect Slack, optionally connect Google Calendar, then create the first rule, gated until step one is done. The resolver, the token-based design system, the event/command bus and all seven screens match the behavior spec; the wru CLI (Command-Line Interface) parses every command but doesn't yet talk to a running app over its local socket; and Slack/Google OAuth (Open Authorization), disk persistence, and Developer ID signing are still ahead — Slack's OAuth (Open Authorization) flow in particular needs a decision about how a desktop binary avoids shipping a client secret before that phase can start.

First run: step 1, connect Slack, is the only one live — step 3, create your first rule, stays greyed out until it's done.

Full feature rundown on the project page.